Manipulation: Don't remove HTML comments from scripts
[jquery.git] / src / manipulation.js
blob19c60fcbcc60b7e37b9dac8d40b1dfe527eef1c3
1 import jQuery from "./core.js";
2 import isAttached from "./core/isAttached.js";
3 import flat from "./var/flat.js";
4 import isIE from "./var/isIE.js";
5 import push from "./var/push.js";
6 import access from "./core/access.js";
7 import rtagName from "./manipulation/var/rtagName.js";
8 import rscriptType from "./manipulation/var/rscriptType.js";
9 import wrapMap from "./manipulation/wrapMap.js";
10 import getAll from "./manipulation/getAll.js";
11 import setGlobalEval from "./manipulation/setGlobalEval.js";
12 import buildFragment from "./manipulation/buildFragment.js";
13 import dataPriv from "./data/var/dataPriv.js";
14 import dataUser from "./data/var/dataUser.js";
15 import acceptData from "./data/var/acceptData.js";
16 import DOMEval from "./core/DOMEval.js";
17 import nodeName from "./core/nodeName.js";
19 import "./core/init.js";
20 import "./traversing.js";
21 import "./selector.js";
22 import "./event.js";
24 var
26         // Support: IE <=10 - 11+
27         // In IE using regex groups here causes severe slowdowns.
28         rnoInnerhtml = /<script|<style|<link/i;
30 // Prefer a tbody over its parent table for containing new rows
31 function manipulationTarget( elem, content ) {
32         if ( nodeName( elem, "table" ) &&
33                 nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) {
35                 return jQuery( elem ).children( "tbody" )[ 0 ] || elem;
36         }
38         return elem;
41 // Replace/restore the type attribute of script elements for safe DOM manipulation
42 function disableScript( elem ) {
43         elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type;
44         return elem;
46 function restoreScript( elem ) {
47         if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) {
48                 elem.type = elem.type.slice( 5 );
49         } else {
50                 elem.removeAttribute( "type" );
51         }
53         return elem;
56 function cloneCopyEvent( src, dest ) {
57         var i, l, type, pdataOld, udataOld, udataCur, events;
59         if ( dest.nodeType !== 1 ) {
60                 return;
61         }
63         // 1. Copy private data: events, handlers, etc.
64         if ( dataPriv.hasData( src ) ) {
65                 pdataOld = dataPriv.get( src );
66                 events = pdataOld.events;
68                 if ( events ) {
69                         dataPriv.remove( dest, "handle events" );
71                         for ( type in events ) {
72                                 for ( i = 0, l = events[ type ].length; i < l; i++ ) {
73                                         jQuery.event.add( dest, type, events[ type ][ i ] );
74                                 }
75                         }
76                 }
77         }
79         // 2. Copy user data
80         if ( dataUser.hasData( src ) ) {
81                 udataOld = dataUser.access( src );
82                 udataCur = jQuery.extend( {}, udataOld );
84                 dataUser.set( dest, udataCur );
85         }
88 function domManip( collection, args, callback, ignored ) {
90         // Flatten any nested arrays
91         args = flat( args );
93         var fragment, first, scripts, hasScripts, node, doc,
94                 i = 0,
95                 l = collection.length,
96                 iNoClone = l - 1,
97                 value = args[ 0 ],
98                 valueIsFunction = typeof value === "function";
100         if ( valueIsFunction ) {
101                 return collection.each( function( index ) {
102                         var self = collection.eq( index );
103                         args[ 0 ] = value.call( this, index, self.html() );
104                         domManip( self, args, callback, ignored );
105                 } );
106         }
108         if ( l ) {
109                 fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );
110                 first = fragment.firstChild;
112                 if ( fragment.childNodes.length === 1 ) {
113                         fragment = first;
114                 }
116                 // Require either new content or an interest in ignored elements to invoke the callback
117                 if ( first || ignored ) {
118                         scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
119                         hasScripts = scripts.length;
121                         // Use the original fragment for the last item
122                         // instead of the first because it can end up
123                         // being emptied incorrectly in certain situations (#8070).
124                         for ( ; i < l; i++ ) {
125                                 node = fragment;
127                                 if ( i !== iNoClone ) {
128                                         node = jQuery.clone( node, true, true );
130                                         // Keep references to cloned scripts for later restoration
131                                         if ( hasScripts ) {
132                                                 jQuery.merge( scripts, getAll( node, "script" ) );
133                                         }
134                                 }
136                                 callback.call( collection[ i ], node, i );
137                         }
139                         if ( hasScripts ) {
140                                 doc = scripts[ scripts.length - 1 ].ownerDocument;
142                                 // Reenable scripts
143                                 jQuery.map( scripts, restoreScript );
145                                 // Evaluate executable scripts on first document insertion
146                                 for ( i = 0; i < hasScripts; i++ ) {
147                                         node = scripts[ i ];
148                                         if ( rscriptType.test( node.type || "" ) &&
149                                                 !dataPriv.access( node, "globalEval" ) &&
150                                                 jQuery.contains( doc, node ) ) {
152                                                 if ( node.src && ( node.type || "" ).toLowerCase()  !== "module" ) {
154                                                         // Optional AJAX dependency, but won't run scripts if not present
155                                                         if ( jQuery._evalUrl && !node.noModule ) {
156                                                                 jQuery._evalUrl( node.src, {
157                                                                         nonce: node.nonce,
158                                                                         crossOrigin: node.crossOrigin
159                                                                 }, doc );
160                                                         }
161                                                 } else {
162                                                         DOMEval( node.textContent, node, doc );
163                                                 }
164                                         }
165                                 }
166                         }
167                 }
168         }
170         return collection;
173 function remove( elem, selector, keepData ) {
174         var node,
175                 nodes = selector ? jQuery.filter( selector, elem ) : elem,
176                 i = 0;
178         for ( ; ( node = nodes[ i ] ) != null; i++ ) {
179                 if ( !keepData && node.nodeType === 1 ) {
180                         jQuery.cleanData( getAll( node ) );
181                 }
183                 if ( node.parentNode ) {
184                         if ( keepData && isAttached( node ) ) {
185                                 setGlobalEval( getAll( node, "script" ) );
186                         }
187                         node.parentNode.removeChild( node );
188                 }
189         }
191         return elem;
194 jQuery.extend( {
195         htmlPrefilter: function( html ) {
196                 return html;
197         },
199         clone: function( elem, dataAndEvents, deepDataAndEvents ) {
200                 var i, l, srcElements, destElements,
201                         clone = elem.cloneNode( true ),
202                         inPage = isAttached( elem );
204                 // Fix IE cloning issues
205                 if ( isIE && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
206                                 !jQuery.isXMLDoc( elem ) ) {
208                         // We eschew jQuery#find here for performance reasons:
209                         // https://jsperf.com/getall-vs-sizzle/2
210                         destElements = getAll( clone );
211                         srcElements = getAll( elem );
213                         for ( i = 0, l = srcElements.length; i < l; i++ ) {
215                                 // Support: IE <=11+
216                                 // IE fails to set the defaultValue to the correct value when
217                                 // cloning textareas.
218                                 if ( nodeName( destElements[ i ], "textarea" ) ) {
219                                         destElements[ i ].defaultValue = srcElements[ i ].defaultValue;
220                                 }
221                         }
222                 }
224                 // Copy the events from the original to the clone
225                 if ( dataAndEvents ) {
226                         if ( deepDataAndEvents ) {
227                                 srcElements = srcElements || getAll( elem );
228                                 destElements = destElements || getAll( clone );
230                                 for ( i = 0, l = srcElements.length; i < l; i++ ) {
231                                         cloneCopyEvent( srcElements[ i ], destElements[ i ] );
232                                 }
233                         } else {
234                                 cloneCopyEvent( elem, clone );
235                         }
236                 }
238                 // Preserve script evaluation history
239                 destElements = getAll( clone, "script" );
240                 if ( destElements.length > 0 ) {
241                         setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
242                 }
244                 // Return the cloned set
245                 return clone;
246         },
248         cleanData: function( elems ) {
249                 var data, elem, type,
250                         special = jQuery.event.special,
251                         i = 0;
253                 for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {
254                         if ( acceptData( elem ) ) {
255                                 if ( ( data = elem[ dataPriv.expando ] ) ) {
256                                         if ( data.events ) {
257                                                 for ( type in data.events ) {
258                                                         if ( special[ type ] ) {
259                                                                 jQuery.event.remove( elem, type );
261                                                         // This is a shortcut to avoid jQuery.event.remove's overhead
262                                                         } else {
263                                                                 jQuery.removeEvent( elem, type, data.handle );
264                                                         }
265                                                 }
266                                         }
268                                         // Support: Chrome <=35 - 45+
269                                         // Assign undefined instead of using delete, see Data#remove
270                                         elem[ dataPriv.expando ] = undefined;
271                                 }
272                                 if ( elem[ dataUser.expando ] ) {
274                                         // Support: Chrome <=35 - 45+
275                                         // Assign undefined instead of using delete, see Data#remove
276                                         elem[ dataUser.expando ] = undefined;
277                                 }
278                         }
279                 }
280         }
281 } );
283 jQuery.fn.extend( {
284         detach: function( selector ) {
285                 return remove( this, selector, true );
286         },
288         remove: function( selector ) {
289                 return remove( this, selector );
290         },
292         text: function( value ) {
293                 return access( this, function( value ) {
294                         return value === undefined ?
295                                 jQuery.text( this ) :
296                                 this.empty().each( function() {
297                                         if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
298                                                 this.textContent = value;
299                                         }
300                                 } );
301                 }, null, value, arguments.length );
302         },
304         append: function() {
305                 return domManip( this, arguments, function( elem ) {
306                         if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
307                                 var target = manipulationTarget( this, elem );
308                                 target.appendChild( elem );
309                         }
310                 } );
311         },
313         prepend: function() {
314                 return domManip( this, arguments, function( elem ) {
315                         if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
316                                 var target = manipulationTarget( this, elem );
317                                 target.insertBefore( elem, target.firstChild );
318                         }
319                 } );
320         },
322         before: function() {
323                 return domManip( this, arguments, function( elem ) {
324                         if ( this.parentNode ) {
325                                 this.parentNode.insertBefore( elem, this );
326                         }
327                 } );
328         },
330         after: function() {
331                 return domManip( this, arguments, function( elem ) {
332                         if ( this.parentNode ) {
333                                 this.parentNode.insertBefore( elem, this.nextSibling );
334                         }
335                 } );
336         },
338         empty: function() {
339                 var elem,
340                         i = 0;
342                 for ( ; ( elem = this[ i ] ) != null; i++ ) {
343                         if ( elem.nodeType === 1 ) {
345                                 // Prevent memory leaks
346                                 jQuery.cleanData( getAll( elem, false ) );
348                                 // Remove any remaining nodes
349                                 elem.textContent = "";
350                         }
351                 }
353                 return this;
354         },
356         clone: function( dataAndEvents, deepDataAndEvents ) {
357                 dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
358                 deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
360                 return this.map( function() {
361                         return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
362                 } );
363         },
365         html: function( value ) {
366                 return access( this, function( value ) {
367                         var elem = this[ 0 ] || {},
368                                 i = 0,
369                                 l = this.length;
371                         if ( value === undefined && elem.nodeType === 1 ) {
372                                 return elem.innerHTML;
373                         }
375                         // See if we can take a shortcut and just use innerHTML
376                         if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
377                                 !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
379                                 value = jQuery.htmlPrefilter( value );
381                                 try {
382                                         for ( ; i < l; i++ ) {
383                                                 elem = this[ i ] || {};
385                                                 // Remove element nodes and prevent memory leaks
386                                                 if ( elem.nodeType === 1 ) {
387                                                         jQuery.cleanData( getAll( elem, false ) );
388                                                         elem.innerHTML = value;
389                                                 }
390                                         }
392                                         elem = 0;
394                                 // If using innerHTML throws an exception, use the fallback method
395                                 } catch ( e ) {}
396                         }
398                         if ( elem ) {
399                                 this.empty().append( value );
400                         }
401                 }, null, value, arguments.length );
402         },
404         replaceWith: function() {
405                 var ignored = [];
407                 // Make the changes, replacing each non-ignored context element with the new content
408                 return domManip( this, arguments, function( elem ) {
409                         var parent = this.parentNode;
411                         if ( jQuery.inArray( this, ignored ) < 0 ) {
412                                 jQuery.cleanData( getAll( this ) );
413                                 if ( parent ) {
414                                         parent.replaceChild( elem, this );
415                                 }
416                         }
418                 // Force callback invocation
419                 }, ignored );
420         }
421 } );
423 jQuery.each( {
424         appendTo: "append",
425         prependTo: "prepend",
426         insertBefore: "before",
427         insertAfter: "after",
428         replaceAll: "replaceWith"
429 }, function( name, original ) {
430         jQuery.fn[ name ] = function( selector ) {
431                 var elems,
432                         ret = [],
433                         insert = jQuery( selector ),
434                         last = insert.length - 1,
435                         i = 0;
437                 for ( ; i <= last; i++ ) {
438                         elems = i === last ? this : this.clone( true );
439                         jQuery( insert[ i ] )[ original ]( elems );
440                         push.apply( ret, elems.get() );
441                 }
443                 return this.pushStack( ret );
444         };
445 } );
447 export default jQuery;