De-eval'd selectors and the various DOM methods (will marginally help our speed and...
[jquery.git] / src / core.js
index 39ffdb5..d8526d7 100644 (file)
@@ -29,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
@@ -290,7 +293,7 @@ jQuery.fn = jQuery.prototype = {
        clone: function( events ) {
                // Do the clone
                var ret = this.map(function(){
-                       if ( jQuery.browser.msie ) {
+                       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
@@ -300,11 +303,9 @@ jQuery.fn = jQuery.prototype = {
                                // as properties will not be copied (such as the
                                // the name attribute on an input).
                                var clone = this.cloneNode(true),
-                                       container = document.createElement("div"),
-                                       container2 = document.createElement("div");
+                                       container = document.createElement("div");
                                container.appendChild(clone);
-                               container2.innerHTML = container.innerHTML;
-                               return container2.firstChild;
+                               return jQuery.clean([container.innerHTML])[0];
                        } else
                                return this.cloneNode(true);
                });
@@ -320,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 )
@@ -342,15 +345,17 @@ 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 ) {
@@ -414,31 +419,33 @@ jQuery.fn = jQuery.prototype = {
 
                        }
 
-               } else
-                       return this.each(function(){
-                               if ( this.nodeType != 1 )
-                                       return;
+                       return undefined;
+               }
+
+               return this.each(function(){
+                       if ( this.nodeType != 1 )
+                               return;
 
-                               if ( value.constructor == Array && /radio|checkbox/.test( this.type ) )
-                                       this.checked = (jQuery.inArray(this.value, value) >= 0 ||
-                                               jQuery.inArray(this.name, value) >= 0);
+                       if ( value.constructor == Array && /radio|checkbox/.test( this.type ) )
+                               this.checked = (jQuery.inArray(this.value, value) >= 0 ||
+                                       jQuery.inArray(this.name, value) >= 0);
 
-                               else if ( jQuery.nodeName( this, "select" ) ) {
-                                       var values = value.constructor == Array ?
-                                               value :
-                                               [ value ];
+                       else if ( jQuery.nodeName( this, "select" ) ) {
+                               var values = value.constructor == Array ?
+                                       value :
+                                       [ value ];
 
-                                       jQuery( "option", this ).each(function(){
-                                               this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
-                                                       jQuery.inArray( this.text, values ) >= 0);
-                                       });
+                               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;
+                               if ( !values.length )
+                                       this.selectedIndex = -1;
 
-                               } else
-                                       this.value = value;
-                       });
+                       } else
+                               this.value = value;
+               });
        },
        
        html: function( value ) {
@@ -491,19 +498,12 @@ jQuery.fn = jQuery.prototype = {
 
                        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 )
@@ -597,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 + "" );
@@ -706,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] ){}
                }
@@ -765,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 ];
                }
 
@@ -775,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 );
        },
 
@@ -851,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 ) {
@@ -953,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 + "></" + tag + ">";
                                });
@@ -1007,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 ) )       
@@ -1173,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
@@ -1250,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 );
 
@@ -1351,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 ?