Created
June 25, 2010 20:53
-
-
Save cowboy/453429 to your computer and use it in GitHub Desktop.
jQuery ready WIP 100% UNTESTED
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* jQuery ready - v0.4pre - 06/28/2010 | |
* http://benalman.com/projects/jquery-ready-plugin/ | |
* | |
* Copyright (c) 2010 "Cowboy" Ben Alman | |
* Dual licensed under the MIT and GPL licenses. | |
* http://benalman.com/about/license/ | |
*/ | |
(function($,undefined){ | |
'$:nomunge'; // Used by YUI compressor. | |
// A handle by which the polling loop can be cancelled. | |
var timeout_id, | |
// Callback queues, by element ID. | |
queues = {}, | |
// How many queues do we have? | |
num_queues = 0, | |
// Compare page HTML to cached copy when polling in an attempt to increase | |
// performance. | |
doc_elem = $('html'), | |
doc_elem_html, | |
// jQuery object references. | |
jq_fn = $.fn, | |
jq_fn_ready_orig = jq_fn.ready, | |
// Internal method reference. | |
jq_fn_ready, | |
jq_unready; | |
// TODO: DOCUMENT THIS | |
jq_fn.ready = jq_fn_ready = function( even_if_unclosed ) { | |
var that = this, | |
// Match simple-ID-selected jQuery collections only! | |
id = get_id( that ), | |
args = arguments, | |
callback; | |
// If selector matches regexp, set `id` to be the 1st match item, which is | |
// the ID string sans leading # and do some cool stuff. Otherwise, run the | |
// original jQuery .ready method. | |
if ( id ) { | |
// Convert `args` to a real array and strip off the first argument. | |
args = Array.prototype.slice.call( args, 1 ), | |
if ( $.isFunction( jq_fn[ even_if_unclosed ] || even_if_unclosed ) ) { | |
// Since `even_if_unclosed` is a function / $.fn.method, it wasn't | |
// explicitly passed. Shuffle `callback` and `even_if_unclosed` | |
// around a little. | |
callback = even_if_unclosed; | |
even_if_unclosed = undefined; | |
} else { | |
// Since `even_if_unclosed` isn't a function / $.fn.method, it was | |
// explicitly passed, so `callback` must be the first remaining | |
// argument. | |
callback = args.shift(); | |
} | |
// If `callback` is a $.fn.method, use that method. | |
callback = jq_fn[ callback ] || callback; | |
if ( that.length && is_element_ready( that[0], even_if_unclosed ) ) { | |
// The element is ready, so execute the callback in the context of the | |
// element, passing any extra arguments through. | |
callback.apply( that[0], args ); | |
// Since function was executed immediately, return `true`. | |
return true; | |
} else { | |
// The element isn't ready, so queue this callback for later execution. | |
// Initialize the queue if it doesn't yet exist, and increment the | |
// queues counter. | |
if ( !queues[ id ] ) { | |
queues[ id ] = []; | |
num_queues++; | |
} | |
// Push this callback with options and arguments onto the queue. | |
queues[ id ].push({ fn: callback, args: args, uc: even_if_unclosed }); | |
// Start the polling loop, if not already started. | |
timeout_id || poll(); | |
// Since function wasn't executed immediately, return `false`. | |
return false; | |
} | |
} | |
// Selector wasn't a simple ID, so execute (and return) the original jQuery | |
// .ready method. | |
return jq_fn_ready_orig.apply( that, args ); | |
}; | |
// TODO: DOCUMENT THIS | |
$.unready = jq_unready = function() { | |
queues = {}; | |
num_queues = 0; | |
}; | |
// TODO: DOCUMENT THIS | |
// Mention https://developer.mozilla.org/en/Case_Sensitivity_in_class_and_id_Names ? | |
jq_fn.unready = function( callback ) { | |
var that = this, | |
// Match simple-ID-selected jQuery collections only! | |
id = get_id( that ), | |
// Get the queue for this ID (if it exists). | |
queue = queues[ id ]; | |
if ( queue ) { | |
// A queue for this ID must actually exist! | |
if ( callback ) { | |
// Remove the passed callback from this ID's queue if it exists. | |
queues[ id ] = $.map( queue, function(obj){ | |
return obj.fn === callback || obj.fn.guid === callback.guid ? null : obj; | |
}); | |
} else { | |
// Since `fn` was omitted, delete the entire queue for this ID. | |
delete queues[ id ]; | |
num_queues--; | |
} | |
} | |
return that; | |
}; | |
// TODO: DOCUMENT THIS | |
jq_fn_ready.delay = 30; | |
// Start polling. | |
function poll() { | |
// TODO: test if page innerHTML changed? | |
// Compare page HTML to cached copy when polling in an attempt to increase | |
// performance. | |
var html = doc_elem.html(); | |
if ( html !== doc_elem_html ) { | |
doc_elem_html = html; | |
// Iterate over each per-element-ID callback queue. | |
$.each( queues, function(id,queue){ | |
var elem = document.getElementById( id ); | |
if ( elem ) { | |
// If the element exists in the DOM, iterate over the queued callbacks. | |
queues[ id ] = $.map( queue, function(obj){ | |
if ( is_element_ready( elem, obj.uc ) ) { | |
// The element is ready, so execute the callback in the context of | |
// the element, passing any extra arguments through. | |
obj.fn.apply( elem, obj.args ); | |
// Since this callback has executed, remove it from the queue. | |
obj = null; | |
} | |
return obj; | |
}); | |
// If no more callbacks remain in the element queue, delete the queue. | |
if ( !queues[ id ].length ) { | |
delete queues[ id ]; | |
num_queues--; | |
} | |
} | |
}); | |
} | |
// If the DOM is ready, remove any remaining IDs / callbacks. | |
$.isReady && jq_unready(); | |
// Continue polling if any remaining queues, otherwise unset timeout_id. | |
timeout_id = num_queues ? setTimeout( poll, jq_fn_ready.delay ) : null; | |
}; | |
// Is DOM element `elem` ready? | |
function is_element_ready( elem, even_if_unclosed ) { | |
// Since the element exists, if we don't care that it's closed or the DOM | |
// is ready, the element is considered "ready". | |
if ( even_if_unclosed || $.isReady ) { | |
return true; | |
} | |
// As an HTML page is initially loading and an element is rendering, it can | |
// be assumed that it is fully ready or "closed" if either it or any of its | |
// ancestors has a nextSibling. If it or any of its parents are the last | |
// element on the page, while none of them will have a nextSibling, since | |
// the DOM will then be ready (and $.isReady will be true), the elemen will | |
// be considered "ready". | |
do { | |
if ( elem.nextSibling ) { | |
return true; | |
} | |
} while ( elem = elem.parentNode ); | |
}; | |
// If selector is a basic ID string, return the ID, otherwise null. | |
function get_id( jq_obj ) { | |
var id = jq_obj.selector.match( /^#([\w-]+)$/ ); | |
return id && id[ 1 ]; | |
}; | |
})(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment