X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=src%2Fcore.js;h=56fd0983e33862cda330974947832bde770bcf3d;hb=58235cc38ea835cde0ae90dd2919b86e47f3dd05;hp=e2f1f0b330ec6f796158ece5dc50dba0fa54c5af;hpb=cc70e03ec7387aaab5f1e090fde2a3faeb001ba6;p=jquery.git diff --git a/src/core.js b/src/core.js index e2f1f0b..56fd098 100644 --- a/src/core.js +++ b/src/core.js @@ -10,7 +10,9 @@ var jQuery = window.jQuery = window.$ = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context ); + return selector === undefined ? + rootjQuery : + new jQuery.fn.init( selector, context ); }, // A simple way to check for HTML strings or ID strings @@ -21,8 +23,11 @@ var jQuery.fn = jQuery.prototype = { init: function( selector, context ) { - // Make sure that a selection was provided - selector = selector || document; + // Handle $("") or $(null) + if ( !selector ) { + this.length = 0; + return this; + } // Handle $(DOMElement) if ( selector.nodeType ) { @@ -31,6 +36,7 @@ jQuery.fn = jQuery.prototype = { this.context = selector; return this; } + // Handle HTML strings if ( typeof selector === "string" ) { // Are we dealing with HTML string or an ID? @@ -40,40 +46,51 @@ jQuery.fn = jQuery.prototype = { if ( match && (match[1] || !context) ) { // HANDLE: $(html) -> $(array) - if ( match[1] ) + if ( match[1] ) { selector = jQuery.clean( [ match[1] ], context ); // HANDLE: $("#id") - else { + } else { var elem = document.getElementById( match[3] ); - // Make sure an element was located - if ( elem ){ - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id != match[3] ) - return jQuery().find( selector ); - - // Otherwise, we inject the element directly into the jQuery object - var ret = jQuery( elem ); - ret.context = document; - ret.selector = selector; - return ret; + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem && elem.id != match[3] ) { + return rootjQuery.find( selector ); } - selector = []; + + // Otherwise, we inject the element directly into the jQuery object + var ret = jQuery( elem || null ); + ret.context = document; + ret.selector = selector; + return ret; } - // HANDLE: $(expr, [context]) + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return (context || rootjQuery).find( selector ); + + // HANDLE: $(expr, context) // (which is just equivalent to: $(content).find(expr) - } else + } else { return jQuery( context ).find( selector ); + } // HANDLE: $(function) // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) - return jQuery( document ).ready( selector ); + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + // Make sure that old selector state is passed along + if ( selector.selector && selector.context ) { + this.selector = selector.selector; + this.context = selector.context; + } - return this.setArray(jQuery.makeArray(selector)); + return this.setArray(jQuery.isArray( selector ) ? + selector : + jQuery.makeArray(selector)); }, // Start with an empty selector @@ -93,7 +110,7 @@ jQuery.fn = jQuery.prototype = { return num === undefined ? // Return a 'clean' array - jQuery.makeArray( this ) : + Array.prototype.slice.call( this ) : // Return just the object this[ num ]; @@ -103,7 +120,7 @@ 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 ); + var ret = jQuery( elems || null ); // Add the old object onto the stack (as a reference) ret.prevObject = this; @@ -200,20 +217,22 @@ jQuery.fn = jQuery.prototype = { }, wrapAll: function( html ) { - if ( this[0] ) + if ( this[0] ) { // The elements to wrap the target around - jQuery( html, this[0].ownerDocument ) - .clone() - .insertBefore( this[0] ) - .map(function(){ - var elem = this; + var wrap = jQuery( html, this[0].ownerDocument ).clone(); - while ( elem.firstChild ) - elem = elem.firstChild; + if ( this[0].parentNode ) + wrap.insertBefore( this[0] ); - return elem; - }) - .append(this); + wrap.map(function(){ + var elem = this; + + while ( elem.firstChild ) + elem = elem.firstChild; + + return elem; + }).append(this); + } return this; }, @@ -257,21 +276,31 @@ jQuery.fn = jQuery.prototype = { }, end: function() { - return this.prevObject || jQuery( [] ); + return this.prevObject || jQuery(null); }, find: function( selector ) { - if ( this.length === 1 ) { - return this.pushStack( jQuery.find( selector, this[0] ), "find", selector ); - } else { - var elems = jQuery.map(this, function(elem){ - return jQuery.find( selector, elem ); - }); - - return this.pushStack( /[^+>] [^+>]/.test( selector ) ? - jQuery.unique( elems ) : - elems, "find", selector ); + var ret = this.pushStack( "", "find", selector ), length = 0, + splice = Array.prototype.splice; + + for ( var i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( var n = length; n < ret.length; n++ ) { + for ( var r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + splice.call(ret, n--, 1); + break; + } + } + } + } } + + return ret; }, clone: function( events ) { @@ -286,33 +315,37 @@ jQuery.fn = jQuery.prototype = { // attributes in IE that are actually only stored // as properties will not be copied (such as the // the name attribute on an input). - var clone = this.cloneNode(true), - container = document.createElement("div"); - container.appendChild(clone); - return jQuery.clean([container.innerHTML])[0]; + var html = this.outerHTML; + if ( !html ) { + var div = this.ownerDocument.createElement("div"); + div.appendChild( this.cloneNode(true) ); + html = div.innerHTML; + } + + return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0]; } else return this.cloneNode(true); }); - // Need to set the expando to null on the cloned set if it exists - // removeData doesn't work here, IE removes it from the original as well - // this is primarily for IE but the data expando shouldn't be copied over in any browser - var clone = ret.find("*").andSelf().each(function(){ - if ( this[ expando ] !== undefined ) - this[ expando ] = null; - }); - // Copy the events from the original to the clone - if ( events === true ) - this.find("*").andSelf().each(function(i){ - if (this.nodeType == 3) + if ( events === true ) { + var orig = this.find("*").andSelf(), i = 0; + + ret.find("*").andSelf().each(function(){ + if ( this.nodeName !== orig[i].nodeName ) return; - var events = jQuery.data( this, "events" ); - for ( var type in events ) - for ( var handler in events[ type ] ) - jQuery.event.add( clone[ i ], type, events[ type ][ handler ], events[ type ][ handler ].data ); + var events = jQuery.data( orig[i], "events" ); + + for ( var type in events ) { + for ( var handler in events[ type ] ) { + jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data ); + } + } + + i++; }); + } // Return the cloned set return ret; @@ -331,12 +364,18 @@ jQuery.fn = jQuery.prototype = { }, closest: function( selector ) { + var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null, + closer = 0; + return this.map(function(){ var cur = this; while ( cur && cur.ownerDocument ) { - if ( jQuery(cur).is(selector) ) + if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) { + jQuery.data(cur, "closest", closer); return cur; + } cur = cur.parentNode; + closer++; } }); }, @@ -449,7 +488,7 @@ jQuery.fn = jQuery.prototype = { html: function( value ) { return value === undefined ? (this[0] ? - this[0].innerHTML : + this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") : null) : this.empty().append( value ); }, @@ -481,13 +520,13 @@ jQuery.fn = jQuery.prototype = { if ( this[0] ) { var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(), scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ), - first = fragment.firstChild, - extra = this.length > 1 ? fragment.cloneNode(true) : fragment; + first = fragment.firstChild; if ( first ) for ( var i = 0, l = this.length; i < l; i++ ) - callback.call( root(this[i], first), i > 0 ? extra.cloneNode(true) : fragment ); - + callback.call( root(this[i], first), this.length > 1 || i > 0 ? + fragment.cloneNode(true) : fragment ); + if ( scripts ) jQuery.each( scripts, evalScript ); } @@ -604,15 +643,13 @@ jQuery.extend({ // check if an element is in a (or is an) XML document isXMLDoc: function( elem ) { - return elem.documentElement && !elem.body || - elem.tagName && elem.ownerDocument && !elem.ownerDocument.body; + return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || + !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument ); }, // Evalulates a script in a global context globalEval: function( data ) { - data = jQuery.trim( data ); - - if ( data ) { + if ( data && /\S/.test(data) ) { // Inspired by code by Andrea Giammarchi // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html var head = document.getElementsByTagName("head")[0] || document.documentElement, @@ -695,7 +732,7 @@ jQuery.extend({ // internal only, use hasClass("class") has: function( elem, className ) { - return jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; + return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; } }, @@ -715,26 +752,32 @@ jQuery.extend({ elem.style[ name ] = old[ name ]; }, - css: function( elem, name, force ) { + css: function( elem, name, force, extra ) { if ( name == "width" || name == "height" ) { var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; function getWH() { val = name == "width" ? elem.offsetWidth : elem.offsetHeight; - var padding = 0, border = 0; + + if ( extra === "border" ) + return; + jQuery.each( which, function() { - padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; - border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; + if ( !extra ) + val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; + if ( extra === "margin" ) + val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0; + else + val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; }); - val -= Math.round(padding + border); } - if ( jQuery(elem).is(":visible") ) + if ( elem.offsetWidth !== 0 ) getWH(); else jQuery.swap( elem, props, getWH ); - return Math.max(0, val); + return Math.max(0, Math.round(val)); } return jQuery.curCSS( elem, name, force ); @@ -840,7 +883,7 @@ jQuery.extend({ }); // Trim whitespace, otherwise indexOf won't work as expected - var tags = jQuery.trim( elem ).toLowerCase(); + var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase(); var wrap = // option or optgroup @@ -880,11 +923,12 @@ jQuery.extend({ if ( !jQuery.support.tbody ) { // String was a , *may* have spurious - var tbody = !tags.indexOf(" or - wrap[1] == "
" && tags.indexOf("" && !hasBody ? div.childNodes : []; @@ -910,8 +954,8 @@ jQuery.extend({ if ( fragment ) { for ( var i = 0; ret[i]; i++ ) { - if ( jQuery.nodeName( ret[i], "script" ) ) { - scripts.push( ret[i].parentNode.removeChild( ret[i] ) ); + if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) { + scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] ); } else { if ( ret[i].nodeType === 1 ) ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) ); @@ -946,7 +990,7 @@ jQuery.extend({ // Safari mis-reports the default selected property of a hidden option // Accessing the parent's selectedIndex property fixes it - if ( name == "selected" ) + if ( name == "selected" && elem.parentNode ) elem.parentNode.selectedIndex; // If applicable, access the attribute via the DOM 0 way @@ -963,11 +1007,17 @@ jQuery.extend({ if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) return elem.getAttributeNode( name ).nodeValue; - // elem.tabindex doesn't always return the correct value + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ - if ( name == jQuery.props.tabindex ) { - var attributeNode = elem.getAttributeNode(jQuery.props.tabindex); - return attributeNode && attributeNode.specified && attributeNode.value || undefined; + if ( name == "tabIndex" ) { + var attributeNode = elem.getAttributeNode( "tabIndex" ); + return attributeNode && attributeNode.specified + ? attributeNode.value + : elem.nodeName.match(/(button|input|object|select|textarea)/i) + ? 0 + : elem.nodeName.match(/^(a|area)$/i) && elem.href + ? 0 + : undefined; } return elem[ name ]; @@ -1114,6 +1164,9 @@ jQuery.extend({ } }); +// All jQuery objects should point back to these +var rootjQuery = jQuery(document); + // Use of jQuery.browser is deprecated. // It's included for backwards compatibility and plugins, // although they should work to migrate away. @@ -1129,9 +1182,6 @@ jQuery.browser = { mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent ) }; -// Check to see if the W3C box model is being used -jQuery.boxModel = !jQuery.browser.msie || document.compatMode == "CSS1Compat"; - jQuery.each({ parent: function(elem){return elem.parentNode;}, parents: function(elem){return jQuery.dir(elem,"parentNode");}, @@ -1160,13 +1210,16 @@ jQuery.each({ insertAfter: "after", replaceAll: "replaceWith" }, function(name, original){ - jQuery.fn[ name ] = function() { - var args = arguments; + jQuery.fn[ name ] = function( selector ) { + var ret = [], insert = jQuery( selector ); - return this.each(function(){ - for ( var i = 0, length = args.length; i < length; i++ ) - jQuery( args[ i ] )[ original ]( this ); - }); + for ( var i = 0, l = insert.length; i < l; i++ ) { + var elems = (i > 0 ? this.clone(true) : this).get(); + jQuery.fn[ original ].apply( jQuery(insert[i]), elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, selector ); }; }); @@ -1192,24 +1245,28 @@ jQuery.each({ }, remove: function( selector ) { - if ( !selector || jQuery.filter( selector, [ this ] ).length ) { - // Prevent memory leaks - jQuery( "*", this ).add([this]).each(function(){ - jQuery.event.remove(this); - jQuery.removeData(this); - }); - if (this.parentNode) + if ( !selector || jQuery.multiFilter( selector, [ this ] ).length ) { + if ( this.nodeType === 1 ) { + cleanData( this.getElementsByTagName("*") ); + cleanData( [this] ); + } + + if ( this.parentNode ) { this.parentNode.removeChild( this ); + } } }, empty: function() { // Remove element nodes and prevent memory leaks - jQuery( ">*", this ).remove(); + if ( this.nodeType === 1 ) { + cleanData( this.getElementsByTagName("*") ); + } // Remove any remaining nodes - while ( this.firstChild ) + while ( this.firstChild ) { this.removeChild( this.firstChild ); + } } }, function(name, fn){ jQuery.fn[ name ] = function(){ @@ -1217,6 +1274,15 @@ jQuery.each({ }; }); +function cleanData( elems ) { + for ( var i = 0, l = elems.length; i < l; i++ ) { + var id = elems[i][expando]; + if ( id ) { + delete jQuery.cache[ id ]; + } + } +} + // Helper function used by the dimensions and offset modules function num(elem, prop) { return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;