X-Git-Url: http://git.asbjorn.biz/?p=jquery.git;a=blobdiff_plain;f=src%2Fcore.js;h=520db45ed6cc179ba99b40d57725e3abe17ddcd9;hp=57a42d09ea4093a26e1b6f03f3135d9e92729142;hb=9d306bd73bb47562cd52f0fc4cc158c534cfdfdf;hpb=846c52d1f8535a6283dcc42c6e8f0c26991936ad diff --git a/src/core.js b/src/core.js index 57a42d0..520db45 100644 --- a/src/core.js +++ b/src/core.js @@ -3,7 +3,7 @@ var jQuery = (function() { // Define a local copy of jQuery var jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context ); + return new jQuery.fn.init( selector, context, rootjQuery ); }, // Map over jQuery in case of overwrite @@ -19,12 +19,8 @@ var jQuery = function( selector, context ) { // (both of which we optimize for) quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/, - // Is it a simple selector - isSimple = /^.[^:#\[\.,]*$/, - // Check if a string has a non-whitespace character in it rnotwhite = /\S/, - rwhite = /\s/, // Used for trimming whitespace trimLeft = /^\s+/, @@ -56,13 +52,16 @@ 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 deferred used on DOM ready readyList, + // Promise methods + promiseMethods = "then done fail isResolved isRejected promise".split( " " ), + // The ready event handler DOMContentLoaded, @@ -73,15 +72,13 @@ var jQuery = function( selector, context ) { slice = Array.prototype.slice, trim = String.prototype.trim, indexOf = Array.prototype.indexOf, - + // [[Class]] -> type pairs - class2type = {}, - - // Marker for deferred - deferredMarker = []; + class2type = {}; jQuery.fn = jQuery.prototype = { - init: function( selector, context ) { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { var match, elem, ret, doc; // Handle $(""), $(null), or $(undefined) @@ -95,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; @@ -115,6 +112,7 @@ jQuery.fn = jQuery.prototype = { // HANDLE: $(html) -> $(array) if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; doc = (context ? context.ownerDocument || context : document); // If a single string is passed in and it's a single tag @@ -132,11 +130,11 @@ jQuery.fn = jQuery.prototype = { } else { ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); - selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; + selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes; } - + return jQuery.merge( this, selector ); - + // HANDLE: $("#id") } else { elem = document.getElementById( match[2] ); @@ -160,13 +158,6 @@ jQuery.fn = jQuery.prototype = { return this; } - // HANDLE: $("TAG") - } else if ( !context && !rnonword.test( selector ) ) { - this.selector = selector; - this.context = document; - selector = document.getElementsByTagName( selector ); - return jQuery.merge( this, selector ); - // HANDLE: $(expr, $(...)) } else if ( !context || context.jquery ) { return (context || rootjQuery).find( selector ); @@ -174,7 +165,7 @@ jQuery.fn = jQuery.prototype = { // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { - return jQuery( context ).find( selector ); + return this.constructor( context ).find( selector ); } // HANDLE: $(function) @@ -225,11 +216,11 @@ jQuery.fn = jQuery.prototype = { // (returning the new matched element set) pushStack: function( elems, name, selector ) { // Build a new jQuery matched element set - var ret = jQuery(); + var ret = this.constructor(); if ( jQuery.isArray( elems ) ) { push.apply( ret, elems ); - + } else { jQuery.merge( ret, elems ); } @@ -255,15 +246,17 @@ jQuery.fn = jQuery.prototype = { each: function( callback, args ) { return jQuery.each( this, callback, args ); }, - - ready: function() { + + ready: function( fn ) { // Attach the listeners jQuery.bindReady(); - - // Change ready & apply - return ( jQuery.fn.ready = readyList.then ).apply( this , arguments ); + + // Add the callback + readyList.done( fn ); + + return this; }, - + eq: function( i ) { return i === -1 ? this.slice( i ) : @@ -288,9 +281,9 @@ jQuery.fn = jQuery.prototype = { return callback.call( elem, i, elem ); })); }, - + end: function() { - return this.prevObject || jQuery(null); + return this.prevObject || this.constructor(null); }, // For internal use only. @@ -377,14 +370,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 @@ -408,15 +401,15 @@ jQuery.extend({ } // If there are functions bound, to execute - readyList.fire( document , [ jQuery ] ); - + readyList.resolveWith( document, [ jQuery ] ); + // Trigger any bound ready events if ( jQuery.fn.trigger ) { jQuery( document ).trigger( "ready" ).unbind( "ready" ); } } }, - + bindReady: function() { if ( readyBound ) { return; @@ -435,7 +428,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 ); @@ -444,7 +437,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 ); @@ -495,20 +488,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 ); }, @@ -518,11 +511,11 @@ jQuery.extend({ } return true; }, - + error: function( msg ) { throw msg; }, - + parseJSON: function( data ) { if ( typeof data !== "string" || !data ) { return null; @@ -530,7 +523,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, "@") @@ -546,7 +539,7 @@ jQuery.extend({ jQuery.error( "Invalid JSON: " + data ); } }, - + // Cross-browser xml parsing // (xml & tmp used internally) parseXML: function( data , xml , tmp ) { @@ -559,13 +552,13 @@ jQuery.extend({ xml.async = "false"; xml.loadXML( data ); } - + tmp = xml.documentElement; - + if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) { jQuery.error( "Invalid XML: " + data ); } - + return xml; }, @@ -581,7 +574,7 @@ jQuery.extend({ script.type = "text/javascript"; - if ( jQuery.support.scriptEval ) { + if ( jQuery.support.scriptEval() ) { script.appendChild( document.createTextNode( data ) ); } else { script.text = data; @@ -694,7 +687,7 @@ jQuery.extend({ for ( var l = second.length; j < l; j++ ) { first[ i++ ] = second[ j ]; } - + } else { while ( second[j] !== undefined ) { first[ i++ ] = second[ j++ ]; @@ -775,7 +768,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 ) { @@ -783,19 +776,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; }, @@ -803,10 +796,9 @@ jQuery.extend({ now: function() { return (new Date()).getTime(); }, - + // Create a simple deferred (one callbacks list) _Deferred: function() { - var // callbacks list callbacks = [], // stored [ context , args ] @@ -817,140 +809,142 @@ jQuery.extend({ cancelled, // the deferred itself deferred = { - - // then( f1, f2, ...) - then: function () { - - if ( ! cancelled ) { - + + // 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++ ) { + for ( i = 0, length = args.length; i < length; i++ ) { elem = args[ i ]; type = jQuery.type( elem ); if ( type === "array" ) { - deferred.then.apply( deferred , elem ); + deferred.done.apply( deferred, elem ); } else if ( type === "function" ) { callbacks.push( elem ); } } - if ( _fired ) { - deferred.fire( _fired[ 0 ] , _fired[ 1 ] ); + deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] ); } } - return this; }, - + // resolve with given context and args - fire: function( context , args ) { - if ( ! cancelled && ! fired && ! firing ) { - + resolveWith: function( context, args ) { + if ( !cancelled && !fired && !firing ) { firing = 1; - try { while( callbacks[ 0 ] ) { - callbacks.shift().apply( context , args ); + callbacks.shift().apply( context, args ); } } finally { - fired = [ context , args ]; + fired = [ context, args ]; firing = 0; } } return this; }, - + // resolve with this as context and given arguments resolve: function() { - deferred.fire( this , arguments ); + deferred.resolveWith( 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; - }, - - // 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 - + var deferred = jQuery._Deferred(), + failDeferred = jQuery._Deferred(), + promise; + // Add errorDeferred methods, then and promise + jQuery.extend( deferred, { + then: function( doneCallbacks, failCallbacks ) { + deferred.done( doneCallbacks ).fail( failCallbacks ); + return this; + }, + fail: failDeferred.done, + rejectWith: failDeferred.resolveWith, + 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 , i /* internal */ ) { + if ( obj == null ) { + if ( promise ) { + return promise; + } + promise = obj = {}; + } + i = promiseMethods.length; + while( i-- ) { + obj[ promiseMethods[ i ] ] = deferred[ promiseMethods[ i ] ]; + } + return obj; + } } ); - - // Remove cancel related - delete deferred.cancel; - delete deferred.isCancelled; - // Make sure only one callback list will be used - deferred.then( errorDeferred.cancel ).fail( successCancel ); - + deferred.then( failDeferred.cancel, deferred.cancel ); + // Unexpose cancel + delete deferred.cancel; // Call given func if any if ( func ) { - func.call( deferred , deferred ); + 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; + when: function( object ) { + var args = arguments, + length = args.length, + deferred = length <= 1 && object && jQuery.isFunction( object.promise ) ? + object : + jQuery.Deferred(), + promise = deferred.promise(), + resolveArray; + + if ( length > 1 ) { + resolveArray = new Array( length ); + jQuery.each( args, function( index, element, args ) { + jQuery.when( element ).then( function( value ) { + args = arguments; + resolveArray[ index ] = args.length > 1 ? slice.call( args, 0 ) : value; + if( ! --length ) { + deferred.resolveWith( promise, resolveArray ); + } + }, deferred.reject ); + } ); + } else if ( deferred !== object ) { + deferred.resolve( object ); + } + return promise; }, // Use of jQuery.browser is frowned upon. @@ -967,13 +961,30 @@ jQuery.extend({ return { browser: match[1] || "", version: match[2] || "0" }; }, + subclass: function(){ + function jQuerySubclass( selector, context ) { + return new jQuerySubclass.fn.init( selector, context ); + } + jQuerySubclass.superclass = this; + jQuerySubclass.fn = jQuerySubclass.prototype = this(); + jQuerySubclass.fn.constructor = jQuerySubclass; + jQuerySubclass.subclass = this.subclass; + jQuerySubclass.fn.init = function init( selector, context ) { + if (context && context instanceof jQuery && !(context instanceof jQuerySubclass)){ + context = jQuerySubclass(context); + } + return jQuery.fn.init.call( this, selector, context, rootjQuerySubclass ); + }; + jQuerySubclass.fn.init.prototype = jQuerySubclass.fn; + var rootjQuerySubclass = jQuerySubclass(document); + return jQuerySubclass; + }, + 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) { @@ -997,9 +1008,8 @@ if ( indexOf ) { }; } -// Verify that \s matches non-breaking spaces -// (IE fails on this test) -if ( !rwhite.test( "\xA0" ) ) { +// IE doesn't match non-breaking spaces with \s +if ( rnotwhite.test( "\xA0" ) ) { trimLeft = /^[\s\xA0]+/; trimRight = /[\s\xA0]+$/; }