How can i replace jquery version of a page with greasemonkey?
Im trying to test a site with a newer version of jquery but i dont have a dev enviroment.
Making a dev environment, using source-control and a written release checklist will save you much grief (and is a requirement for most paid work).
~~~
The best way to replace the jQuery version on an existing page (that you don't control) is to:
<script>
node, containing the jQuery version you want, that loads before any subsequent scripts that use jQuery.Unfortunately, Greasemonkey cannot do #1. It can only tamper with the page's native jQuery after it has loaded -- An approach that might work, but that slows the page and risks race conditions.
So, I recommend a two-tool solution:
Make sure your Firefox has the Adblock add-on (which is an excellent add-on to use irregardless) and/or the RequestPolicy add-on.
Use Adblock or RequestPolicy to temporarily block the old jQuery.
For example, for StackOverflow, block http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js
. (Note that RequestPolicy allows you to limit URL blocking to only a specific site.)
Then use Greasemonkey to load the desired version of jQuery (which it can do irregardless of Adblock or RequestPolicy settings) at the beginning of the page.
For example, this script will upgrade Meta SO's jQuery to 1.6.2, in conjunction with the block from step 2:
// ==UserScript==
// @name _upgrade jQuery
// @include http://meta.stackexchange.com/questions/*
// @run-at document-start
// ==/UserScript==
/*--- Important!
(1) We need another add-on, besides Greasemonkey, to
disable the old, undesired script.
(2) The DOM is not available yet
(@run-at == document-start).
(3) We cannot use a loop to check for the DOM because
loading is halted while the loop runs.
(4) setTimeout() and setInterval() are not fast enough due
to minimum interval clamping. By the time they detect
the DOM, scripts that we need to precede may have
loaded.
(5) Therefor, we use a "set Zero Timeout" function as
explained by David Baron at
http://dbaron.org/log/20100309-faster-timeouts .
(6) By the time FF reports that the `document.head` is
available, several head elements have loaded!
(Is this a bug?)
That means that if any dependent scripts are loaded
before we can inject our jQuery version, then we must
also reload those dependent scripts.
*/
////// setZeroTimeout() implementation: BEGIN
/*--- Only add setZeroTimeout to the window object, and hide
everything else in a closure.
*/
( function () {
var timeouts = [];
var messageName = "zero-timeout-message";
/*--- Like setTimeout, but only takes a function argument.
There's no time argument (always zero) and no arguments.
You have to use a closure.
*/
function setZeroTimeout(fn) {
timeouts.push(fn);
window.postMessage(messageName, "*");
}
function handleMessage(event) {
if (event.source == window && event.data == messageName) {
event.stopPropagation();
if (timeouts.length > 0) {
var fn = timeouts.shift();
fn();
}
}
}
window.addEventListener ("message", handleMessage, true);
// Add the one thing we want added to the window object.
window.setZeroTimeout = setZeroTimeout;
})();
////// setZeroTimeout() implementation: END
/*--- Now wait for the DOM and then add our version of jQuery,
first thing.
*/
function SearchForDOM () {
var targetNode;
if (typeof document.head == "undefined")
targetNode = document.querySelector ("head, body");
else
targetNode = document.head;
if (targetNode) {
var scriptNode = document.createElement ("script");
scriptNode.src = 'http://ajax.googleapis.com/ajax/'
+ 'libs/jquery/1.6.2/jquery.min.js';
targetNode.appendChild (scriptNode);
/*--- By the time FF reports that the head element is
available, a key dependent script has loaded!
So, we reload it here, so that it can run with jQuery
available.
*/
var scriptNode = document.createElement ("script");
scriptNode.src = location.protocol
+ '\/\/' + location.host
+ '/content/js/stub.js?v=49f661361016';
targetNode.appendChild (scriptNode);
}
else
setZeroTimeout (SearchForDOM);
}
SearchForDOM ();
Note: that due to limitations of JS, GM, and Firefox, it may be necessary to reload scripts that depend on jQuery too. (This is the case for Meta Stack Overflow, at the moment.)
I know you're asking about jQuery and Greasemonkey for doing this, but let me off a completely different alternative, Fiddler.
If you're testing a site out with a new version of jQuery and you actually want to test any breaking changes, etc...you want to test the site as it will be, whereas a JavaScript (greasemonkey)-based after-the-fact replacement isn't an accurate simulation.
With fiddler you can replace the jQuery file that the browser requests (without the browser knowing). This way you're actually testing it completely, you're straight up dropping the new version in the page, no in-page JS tricks which may not be at all accurate to get there (handlers already hooked up, load order, etc.)
Eric Law (creator of Fiddler) has an excellent blog post on how to do exactly this:
Swapping out JQuery with Fiddler
The post was written for swapping out the minified version with the full version (also handy!) or a newer version for debugging, be sure to note both uses...they're both very useful in saving some debug/testing time.
Not sure about Greasemonkey, but an easy approach to modify any resource on (url, image, script) would be using Requestly Redirect Rule.
Note:
PS: I work at Requestly.
What didn't work for my case, seems to suit this question, no blocker is required. In GreaseMonkey:
// ==UserScript==
// @name alternative jquery
// @namespace ben@host
// @description intercept
// @include https://somehost.com/*
// @grant GM_getResourceText
// @resource jquery_new jquery_new.js
// @run-at document-start
// @version 1
// ==/UserScript==
var jquery_source = GM_getResourceText( 'jquery_new' );
console.log('jQuery Lenght =',jquery_source.length);
window.addEventListener('beforescriptexecute',
function(e){
if (e.target.src &&
e.target.src.search('/1.12.4/jquery.min.js') >= 0 ) {
console.log('intercepted: '+e.target.src);
e.preventDefault();
e.stopPropagation();
window.eval(jquery_source);
}
});
Note: downside - beforescriptexecute
fires after the original script is retrieved and ready to be executed, so should the original external js file failed to load, this approach will not work.