jQuery.merge speedup, bug#444
[jquery.git] / src / jquery / jquery.js
index 9f9aac6..d3ed8c6 100644 (file)
@@ -22,7 +22,7 @@ window.undefined = window.undefined;
  */\r
 var jQuery = function(a,c) {\r
 \r
-       // Shortcut for document ready (because $(document).each() is silly)\r
+       // Shortcut for document ready\r
        if ( a && typeof a == "function" && jQuery.fn.ready && !a.nodeType && a[0] == undefined ) // Safari reports typeof on DOM NodeLists as a function\r
                return jQuery(document).ready(a);\r
 \r
@@ -55,6 +55,13 @@ var jQuery = function(a,c) {
                // Find the matching elements and save them for later\r
                jQuery.find( a, c ) );\r
 \r
+       // See if an extra function was provided\r
+       var fn = arguments[ arguments.length - 1 ];\r
+\r
+       // If so, execute it in context\r
+       if ( fn && typeof fn == "function" )\r
+               this.each(fn);\r
+\r
        return this;\r
 };\r
 \r
@@ -383,7 +390,7 @@ jQuery.fn = jQuery.prototype = {
         */\r
        attr: function( key, value, type ) {\r
                // Check to see if we're setting style values\r
-               return key.constructor != String || value != undefined ?\r
+               return typeof key != "string" || value != undefined ?\r
                        this.each(function(){\r
                                // See if we're setting a hash of styles\r
                                if ( value == undefined )\r
@@ -789,7 +796,7 @@ jQuery.fn = jQuery.prototype = {
        find: function(t) {\r
                return this.pushStack( jQuery.map( this, function(a){\r
                        return jQuery.find(t,a);\r
-               }));\r
+               }), arguments );\r
        },\r
 \r
        /**\r
@@ -810,7 +817,7 @@ jQuery.fn = jQuery.prototype = {
        clone: function(deep) {\r
                return this.pushStack( jQuery.map( this, function(a){\r
                        return a.cloneNode( deep != undefined ? deep : true );\r
-               }));\r
+               }), arguments );\r
        },\r
 \r
        /**\r
@@ -865,7 +872,7 @@ jQuery.fn = jQuery.prototype = {
                        typeof t == "function" &&\r
                        jQuery.grep( this, t ) ||\r
 \r
-                       jQuery.filter(t,this).r );\r
+                       jQuery.filter(t,this).r, arguments );\r
        },\r
 \r
        /**\r
@@ -899,7 +906,7 @@ jQuery.fn = jQuery.prototype = {
        not: function(t) {\r
                return this.pushStack( typeof t == "string" ?\r
                        jQuery.filter(t,this,false).r :\r
-                       jQuery.grep(this,function(a){ return a != t; }) );\r
+                       jQuery.grep(this,function(a){ return a != t; }), arguments );\r
        },\r
 \r
        /**\r
@@ -945,7 +952,7 @@ jQuery.fn = jQuery.prototype = {
         */\r
        add: function(t) {\r
                return this.pushStack( jQuery.merge( this, typeof t == "string" ?\r
-                       jQuery.find(t) : t.constructor == Array ? t : [t] ) );\r
+                       jQuery.find(t) : t.constructor == Array ? t : [t] ), arguments );\r
        },\r
 \r
        /**\r
@@ -981,33 +988,27 @@ jQuery.fn = jQuery.prototype = {
         * @private\r
         * @name domManip\r
         * @param Array args\r
-        * @param Boolean table\r
-        * @param Number dir\r
+        * @param Boolean table Insert TBODY in TABLEs if one is not found.\r
+        * @param Number dir If dir<0, process args in reverse order.\r
         * @param Function fn The function doing the DOM manipulation.\r
         * @type jQuery\r
         * @cat Core\r
         */\r
        domManip: function(args, table, dir, fn){\r
-               var clone = this.size() > 1;\r
+               var clone = this.length > 1; \r
                var a = jQuery.clean(args);\r
+               if ( dir < 0 )\r
+                       a.reverse();\r
 \r
                return this.each(function(){\r
                        var obj = this;\r
 \r
-                       if ( table && this.nodeName.toUpperCase() == "TABLE" && a[0].nodeName.toUpperCase() != "THEAD" ) {\r
-                               var tbody = this.getElementsByTagName("tbody");\r
+                       if ( table && this.nodeName.toUpperCase() == "TABLE" && a[0].nodeName.toUpperCase() == "TR" )\r
+                               obj = this.getElementsByTagName("tbody")[0] || this.appendChild(document.createElement("tbody"));\r
 \r
-                               if ( !tbody.length ) {\r
-                                       obj = document.createElement("tbody");\r
-                                       this.appendChild( obj );\r
-                               } else\r
-                                       obj = tbody[0];\r
-                       }\r
+                       for ( var i=0; i < a.length; i++ )\r
+                               fn.apply( obj, [ clone ? a[i].cloneNode(true) : a[i] ] );\r
 \r
-                       for ( var i = ( dir < 0 ? a.length - 1 : 0 );\r
-                               i != ( dir < 0 ? dir : a.length ); i += dir ) {\r
-                                       fn.apply( obj, [ clone ? a[i].cloneNode(true) : a[i] ] );\r
-                       }\r
                });\r
        },\r
 \r
@@ -1021,11 +1022,28 @@ jQuery.fn = jQuery.prototype = {
         * @type jQuery\r
         * @cat Core\r
         */\r
-       pushStack: function(a) {\r
-               if ( !this.stack )\r
-                       this.stack = [];\r
-               this.stack.push( this.get() );\r
-               return this.set( a );\r
+       pushStack: function(a,args) {\r
+               var fn = args && args.length > 1 && args[args.length-1];\r
+               var fn2 = args && args.length > 2 && args[args.length-2];\r
+               \r
+               if ( fn && fn.constructor != Function ) fn = null;\r
+               if ( fn2 && fn2.constructor != Function ) fn2 = null;\r
+\r
+               if ( !fn ) {\r
+                       if ( !this.stack ) this.stack = [];\r
+                       this.stack.push( this.get() );\r
+                       this.set( a );\r
+               } else {\r
+                       var old = this.get();\r
+                       this.set( a );\r
+\r
+                       if ( fn2 && a.length || !fn2 )\r
+                               this.each( fn2 || fn ).set( old );\r
+                       else\r
+                               this.set( old ).each( fn );\r
+               }\r
+\r
+               return this;\r
        }\r
 };\r
 \r
@@ -1285,11 +1303,6 @@ jQuery.extend({
 \r
                        ret = elem.style[prop];\r
 \r
-               } else if (elem.currentStyle) {\r
-\r
-                       var newProp = prop.replace(/\-(\w)/g,function(m,c){return c.toUpperCase();});\r
-                       ret = elem.currentStyle[prop] || elem.currentStyle[newProp];\r
-\r
                } else if (document.defaultView && document.defaultView.getComputedStyle) {\r
 \r
                        if (prop == "cssFloat" || prop == "styleFloat")\r
@@ -1308,31 +1321,54 @@ jQuery.extend({
                                    ret = c && c.getPropertyValue(prop) || '';\r
                                });\r
 \r
+               } else if (elem.currentStyle) {\r
+\r
+                       var newProp = prop.replace(/\-(\w)/g,function(m,c){return c.toUpperCase();});\r
+                       ret = elem.currentStyle[prop] || elem.currentStyle[newProp];\r
+                       \r
                }\r
 \r
                return ret;\r
        },\r
        \r
-       clean: function(a) {\r
+               clean: function(a) {\r
                var r = [];\r
                for ( var i = 0; i < a.length; i++ ) {\r
                        var arg = a[i];\r
                        if ( typeof arg == "string" ) { // Convert html string into DOM nodes\r
                                // Trim whitespace, otherwise indexOf won't work as expected\r
-                               var s = jQuery.trim(arg), div = document.createElement("div"), wrap = [0,"",""];\r
+                               var s = jQuery.trim(arg), s3 = s.substring(0,3), s6 = s.substring(0,6),\r
+                                       div = document.createElement("div"), wrap = [0,"",""];\r
 \r
-                               if ( !s.indexOf("<opt") ) // option or optgroup\r
+                               if ( s.substring(0,4) == "<opt" ) // option or optgroup\r
                                        wrap = [1, "<select>", "</select>"];\r
-                               else if ( !s.indexOf("<thead") || !s.indexOf("<tbody") )\r
+                               else if ( s6 == "<thead" || s6 == "<tbody" || s6 == "<tfoot" )\r
                                        wrap = [1, "<table>", "</table>"];\r
-                               else if ( !s.indexOf("<tr") )\r
-                                       wrap = [2, "<table>", "</table>"];      // tbody auto-inserted\r
-                               else if ( !s.indexOf("<td") || !s.indexOf("<th") )\r
+                               else if ( s3 == "<tr" )\r
+                                       wrap = [2, "<table><tbody>", "</tbody></table>"];\r
+                               else if ( s3 == "<td" || s3 == "<th" ) // <thead> matched above\r
                                        wrap = [3, "<table><tbody><tr>", "</tr></tbody></table>"];\r
 \r
                                // Go to html and back, then peel off extra wrappers\r
                                div.innerHTML = wrap[1] + s + wrap[2];\r
                                while ( wrap[0]-- ) div = div.firstChild;\r
+                               \r
+                               // Remove IE's autoinserted <tbody> from table fragments\r
+                               if ( jQuery.browser.msie ) {\r
+                                       var tb = null;\r
+                                       // String was a <table>, *may* have spurious <tbody>\r
+                                       if ( s6 == "<table" && s.indexOf("<tbody") < 0 ) \r
+                                               tb = div.firstChild && div.firstChild.childNodes;\r
+                                       // String was a bare <thead> or <tfoot>\r
+                                       else if ( wrap[1] == "<table>" && s.indexOf("<tbody") < 0 )\r
+                                               tb = div.childNodes;\r
+                                       if ( tb ) {\r
+                                               for ( var n = tb.length-1; n >= 0 ; --n )\r
+                                                       if ( tb[n].nodeName.toUpperCase() == "TBODY" && !tb[n].childNodes.length )\r
+                                                               tb[n].parentNode.removeChild(tb[n]);\r
+                                       }\r
+                               }\r
+                               \r
                                arg = div.childNodes;\r
                        } \r
                        \r
@@ -1349,7 +1385,7 @@ jQuery.extend({
 \r
        expr: {\r
                "": "m[2]== '*'||a.nodeName.toUpperCase()==m[2].toUpperCase()",\r
-               "#": "a.getAttribute('id')&&a.getAttribute('id')==m[2]",\r
+               "#": "a.getAttribute('id')==m[2]",\r
                ":": {\r
                        // Position Checks\r
                        lt: "i<m[3]-0",\r
@@ -1432,7 +1468,7 @@ jQuery.extend({
                // Set the correct context (if none is provided)\r
                context = context || document;\r
 \r
-               if ( t.constructor != String ) return [t];\r
+               if ( typeof t != "string" ) return [t];\r
 \r
                if ( !t.indexOf("//") ) {\r
                        context = context.documentElement;\r
@@ -1480,13 +1516,13 @@ jQuery.extend({
                                        var re2 = /^([#.]?)([a-z0-9\\*_-]*)/i;\r
                                        var m = re2.exec(t);\r
 \r
-                                       if ( m[1] == "#" ) {\r
-                                               // Ummm, should make this work in all XML docs\r
-                                               var oid = document.getElementById(m[2]);\r
+                                       if ( m[1] == "#" && ret[ret.length-1].getElementById ) {\r
+                                               // Optimization for HTML document case\r
+                                               var oid = ret[ret.length-1].getElementById(m[2]);\r
                                                r = ret = oid ? [oid] : [];\r
                                                t = t.replace( re2, "" );\r
                                        } else {\r
-                                               if ( !m[2] || m[1] == "." ) m[2] = "*";\r
+                                               if ( !m[2] || m[1] == "." || m[1] == "#" ) m[2] = "*";\r
 \r
                                                for ( var i = 0; i < ret.length; i++ )\r
                                                        r = jQuery.merge( r,\r
@@ -1621,7 +1657,7 @@ jQuery.extend({
                        // Otherwise, find the expression to execute\r
                        else {\r
                                var f = jQuery.expr[m[1]];\r
-                               if ( f.constructor != String )\r
+                               if ( typeof f != "string" )\r
                                        f = jQuery.expr[m[1]][m[2]];\r
 \r
                                // Build a custom macro to enclose it\r
@@ -1730,19 +1766,15 @@ jQuery.extend({
                for ( var k = 0; k < first.length; k++ )\r
                        result[k] = first[k];\r
 \r
-               // Now check for duplicates between a and b and only\r
-               // add the unique items\r
+               // Now check for duplicates between a and b\r
+               // and only add the unique items\r
+          DupCheck:\r
                for ( var i = 0; i < second.length; i++ ) {\r
-                       var noCollision = true;\r
-\r
-                       // The collision-checking process\r
                        for ( var j = 0; j < first.length; j++ )\r
                                if ( second[i] == first[j] )\r
-                                       noCollision = false;\r
-\r
-                       // If the item is unique, add it\r
-                       if ( noCollision )\r
-                               result.push( second[i] );\r
+                                       continue DupCheck;\r
+                       // The item is unique, add it\r
+                       result.push( second[i] );\r
                }\r
 \r
                return result;\r
@@ -1992,6 +2024,11 @@ jQuery.extend({
  * This property is available before the DOM is ready, therefore you can\r
  * use it to add ready events only for certain browsers.\r
  *\r
+ * There are situations where object detections is not reliable enough, in that\r
+ * cases it makes sense to use browser detection. Simply try to avoid both!\r
+ *\r
+ * A combination of browser and object detection yields quite reliable results.\r
+ *\r
  * @example $.browser.msie\r
  * @desc Returns true if the current useragent is some version of microsoft's internet explorer\r
  *\r