I'm trying to handle a conflict between XenForo and an essential plugin. Neither is code I can directly modify. jQuery 1.11.3 is the version in use, and that can't be upgraded either.
I can wrap the calling function with a try/catch and avoid the conflict, but the real fix would involve wrapping jQuery's isArrayLike with a try/catch.
isArrayLike is in the (current version) jQuery source at line 464 in https://github.com/jquery/jquery/blob/master/src/core.js#L464
What I need to catch seems to be exactly what was being debated in https://forum.jquery.com/topic/jquery-isarraylike-for-consistency with regard to isArrayLike throwing errors when passed invalid types.
So, since jQuery's isArrayLike isn't exposed, is there any way to either wrap it, or worst case, get in there and replace it?
Edit: Note, all the comments so far are trying to debug the error itself. That is not my question. I am asking if there is any way to access isArrayLike, so it can be wrapped with another function.
I would suggest replacing the jQuery each and map methods, which seem to be the only methods calling on isArrayLike, except for makeArray. But the fix I am suggesting is not necessary for the latter method.
Add this:
(function ($, origEach, origMap) {
$.each = function (elems, callback, arg) {
return origEach.call(this, Object(elems), callback, arg);
};
$.map = function (elems, callback, arg) {
return origMap.call(this, Object(elems), callback, arg);
};
})(jQuery, jQuery.each, jQuery.map);
It alters the first argument that is passed to map or each: it gets wrapped in an Object call, which changes nothing when it is an array, but turns the argument in an object if it is not an object (for some odd reason).
Patching jQuery
If you can use a patched version of jQuery, just store a copy of jQuery, and modify this line in the function isArrayLike:
var length = !!obj && "length" in obj && obj.length,
to:
var length = !!obj && "length" in Object(obj) && obj.length,
It should not be a problem for XenForo, as long as you explicitly add the patched jQuery via another script tag, after having included XenForo. The latest jQuery object overwrites the previous one, so XenForo will then also use the patched version, even though it included the non-patched jQuery library itself.
The downside of patching is that you cannot upgrade jQuery (implicitly with a XenForoupgrade) unless you patch it every time you upgrade (until the version where it is no longer necessary).
That is a problem you won't have with the first solution.
Related
I'm trying to update some old code someone left before, at some point, it made a call to the attr function of an old 3.3.1 JQuery like this:
$("#myiframe").attr("src", url, function () {
$(window).on("unload", function () {
// Some function stuff
});
});
My problem is than I want to know what exactly this does (I already know what attr does when it has 2 parameters, but not 3) before adapt it to a newer version, but when I search about the .attr function in JQuery, all I found is with only two parameters, not 3 like this, I don't know if is properly a callback function (unlikely, because none of the parameters this function accepts is a callback function), or other thing.
The JQuery file is the one served by googleapis, so is not likely it has been specially modified for this.
Please, can someone explain me what it does?
No signature of attr() accepts 3 arguments. The last argument is redundant. Even if it did serve a purpose, putting a window.unload event handler in there would not do anything useful.
Regards your comment under the question:
Why doesn't it launch a fatal error for the number of arguments?
It's because JS is a very permissive language. You can pass as many arguments to a function as you like, JS will ignore any extras and only bind those which you defined in the function definition (up to 2 in this case using attr()). Although note that it's still possible to retrieve all arguments that were used in the function invocation using the arguments keyword
I'm writing a plugin based on this handy template for class-based CoffeeScript jQuery plugins: https://gist.github.com/rjz/3610858
Everything works pretty well. However, there is some unexpected behavior at the end when I register the plugin:
$.fn.extend markdownAsides: (option, args...) ->
#each ->
$this = $(this)
data = $this.data('markdownAsides')
if not data?
$this.data 'markdownAsides', (data = new MarkdownAsides(this, option))
if typeof option is 'string'
data[option].apply(data, args)
data # Plugin breaks without this line
Before I added that final line (a solution I discovered purely on accident), the initial construction of the plugin worked fine, but on successive method calls, the jQuery each loop sometimes failed to iterate through every element.
Checking this.size() outside the each loop returned the correct value, and checking individual elements outside the loop also looked fine. But inside the loop, elements would sometimes be skipped, in a pattern I could not discern.
Like I said, the problem is fixed by adding the final line. (Perhaps the return value of the function being passed to each matters somehow?) My question isn't "how do I fix this?" but "why does this work?"
Returning false from the callback function passed to each will break out of the loop. I haven't verified but perhaps jQuery will also break on any falsey value except undefined.
Since in CoffeeScript there's an implicit return, you were possibly returning something falsey or even false from the callback depending on the operation performed in it.
To avoid any issues, just change data for true at the end.
I've got an array, and it's got a method I threw onto it called add, which I use as a wrapper around push. I've found myself using push a few times when I should have used add, and now I'm thinking it would be nice to assign a reference to my add method to the array's native push. Thus, calling push on the array would call add.
Do internals depend on externally available native methods like push? How would this affect compatibility? Is this a bad idea? If so, why?
Some code:
PR.state = {
views: []
};
_.extend(PR.state.views, {
add: function(view) {
var parent = view.parent;
if ((!this.length && view instanceof PR.Views.App) || (parent && _.contains(this, parent)))
this.push(view);
}
});
// I am thinking:
PR.state.views.push = PR.state.views.add;
I would strongly advise against changing the behavior of a standard array method. If you really want a custom method, then just create a new method and give it it's own unique name and use that.
Changing the behavior of existing methods could have all sorts of bad consequences:
Incompatibility with code retrieved from any other source.
Creates a non-standard and unexpected implementation if anybody else ever works on this project. This is like adding in a time bomb to trip up some future developer.
Training yourself to use your own custom method instead of .push() is just something that a decent developer would do. Just do it.
Creating a newly named method with an appropriate and descriptive name improves the readability, understandability and maintainability of your code. Replacing an existing method with something that works differently does the opposite.
It's not so bad if you just replace the method on one instance of an array, not the whole array prototype, but it's still not a good idea.
What a stupid question. If I replace push with add, then what happens when I call push from add? :< :< I haven't tested it, but I suspect that while Array.prototype.push will still be available, unless I use Array.prototype.push explicitly, calling add will result in a mondo endless loop.
Excuse me first. because i don't know this is question is valid or not. i if any one clear my doubt then i am happy.
Basically : what is the different between calling a method like:
object.methodname();
$('#element').methodname();
calling both way is working, but what is the different between, in which criteria make first and second type of methods. is it available in the core javascript as well?
In case if i have a function is it possible to make 2 type of method call always?
Can any one give some good reference to understand correctly?
Thanks in advance.
The first syntax:
object.methodName();
Says to call a function, methodName(), that is defined as a property of object.
The second syntax:
$('#element').methodname();
Says to call a function called $() which (in order for this to work) must return an object and then call methodname() on that returned object.
You said that "calling both way is working," - so presumably you've got some code something like this:
var myObject = $('#element');
myObject.methodname();
This concept of storing the result of the $() function in a variable is commonly called "caching" the jQuery object, and is more efficient if you plan to call a lot of methods on that object because every time you call the jQuery $() function it creates another jQuery object.
"Is it available in the core javascript as well?" Yes, if you implement functions that return objects. That is, JS supports this (it would have to, since jQuery is just a JS library) but it doesn't happen automatically, you have to write appropriate function code. For example:
function getObject() {
return {
myMethod1 : function() { alert("myMethod1"); return this; },
myMethod2 : function() { alert("myMethod2"); return this; }
};
}
getObject().myMethod1().myMethod2();
In my opinion explaining this concept in more depth is beyond the scope of a Stack Overflow answer - you need to read some JavaScript tutorials. MDN's Working With Objects article is a good place to start once you have learned the JS fundamentals (it could be argued that working with objects is a JS fundamental, but obviously I mean even more fundamental stuff than that).
The difference is very subtle.
object.methodname();
This is when JavaScript has the object at hand.
$('#element').methodname();
If you are using jQuery, you are asking jQuery to select the object that has the id of #element. After that you invoke the method on the selected object.
I was previously using the jQuery autosave plugin but have recently removed it. Some code is still trying to use this function, so how can I temporarily extend the jQuery object so there is an empty function to prevent errors until I can get through all of the code that is trying to use the function?
The plugin is called in this way:
jQuery().autosave.restore();
jQuery().autosave.save();
I think those are the only two functions that exist, so it would be OK to create two empty functions, but is there a way to create a catch-all function on this type of object?
Note
This is a temporary solution until I can go through a lot of code. I do believe the question is a valid coding question even if you think this workaround is not ideal (it isn't).
There is a way to do this. You can create a dummy plugin (check out jQuery's documentation for creating plugins):
(function( $ ){
$.fn.autosave = {
restore: function() {};
save: function() {};
};
})( jQuery );
I would highly recommend against doing this, however. Instead, you should look at those errors and fix them, i.e., stop your code from using them. Otherwise you're simply hiding the problem.
Nope. Standard JavaScript does not support "catch-all" methods.