Simplified the structure of the .css() and .attr() methods (reducing the number of...
[jquery.git] / src / core.js
index 508060a..c12b583 100644 (file)
@@ -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,34 +46,41 @@ 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] );
 
                                        // Handle the case where IE and Opera return items
                                        // by name instead of ID
-                                       if ( elem && elem.id != match[3] )
-                                               return jQuery().find( selector );
+                                       if ( elem && elem.id != match[3] ) {
+                                               return rootjQuery.find( selector );
+                                       }
 
                                        // Otherwise, we inject the element directly into the jQuery object
-                                       var ret = jQuery( elem || [] );
+                                       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 ) {
@@ -75,7 +88,9 @@ jQuery.fn = jQuery.prototype = {
                        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
@@ -95,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 ];
@@ -105,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;
@@ -151,29 +166,42 @@ jQuery.fn = jQuery.prototype = {
        },
 
        attr: function( name, value, type ) {
-               var options = name;
+               var options = name, isFunction = jQuery.isFunction( value );
 
                // Look for the case where we're accessing a style value
-               if ( typeof name === "string" )
-                       if ( value === undefined )
-                               return this[0] && jQuery[ type || "attr" ]( this[0], name );
+               if ( typeof name === "string" ) {
+                       if ( value === undefined ) {
+                               return this.length ?
+                                       jQuery[ type || "attr" ]( this[0], name ) :
+                                       null;
 
-                       else {
+                       } else {
                                options = {};
                                options[ name ] = value;
                        }
+               }
 
                // Check to see if we're setting style values
-               return this.each(function(i){
+               for ( var i = 0, l = this.length; i < l; i++ ) {
+                       var elem = this[i];
+
                        // Set all the styles
-                       for ( name in options )
-                               jQuery.attr(
-                                       type ?
-                                               this.style :
-                                               this,
-                                       name, jQuery.prop( this, options[ name ], type, i, name )
-                               );
-               });
+                       for ( var prop in options ) {
+                               value = options[prop];
+
+                               if ( isFunction ) {
+                                       value = value.call( elem, i );
+                               }
+
+                               if ( typeof value === "number" && type === "curCSS" && !exclude.test(prop) ) {
+                                       value = value + "px";
+                               }
+
+                               jQuery.attr( type ? elem.style : elem, prop, value );
+                       }
+               }
+
+               return this;
        },
 
        css: function( key, value ) {
@@ -261,28 +289,31 @@ jQuery.fn = jQuery.prototype = {
        },
 
        end: function() {
-               return this.prevObject || jQuery( [] );
+               return this.prevObject || jQuery(null);
        },
 
-       // For internal use only.
-       // Behaves like an Array's .push method, not like a jQuery method.
-       push: [].push,
-
        find: function( selector ) {
-               if ( this.length === 1 && !/,/.test(selector) ) {
-                       var ret = this.pushStack( [], "find", selector );
-                       ret.length = 0;
-                       jQuery.find( selector, this[0], ret );
-                       return ret;
-               } 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 ) {
@@ -502,13 +533,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 );
                }
@@ -626,7 +657,7 @@ jQuery.extend({
        // check if an element is in a (or is an) XML document
        isXMLDoc: function( elem ) {
                return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
-                       !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument );
+                       !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
        },
 
        // Evalulates a script in a global context
@@ -682,17 +713,6 @@ jQuery.extend({
                return object;
        },
 
-       prop: function( elem, value, type, i, name ) {
-               // Handle executable functions
-               if ( jQuery.isFunction( value ) )
-                       value = value.call( elem, i );
-
-               // Handle passing in a number to a CSS property
-               return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ?
-                       value + "px" :
-                       value;
-       },
-
        className: {
                // internal only, use addClass("class")
                add: function( elem, classNames ) {
@@ -956,7 +976,7 @@ jQuery.extend({
                if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
                        return undefined;
 
-               var notxml = !jQuery.isXMLDoc( elem ),
+               var notxml = !elem.tagName || !jQuery.isXMLDoc( elem ),
                        // Whether we are setting (or getting)
                        set = value !== undefined;
 
@@ -1146,6 +1166,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.
@@ -1189,13 +1212,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 );
        };
 });
 
@@ -1221,24 +1247,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(){
@@ -1246,6 +1276,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;