Greasemonkey comes in incredibly handy (especially when your job forces you to use ill-conceived internal web applications). However, ever since I tinkered around with jQuery, I can’t imagine trying to grease a page without that functional goodness. I therefore decided to use jQuery in my Greasemonkey scripts, and that turned out to be just a little more complicated than I thought it would be.
The first thing that pops up in Google is Joan Piedra’s solution, where he provides a userscript that downloads jQuery from jquery.com every time. That’s not exactly the solution I had in mind since I didn’t want to download jQuery every time the script ran, so I searched some more and found this post by Sean Catchpole about how to patch jQuery to get rid of a loading problem. I downloaded his userscript and everything seemed to work great for me until I tried to use siblings(), children(), or parents(). Then Firefox would complain about ‘jQuery’ not being defined and script would halt.
I searched through the code and found where these functions are being declared:
jQuery.each({
parent: "a.parentNode",
parents: "jQuery.parents(a)",
next: "jQuery.nth(a,2,'nextSibling')",
prev: "jQuery.nth(a,2,'previousSibling')",
siblings: "jQuery.sibling(a.parentNode.firstChild,a)",
children: "jQuery.sibling(a.firstChild)"
}, function(i,n){
jQuery.fn[ i ] = function(a) {
var ret = jQuery.map(this,n);
if ( a && typeof a == "string" )
ret = jQuery.multiFilter(a,ret);
return this.pushStack( ret );
};
});
Each of these pairs of function names and strings is passed to the map function:
map: function(elems, fn) {
// If a string is passed in for the function, make a function
// for it (a handy shortcut)
if ( typeof fn == "string" ) {
fn = new Function("a","return " + fn);
}
var result = [];
// Go through the array, translating each of the items to their
// new value (or values).
for ( var i = 0, el = elems.length; i < el; i++ ) {
var val = fn(elems[i],i);
if ( val !== null && val != undefined ) {
if ( val.constructor != Array ) val = [val];
result = result.concat( val );
}
}
return result;
}
The script was crashing on the line that declares the new Function object inside the first if-statement. 4 out of 5 of those functions above reference the jQuery object. I couldn’t find out why the jQuery object didn’t exist in the scope of the new Function object, so I just gave up on that and started trying different but functionally equivalent replacements for that line of code. I finally found something that worked:
fn = eval("function(a) { return " + fn + " };");
It turned out that I really didn’t need those functions for what I was working on, but it’s still nice to know that jQuery now works completely (as far as I know) with Greasemonkey.