X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=src%2Fcore.js;h=098d4bd39568b2af9e91ae1166471f647994cc03;hb=25c76232c6cacc643d1142b5f951a0e5fcc12b6b;hp=4c12150dbb1dc239ca31482b8f636b0f77544e84;hpb=95c0265486fc551a08ec20d6060a3a382aa8a76b;p=jquery.git diff --git a/src/core.js b/src/core.js index 4c12150..098d4bd 100644 --- a/src/core.js +++ b/src/core.js @@ -1,7 +1,7 @@ /* * jQuery @VERSION - New Wave Javascript * - * Copyright (c) 2007 John Resig (jquery.com) + * Copyright (c) 2008 John Resig (jquery.com) * Dual licensed under the MIT (MIT-LICENSE.txt) * and GPL (GPL-LICENSE.txt) licenses. * @@ -14,10 +14,8 @@ if ( window.jQuery ) var _jQuery = window.jQuery; var jQuery = window.jQuery = function( selector, context ) { - // If the context is a namespace object, return a new object - return this instanceof jQuery ? - this.init( selector, context ) : - new jQuery( selector, context ); + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.prototype.init( selector, context ); }; // Map over the $ in case of overwrite @@ -31,6 +29,9 @@ window.$ = jQuery; // (both of which we optimize for) var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/; +// Is it a simple selector +var isSimple = /^.[^:#\[\.]*$/; + jQuery.fn = jQuery.prototype = { init: function( selector, context ) { // Make sure that a selection was provided @@ -202,7 +203,7 @@ jQuery.fn = jQuery.prototype = { text: function( text ) { if ( typeof text != "object" && text != null ) - return this.empty().append( document.createTextNode( text ) ); + return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); var ret = ""; @@ -251,13 +252,15 @@ jQuery.fn = jQuery.prototype = { append: function() { return this.domManip(arguments, true, false, function(elem){ - this.appendChild( elem ); + if (this.nodeType == 1) + this.appendChild( elem ); }); }, prepend: function() { return this.domManip(arguments, true, true, function(elem){ - this.insertBefore( elem, this.firstChild ); + if (this.nodeType == 1) + this.insertBefore( elem, this.firstChild ); }); }, @@ -290,9 +293,21 @@ jQuery.fn = jQuery.prototype = { clone: function( events ) { // Do the clone var ret = this.map(function(){ - return this.outerHTML ? - jQuery( this.outerHTML )[0] : - this.cloneNode( true ); + if ( jQuery.browser.msie && !jQuery.isXMLDoc(this) ) { + // IE copies events bound via attachEvent when + // using cloneNode. Calling detachEvent on the + // clone will also remove the events from the orignal + // In order to get around this, we use innerHTML. + // Unfortunately, this means some modifications to + // 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]; + } else + return this.cloneNode(true); }); // Need to set the expando to null on the cloned set if it exists @@ -306,6 +321,8 @@ jQuery.fn = jQuery.prototype = { // Copy the events from the original to the clone if ( events === true ) this.find("*").andSelf().each(function(i){ + if (this.nodeType == 3) + return; var events = jQuery.data( this, "events" ); for ( var type in events ) @@ -328,19 +345,21 @@ jQuery.fn = jQuery.prototype = { }, not: function( selector ) { - return this.pushStack( - selector.constructor == String && - jQuery.multiFilter( selector, this, true ) || - - jQuery.grep(this, function(elem) { - return selector.constructor == Array || selector.jquery ? - jQuery.inArray( elem, selector ) < 0 : - elem != selector; - }) ); + if ( selector.constructor == String ) + // test special case where just one selector is passed in + if ( isSimple.test( selector ) ) + return this.pushStack( jQuery.multiFilter( selector, this, true ) ); + else + selector = jQuery.multiFilter( selector, this ); + + var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType; + return this.filter(function() { + return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector; + }); }, add: function( selector ) { - return this.pushStack( jQuery.merge( + return !selector ? this : this.pushStack( jQuery.merge( this.get(), selector.constructor == String ? jQuery( selector ).get() : @@ -400,28 +419,33 @@ jQuery.fn = jQuery.prototype = { } - } else - return this.each(function(){ - if ( value.constructor == Array && /radio|checkbox/.test( this.type ) ) - this.checked = (jQuery.inArray(this.value, value) >= 0 || - jQuery.inArray(this.name, value) >= 0); + return undefined; + } - else if ( jQuery.nodeName( this, "select" ) ) { - var values = value.constructor == Array ? - value : - [ value ]; + return this.each(function(){ + if ( this.nodeType != 1 ) + return; - jQuery( "option", this ).each(function(){ - this.selected = (jQuery.inArray( this.value, values ) >= 0 || - jQuery.inArray( this.text, values ) >= 0); - }); + if ( value.constructor == Array && /radio|checkbox/.test( this.type ) ) + this.checked = (jQuery.inArray(this.value, value) >= 0 || + jQuery.inArray(this.name, value) >= 0); - if ( !values.length ) - this.selectedIndex = -1; + else if ( jQuery.nodeName( this, "select" ) ) { + var values = value.constructor == Array ? + value : + [ value ]; - } else - this.value = value; - }); + jQuery( "option", this ).each(function(){ + this.selected = (jQuery.inArray( this.value, values ) >= 0 || + jQuery.inArray( this.text, values ) >= 0); + }); + + if ( !values.length ) + this.selectedIndex = -1; + + } else + this.value = value; + }); }, html: function( value ) { @@ -468,25 +492,18 @@ jQuery.fn = jQuery.prototype = { var obj = this; if ( table && jQuery.nodeName( this, "table" ) && jQuery.nodeName( elems[0], "tr" ) ) - obj = this.getElementsByTagName("tbody")[0] || this.appendChild( document.createElement("tbody") ); + obj = this.getElementsByTagName("tbody")[0] || this.appendChild( this.ownerDocument.createElement("tbody") ); var scripts = jQuery( [] ); jQuery.each(elems, function(){ var elem = clone ? - this.cloneNode( true ) : + jQuery( this ).clone( true )[0] : this; + // execute all scripts after the elements have been injected if ( jQuery.nodeName( elem, "script" ) ) { - - // If scripts are waiting to be executed, wait on this script as well - if ( scripts.length ) - scripts = scripts.add( elem ); - - // If nothing is waiting to be executed, run immediately - else - evalScript( 0, elem ); - + scripts = scripts.add( elem ); } else { // Remove any inner scripts for later evaluation if ( elem.nodeType == 1 ) @@ -502,6 +519,9 @@ jQuery.fn = jQuery.prototype = { } }; +// Give the init function the jQuery prototype for later instantiation +jQuery.prototype.init.prototype = jQuery.prototype; + function evalScript( i, elem ) { if ( elem.src ) jQuery.ajax({ @@ -530,7 +550,7 @@ jQuery.extend = jQuery.fn.extend = function() { } // Handle case when target is a string or something (possible in deep copy) - if ( typeof target != "object" ) + if ( typeof target != "object" && typeof target != "function" ) target = {}; // extend jQuery itself if only one argument is passed @@ -553,7 +573,7 @@ jQuery.extend = jQuery.fn.extend = function() { target[ name ] = jQuery.extend( target[ name ], options[ name ] ); // Don't bring in undefined values - else if ( options[ name ] !== undefined ) + else if ( options[ name ] != undefined ) target[ name ] = options[ name ]; } @@ -577,8 +597,7 @@ jQuery.extend({ return jQuery; }, - // This may seem like some crazy code, but trust me when I say that this - // is the only cross-browser way to do this. --John + // See test/unit/core.js for details concerning this function. isFunction: function( fn ) { return !!fn && typeof fn != "string" && !fn.nodeName && fn.constructor != Array && /function/i.test( fn + "" ); @@ -591,7 +610,6 @@ jQuery.extend({ }, // Evalulates a script in a global context - // Evaluates Async. in Safari 2 :-( globalEval: function( data ) { data = jQuery.trim( data ); @@ -687,20 +705,22 @@ jQuery.extend({ // args is for internal usage only each: function( object, callback, args ) { if ( args ) { - if ( object.length == undefined ) + if ( object.length == undefined ) { for ( var name in object ) - callback.apply( object[ name ], args ); - else + if ( callback.apply( object[ name ], args ) === false ) + break; + } else for ( var i = 0, length = object.length; i < length; i++ ) if ( callback.apply( object[ i ], args ) === false ) break; // A special, fast, case for the most common use of each } else { - if ( object.length == undefined ) + if ( object.length == undefined ) { for ( var name in object ) - callback.call( object[ name ], name, object[ name ] ); - else + if ( callback.call( object[ name ], name, object[ name ] ) === false ) + break; + } else for ( var i = 0, length = object.length, value = object[0]; i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} } @@ -723,18 +743,19 @@ jQuery.extend({ // internal only, use addClass("class") add: function( elem, classNames ) { jQuery.each((classNames || "").split(/\s+/), function(i, className){ - if ( !jQuery.className.has( elem.className, className ) ) + if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) ) elem.className += (elem.className ? " " : "") + className; }); }, // internal only, use removeClass("class") remove: function( elem, classNames ) { - elem.className = classNames != undefined ? - jQuery.grep(elem.className.split(/\s+/), function(className){ - return !jQuery.className.has( classNames, className ); - }).join(" ") : - ""; + if (elem.nodeType == 1) + elem.className = classNames != undefined ? + jQuery.grep(elem.className.split(/\s+/), function(className){ + return !jQuery.className.has( classNames, className ); + }).join(" ") : + ""; }, // internal only, use is(".class") @@ -745,9 +766,10 @@ jQuery.extend({ // A method for quickly swapping in/out CSS properties to get correct calculations swap: function( elem, options, callback ) { + var old = {}; // Remember the old values, and insert the new ones for ( var name in options ) { - elem.style[ "old" + name ] = elem.style[ name ]; + old[ name ] = elem.style[ name ]; elem.style[ name ] = options[ name ]; } @@ -755,59 +777,31 @@ jQuery.extend({ // Revert the old values for ( var name in options ) - elem.style[ name ] = elem.style[ "old" + name ]; + elem.style[ name ] = old[ name ]; }, css: function( elem, name, force ) { - if ( name == "height" || name == "width" ) { - var old = {}, height, width; - - // Revert the padding and border widths to get the - // correct height/width values - jQuery.each([ "Top", "Bottom", "Right", "Left" ], function(){ - old[ "padding" + this ] = 0; - old[ "border" + this + "Width" ] = 0; - }); - - // Swap out the padding/border values temporarily - jQuery.swap( elem, old, function() { - - // If the element is visible, then the calculation is easy - if ( jQuery( elem ).is(":visible") ) { - height = elem.offsetHeight; - width = elem.offsetWidth; - - // Otherwise, we need to flip out more values - } else { - elem = jQuery( elem.cloneNode(true) ) - .find(":radio").removeAttr("checked").removeAttr("defaultChecked").end() - .css({ - visibility: "hidden", - position: "absolute", - display: "block", - right: "0", - left: "0" - }).appendTo( elem.parentNode )[0]; - - var position = jQuery.css( elem.parentNode, "position" ) || "static"; - if ( position == "static" ) - elem.parentNode.style.position = "relative"; - - height = elem.clientHeight; - width = elem.clientWidth; - - if ( position == "static" ) - elem.parentNode.style.position = "static"; - - elem.parentNode.removeChild( elem ); - } - }); - - return name == "height" ? - height : - width; + 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; + jQuery.each( which, function() { + padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; + border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; + }); + val -= Math.round(padding + border); + } + + if ( jQuery(elem).is(":visible") ) + getWH(); + else + jQuery.swap( elem, props, getWH ); + + return Math.max(0, val); } - + return jQuery.curCSS( elem, name, force ); }, @@ -831,12 +825,18 @@ jQuery.extend({ "1" : ret; } + // Opera sometimes will give the wrong display answer, this fixes it, see #2037 + if ( jQuery.browser.opera && name == "display" ) { + var save = elem.style.display; + elem.style.display = "block"; + elem.style.display = save; + } // Make sure we're using the right name for getting the float value if ( name.match( /float/i ) ) name = styleFloat; - if ( !force && elem.style[ name ] ) + if ( !force && elem.style && elem.style[ name ] ) ret = elem.style[ name ]; else if ( document.defaultView && document.defaultView.getComputedStyle ) { @@ -918,6 +918,9 @@ jQuery.extend({ clean: function( elems, context ) { var ret = []; context = context || document; + // !context.createElement fails in IE with an error but returns typeof 'object' + if (typeof context.createElement == 'undefined') + context = context.ownerDocument || context[0] && context[0].ownerDocument || document; jQuery.each(elems, function(i, elem){ if ( !elem ) @@ -930,7 +933,7 @@ jQuery.extend({ if ( typeof elem == "string" ) { // Fix "XHTML"-style tags in all browsers elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ - return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area)$/i) ? + return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? all : front + ">"; }); @@ -984,9 +987,9 @@ jQuery.extend({ div.childNodes : []; - for ( var i = tbody.length - 1; i >= 0 ; --i ) - if ( jQuery.nodeName( tbody[ i ], "tbody" ) && !tbody[ i ].childNodes.length ) - tbody[ i ].parentNode.removeChild( tbody[ i ] ); + for ( var j = tbody.length - 1; j >= 0 ; --j ) + if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) + tbody[ j ].parentNode.removeChild( tbody[ j ] ); // IE completely kills leading whitespace when innerHTML is used if ( /^\s/.test( elem ) ) @@ -1012,6 +1015,10 @@ jQuery.extend({ }, attr: function( elem, name, value ) { + // don't set attributes on text and comment nodes + if (!elem || elem.nodeType == 3 || elem.nodeType == 8) + return undefined; + var fix = jQuery.isXMLDoc( elem ) ? {} : jQuery.props; @@ -1065,7 +1072,7 @@ jQuery.extend({ (parseFloat( value ).toString() == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")"); } - return elem.filter ? + return elem.filter && elem.filter.indexOf("opacity=") >= 0 ? (parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100).toString() : ""; } @@ -1146,11 +1153,6 @@ jQuery.extend({ }, grep: function( elems, callback, inv ) { - // If a string is passed in for the function, make a function - // for it (a handy shortcut) - if ( typeof callback == "string" ) - callback = eval("false||function(a,i){return " + callback + "}"); - var ret = []; // Go through the array, only saving the items @@ -1223,18 +1225,16 @@ jQuery.extend({ }); jQuery.each({ - parent: "elem.parentNode", - parents: "jQuery.dir(elem,'parentNode')", - next: "jQuery.nth(elem,2,'nextSibling')", - prev: "jQuery.nth(elem,2,'previousSibling')", - nextAll: "jQuery.dir(elem,'nextSibling')", - prevAll: "jQuery.dir(elem,'previousSibling')", - siblings: "jQuery.sibling(elem.parentNode.firstChild,elem)", - children: "jQuery.sibling(elem.firstChild)", - contents: "jQuery.nodeName(elem,'iframe')?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes)" + parent: function(elem){return elem.parentNode;}, + parents: function(elem){return jQuery.dir(elem,"parentNode");}, + next: function(elem){return jQuery.nth(elem,2,"nextSibling");}, + prev: function(elem){return jQuery.nth(elem,2,"previousSibling");}, + nextAll: function(elem){return jQuery.dir(elem,"nextSibling");}, + prevAll: function(elem){return jQuery.dir(elem,"previousSibling");}, + siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);}, + children: function(elem){return jQuery.sibling(elem.firstChild);}, + contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);} }, function(name, fn){ - fn = eval("false||function(elem){return " + fn + "}"); - jQuery.fn[ name ] = function( selector ) { var ret = jQuery.map( this, fn ); @@ -1265,7 +1265,8 @@ jQuery.each({ jQuery.each({ removeAttr: function( name ) { jQuery.attr( this, name, "" ); - this.removeAttribute( name ); + if (this.nodeType == 1) + this.removeAttribute( name ); }, addClass: function( classNames ) { @@ -1323,8 +1324,11 @@ jQuery.each([ "Height", "Width" ], function(i, name){ // Get document width or height this[0] == document ? - // Either scroll[Width/Height] or offset[Width/Height], whichever is greater (Mozilla reports scrollWidth the same as offsetWidth) - Math.max( document.body[ "scroll" + name ], document.body[ "offset" + name ] ) : + // Either scroll[Width/Height] or offset[Width/Height], whichever is greater + Math.max( + Math.max(document.body["scroll" + name], document.documentElement["scroll" + name]), + Math.max(document.body["offset" + name], document.documentElement["offset" + name]) + ) : // Get or set width or height on the element size == undefined ?