@@ -25,6 +25,7 @@ var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figca
25
25
rnoInnerhtml = / < (?: s c r i p t | s t y l e ) / i,
26
26
rnocache = / < (?: s c r i p t | o b j e c t | e m b e d | o p t i o n | s t y l e ) / i,
27
27
rnoshimcache = new RegExp ( "<(?:" + nodeNames + ")[\\s/>]" , "i" ) ,
28
+ rcheckableType = / ^ (?: c h e c k b o x | r a d i o ) $ / ,
28
29
// checked="checked" or checked
29
30
rchecked = / c h e c k e d \s * (?: [ ^ = ] | = \s * .c h e c k e d .) / i,
30
31
rscriptType = / \/ ( j a v a | e c m a ) s c r i p t / i,
@@ -287,21 +288,23 @@ jQuery.fn.extend({
287
288
} ,
288
289
289
290
domManip : function ( args , table , callback ) {
290
- var results , first , fragment ,
291
+ var results , first , fragment , iNoClone ,
292
+ i = 0 ,
291
293
value = args [ 0 ] ,
292
- scripts = [ ] ;
294
+ scripts = [ ] ,
295
+ l = this . length ;
293
296
294
297
// We can't cloneNode fragments that contain checked, in WebKit
295
- if ( ! jQuery . support . checkClone && arguments . length === 3 && typeof value === "string" && rchecked . test ( value ) ) {
298
+ if ( ! jQuery . support . checkClone && l > 1 && typeof value === "string" && rchecked . test ( value ) ) {
296
299
return this . each ( function ( ) {
297
- jQuery ( this ) . domManip ( args , table , callback , true ) ;
300
+ jQuery ( this ) . domManip ( args , table , callback ) ;
298
301
} ) ;
299
302
}
300
303
301
304
if ( jQuery . isFunction ( value ) ) {
302
305
return this . each ( function ( i ) {
303
306
var self = jQuery ( this ) ;
304
- args [ 0 ] = value . call ( this , i , table ? self . html ( ) : undefined ) ;
307
+ args [ 0 ] = value . call ( this , i , table ? self . html ( ) : undefined ) ;
305
308
self . domManip ( args , table , callback ) ;
306
309
} ) ;
307
310
}
@@ -310,30 +313,25 @@ jQuery.fn.extend({
310
313
results = jQuery . buildFragment ( args , this , scripts ) ;
311
314
fragment = results . fragment ;
312
315
313
- if ( fragment . childNodes . length === 1 ) {
314
- first = fragment = fragment . firstChild ;
315
- } else {
316
316
first = fragment . firstChild ;
317
+ if ( fragment . childNodes . length === 1 ) {
318
+ fragment = first ;
317
319
}
318
320
319
321
if ( first ) {
320
322
table = table && jQuery . nodeName ( first , "tr" ) ;
321
323
322
- for ( var i = 0 , l = this . length , lastIndex = l - 1 ; i < l ; i ++ ) {
324
+ // Use the original fragment for the last item instead of the first because it can end up
325
+ // being emptied incorrectly in certain situations (#8070).
326
+ // Fragments from the fragment cache must always be cloned and never used in place.
327
+ for ( iNoClone = results . cacheable || l - 1 ; i < l ; i ++ ) {
323
328
callback . call (
324
- table ?
325
- root ( this [ i ] , first ) :
329
+ table && jQuery . nodeName ( this [ i ] , "table" ) ?
330
+ findOrAppend ( this [ i ] , "tbody" ) :
326
331
this [ i ] ,
327
- // Make sure that we do not leak memory by inadvertently discarding
328
- // the original fragment (which might have attached data) instead of
329
- // using it; in addition, use the original fragment object for the last
330
- // item instead of first because it can end up being emptied incorrectly
331
- // in certain situations (Bug #8070).
332
- // Fragments from the fragment cache must always be cloned and never used
333
- // in place.
334
- results . cacheable || ( l > 1 && i < lastIndex ) ?
335
- jQuery . clone ( fragment , true , true ) :
336
- fragment
332
+ i === iNoClone ?
333
+ fragment :
334
+ jQuery . clone ( fragment , true , true )
337
335
) ;
338
336
}
339
337
}
@@ -363,11 +361,8 @@ jQuery.fn.extend({
363
361
}
364
362
} ) ;
365
363
366
- function root ( elem , cur ) {
367
- return jQuery . nodeName ( elem , "table" ) ?
368
- ( elem . getElementsByTagName ( "tbody" ) [ 0 ] ||
369
- elem . appendChild ( elem . ownerDocument . createElement ( "tbody" ) ) ) :
370
- elem ;
364
+ function findOrAppend ( elem , tag ) {
365
+ return elem . getElementsByTagName ( tag ) [ 0 ] || elem . appendChild ( elem . ownerDocument . createElement ( tag ) ) ;
371
366
}
372
367
373
368
function cloneCopyEvent ( src , dest ) {
@@ -426,7 +421,7 @@ function cloneFixAttributes( src, dest ) {
426
421
if ( nodeName === "object" ) {
427
422
dest . outerHTML = src . outerHTML ;
428
423
429
- } else if ( nodeName === "input" && ( src . type === "checkbox" || src . type === "radio" ) ) {
424
+ } else if ( nodeName === "input" && rcheckableType . test ( src . type ) ) {
430
425
// IE6-8 fails to persist the checked state of a cloned checkbox
431
426
// or radio button. Worse, IE6-7 fail to give the cloned element
432
427
// a checked appearance if the defaultChecked value isn't also set
@@ -465,49 +460,45 @@ function cloneFixAttributes( src, dest ) {
465
460
dest . removeAttribute ( "_change_attached" ) ;
466
461
}
467
462
468
- jQuery . buildFragment = function ( args , nodes , scripts ) {
469
- var fragment , cacheable , cacheresults , doc ,
463
+ jQuery . buildFragment = function ( args , context , scripts ) {
464
+ var fragment , cacheable , cachehit ,
470
465
first = args [ 0 ] ;
471
466
472
- // nodes may contain either an explicit document object,
473
- // a jQuery collection or context object.
474
- // If nodes[0] contains a valid object to assign to doc
475
- if ( nodes && nodes [ 0 ] ) {
476
- doc = nodes [ 0 ] . ownerDocument || nodes [ 0 ] ;
477
- }
467
+ // Set context from what may come in as undefined or a jQuery collection or a node
468
+ context = context || document ;
469
+ context = ( context [ 0 ] || context ) . ownerDocument || context [ 0 ] || context ;
478
470
479
471
// Ensure that an attr object doesn't incorrectly stand in as a document object
480
472
// Chrome and Firefox seem to allow this to occur and will throw exception
481
473
// Fixes #8950
482
- if ( ! doc . createDocumentFragment ) {
483
- doc = document ;
474
+ if ( typeof context . createDocumentFragment === "undefined" ) {
475
+ context = document ;
484
476
}
485
477
486
478
// Only cache "small" (1/2 KB) HTML strings that are associated with the main document
487
479
// Cloning options loses the selected state, so don't cache them
488
480
// IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
489
481
// Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
490
482
// Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501
491
- if ( args . length === 1 && typeof first === "string" && first . length < 512 && doc === document &&
483
+ if ( args . length === 1 && typeof first === "string" && first . length < 512 && context === document &&
492
484
first . charAt ( 0 ) === "<" && ! rnocache . test ( first ) &&
493
485
( jQuery . support . checkClone || ! rchecked . test ( first ) ) &&
494
486
( jQuery . support . html5Clone || ! rnoshimcache . test ( first ) ) ) {
495
487
488
+ // Mark cacheable and look for a hit
496
489
cacheable = true ;
497
-
498
- cacheresults = jQuery . fragments [ first ] ;
499
- if ( cacheresults && cacheresults !== 1 ) {
500
- fragment = cacheresults ;
490
+ fragment = jQuery . fragments [ first ] ;
491
+ cachehit = fragment !== undefined ;
501
492
}
502
- }
503
493
504
494
if ( ! fragment ) {
505
- fragment = doc . createDocumentFragment ( ) ;
506
- jQuery . clean ( args , doc , fragment , scripts ) ;
507
- }
508
-
495
+ fragment = context . createDocumentFragment ( ) ;
496
+ jQuery . clean ( args , context , fragment , scripts ) ;
509
497
if ( cacheable ) {
510
- jQuery . fragments [ first ] = cacheresults ? fragment : 1 ;
498
+ // Update the cache, but only store false
499
+ // unless this is a second parsing of the same content
500
+ jQuery . fragments [ first ] = cachehit && fragment ;
501
+ }
511
502
}
512
503
513
504
return { fragment : fragment , cacheable : cacheable } ;
@@ -557,20 +548,10 @@ function getAll( elem ) {
557
548
558
549
// Used in clean, fixes the defaultChecked property
559
550
function fixDefaultChecked ( elem ) {
560
- if ( elem . type === "checkbox" || elem . type === "radio" ) {
551
+ if ( rcheckableType . test ( elem . type ) ) {
561
552
elem . defaultChecked = elem . checked ;
562
553
}
563
554
}
564
- // Finds all inputs and passes them to fixDefaultChecked
565
- function findInputs ( elem ) {
566
- var nodeName = ( elem . nodeName || "" ) . toLowerCase ( ) ;
567
- if ( nodeName === "input" ) {
568
- fixDefaultChecked ( elem ) ;
569
- // Skip scripts, get other children
570
- } else if ( nodeName !== "script" && typeof elem . getElementsByTagName !== "undefined" ) {
571
- jQuery . grep ( elem . getElementsByTagName ( "input" ) , fixDefaultChecked ) ;
572
- }
573
- }
574
555
575
556
// Derived From: http://www.iecss.com/shimprove/javascript/shimprove.1-0-1.js
576
557
function shimCloneNode ( elem ) {
@@ -637,17 +618,17 @@ jQuery.extend({
637
618
} ,
638
619
639
620
clean : function ( elems , context , fragment , scripts ) {
640
- var checkScriptType , script , j ,
621
+ var j , safe , elem , tag , wrap , depth , div , hasBody , tbody , len , handleScript , jsTags ,
622
+ i = 0 ,
641
623
ret = [ ] ;
642
624
643
- context = context || document ;
644
-
645
- // !context.createElement fails in IE with an error but returns typeof 'object'
646
- if ( typeof context . createElement === "undefined" ) {
647
- context = context . ownerDocument || context [ 0 ] && context [ 0 ] . ownerDocument || document ;
625
+ // Ensure that context is a document
626
+ if ( ! context || typeof context . createDocumentFragment === "undefined" ) {
627
+ context = document ;
648
628
}
649
629
650
- for ( var i = 0 , elem ; ( elem = elems [ i ] ) != null ; i ++ ) {
630
+ // Use the already-created safe fragment if context permits
631
+ for ( safe = context === document && safeFragment ; ( elem = elems [ i ] ) != null ; i ++ ) {
651
632
if ( typeof elem === "number" ) {
652
633
elem += "" ;
653
634
}
@@ -661,27 +642,17 @@ jQuery.extend({
661
642
if ( ! rhtml . test ( elem ) ) {
662
643
elem = context . createTextNode ( elem ) ;
663
644
} else {
645
+ // Ensure a safe container in which to render the html
646
+ safe = safe || createSafeFragment ( context ) ;
647
+ div = div || safe . appendChild ( context . createElement ( "div" ) ) ;
648
+
664
649
// Fix "XHTML"-style tags in all browsers
665
650
elem = elem . replace ( rxhtmlTag , "<$1></$2>" ) ;
666
651
667
- // Trim whitespace, otherwise indexOf won't work as expected
668
- var tag = ( rtagName . exec ( elem ) || [ "" , "" ] ) [ 1 ] . toLowerCase ( ) ,
669
- wrap = wrapMap [ tag ] || wrapMap . _default ,
670
- depth = wrap [ 0 ] ,
671
- div = context . createElement ( "div" ) ,
672
- safeChildNodes = safeFragment . childNodes ,
673
- remove ;
674
-
675
- // Append wrapper element to unknown element safe doc fragment
676
- if ( context === document ) {
677
- // Use the fragment we've already created for this document
678
- safeFragment . appendChild ( div ) ;
679
- } else {
680
- // Use a fragment created with the owner document
681
- createSafeFragment ( context ) . appendChild ( div ) ;
682
- }
683
-
684
652
// Go to html and back, then peel off extra wrappers
653
+ tag = ( rtagName . exec ( elem ) || [ "" , "" ] ) [ 1 ] . toLowerCase ( ) ;
654
+ wrap = wrapMap [ tag ] || wrapMap . _default ;
655
+ depth = wrap [ 0 ] ;
685
656
div . innerHTML = wrap [ 1 ] + elem + wrap [ 2 ] ;
686
657
687
658
// Move to the right depth
@@ -693,7 +664,7 @@ jQuery.extend({
693
664
if ( ! jQuery . support . tbody ) {
694
665
695
666
// String was a <table>, *may* have spurious <tbody>
696
- var hasBody = rtbody . test ( elem ) ,
667
+ hasBody = rtbody . test ( elem ) ;
697
668
tbody = tag === "table" && ! hasBody ?
698
669
div . firstChild && div . firstChild . childNodes :
699
670
@@ -716,59 +687,63 @@ jQuery.extend({
716
687
717
688
elem = div . childNodes ;
718
689
719
- // Clear elements from DocumentFragment (safeFragment or otherwise)
720
- // to avoid hoarding elements. Fixes #11356
721
- if ( div ) {
722
- div . parentNode . removeChild ( div ) ;
723
-
724
- // Guard against -1 index exceptions in FF3.6
725
- if ( safeChildNodes . length > 0 ) {
726
- remove = safeChildNodes [ safeChildNodes . length - 1 ] ;
727
-
728
- if ( remove && remove . parentNode ) {
729
- remove . parentNode . removeChild ( remove ) ;
690
+ // Remember the top-level container for proper cleanup
691
+ div = safe . lastChild ;
730
692
}
731
693
}
694
+
695
+ if ( elem . nodeType ) {
696
+ ret . push ( elem ) ;
697
+ } else {
698
+ ret = jQuery . merge ( ret , elem ) ;
732
699
}
733
700
}
701
+
702
+ // Fix #11356: Clear elements from safeFragment
703
+ if ( div ) {
704
+ safe . removeChild ( div ) ;
705
+ div = safe = null ;
734
706
}
735
707
736
- // Resets defaultChecked for any radios and checkboxes
708
+ // Reset defaultChecked for any radios and checkboxes
737
709
// about to be appended to the DOM in IE 6/7 (#8060)
738
- var len ;
739
710
if ( ! jQuery . support . appendChecked ) {
740
- if ( elem [ 0 ] && typeof ( len = elem . length ) === "number" ) {
741
- for ( j = 0 ; j < len ; j ++ ) {
742
- findInputs ( elem [ j ] ) ;
711
+ for ( i = 0 ; ( elem = ret [ i ] ) != null ; i ++ ) {
712
+ if ( jQuery . nodeName ( elem , "input" ) ) {
713
+ fixDefaultChecked ( elem ) ;
714
+ } else if ( typeof elem . getElementsByTagName !== "undefined" ) {
715
+ jQuery . grep ( elem . getElementsByTagName ( "input" ) , fixDefaultChecked ) ;
743
716
}
744
- } else {
745
- findInputs ( elem ) ;
746
717
}
747
718
}
748
719
749
- if ( elem . nodeType ) {
750
- ret . push ( elem ) ;
751
- } else {
752
- ret = jQuery . merge ( ret , elem ) ;
753
- }
754
- }
755
-
720
+ // Append elements to a provided document fragment
756
721
if ( fragment ) {
757
- checkScriptType = function ( elem ) {
758
- return ! elem . type || rscriptType . test ( elem . type ) ;
722
+ // Special handling of each script element
723
+ handleScript = function ( elem ) {
724
+ // Check if we consider it executable
725
+ if ( ! elem . type || rscriptType . test ( elem . type ) ) {
726
+ // Detach the script and store it in the scripts array (if provided) or the fragment
727
+ // Return truthy to indicate that it has been handled
728
+ return scripts ?
729
+ scripts . push ( elem . parentNode ? elem . parentNode . removeChild ( elem ) : elem ) :
730
+ fragment . appendChild ( elem ) ;
731
+ }
759
732
} ;
760
- for ( i = 0 ; ret [ i ] ; i ++ ) {
761
- script = ret [ i ] ;
762
- if ( scripts && jQuery . nodeName ( script , "script" ) && ( ! script . type || rscriptType . test ( script . type ) ) ) {
763
- scripts . push ( script . parentNode ? script . parentNode . removeChild ( script ) : script ) ;
764
733
765
- } else {
766
- if ( script . nodeType === 1 ) {
767
- var jsTags = jQuery . grep ( script . getElementsByTagName ( "script" ) , checkScriptType ) ;
734
+ for ( i = 0 ; ( elem = ret [ i ] ) != null ; i ++ ) {
735
+ // Check if we're done after handling an executable script
736
+ if ( ! ( jQuery . nodeName ( elem , "script" ) && handleScript ( elem ) ) ) {
737
+ // Append to fragment and handle embedded scripts
738
+ fragment . appendChild ( elem ) ;
739
+ if ( typeof elem . getElementsByTagName !== "undefined" ) {
740
+ // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration
741
+ jsTags = jQuery . grep ( jQuery . merge ( [ ] , elem . getElementsByTagName ( "script" ) ) , handleScript ) ;
768
742
743
+ // Splice the scripts into ret after their former ancestor and advance our index beyond them
769
744
ret . splice . apply ( ret , [ i + 1 , 0 ] . concat ( jsTags ) ) ;
745
+ i += jsTags . length ;
770
746
}
771
- fragment . appendChild ( script ) ;
772
747
}
773
748
}
774
749
}
0 commit comments