Moved unload abort code so that the event is only bound if the xhr transport is used...
[jquery.git] / src / core.js
index 99521d9..b9e6d81 100644 (file)
@@ -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,7 +73,7 @@ var jQuery = function( selector, context ) {
        slice = Array.prototype.slice,
        trim = String.prototype.trim,
        indexOf = Array.prototype.indexOf,
-       
+
        // [[Class]] -> type pairs
        class2type = {};
 
@@ -92,7 +92,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 +131,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 +215,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 +226,7 @@ jQuery.fn = jQuery.prototype = {
 
                if ( jQuery.isArray( elems ) ) {
                        push.apply( ret, elems );
-               
+
                } else {
                        jQuery.merge( ret, elems );
                }
@@ -252,25 +252,15 @@ jQuery.fn = jQuery.prototype = {
        each: function( callback, args ) {
                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.done ).apply( this , arguments );
        },
-       
+
        eq: function( i ) {
                return i === -1 ?
                        this.slice( i ) :
@@ -295,7 +285,7 @@ jQuery.fn = jQuery.prototype = {
                        return callback.call( elem, i, elem );
                }));
        },
-       
+
        end: function() {
                return this.prevObject || jQuery(null);
        },
@@ -311,8 +301,11 @@ jQuery.fn = jQuery.prototype = {
 jQuery.fn.init.prototype = jQuery.fn;
 
 jQuery.extend = jQuery.fn.extend = function() {
-       // copy reference to target object
-       var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy, copyIsArray, clone;
+        var options, name, src, copy, copyIsArray, clone,
+               target = arguments[0] || {},
+               i = 1,
+               length = arguments.length,
+               deep = false;
 
        // Handle a deep copy situation
        if ( typeof target === "boolean" ) {
@@ -381,14 +374,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
@@ -412,24 +405,15 @@ jQuery.extend({
                        }
 
                        // If there are functions bound, to execute
-                       if ( readyList ) {
-                               // Execute all of them
-                               var fn, i = 0;
-                               while ( (fn = readyList[ i++ ]) ) {
-                                       fn.call( document, jQuery );
-                               }
-
-                               // Reset the list of functions
-                               readyList = null;
-                       }
+                       readyList.fire( document , [ jQuery ] );
 
                        // Trigger any bound ready events
-                       if ( jQuery.fn.triggerHandler ) {
-                               jQuery( document ).triggerHandler( "ready" );
+                       if ( jQuery.fn.trigger ) {
+                               jQuery( document ).trigger( "ready" ).unbind( "ready" );
                        }
                }
        },
-       
+
        bindReady: function() {
                if ( readyBound ) {
                        return;
@@ -448,7 +432,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 );
 
@@ -457,7 +441,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 );
 
@@ -508,20 +492,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 );
        },
 
@@ -531,11 +515,11 @@ jQuery.extend({
                }
                return true;
        },
-       
+
        error: function( msg ) {
                throw msg;
        },
-       
+
        parseJSON: function( data ) {
                if ( typeof data !== "string" || !data ) {
                        return null;
@@ -543,7 +527,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, "@")
@@ -560,6 +544,28 @@ jQuery.extend({
                }
        },
 
+       // 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() {},
 
        // Evalulates a script in a global context
@@ -678,13 +684,14 @@ jQuery.extend({
        },
 
        merge: function( first, second ) {
-               var i = first.length, j = 0;
+               var i = first.length,
+                       j = 0;
 
                if ( typeof second.length === "number" ) {
                        for ( var l = second.length; j < l; j++ ) {
                                first[ i++ ] = second[ j ];
                        }
-               
+
                } else {
                        while ( second[j] !== undefined ) {
                                first[ i++ ] = second[ j++ ];
@@ -726,6 +733,7 @@ jQuery.extend({
                        }
                }
 
+               // Flatten any nested arrays
                return ret.concat.apply( [], ret );
        },
 
@@ -764,7 +772,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 ) {
@@ -772,19 +780,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;
        },
@@ -793,6 +801,151 @@ jQuery.extend({
                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  = {
+
+                               // done( f1, f2, ...)
+                               done: 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.done.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( jQuery.isFunction( this.promise ) ? this.promise() : this , arguments );
+                                       return this;
+                               },
+
+                               // Has this deferred been resolved?
+                               isResolved: function() {
+                                       return !!( firing || fired );
+                               },
+
+                               // Cancel
+                               cancel: function() {
+                                       cancelled = 1;
+                                       callbacks = [];
+                                       return this;
+                               }
+                       };
+
+               return deferred;
+       },
+
+       // Full fledged deferred (two callbacks list)
+       // Typical success/error system
+       Deferred: function( func ) {
+
+               var deferred = jQuery._Deferred(),
+                       failDeferred = jQuery._Deferred();
+
+               // Add errorDeferred methods and redefine cancel
+               jQuery.extend( deferred , {
+
+                       then: function( doneCallbacks , failCallbacks ) {
+                               deferred.done( doneCallbacks ).fail( failCallbacks );
+                               return this;
+                       },
+                       fail: failDeferred.done,
+                       fireReject: failDeferred.fire,
+                       reject: failDeferred.resolve,
+                       isRejected: failDeferred.isResolved,
+                       // Get a promise for this deferred
+                       // If obj is provided, the promise aspect is added to the object
+                       promise: function( obj ) {
+                               obj = obj || {};
+                               jQuery.each( "then done fail isResolved isRejected".split( " " ) , function( _ , method ) {
+                                       obj[ method ] = deferred[ method ];
+                               });
+                               obj.promise = function() {
+                                       return obj;
+                               };
+                               return obj;
+                       }
+
+               } );
+
+               // Make sure only one callback list will be used
+               deferred.then( failDeferred.cancel , deferred.cancel );
+
+               // Unexpose cancel
+               delete deferred.cancel;
+
+               // Call given func if any
+               if ( func ) {
+                       func.call( deferred , deferred );
+               }
+
+               return deferred;
+       },
+
+       // Deferred helper
+       when: function( object ) {
+               object = object && jQuery.isFunction( object.promise ) ?
+                       object :
+                       jQuery.Deferred().resolve( object );
+               return object.promise();
+       },
+
        // Use of jQuery.browser is frowned upon.
        // More details: http://docs.jquery.com/Utilities/jQuery.browser
        uaMatch: function( ua ) {
@@ -810,6 +963,9 @@ jQuery.extend({
        browser: {}
 });
 
+// Create readyList deferred
+readyList = jQuery._Deferred();
+
 // Populate the class2type map
 jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
        class2type[ "[object " + name + "]" ] = name.toLowerCase();