technology and zen of life

“A heisenbug (named after the Heisenberg Uncertainty Principle) is a computer bug that disappears or alters its characteristics when an attempt is made to study it.”

$.getScript() and Firebug: But then, who was code?

If you’ve ever tried to import javascript via jQuery.getScript(), you might be rendered unable to actually debug the fetched js in firebug or similar debuggers. The reason is simple and lies in how jQuery implements getscript :

getScript: function( url, callback ) {
        return jQuery.get(url, null, callback, "script");
}

where get itself is implemented as :

get: function( url, data, callback, type ) {
        // shift arguments if data argument was omited
        if ( jQuery.isFunction( data ) ) {
                type = type || callback;
                callback = data;
                data = null;
        }

        return jQuery.ajax({
                type: "GET",
                url: url,
                data: data,
                success: callback,
                dataType: type
        });
}

Debugging further you’ll find that ajax uses JSONP to allow linking scripts from non-origin domain by taking advantage of the behavior of src attribute in script tag. However local scripts are just imported inline as:

<script type="text/javascript">local code</script>

as seen in implementation of $.ajax() :

// Matches an absolute URL, and saves the domain
var parts = rurl.exec( s.url ), remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);

// If we're requesting a remote document
// and trying to load JSON or Script with a GET
if ( s.dataType === "script" && type === "GET" && remote ) {
        var head = document.getElementsByTagName("head")[0] || document.documentElement;
        var script = document.createElement("script");
        script.src = s.url;
        if ( s.scriptCharset ) {
                script.charset = s.scriptCharset;
        }

        // Handle Script loading
        if ( !jsonp ) {
                var done = false;

                // Attach handlers for all browsers
                script.onload = script.onreadystatechange = function() {
                        if ( !done && (!this.readyState ||
                                        this.readyState === "loaded" || this.readyState === "complete") ) {
                                done = true;
                                success();
                                complete();

                                // Handle memory leak in IE
                                script.onload = script.onreadystatechange = null;
                                if ( head && script.parentNode ) {
                                        head.removeChild( script );
                                }
                        }
                };
        }

        // Use insertBefore instead of appendChild  to circumvent an IE6 bug.
        // This arises when a base node is used (#2709 and #4378).
        head.insertBefore( script, head.firstChild );

        // We handle everything using the script element injection
        return undefined;
}

As you can see, splitting URL and domain causes local scripts to be treated as non-remote objects. So now, since your local scripts are loaded asynchronously and added inline, you only need to override jQuery.getScript() to make it work as how ajax handles remote requests, per say:

jQuery.extend({
        getScript: function(url, callback) {
                var head = document.getElementsByTagName("head")[0] || document.documentElement;
                var script = document.createElement("script");
                script.src = url;

                // Handle Script loading
                {
                        var done = false;

                        // Attach handlers for all browsers
                        script.onload = script.onreadystatechange = function() {
                                if ( !done && (!this.readyState || this.readyState === "loaded" ||
                                       this.readyState === "complete") ) {
                                        done = true;
                                        //success();
                                        //complete();
                                        if ( callback)
                                                callback();

                                        // Handle memory leak in IE
                                        script.onload = script.onreadystatechange = null;
                                        if ( head && script.parentNode ) {
                                                head.removeChild( script );
                                        }
                                }
                        };
                }

                head.insertBefore( script, head.firstChild );
                return undefined;
        }
});

We’ve implemented our own call to callback since in $.ajax uses success() to fire the callback instead. While a success can be written for this new getScript(), its beyond the scope of this article.

Leave a Reply

Email Subscription

Disclaimer

The views expressed on this blog are personal. We do not claim to be a representative voice of the views of any organisation whatsoever. We are not responsible for the content present on the blogs to which we have linked.Views expressed are solely that of the author and does not reflect a collective opinion of contributors.
%d bloggers like this: