X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=src%2Fcore.js;h=ba5d7f5288aa0f753b82bd959afc2d4eb1893625;hb=9e06903a99caf5619d0db858ed3d24f0e6ee15db;hp=0a5bd53ce798a2a77c6af2ed434397a1da3416c7;hpb=17791c9a3e8a88e129a6956e7053b964d7b34778;p=jquery.git diff --git a/src/core.js b/src/core.js index 0a5bd53..ba5d7f5 100644 --- a/src/core.js +++ b/src/core.js @@ -1,9 +1,7 @@ // Define a local copy of jQuery var jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' - return arguments.length === 0 ? - rootjQuery : - new jQuery.fn.init( selector, context ); + return new jQuery.fn.init( selector, context ); }, // Map over jQuery in case of overwrite @@ -35,7 +33,10 @@ var jQuery = function( selector, context ) { rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, // Keep a UserAgent string for use with jQuery.browser - userAgent = navigator.userAgent.toLowerCase(), + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, // Has the ready events already been bound? readyBound = false, @@ -43,6 +44,9 @@ var jQuery = function( selector, context ) { // The functions to execute on DOM ready readyList = [], + // The ready event handler + DOMContentLoaded, + // Save a reference to some core methods toString = Object.prototype.toString, hasOwnProperty = Object.prototype.hasOwnProperty, @@ -65,6 +69,15 @@ jQuery.fn = jQuery.prototype = { this.length = 1; return this; } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context ) { + this.context = document; + this[0] = document.body; + this.selector = "body"; + this.length = 1; + return this; + } // Handle HTML strings if ( typeof selector === "string" ) { @@ -83,13 +96,21 @@ jQuery.fn = jQuery.prototype = { ret = rsingleTag.exec( selector ); if ( ret ) { - selector = [ doc.createElement( ret[1] ) ]; + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } } else { ret = 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] ); @@ -116,6 +137,7 @@ jQuery.fn = jQuery.prototype = { this.selector = selector; this.context = document; selector = document.getElementsByTagName( selector ); + return jQuery.merge( this, selector ); // HANDLE: $(expr, $(...)) } else if ( !context || context.jquery ) { @@ -138,9 +160,7 @@ jQuery.fn = jQuery.prototype = { this.context = selector.context; } - return jQuery.isArray( selector ) ? - this.setArray( selector ) : - jQuery.makeArray( selector, this ); + return jQuery.makeArray( selector, this ); }, // Start with an empty selector @@ -157,7 +177,7 @@ jQuery.fn = jQuery.prototype = { return this.length; }, - toArray: function(){ + toArray: function() { return slice.call( this, 0 ); }, @@ -177,7 +197,14 @@ 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( elems || null ); + var ret = jQuery(); + + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + jQuery.merge( ret, elems ); + } // Add the old object onto the stack (as a reference) ret.prevObject = this; @@ -194,43 +221,12 @@ jQuery.fn = jQuery.prototype = { return ret; }, - // Force the current matched set of elements to become - // the specified array of elements (destroying the stack in the process) - // You should use pushStack() in order to do this, but maintain the stack - setArray: function( elems ) { - // Resetting the length to 0, then using the native Array push - // is a super-fast way to populate an object with array-like properties - this.length = 0; - push.apply( this, elems ); - - return this; - }, - // Execute a callback for every element in the matched set. // (You can seed the arguments with an array of args, but this is // only used internally.) each: function( callback, args ) { return jQuery.each( this, callback, args ); }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - if ( !elem || typeof elem === "string" ) { - return jQuery.inArray( this[0], - // If it receives a string, the selector is used - // If it receives nothing, the siblings are used - elem ? jQuery( elem ) : this.parent().children() ); - } - // Locate the position of the desired element - return jQuery.inArray( - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[0] : elem, this ); - }, - - is: function( selector ) { - return !!selector && jQuery.filter( selector, this ).length > 0; - }, ready: function( fn ) { // Attach the listeners @@ -242,13 +238,42 @@ jQuery.fn = jQuery.prototype = { fn.call( document, jQuery ); // Otherwise, remember the function for later - } else { + } else if ( readyList ) { // Add the function to the wait list readyList.push( fn ); } return this; }, + + eq: function( i ) { + return i === -1 ? + this.slice( i ) : + this.slice( i, +i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || jQuery(null); + }, // For internal use only. // Behaves like an Array's method, not like a jQuery method. @@ -296,10 +321,10 @@ jQuery.extend = jQuery.fn.extend = function() { continue; } - // Recurse if we're merging object literal values - if ( deep && copy && jQuery.isPlainObject(copy) ) { - // Don't extend not object literals - var clone = src && jQuery.isPlainObject(src) ? src : {}; + // Recurse if we're merging object literal values or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) { + var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src + : jQuery.isArray(copy) ? [] : {}; // Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy ); @@ -334,6 +359,7 @@ jQuery.extend({ ready: function() { // Make sure that the DOM is not already loaded if ( !jQuery.isReady ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). if ( !document.body ) { return setTimeout( jQuery.ready, 13 ); } @@ -361,7 +387,10 @@ jQuery.extend({ }, bindReady: function() { - if ( readyBound ) { return; } + if ( readyBound ) { + return; + } + readyBound = true; // Catch cases where $(document).ready() is called after the @@ -373,10 +402,7 @@ jQuery.extend({ // Mozilla, Opera and webkit nightlies currently support this event if ( document.addEventListener ) { // Use the handy event callback - document.addEventListener( "DOMContentLoaded", function DOMContentLoaded() { - document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - jQuery.ready(); - }, false ); + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); // A fallback to window.onload, that will always work window.addEventListener( "load", jQuery.ready, false ); @@ -385,13 +411,7 @@ jQuery.extend({ } else if ( document.attachEvent ) { // ensure firing before onload, // maybe late but safe also for iframes - document.attachEvent("onreadystatechange", function onreadystatechange() { - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( document.readyState === "complete" ) { - document.detachEvent( "onreadystatechange", onreadystatechange ); - jQuery.ready(); - } - }); + document.attachEvent("onreadystatechange", DOMContentLoaded); // A fallback to window.onload, that will always work window.attachEvent( "onload", jQuery.ready ); @@ -402,28 +422,10 @@ jQuery.extend({ try { toplevel = window.frameElement == null; - } catch(e){} + } catch(e) {} if ( document.documentElement.doScroll && toplevel ) { doScrollCheck(); - - function doScrollCheck() { - if ( jQuery.isReady ) { - return; - } - - try { - // If IE is used, use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - document.documentElement.doScroll("left"); - } catch( error ) { - setTimeout( doScrollCheck, 1 ); - return; - } - - // and execute any waiting functions - jQuery.ready(); - } } } }, @@ -440,19 +442,22 @@ jQuery.extend({ }, isPlainObject: function( obj ) { - if ( toString.call(obj) !== "[object Object]" || typeof obj.nodeType === "number" ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) { return false; } - // not own constructor property must be Object + // Not own constructor property must be Object if ( obj.constructor - && !hasOwnProperty.call(obj, "constructor") - && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) { + && !hasOwnProperty.call(obj, "constructor") + && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) { return false; } - //own properties are iterated firstly, - //so to speed up, we can test last one if it is own or not + // 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 ) {} @@ -466,6 +471,36 @@ jQuery.extend({ } return true; }, + + error: function( msg ) { + throw msg; + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // 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 ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@") + .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]") + .replace(/(?:^|:|,)(?:\s*\[)+/g, "")) ) { + + // Try to use the native JSON parser first + return window.JSON && window.JSON.parse ? + window.JSON.parse( data ) : + (new Function("return " + data))(); + + } else { + jQuery.error( "Invalid JSON: " + data ); + } + }, + + noop: function() {}, // Evalulates a script in a global context globalEval: function( data ) { @@ -483,7 +518,7 @@ jQuery.extend({ script.text = data; } - // Use insertBefore instead of appendChild to circumvent an IE6 bug. + // Use insertBefore instead of appendChild to circumvent an IE6 bug. // This arises when a base node is used (#2709). head.insertBefore( script, head.firstChild ); head.removeChild( script ); @@ -569,23 +604,21 @@ jQuery.extend({ }, merge: function( first, second ) { - var pos, i = second.length; + var i = first.length, j = 0; - // We have to get length this way when IE & Opera overwrite the length - // expando of getElementsByTagName - if ( i && i.nodeType ) { - for ( i = 0; second[i]; ++i ) {} - } - - pos = i + first.length; - - // Correct length for non Arrays - first.length = pos; + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } - while ( i ) { - first[ --pos ] = second[ --i ]; + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } } + first.length = i; + return first; }, @@ -620,20 +653,64 @@ jQuery.extend({ return ret.concat.apply( [], ret ); }, + // A global GUID counter for objects + guid: 1, + + proxy: function( fn, proxy, thisObject ) { + if ( arguments.length === 2 ) { + if ( typeof proxy === "string" ) { + thisObject = fn; + fn = thisObject[ proxy ]; + proxy = undefined; + + } else if ( proxy && !jQuery.isFunction( proxy ) ) { + thisObject = proxy; + proxy = undefined; + } + } + + if ( !proxy && fn ) { + proxy = function() { + return fn.apply( thisObject || this, arguments ); + }; + } + + // Set the guid of unique handler to the same of original handler, so it can be removed + if ( fn ) { + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + } + + // So proxy can be declared as an argument + return proxy; + }, + // Use of jQuery.browser is frowned upon. - // It's included for backwards compatibility and plugins, - // although they should work to migrate away. - browser: { - version: (/.*?(?:firefox|safari|opera|msie)[\/ ]([\d.]+)/.exec(userAgent) || [0,'0'])[1], - safari: /safari/.test( userAgent ), - opera: /opera/.test( userAgent ), - msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ), - firefox: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent ) - } + // More details: http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function( ua ) { + ua = ua.toLowerCase(); + + var match = /(webkit)[ \/]([\w.]+)/.exec( ua ) || + /(opera)(?:.*version)?[ \/]([\w.]+)/.exec( ua ) || + /(msie) ([\w.]+)/.exec( ua ) || + !/compatible/.test( ua ) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec( ua ) || + []; + + return { browser: match[1] || "", version: match[2] || "0" }; + }, + + browser: {} }); -// Deprecated -jQuery.browser.mozilla = jQuery.browser.firefox; +browserMatch = jQuery.uaMatch( userAgent ); +if ( browserMatch.browser ) { + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; +} + +// Deprecated, use jQuery.browser.webkit instead +if ( jQuery.browser.webkit ) { + jQuery.browser.safari = true; +} if ( indexOf ) { jQuery.inArray = function( elem, array ) { @@ -644,6 +721,42 @@ if ( indexOf ) { // All jQuery objects should point back to these rootjQuery = jQuery(document); +// Cleanup functions for the document ready method +if ( document.addEventListener ) { + DOMContentLoaded = function() { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + }; + +} else if ( document.attachEvent ) { + DOMContentLoaded = function() { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; +} + +// The DOM ready check for Internet Explorer +function doScrollCheck() { + if ( jQuery.isReady ) { + return; + } + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch( error ) { + setTimeout( doScrollCheck, 1 ); + return; + } + + // and execute any waiting functions + jQuery.ready(); +} + function evalScript( i, elem ) { if ( elem.src ) { jQuery.ajax({ @@ -662,32 +775,31 @@ function evalScript( i, elem ) { // Mutifunctional method to get and set values to a collection // The value/s can be optionally by executed if its a function -function access( elems, key, value, exec, fn ) { - var l = elems.length; +function access( elems, key, value, exec, fn, pass ) { + var length = elems.length; // Setting many attributes if ( typeof key === "object" ) { - for (var k in key) { - access(elems, k, key[k], exec, fn); - } + for ( var k in key ) { + access( elems, k, key[k], exec, fn, value ); + } return elems; } // Setting one attribute - if (value !== undefined) { + if ( value !== undefined ) { // Optionally, function values get executed if exec is true - exec = exec && jQuery.isFunction(value); + exec = !pass && exec && jQuery.isFunction(value); - for (var i = 0; i < l; i++) { - var elem = elems[i], - val = exec ? value.call(elem, i) : value; - fn(elem, key, val); + 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 l ? fn(elems[0], key) : null; + return length ? fn( elems[0], key ) : undefined; } function now() {