X-Git-Url: http://git.asbjorn.biz/?p=jquery.git;a=blobdiff_plain;f=src%2Fcore.js;h=efb168306de86316036a563437e6eba5079c0091;hp=9e1bfc67fbe0986a4eac88d43e23e81cd9f127f5;hb=23d455b4469f2b47363c21867aad271d6930f112;hpb=983548f8ebc3fcd1bb4600bc4b740cb8a5d4c48b diff --git a/src/core.js b/src/core.js index 9e1bfc6..efb1683 100644 --- a/src/core.js +++ b/src/core.js @@ -56,12 +56,12 @@ var jQuery = function( selector, context ) { // For matching the engine and version of the browser browserMatch, - + // Has the ready events already been bound? readyBound = false, - // The functions to execute on DOM ready - readyList = [], + // The deferred used on DOM ready + readyList, // The ready event handler DOMContentLoaded, @@ -73,9 +73,12 @@ var jQuery = function( selector, context ) { slice = Array.prototype.slice, trim = String.prototype.trim, indexOf = Array.prototype.indexOf, - + // [[Class]] -> type pairs - class2type = {}; + class2type = {}, + + // Marker for deferred + deferredMarker = []; jQuery.fn = jQuery.prototype = { init: function( selector, context ) { @@ -92,7 +95,7 @@ jQuery.fn = jQuery.prototype = { this.length = 1; return this; } - + // The body element only exists once, optimize finding it if ( selector === "body" && !context && document.body ) { this.context = document; @@ -131,9 +134,9 @@ jQuery.fn = jQuery.prototype = { ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; } - + return jQuery.merge( this, selector ); - + // HANDLE: $("#id") } else { elem = document.getElementById( match[2] ); @@ -215,7 +218,7 @@ jQuery.fn = jQuery.prototype = { this.toArray() : // Return just the object - ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] ); + ( num < 0 ? this[ this.length + num ] : this[ num ] ); }, // Take an array of elements and push it onto the stack @@ -226,7 +229,7 @@ jQuery.fn = jQuery.prototype = { if ( jQuery.isArray( elems ) ) { push.apply( ret, elems ); - + } else { jQuery.merge( ret, elems ); } @@ -253,24 +256,14 @@ jQuery.fn = jQuery.prototype = { return jQuery.each( this, callback, args ); }, - ready: function( fn ) { + ready: function() { // Attach the listeners jQuery.bindReady(); - - // If the DOM is already ready - if ( jQuery.isReady ) { - // Execute the function immediately - fn.call( document, jQuery ); - - // Otherwise, remember the function for later - } else if ( readyList ) { - // Add the function to the wait list - readyList.push( fn ); - } - - return this; + + // Change ready & apply + return ( jQuery.fn.ready = readyList.then ).apply( this , arguments ); }, - + eq: function( i ) { return i === -1 ? this.slice( i ) : @@ -295,7 +288,7 @@ jQuery.fn = jQuery.prototype = { return callback.call( elem, i, elem ); })); }, - + end: function() { return this.prevObject || jQuery(null); }, @@ -384,14 +377,14 @@ jQuery.extend({ return jQuery; }, - + // Is the DOM ready to be used? Set to true once it occurs. isReady: false, // A counter to track how many items to wait for before // the ready event fires. See #6781 readyWait: 1, - + // Handle when the DOM is ready ready: function( wait ) { // A third-party is pushing the ready event forwards @@ -415,27 +408,15 @@ jQuery.extend({ } // If there are functions bound, to execute - if ( readyList ) { - // Execute all of them - var fn, - i = 0, - ready = readyList; - - // Reset the list of functions - readyList = null; - - while ( (fn = ready[ i++ ]) ) { - fn.call( document, jQuery ); - } - - // Trigger any bound ready events - if ( jQuery.fn.trigger ) { - jQuery( document ).trigger( "ready" ).unbind( "ready" ); - } + readyList.fire( document , [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger( "ready" ).unbind( "ready" ); } } }, - + bindReady: function() { if ( readyBound ) { return; @@ -454,7 +435,7 @@ jQuery.extend({ if ( document.addEventListener ) { // Use the handy event callback document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - + // A fallback to window.onload, that will always work window.addEventListener( "load", jQuery.ready, false ); @@ -463,7 +444,7 @@ jQuery.extend({ // ensure firing before onload, // maybe late but safe also for iframes document.attachEvent("onreadystatechange", DOMContentLoaded); - + // A fallback to window.onload, that will always work window.attachEvent( "onload", jQuery.ready ); @@ -514,20 +495,20 @@ jQuery.extend({ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { return false; } - + // Not own constructor property must be Object if ( obj.constructor && !hasOwn.call(obj, "constructor") && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { return false; } - + // Own properties are enumerated firstly, so to speed up, // if last one is own, then all properties are own. - + var key; for ( key in obj ) {} - + return key === undefined || hasOwn.call( obj, key ); }, @@ -537,11 +518,11 @@ jQuery.extend({ } return true; }, - + error: function( msg ) { throw msg; }, - + parseJSON: function( data ) { if ( typeof data !== "string" || !data ) { return null; @@ -549,7 +530,7 @@ jQuery.extend({ // Make sure leading/trailing whitespace is removed (IE can't handle it) data = jQuery.trim( data ); - + // Make sure the incoming data is actual JSON // Logic borrowed from http://json.org/json2.js if ( rvalidchars.test(data.replace(rvalidescape, "@") @@ -565,6 +546,28 @@ jQuery.extend({ jQuery.error( "Invalid JSON: " + data ); } }, + + // Cross-browser xml parsing + // (xml & tmp used internally) + parseXML: function( data , xml , tmp ) { + + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + + tmp = xml.documentElement; + + if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) { + jQuery.error( "Invalid XML: " + data ); + } + + return xml; + }, noop: function() {}, @@ -691,7 +694,7 @@ jQuery.extend({ for ( var l = second.length; j < l; j++ ) { first[ i++ ] = second[ j ]; } - + } else { while ( second[j] !== undefined ) { first[ i++ ] = second[ j++ ]; @@ -733,6 +736,7 @@ jQuery.extend({ } } + // Flatten any nested arrays return ret.concat.apply( [], ret ); }, @@ -771,7 +775,7 @@ jQuery.extend({ // The value/s can be optionally by executed if its a function access: function( elems, key, value, exec, fn, pass ) { var length = elems.length; - + // Setting many attributes if ( typeof key === "object" ) { for ( var k in key ) { @@ -779,19 +783,19 @@ jQuery.extend({ } return elems; } - + // Setting one attribute if ( value !== undefined ) { // Optionally, function values get executed if exec is true exec = !pass && exec && jQuery.isFunction(value); - + for ( var i = 0; i < length; i++ ) { fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); } - + return elems; } - + // Getting an attribute return length ? fn( elems[0], key ) : undefined; }, @@ -799,6 +803,155 @@ jQuery.extend({ now: function() { return (new Date()).getTime(); }, + + // Create a simple deferred (one callbacks list) + _Deferred: function() { + + var // callbacks list + callbacks = [], + // stored [ context , args ] + fired, + // to avoid firing when already doing so + firing, + // flag to know if the deferred has been cancelled + cancelled, + // the deferred itself + deferred = { + + // then( f1, f2, ...) + then: function () { + + if ( ! cancelled ) { + + var args = arguments, + i, + length, + elem, + type, + _fired; + + if ( fired ) { + _fired = fired; + fired = 0; + } + + for ( i = 0, length = args.length ; i < length ; i++ ) { + elem = args[ i ]; + type = jQuery.type( elem ); + if ( type === "array" ) { + deferred.then.apply( deferred , elem ); + } else if ( type === "function" ) { + callbacks.push( elem ); + } + } + + if ( _fired ) { + deferred.fire( _fired[ 0 ] , _fired[ 1 ] ); + } + } + + return this; + }, + + // resolve with given context and args + fire: function( context , args ) { + if ( ! cancelled && ! fired && ! firing ) { + + firing = 1; + + try { + while( callbacks[ 0 ] ) { + callbacks.shift().apply( context , args ); + } + } + finally { + fired = [ context , args ]; + firing = 0; + } + } + return this; + }, + + // resolve with this as context and given arguments + resolve: function() { + deferred.fire( this , arguments ); + return this; + }, + + // Has this deferred been resolved? + isResolved: function() { + return !!( firing || fired ); + }, + + // Cancel + cancel: function() { + cancelled = 1; + callbacks = []; + return this; + }, + + // Has this deferred been cancelled? + isCancelled: function() { + return !!cancelled; + } + }; + + // Add the deferred marker + deferred.then._ = deferredMarker; + + return deferred; + }, + + // Full fledged deferred (two callbacks list) + // Typical success/error system + Deferred: function( func ) { + + var errorDeferred = jQuery._Deferred(), + deferred = jQuery._Deferred(), + successCancel = deferred.cancel; + + // Add errorDeferred methods and redefine cancel + jQuery.extend( deferred , { + + fail: errorDeferred.then, + fireReject: errorDeferred.fire, + reject: errorDeferred.resolve, + isRejected: errorDeferred.isResolved + + } ); + + // Remove cancel related + delete deferred.cancel; + delete deferred.isCancelled; + + // Make sure only one callback list will be used + deferred.then( errorDeferred.cancel ).fail( successCancel ); + + // Call given func if any + if ( func ) { + func.call( deferred , deferred ); + } + + return deferred; + }, + + // Check if an object is a deferred + isDeferred: function( object , method ) { + method = method || "then"; + return !!( object && object[ method ] && object[ method ]._ === deferredMarker ); + }, + + // Deferred helper + when: function( object , method ) { + method = method || "then"; + object = jQuery.isDeferred( object , method ) ? + object : + jQuery.Deferred().resolve( object ); + object.fail = object.fail || function() { return this; }; + object[ method ] = object[ method ] || object.then; + object.then = object.then || object[ method ]; + return object; + }, // Use of jQuery.browser is frowned upon. // More details: http://docs.jquery.com/Utilities/jQuery.browser @@ -817,6 +970,11 @@ jQuery.extend({ browser: {} }); +// Create readyList deferred +// also force $.fn.ready to be recognized as a defer +readyList = jQuery._Deferred(); +jQuery.fn.ready._ = deferredMarker; + // Populate the class2type map jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); @@ -885,6 +1043,11 @@ function doScrollCheck() { jQuery.ready(); } +// Expose jQuery as an Asynchronous Module +if ( typeof define === "function" ) { + define( "jquery", [], function () { return jQuery; } ); +} + // Expose jQuery to the global object return (window.jQuery = window.$ = jQuery);