While not that specific issue, the issue described here - https://wordpress.org/support/topic/select2-conflicting-with-acf-v5 is basically my problem as well.
A wordpress site has two wordpress plugins, one uses select2 version 3.5 to handle all select elements, the other (included in a plugin I wrote) uses 4.0 to handle some specific ones. This result in an error of
Error: select2.min.js?ver=3.4.5:21 Uncaught Error: Option 'multiple' is not allowed for Select2 when attached to a element.
Is there any possible solution to this kind of problem? Should I just try to detect that 3.5 is loaded and try to avoid conflicting settings? maybe try to have "my" select2 to load first and hope that since I am more specific, there will not be conflicts with the code that uses 3.5? (my knowledge of jQuery internals is lacking, what does happen when different plugins try to use the same name?)
I am facing the exact same issue with my plugin Popup Maker. I found this select2 multiple versions on same page/site that may be useful, the only issue I forsee there is that we can't always control the order plugins load their JS using wp_enqueue_script.
Ok so what I did was made my script require select2 (DUH), then just inside my scripts enclosure I did something like this.
$.fn.pumSelect2 = $.fn.select2;
Then later instead of calling .select2() I use .pumSelect2().
This works great for my situation which is that I use v4, others use v3.5, some even bootstrap it into their own admin.js file so it loads even though select2 is already loaded.
So all in all its a hack but since we are doing it the right way by loading using wp_enqueue_script.
I also dequeue select2 if its enqueued.
// Deregister older versions, loaded by Types, Advanced Custom Fields etc.
if ( wp_script_is( 'select2', 'registered' ) ) {
wp_deregister_script( 'select2' );
}
wp_register_script( 'select2', $js_dir . 'select2.full' . $suffix, array( 'jquery' ), '4.0.1' );
`
Final Method For Now
After further testing and since so many other plugins use old versions with customizations that will likely break this is what I came up with.
In my select2.full.js file
For the opening section that loads jQuery and calls factory
(function (factory) {
var existingVersion = jQuery.fn.select2 || null;
if (existingVersion) {
delete jQuery.fn.select2;
}
if (typeof define === 'function' && define.amd !== undefined && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node/CommonJS
factory(require('jquery'));
} else {
// Browser globals
factory(jQuery);
}
jQuery.fn.pumselect2 = jQuery.fn.select2;
if (existingVersion) {
delete jQuery.fn.select2;
jQuery.fn.select2 = existingVersion;
}
}(function (jQuery) {
Basically I bootstrap it to a new name in this case pumselect2, and if there was previously a loaded version I restore it, otherwise I wipe the slate so that if another is loaded no conflict arises.
Also I modified my wp_enqueue_script handle to be pumselect2 so that it gets loaded even if someone else loads select2.
Hope that helps.
For WordPress plugin authors using select2 my answer here: Jquery Select2 plugin version check could provide some guidance on version checking.
Related
I want to add JS and CSS files to back office in my module. But I get error: Attempted to call an undefined method named "registerStylesheet" of class "AdminModulesController".
I've seen other posts (like this Show my module JS at footer in prestashop) or here https://devdocs.prestashop.com/1.7/themes/getting-started/asset-management/
So I want to avoid addJS() function as this is depreciated. But when I try to use $this->context->controller->registerStylesheet() and $this->context->controller->registerJavascript() I get the above error.
This is my whole hook code:
public function hookActionAdminControllerSetMedia($params)
{
$this->context->controller->registerStylesheet(
'mb_pages_content',
'modules/'.$this->name.'/styles/admin.min.css'
);
$this->context->controller->registerJavascript(
'mb_pages_content',
'modules/'.$this->name.'/js/admin.js'
);
}
I've checked what kind of thing is my: $this->context->controller
but it doesn't indeed have registerStylesheet() and registerJavascript() methods. What am I missing? i do everything exactly as described everywhere in the internet, why do I get the error?
The explanation of which methods to use:
These are FrontController methods in PrestaShop 1.7: registerJavascript and registerStylesheet.
These are legacy (deprecated) FrontController methods in PrestaShop 1.7: addJS and addCSS.
These are AdminController methods in PrestaShop 1.7, 1.6, 1.5: addJS and addCSS.
So, the correct example to add a JS and a CSS files for a back-office (i.e. for AdminController) via a module class is:
public function hookActionAdminControllerSetMedia($params)
{
// Adds your's CSS file from a module's directory
$this->context->controller->addCSS($this->_path . 'views/css/example.css');
// Adds your's JavaScript file from a module's directory
$this->context->controller->addJS($this->_path . 'views/js/example.js');
}
For an additional information see my yet another answer how to register JavaScript in a back-office (in admin pages). I have updated it after this question.
Try with :
$this->addJs(
_PS_MODULE_DIR_ .'objet/views/js/feature.js',
'all'
);
$this->addCss(
_PS_MODULE_DIR_ .'objet/views/css/feature.css',
'all'
);
Regards
I need to configure angular bootstrap tooltip (uibTooltip) to be disabled for mobile devices when using angular-bowser for device detection.
This could be done simply by:
isMobile = _bowser.mobile
$uibTooltipProvider.options = { trigger: isMobile ? "none" : "mouseenter" }
Problem: $uibTooltipProvider is a provider and bowser is a service.
I have to use $uibTooltipProvider in a config function while I can't use bowser service in a config function. And neither I can use $uibTooltipProvider in a run function where I can use bowser
I have already tried overriding the $get function as they suggest here but the "ontouchstart" event in $window does not apply for tablets where I want to keep tooltips enabled.
Is there any way I can get a workaround this?
I finally decided to inject a small css modification in run time. I had the three following options:
1. Use that hack on GitHub: I did not like the fact that in order to make it work I had to empirically (by placing a breaking point) find out which were the actual services injected in the $get function of the uibTooltipProvider. (see there is a difference between the services injected in the github thread and the ones I had to inject (see code snippet)
2. Add the bowser library and use it statically: I did not like this option because we are already using angular-bowser as a dependency for our DeviceDetector service, so we would be using the same library twice: one statically to configure the tooltip options and one in runtime for everything else.
3. Inject a small css modification (the option I chose):
public disableTooltipsForTouchScreen(): void {
if ( this._deviceDetector.isMobile() || this._deviceDetector.isTablet() ) {
let styleSheet = document.createElement("style");
styleSheet["innerHTML"] = ".tooltip { display: none; }";
document.body.appendChild(styleSheet);
}
}
And if we ever need to have a finest control over the bootstrap-tooltip configuration then I will consider option 2.
server side background, getting deeper and deeper into client side.
I've got a site with a lot of legacy that I'm new to, and I'm just trying to get a handle on how things are working/what's available.
Is there a way to have jquery tell me(for a page/pages) all its current info and any plugins it can/is use/ing, similar to what phpinfo does?
Some proof of concept how you can get names for all plugins
var plugins = (function() {
var plugins = [];
for(var plugin in jQuery.fn) {
plugins.push(plugin)
}
return plugins;
}());
var filterValue = ['constructor', 'init', 'add', 'parents'] // // you must add all standard methods here
filterValue.forEach(function(value) {
var position = function(value) {
return plugins.indexOf(value);
}
while(position(value) >= 0) {
plugins.splice(position(value), 1)
}
})
console.log(plugins)
You can use the following for jQuery
console.log( jQuery.fn.jquery );
To answer your question directly, No jQuery does not have a mechanism that lists installed plug-ins.
jQuery does not keep such a registry of installed plugins. The jQuery plugin mechanism is to just add a method to the jQuery prototype (right along-side all the other jQuery methods). So, there's no separate list of which methods have been added by some outside agent (e.g. a plug-in). In addition, there's no one-to-one correspondence between methods and a particular plug-in as a single plug-in could add multiple methods.
It would be possible to create a master list (for any specific jQuery version) of what methods are there by default and then enumerate a jQuery object to find out which methods have been added since then, but you'd have to create that master list ahead of time and store it or create the master list immediately after jQuery was loaded before any plug-ins were loaded.
You can always test to see if any specific jQuery plug-in is loaded by just checking to see if any of its signature methods are available on a jQuery object.
So, if you really just wanted to know which of 10 plugins happen to be available in any given page, you could write a quick function that would test for each of the 10 plugins (by looking for the existence of known methods in those plugins) and would return a list of the ones installed, but this has to be done with specific knowledge of each plugin as there is no "generic plugin identification mechanism" since a plug-in is nothing more than a piece of code that adds methods to the jQuery prototype. It doesn't actually have any identity of its own.
I'm learning the module pattern for javascript in order to tidy up my code and reduce the need for a long 'global' javascript file.
As a consequence of this, I have a top level 'namespace' module, and a utility module in the same file. The utility module has some functions that require jquery, and others that do not.
On lightweight pages that use no jQuery, I don't want to load the library (I have a very good reason for not doing so).
The problem arises when jQuery is passed as a parameter to the module as in the following:
MODULE.vars = (function (variables,$) {
variables.cdn = undefined; //global for clientside cdn
variables.version = undefined; //global for clientside cdn version
variables.isTouchScreen = undefined; //global for touchscreen status
//Check if jquery exists, if so patch the jquery dependent functions
if ($) {
$(document).ready(function () {
variables.isTouchScreen = $('html').is('.touch');
});
}
return variables;
}(MODULE.vars || {}, jQuery));
The code stops on pages that I don't load jquery, stating that jQuery is undefined - fair enough. If I change the last line to:
}(MODULE.vars || {}, jQuery || false));
the code still complains that jQuery is undefined. I thought, perhaps erroneously, that if jQuery was undefined, it would be passed as undefined in this instance and instead take up the value false (which logic dictates wouldn't be necessary anyway).
How do I get around this problem when jQuery may or may not be present? I attempted to put the following at the top of the script:
var jQuery = jQuery || false;
thinking that this would then take up the value of jQuery if it was loaded. It works in modern browsers with this, but IE8 complains as it gets set to false even if jQuery is being loaded first on a page.
The scripts are all loaded in the correct order in the html, jQuery first, then my module afterwards.
When checking for the cause, IE8 returns $ as an object and jQuery as false. If i do not set the above line in the script, jQuery returns as the same object as $.
Sadly I have to cater for IE8 as well, so how do I get around this issue of the optional presence of jQuery?
Edit: This is only a snippet of the module and there are other functions that depend on jquery, but simply won't get implemented if jquery is not available
I seem to have found an answer that works after I worked out how to implement elanclrs suggestion - I put the following at the top of my modules:
var jQ = jQ || false;
if (typeof jQuery !== 'undefined') {
jQ = jQuery;
}
Then in my module, I pass jQ instead of jQuery.
The reasoning behind the answer was pointed at in this question: Error when passing undefined variable to function?
Edit: Details updated to add differences in Firefox/Chrome behavior
I am trying to create a bookmarklet that will load both jQuery and jQueryUI. The jQuery load uses javascript, but I figured since jQuery was loaded I'd go ahead and use it for the UI loading. More than getting it to work I really want to understand why this doesn't work. I'm still wrapping my head around scope/closures/etc. But I just don't see why in firefox $ doesn't work but "jQuery" does! The $ works fine in Chrome but I get a DIFFERENT issue there.
Notes:
1) In FireBug/FireFox I get '$("head") is undefined'
2) In Chrome the "$" works fine, but the jQueryUI call fails with Object [object Object] has no method 'dialog'
3) the callback guarantees jQuery is loaded by the time I try to use it. In Firefox if I replace "$" with "jQuery" ( such as jQuery("head) ) then the code works!.
4) there are no other libraries on the page already using $
5) Even more frustrating, in Firefox if I just give in and use "jQuery" rather than "$" and then set the callback from $("#jquilib").load() to call a third function, jQueryUI functions such as .tabs() and .dialog() are unavailalble even though the callback itself was triggered by jQueryUI being available!
6) In Chrome the jQueryUI issue goes away if I use setTimeout() to 100ms. If I go down to 1ms or something than the issue persists.
I am using the getScript function from this post: http://www.learningjquery.com/2009/04/better-stronger-safer-jquerify-bookmarklet.
Below is my code:
function getScript(url,success){
var script=document.createElement('script');
script.src=url;
var head=document.getElementsByTagName('head')[0],
done=false;
// Attach handlers for all browsers
script.onload=script.onreadystatechange = function(){
if ( !done && (!this.readyState
|| this.readyState == 'loaded'
|| this.readyState == 'complete') ) {
done=true;
success();
script.onload = script.onreadystatechange = null;
head.removeChild(script);
}
};
head.appendChild(script);
}
function initializejQueryUI(){
if (typeof jQuery.ui == 'undefined'){
// jQueryUI library & jQueryUI cupertino theme
$('head').append("<link href='https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/themes/cupertino/jquery-ui.css' type='text/css' rel='stylesheet'>");
$('head').append("<script id='jquilib' type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js'></script>");
}
$("#jquilib").load($("<div>jQuery & UI Loaded!</div>").dialog());
}
getScript('https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js', initializejQueryUI); // call initializejQueryUI as callback when jQuery loads
Ok, figured it out after a lot of trial an error. I went ahead and continued developing my script in Chrome seems it seemed I got further than in Firefox. When I completed my bookmarklet I tried it on a whim in Firefox and it worked there too! Here's what I learned:
1) The $("#jquilib").load($("<div>jQuery & UI Loaded!</div>").dialog()); call doesn't work because jQuery removes elements added via append from the DOM after processing the script! It was easier just to re-use the getScript() function to also get jQueryUI and put the alert in a function called from the callback. The tab creation issue I encountered (item #5 in the question above) was a result of this quirk.
Reference: http://api.jquery.com/append/ Search for Karl Swedberg saying "yes, it's normal"
2) Firebug seems to make use of the "$" while in the console, leading a situation like my description above where "$" doesn't work but jQuery() does work. There seem to be some rules governing when it releases the "$" because if i just try running the script again jQuery's $ shortcut suddenly works. This was the most frustrating part because it made it appear like a scope and/or timing issue!