Abstracting a bit my bookmarklet for injecting jQuery, I’ve come to this one where you can orderly require needed scripts before executing a given payload.
javascript:(function () {
var debug = !false;
var requested = false;
orderly(
require_jQuery('1.8.3', 'j'),
require_SimpleModal('j'),
function() {
payload(window.j);
}
);
function payload(jQuery) {
jQuery(function($) {
$('body').click(function() {
$('span:first').modal({
overlayClose: true,
opacity: 80,
overlayCss: {backgroundColor: "#000"},
maxHeight: 480,
maxWidth: 640,
containerCss:{
backgroundColor:"#fff",
borderColor:"#fff",
padding:20
}
});
});
});
}
function require_jQuery(version, symbol) {
return {
url: 'http://ajax.googleapis.com/ajax/libs/jquery/' + version + '/jquery.min.js',
is_loaded: function() {
return !requested && typeof window[symbol] != 'undefined' && window[symbol].fn.jquery == version
|| requested && typeof window.jQuery != 'undefined' && window.jQuery.fn.jquery == version;
},
before: function() {
window.oldJQuery = window.jQuery;
window.jQuery = window.undefined;
},
success: function() {
jQuery.noConflict();
window[symbol] = jQuery;
console.info('jQuery ' + jQuery.fn.jquery + ' has been injected. (as "' + symbol + '")');
},
ensure: function () {
window.jQuery = window.oldJQuery;
}
};
}
function require_SimpleModal(jQuerySymbol) {
return {
url: 'http://cdn.jsdelivr.net/simplemodal/1.4.2/jquery.simplemodal.1.4.2.min.js',
is_loaded: function() {
return !requested && typeof window[jQuerySymbol].modal != 'undefined'
|| requested && typeof window.jQuery.modal != 'undefined';
},
before: function() {
window.oldJQuery = window.jQuery;
window.jQuery = window[jQuerySymbol];
},
ensure: function () {
window.jQuery = window.oldJQuery;
}
};
}
function orderly(i) {
var stuff = Array.prototype.slice.call(arguments);
if (typeof i === 'number') {
stuff.shift();
}
else {
i = 0;
}
if (i == stuff.length) return;
console_log('orderly ' + i);
switch (typeof stuff[i]) {
case 'string':
break;
case 'function':
stuff[i]();
orderly.apply(null, [i+1].concat(stuff));
break;
case 'object':
load_script(stuff, i);
break;
default:
throw 'Expected a valid argument (' + i + ')';
break;
}
}
function load_script(stuff, i) {
var current = stuff[i];
var url = current.url;
console_log('load_script: request for ' + url);
requested = false;
var is_loaded = current.is_loaded;
if (is_loaded()) {
console_log('load_script: already available');
orderly.apply(null, [i+1].concat(stuff));
return;
}
var before = current.before;
var success = function() {
call_function(current.success);
call_function(current.ensure);
orderly.apply(null, [i+1].concat(stuff));
};
var failure = function() {
call_function(current.failure);
call_function(current.ensure);
};
call_function(before);
console_log('load_script: requesting');
var s = document.createElement('script');
s.setAttribute('src', url);
document.getElementsByTagName('head')[0].appendChild(s);
requested = true;
var time = 0;
var id = setInterval(function () {
console_log('load_script: retry #' + time);
if (is_loaded()) {
clearInterval(id);
console_log('load_script: done');
success();
return;
}
++time;
if (time == 50) {
clearInterval(id);
console_log('load_script: giving up');
failure();
return;
}
console_log('load_script: waiting');
}, 100);
console_log('load_script: requested');
}
function call_function(f) {
return typeof f == 'function' ? f() : null;
}
function console_log(msg) {
if (debug) console.log(msg);
}
})();
The require_* functions could be made available from a central repository, so that you can find, copy and paste what you need. But going a bit forward, one could also make orderly support URIs like ‘ord://symbol:module/version’ or ‘ord://symbol:host/module/version’ like ‘ord://j:jquery/1.8.3′ or ‘ord://j:jquery/modal/1.4.2′. They could be used like this:
orderly(
'ord://j:jquery/1.8.3',
'ord://j:jquery/modal/1.4.2',
function() {
payload(window.j);
}
);
and because they are strings, they would not interfere with objects and functions. Execution will then be in two steps: first, request the definition object from the repository and, finally, request the script as specified by the definition object. This will be very similar to what Bundler does for Ruby.