Use a for loop rather than for/in loop when copying events, so that code will work...
[jquery.git] / src / manipulation.js
index 9b7cfef..9d70a7b 100644 (file)
@@ -193,28 +193,20 @@ jQuery.fn.extend({
                                // from the original. In order to get around this, we use some
                                // proprietary methods to clear the events. Thanks to MooTools
                                // guys for this hotness.
-                               var srcElements = jQuery(this).find('*').andSelf();
-                               jQuery(clone).find('*').andSelf().each(function (i, clone) {
-                                       // We do not need to do anything for non-Elements
-                                       if (this.nodeType !== 1) {
-                                               return;
-                                       }
 
-                                       // clearAttributes removes the attributes, but also
-                                       // removes the attachEvent events
-                                       clone.clearAttributes();
+                               // Using Sizzle here is crazy slow, so we use getElementsByTagName
+                               // instead
+                               var srcElements = this.getElementsByTagName("*"),
+                                       destElements = clone.getElementsByTagName("*");
 
-                                       // mergeAttributes only merges back on the original attributes,
-                                       // not the events
-                                       clone.mergeAttributes(srcElements[i]);
+                               // Weird iteration because IE will replace the length property
+                               // with an element if you are cloning the body and one of the
+                               // elements on the page has a name or id of "length"
+                               for ( var i = 0; srcElements[i]; ++i ) {
+                                       cloneFixAttributes( srcElements[i], destElements[i] );
+                               }
 
-                                       // IE6-8 fail to clone children inside object elements that use
-                                       // the proprietary classid attribute value (rather than the type
-                                       // attribute) to identify the type of content to display
-                                       if (clone.nodeName.toLowerCase() === 'object') {
-                                               clone.outerHTML = srcElements[i].outerHTML;
-                                       }
-                               });
+                               cloneFixAttributes( this, clone );
                        }
 
                        return clone;
@@ -378,14 +370,14 @@ function root( elem, cur ) {
 }
 
 function cloneCopyEvent(orig, ret) {
-       var i = 0;
+       var node = 0;
 
        ret.each(function() {
-               if ( this.nodeType !== 1 || this.nodeName !== (orig[i] && orig[i].nodeName) ) {
+               if ( this.nodeType !== 1 || this.nodeName !== (orig[node] && orig[node].nodeName) ) {
                        return;
                }
 
-               var oldData = jQuery.data( orig[i++] ),
+               var oldData = jQuery.data( orig[node++] ),
                        curData = jQuery.data( this, oldData ),
                        events = oldData && oldData.events;
 
@@ -394,14 +386,52 @@ function cloneCopyEvent(orig, ret) {
                        curData.events = {};
 
                        for ( var type in events ) {
-                               for ( var handler in events[ type ] ) {
-                                       jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
+                               for ( var i = 0, l = events[ type ].length; i < l; i++ ) {
+                                       jQuery.event.add( this, type, events[ type ][ i ], events[ type ][ i ].data );
                                }
                        }
                }
        });
 }
 
+function cloneFixAttributes(src, dest) {
+       // We do not need to do anything for non-Elements
+       if ( dest.nodeType !== 1 ) {
+               return;
+       }
+
+       var nodeName = dest.nodeName.toLowerCase();
+
+       // clearAttributes removes the attributes, which we don't want,
+       // but also removes the attachEvent events, which we *do* want
+       dest.clearAttributes();
+
+       // mergeAttributes, in contrast, only merges back on the
+       // original attributes, not the events
+       dest.mergeAttributes(src);
+
+       // IE6-8 fail to clone children inside object elements that use
+       // the proprietary classid attribute value (rather than the type
+       // attribute) to identify the type of content to display
+       if ( nodeName === "object" ) {
+               dest.outerHTML = src.outerHTML;
+
+       // IE6-8 fails to persist the checked state of a cloned checkbox
+       // or radio button
+       } else if ( nodeName === "input" && src.checked ) {
+               dest.defaultChecked = dest.checked = src.checked;
+
+       // IE6-8 fails to return the selected option to the default selected
+       // state when cloning options
+       } else if ( nodeName === "option" ) {
+               dest.selected = src.defaultSelected;
+       }
+
+       // Event data gets referenced instead of copied if the expando
+       // gets copied too
+       dest.removeAttribute( jQuery.expando );
+}
+
 jQuery.buildFragment = function( args, nodes, scripts ) {
        var fragment, cacheable, cacheresults,
                doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);