Orderly require scripts from a bookmarklet

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.

Posted in JavaScript

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>