Further optimize the empty/remove/cleanData logic.
[jquery.git] / src / manipulation.js
index 2af2d7e..ed049de 100644 (file)
@@ -5,6 +5,7 @@ var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
        rtagName = /<([\w:]+)/,
        rtbody = /<tbody/i,
        rhtml = /<|&\w+;/,
+       rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,  // checked="checked" or checked (html5)
        fcloseTag = function( all, front, tag ) {
                return rselfClosing.test( tag ) ?
                        all :
@@ -33,8 +34,9 @@ if ( !jQuery.support.htmlSerialize ) {
 jQuery.fn.extend({
        text: function( text ) {
                if ( jQuery.isFunction(text) ) {
-                       return this.each(function() {
-                               return jQuery(this).text( text.call(this) );
+                       return this.each(function(i) {
+                               var self = jQuery(this);
+                               self.text( text.call(this, i, self.text()) );
                        });
                }
 
@@ -47,8 +49,8 @@ jQuery.fn.extend({
 
        wrapAll: function( html ) {
                if ( jQuery.isFunction( html ) ) {
-                       return this.each(function() {
-                               jQuery(this).wrapAll( html.apply(this, arguments) );
+                       return this.each(function(i) {
+                               jQuery(this).wrapAll( html.call(this, i) );
                        });
                }
 
@@ -75,8 +77,21 @@ jQuery.fn.extend({
        },
 
        wrapInner: function( html ) {
+               if ( jQuery.isFunction( html ) ) {
+                       return this.each(function(i) {
+                               jQuery(this).wrapInner( html.call(this, i) );
+                       });
+               }
+
                return this.each(function() {
-                       jQuery( this ).contents().wrapAll( html );
+                       var self = jQuery( this ), contents = self.contents();
+
+                       if ( contents.length ) {
+                               contents.wrapAll( html );
+
+                       } else {
+                               self.append( html );
+                       }
                });
        },
 
@@ -133,6 +148,40 @@ jQuery.fn.extend({
                        return set;
                }
        },
+       
+       // keepData is for internal use only--do not document
+       remove: function( selector, keepData ) {
+               for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+                       if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
+                               if ( !keepData && elem.nodeType === 1 ) {
+                                       jQuery.cleanData( elem.getElementsByTagName("*") );
+                                       jQuery.cleanData( [ elem ] );
+                               }
+
+                               if ( elem.parentNode ) {
+                                        elem.parentNode.removeChild( elem );
+                               }
+                       }
+               }
+               
+               return this;
+       },
+
+       empty: function() {
+               for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+                       // Remove element nodes and prevent memory leaks
+                       if ( elem.nodeType === 1 ) {
+                               jQuery.cleanData( elem.getElementsByTagName("*") );
+                       }
+
+                       // Remove any remaining nodes
+                       while ( elem.firstChild ) {
+                               elem.removeChild( elem.firstChild );
+                       }
+               }
+               
+               return this;
+       },
 
        clone: function( events ) {
                // Do the clone
@@ -172,7 +221,7 @@ jQuery.fn.extend({
 
        html: function( value ) {
                if ( value === undefined ) {
-                       return this[0] ?
+                       return this[0] && this[0].nodeType === 1 ?
                                this[0].innerHTML.replace(rinlinejQuery, "") :
                                null;
 
@@ -181,11 +230,13 @@ jQuery.fn.extend({
                        (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
                        !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
 
+                       value = value.replace(rxhtmlTag, fcloseTag);
+
                        try {
                                for ( var i = 0, l = this.length; i < l; i++ ) {
                                        // Remove element nodes and prevent memory leaks
                                        if ( this[i].nodeType === 1 ) {
-                                               cleanData( this[i].getElementsByTagName("*") );
+                                               jQuery.cleanData( this[i].getElementsByTagName("*") );
                                                this[i].innerHTML = value;
                                        }
                                }
@@ -195,6 +246,14 @@ jQuery.fn.extend({
                                this.empty().append( value );
                        }
 
+               } else if ( jQuery.isFunction( value ) ) {
+                       this.each(function(i){
+                               var self = jQuery(this), old = self.html();
+                               self.empty().append(function(){
+                                       return value.call( this, i, old );
+                               });
+                       });
+
                } else {
                        this.empty().append( value );
                }
@@ -204,6 +263,18 @@ jQuery.fn.extend({
 
        replaceWith: function( value ) {
                if ( this[0] && this[0].parentNode ) {
+                       // Make sure that the elements are removed from the DOM before they are inserted
+                       // this can help fix replacing a parent with child elements
+                       if ( !jQuery.isFunction( value ) ) {
+                               value = jQuery( value ).detach();
+
+                       } else {
+                               return this.each(function(i) {
+                                       var self = jQuery(this), old = self.html();
+                                       self.replaceWith( value.call( this, i, old ) );
+                               });
+                       }
+
                        return this.each(function() {
                                var next = this.nextSibling, parent = this.parentNode;
 
@@ -227,10 +298,18 @@ jQuery.fn.extend({
        domManip: function( args, table, callback ) {
                var results, first, value = args[0], scripts = [];
 
-               if ( jQuery.isFunction(value) ) {
+               // We can't cloneNode fragments that contain checked, in WebKit
+               if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
                        return this.each(function() {
-                               args[0] = value.call(this);
-                               return jQuery(this).domManip( args, table, callback );
+                               jQuery(this).domManip( args, table, callback, true );
+                       });
+               }
+
+               if ( jQuery.isFunction(value) ) {
+                       return this.each(function(i) {
+                               var self = jQuery(this);
+                               args[0] = value.call(this, i, table ? self.html() : undefined);
+                               self.domManip( args, table, callback );
                        });
                }
 
@@ -299,16 +378,16 @@ function cloneCopyEvent(orig, ret) {
 }
 
 function buildFragment( args, nodes, scripts ) {
-       var fragment, cacheable, cached, cacheresults, doc;
+       var fragment, cacheable, cacheresults, doc;
 
-       if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && args[0].indexOf("<option") < 0 ) {
+       // webkit does not clone 'checked' attribute of radio inputs on cloneNode, so don't cache if string has a checked
+       if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && args[0].indexOf("<option") < 0 && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
                cacheable = true;
                cacheresults = jQuery.fragments[ args[0] ];
                if ( cacheresults ) {
                        if ( cacheresults !== 1 ) {
                                fragment = cacheresults;
                        }
-                       cached = true;
                }
        }
 
@@ -346,38 +425,6 @@ jQuery.each({
        };
 });
 
-jQuery.each({
-       // keepData is for internal use only--do not document
-       remove: function( selector, keepData ) {
-               if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
-                       if ( !keepData && this.nodeType === 1 ) {
-                               cleanData( this.getElementsByTagName("*") );
-                               cleanData( [ this ] );
-                       }
-
-                       if ( this.parentNode ) {
-                                this.parentNode.removeChild( this );
-                       }
-               }
-       },
-
-       empty: function() {
-               // Remove element nodes and prevent memory leaks
-               if ( this.nodeType === 1 ) {
-                       cleanData( this.getElementsByTagName("*") );
-               }
-
-               // Remove any remaining nodes
-               while ( this.firstChild ) {
-                       this.removeChild( this.firstChild );
-               }
-       }
-}, function( name, fn ) {
-       jQuery.fn[ name ] = function() {
-               return this.each( fn, arguments );
-       };
-});
-
 jQuery.extend({
        clean: function( elems, context, fragment, scripts ) {
                context = context || document;
@@ -471,13 +518,27 @@ jQuery.extend({
                }
 
                return ret;
-       }
-});
-
-function cleanData( elems ) {
-       for ( var i = 0, elem, id; (elem = elems[i]) != null; i++ ) {
-               if ( !jQuery.noData[elem.nodeName.toLowerCase()] && (id = elem[expando]) ) {
-                       delete jQuery.cache[ id ];
+       },
+       
+       cleanData: function( elems ) {
+               var data, id, cache = jQuery.cache;
+               
+               for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+                       id = elem[ jQuery.expando ];
+                       
+                       if ( id ) {
+                               data = cache[ id ];
+                               
+                               if ( data.events ) {
+                                       for ( var event in data.events ) {
+                                               removeEvent( elem, event, data.handle );
+                                       }
+                               }
+                               
+                               removeExpando( elem );
+                               
+                               delete cache[ id ];
+                       }
                }
        }
-}
+});