From: jeresig Date: Fri, 21 Jan 2011 16:20:05 +0000 (-0500) Subject: Merge branch '8013p' of https://github.com/rwldrn/jquery into rwldrn-8013p X-Git-Url: http://git.asbjorn.biz/?p=jquery.git;a=commitdiff_plain;h=61e80c55df9231c177d194ff075472898933c2c9;hp=328a86f9a0d3f0907cc950f7543e34cb3efbda3f Merge branch '8013p' of https://github.com/rwldrn/jquery into rwldrn-8013p --- diff --git a/src/core.js b/src/core.js index 236f84d..aa97202 100644 --- a/src/core.js +++ b/src/core.js @@ -130,7 +130,7 @@ jQuery.fn = jQuery.prototype = { } else { ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); - selector = (ret.cacheable ? jQuery(ret.fragment).clone()[0] : ret.fragment).childNodes; + selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes; } return jQuery.merge( this, selector ); diff --git a/src/manipulation.js b/src/manipulation.js index 596a457..d758d80 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -183,43 +183,13 @@ jQuery.fn.extend({ return this; }, - clone: function( events ) { - // Do the clone - var ret = this.map(function() { - var clone = this.cloneNode(true); - if ( !jQuery.support.noCloneEvent && (this.nodeType === 1 || this.nodeType === 11) && !jQuery.isXMLDoc(this) ) { - // IE copies events bound via attachEvent when using cloneNode. - // Calling detachEvent on the clone will also remove the events - // 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. - - // Using Sizzle here is crazy slow, so we use getElementsByTagName - // instead - var srcElements = this.getElementsByTagName("*"), - destElements = clone.getElementsByTagName("*"); - - // 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] ); - } - - cloneFixAttributes( this, clone ); - } - - return clone; + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? true : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function () { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); }); - - // Copy the events from the original to the clone - if ( events === true ) { - cloneCopyEvent( this, ret ); - cloneCopyEvent( this.find("*"), ret.find("*") ); - } - - // Return the cloned set - return ret; }, html: function( value ) { @@ -347,7 +317,7 @@ jQuery.fn.extend({ root(this[i], first) : this[i], i > 0 || results.cacheable || (this.length > 1 && i > 0) ? - jQuery(fragment).clone(true)[0] : + jQuery.clone( fragment, true, true ) : fragment ); } @@ -369,40 +339,33 @@ function root( elem, cur ) { elem; } -function cloneCopyEvent(orig, ret) { - ret.each(function (nodeIndex) { - if ( this.nodeType !== 1 || !jQuery.hasData(orig[nodeIndex]) ) { - return; - } +function cloneCopyEvent( src, dest ) { - // XXX remove for 1.5 RC or merge back in if there is actually a reason for this check that has been - // unexposed by unit tests - if ( this.nodeName !== (orig[nodeIndex] && orig[nodeIndex].nodeName) ) { - throw "Cloned data mismatch"; - } + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } - var internalKey = jQuery.expando, - oldData = jQuery.data( orig[nodeIndex] ), - curData = jQuery.data( this, oldData ); + var internalKey = jQuery.expando, + oldData = jQuery.data( src ), + curData = jQuery.data( dest, oldData ); - // Switch to use the internal data object, if it exists, for the next - // stage of data copying - if ( (oldData = oldData[ internalKey ]) ) { - var events = oldData.events; - curData = curData[ internalKey ] = jQuery.extend({}, oldData); + // Switch to use the internal data object, if it exists, for the next + // stage of data copying + if ( (oldData = oldData[ internalKey ]) ) { + var events = oldData.events; + curData = curData[ internalKey ] = jQuery.extend({}, oldData); - if ( events ) { - delete curData.handle; - curData.events = {}; + if ( events ) { + delete curData.handle; + curData.events = {}; - for ( var type in events ) { - for ( var i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( this, type, events[ type ][ i ], events[ type ][ i ].data ); - } + for ( var type in events ) { + for ( var i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ], events[ type ][ i ].data ); } } } - }); + } } function cloneFixAttributes(src, dest) { @@ -520,6 +483,53 @@ jQuery.each({ }); jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var clone = elem.cloneNode(true), + srcElements, + destElements; + + if ( !jQuery.support.noCloneEvent && (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + // IE copies events bound via attachEvent when using cloneNode. + // Calling detachEvent on the clone will also remove the events + // 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. + + // Using Sizzle here is crazy slow, so we use getElementsByTagName + // instead + srcElements = elem.getElementsByTagName("*"); + destElements = clone.getElementsByTagName("*"); + + // 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] ); + } + + cloneFixAttributes( elem, clone ); + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + + cloneCopyEvent( elem, clone ); + + if ( deepDataAndEvents && "getElementsByTagName" in elem ) { + + srcElements = elem.getElementsByTagName("*"); + destElements = clone.getElementsByTagName("*"); + + if ( srcElements.length ) { + for ( var i = 0; srcElements[i]; ++i ) { + cloneCopyEvent( srcElements[i], destElements[i] ); + } + } + } + } + // Return the cloned set + return clone; + }, clean: function( elems, context, fragment, scripts ) { context = context || document; diff --git a/test/unit/manipulation.js b/test/unit/manipulation.js index 37234d8..a68c214 100644 --- a/test/unit/manipulation.js +++ b/test/unit/manipulation.js @@ -871,6 +871,18 @@ test("replaceAll(String|Element|Array<Element>|jQuery)", function() { ok( !jQuery("#yahoo")[0], 'Verify that original element is gone, after set of elements' ); }); +test("jQuery.clone() (#8017)", function() { + + expect(2); + + ok( jQuery.clone && jQuery.isFunction( jQuery.clone ) , "jQuery.clone() utility exists and is a function."); + + var main = jQuery("#main")[0], + clone = jQuery.clone( main ); + + equals( main.children.length, clone.children.length, "Simple child length to ensure a large dom tree copies correctly" ); +}); + test("clone()", function() { expect(37); equals( 'This is a normal link: Yahoo', jQuery('#en').text(), 'Assert text for #en' );