@@ -86,9 +86,12 @@ module.exports = function($window) {
86
86
}
87
87
vnode . dom = temp . firstChild
88
88
vnode . domSize = temp . childNodes . length
89
+ // Capture nodes to remove, so we don't confuse them.
90
+ vnode . instance = [ ]
89
91
var fragment = $doc . createDocumentFragment ( )
90
92
var child
91
93
while ( child = temp . firstChild ) {
94
+ vnode . instance . push ( child )
92
95
fragment . appendChild ( child )
93
96
}
94
97
insertNode ( parent , fragment , nextSibling )
@@ -272,7 +275,7 @@ module.exports = function($window) {
272
275
function updateNodes ( parent , old , vnodes , hooks , nextSibling , ns ) {
273
276
if ( old === vnodes || old == null && vnodes == null ) return
274
277
else if ( old == null || old . length === 0 ) createNodes ( parent , vnodes , 0 , vnodes . length , hooks , nextSibling , ns )
275
- else if ( vnodes == null || vnodes . length === 0 ) removeNodes ( old , 0 , old . length )
278
+ else if ( vnodes == null || vnodes . length === 0 ) removeNodes ( parent , old , 0 , old . length )
276
279
else {
277
280
var isOldKeyed = old [ 0 ] != null && old [ 0 ] . key != null
278
281
var isKeyed = vnodes [ 0 ] != null && vnodes [ 0 ] . key != null
@@ -281,7 +284,7 @@ module.exports = function($window) {
281
284
if ( ! isKeyed ) while ( start < vnodes . length && vnodes [ start ] == null ) start ++
282
285
if ( isKeyed === null && isOldKeyed == null ) return // both lists are full of nulls
283
286
if ( isOldKeyed !== isKeyed ) {
284
- removeNodes ( old , oldStart , old . length )
287
+ removeNodes ( parent , old , oldStart , old . length )
285
288
createNodes ( parent , vnodes , start , vnodes . length , hooks , nextSibling , ns )
286
289
} else if ( ! isKeyed ) {
287
290
// Don't index past the end of either list (causes deopts).
@@ -295,10 +298,10 @@ module.exports = function($window) {
295
298
v = vnodes [ start ]
296
299
if ( o === v || o == null && v == null ) continue
297
300
else if ( o == null ) createNode ( parent , v , hooks , ns , getNextSibling ( old , start + 1 , nextSibling ) )
298
- else if ( v == null ) removeNode ( o )
301
+ else if ( v == null ) removeNode ( parent , o )
299
302
else updateNode ( parent , o , v , hooks , getNextSibling ( old , start + 1 , nextSibling ) , ns )
300
303
}
301
- if ( old . length > commonLength ) removeNodes ( old , start , old . length )
304
+ if ( old . length > commonLength ) removeNodes ( parent , old , start , old . length )
302
305
if ( vnodes . length > commonLength ) createNodes ( parent , vnodes , start , vnodes . length , hooks , nextSibling , ns )
303
306
} else {
304
307
// keyed diff
@@ -326,9 +329,9 @@ module.exports = function($window) {
326
329
if ( start === end ) break
327
330
if ( o . key !== ve . key || oe . key !== v . key ) break
328
331
topSibling = getNextSibling ( old , oldStart , nextSibling )
329
- insertNode ( parent , toFragment ( oe ) , topSibling )
332
+ moveNodes ( parent , oe , topSibling )
330
333
if ( oe !== v ) updateNode ( parent , oe , v , hooks , topSibling , ns )
331
- if ( ++ start <= -- end ) insertNode ( parent , toFragment ( o ) , nextSibling )
334
+ if ( ++ start <= -- end ) moveNodes ( parent , o , nextSibling )
332
335
if ( o !== ve ) updateNode ( parent , o , ve , hooks , nextSibling , ns )
333
336
if ( ve . dom != null ) nextSibling = ve . dom
334
337
oldStart ++ ; oldEnd --
@@ -346,7 +349,7 @@ module.exports = function($window) {
346
349
oe = old [ oldEnd ]
347
350
ve = vnodes [ end ]
348
351
}
349
- if ( start > end ) removeNodes ( old , oldStart , oldEnd + 1 )
352
+ if ( start > end ) removeNodes ( parent , old , oldStart , oldEnd + 1 )
350
353
else if ( oldStart > oldEnd ) createNodes ( parent , vnodes , start , end + 1 , hooks , nextSibling , ns )
351
354
else {
352
355
// inspired by ivi https://github.com/ivijs/ivi/ by Boris Kaul
@@ -367,7 +370,7 @@ module.exports = function($window) {
367
370
}
368
371
}
369
372
nextSibling = originalNextSibling
370
- if ( matched !== oldEnd - oldStart + 1 ) removeNodes ( old , oldStart , oldEnd + 1 )
373
+ if ( matched !== oldEnd - oldStart + 1 ) removeNodes ( parent , old , oldStart , oldEnd + 1 )
371
374
if ( matched === 0 ) createNodes ( parent , vnodes , start , end + 1 , hooks , nextSibling , ns )
372
375
else {
373
376
if ( pos === - 1 ) {
@@ -380,7 +383,7 @@ module.exports = function($window) {
380
383
if ( oldIndices [ i - start ] === - 1 ) createNode ( parent , v , hooks , ns , nextSibling )
381
384
else {
382
385
if ( lisIndices [ li ] === i - start ) li --
383
- else insertNode ( parent , toFragment ( v ) , nextSibling )
386
+ else moveNodes ( parent , v , nextSibling )
384
387
}
385
388
if ( v . dom != null ) nextSibling = vnodes [ i ] . dom
386
389
}
@@ -416,7 +419,7 @@ module.exports = function($window) {
416
419
else updateComponent ( parent , old , vnode , hooks , nextSibling , ns )
417
420
}
418
421
else {
419
- removeNode ( old )
422
+ removeNode ( parent , old )
420
423
createNode ( parent , vnode , hooks , ns , nextSibling )
421
424
}
422
425
}
@@ -428,7 +431,7 @@ module.exports = function($window) {
428
431
}
429
432
function updateHTML ( parent , old , vnode , ns , nextSibling ) {
430
433
if ( old . children !== vnode . children ) {
431
- toFragment ( old )
434
+ removeHTML ( parent , old )
432
435
createHTML ( parent , vnode , ns , nextSibling )
433
436
}
434
437
else vnode . dom = old . dom , vnode . domSize = old . domSize
@@ -483,7 +486,7 @@ module.exports = function($window) {
483
486
vnode . domSize = vnode . instance . domSize
484
487
}
485
488
else if ( old . instance != null ) {
486
- removeNode ( old . instance )
489
+ removeNode ( parent , old . instance )
487
490
vnode . dom = undefined
488
491
vnode . domSize = 0
489
492
}
@@ -550,26 +553,52 @@ module.exports = function($window) {
550
553
return result
551
554
}
552
555
553
- function toFragment ( vnode ) {
554
- var count = vnode . domSize
555
- if ( count != null || vnode . dom == null ) {
556
- var fragment = $doc . createDocumentFragment ( )
557
- if ( count > 0 ) {
558
- var dom = vnode . dom
559
- while ( -- count ) fragment . appendChild ( dom . nextSibling )
560
- fragment . insertBefore ( dom , fragment . firstChild )
561
- }
562
- return fragment
563
- }
564
- else return vnode . dom
565
- }
566
556
function getNextSibling ( vnodes , i , nextSibling ) {
567
557
for ( ; i < vnodes . length ; i ++ ) {
568
558
if ( vnodes [ i ] != null && vnodes [ i ] . dom != null ) return vnodes [ i ] . dom
569
559
}
570
560
return nextSibling
571
561
}
572
562
563
+ // This covers a really specific edge case:
564
+ // - Parent node is keyed and contains child
565
+ // - Child is removed, returns unresolved promise in `onbeforeremove`
566
+ // - Parent node is moved in keyed diff
567
+ // - Remaining children still need moved appropriately
568
+ //
569
+ // Ideally, I'd track removed nodes as well, but that introduces a lot more
570
+ // complexity and I'm not exactly interested in doing that.
571
+ function moveNodes ( parent , vnode , nextSibling ) {
572
+ var frag = $doc . createDocumentFragment ( )
573
+ moveChildToFrag ( parent , frag , vnode )
574
+ insertNode ( parent , frag , nextSibling )
575
+ }
576
+ function moveChildToFrag ( parent , frag , vnode ) {
577
+ // Dodge the recursion overhead in a few of the most common cases.
578
+ while ( vnode . dom != null && vnode . dom . parentNode === parent ) {
579
+ if ( typeof vnode . tag !== "string" ) {
580
+ vnode = vnode . instance
581
+ if ( vnode != null ) continue
582
+ } else if ( vnode . tag === "<" ) {
583
+ for ( var i = 0 ; i < vnode . instance . length ; i ++ ) {
584
+ frag . appendChild ( vnode . instance [ i ] )
585
+ }
586
+ } else if ( vnode . tag !== "[" ) {
587
+ // Don't recurse for text nodes *or* elements, just fragments
588
+ frag . appendChild ( vnode . dom )
589
+ } else if ( vnode . children . length === 1 ) {
590
+ vnode = vnode . children [ 0 ]
591
+ if ( vnode != null ) continue
592
+ } else {
593
+ for ( var i = 0 ; i < vnode . children . length ; i ++ ) {
594
+ var child = vnode . children [ i ]
595
+ if ( child != null ) moveChildToFrag ( parent , frag , child )
596
+ }
597
+ }
598
+ break
599
+ }
600
+ }
601
+
573
602
function insertNode ( parent , dom , nextSibling ) {
574
603
if ( nextSibling != null ) parent . insertBefore ( dom , nextSibling )
575
604
else parent . appendChild ( dom )
@@ -589,41 +618,89 @@ module.exports = function($window) {
589
618
}
590
619
591
620
//remove
592
- function removeNodes ( vnodes , start , end ) {
621
+ function removeNodes ( parent , vnodes , start , end ) {
593
622
for ( var i = start ; i < end ; i ++ ) {
594
623
var vnode = vnodes [ i ]
595
- if ( vnode != null ) removeNode ( vnode )
624
+ if ( vnode != null ) removeNode ( parent , vnode )
596
625
}
597
626
}
598
- function removeNode ( vnode ) {
599
- var expected = 1 , called = 0
627
+ function removeNode ( parent , vnode ) {
628
+ var mask = 0
600
629
var original = vnode . state
630
+ var stateResult , attrsResult
601
631
if ( typeof vnode . tag !== "string" && typeof vnode . state . onbeforeremove === "function" ) {
602
632
var result = callHook . call ( vnode . state . onbeforeremove , vnode )
603
633
if ( result != null && typeof result . then === "function" ) {
604
- expected ++
605
- result . then ( continuation , continuation )
634
+ mask = 1
635
+ stateResult = result
606
636
}
607
637
}
608
638
if ( vnode . attrs && typeof vnode . attrs . onbeforeremove === "function" ) {
609
639
var result = callHook . call ( vnode . attrs . onbeforeremove , vnode )
610
640
if ( result != null && typeof result . then === "function" ) {
611
- expected ++
612
- result . then ( continuation , continuation )
613
- }
614
- }
615
- continuation ( )
616
- function continuation ( ) {
617
- if ( ++ called === expected ) {
618
- checkState ( vnode , original )
619
- onremove ( vnode )
620
- if ( vnode . dom ) {
621
- var parent = vnode . dom . parentNode
622
- var count = vnode . domSize || 1
623
- while ( -- count ) parent . removeChild ( vnode . dom . nextSibling )
641
+ // eslint-disable-next-line no-bitwise
642
+ mask |= 2
643
+ attrsResult = result
644
+ }
645
+ }
646
+ checkState ( vnode , original )
647
+
648
+ // If we can, try to fast-path it and avoid all the overhead of awaiting
649
+ if ( ! mask ) {
650
+ onremove ( vnode )
651
+ removeChild ( parent , vnode )
652
+ } else {
653
+ if ( stateResult != null ) {
654
+ var next = function ( ) {
655
+ // eslint-disable-next-line no-bitwise
656
+ if ( mask & 1 ) { mask &= 2 ; if ( ! mask ) reallyRemove ( ) }
657
+ }
658
+ stateResult . then ( next , next )
659
+ }
660
+ if ( attrsResult != null ) {
661
+ var next = function ( ) {
662
+ // eslint-disable-next-line no-bitwise
663
+ if ( mask & 2 ) { mask &= 1 ; if ( ! mask ) reallyRemove ( ) }
664
+ }
665
+ attrsResult . then ( next , next )
666
+ }
667
+ }
668
+
669
+ function reallyRemove ( ) {
670
+ checkState ( vnode , original )
671
+ onremove ( vnode )
672
+ removeChild ( parent , vnode )
673
+ }
674
+ }
675
+ function removeHTML ( parent , vnode ) {
676
+ for ( var i = 0 ; i < vnode . instance . length ; i ++ ) {
677
+ parent . removeChild ( vnode . instance [ i ] )
678
+ }
679
+ }
680
+ function removeChild ( parent , vnode ) {
681
+ // Dodge the recursion overhead in a few of the most common cases.
682
+ while ( vnode . dom != null && vnode . dom . parentNode === parent ) {
683
+ if ( typeof vnode . tag !== "string" ) {
684
+ vnode = vnode . instance
685
+ if ( vnode != null ) continue
686
+ } else if ( vnode . tag === "<" ) {
687
+ removeHTML ( parent , vnode )
688
+ } else {
689
+ if ( vnode . tag !== "[" ) {
624
690
parent . removeChild ( vnode . dom )
691
+ if ( ! Array . isArray ( vnode . children ) ) break
692
+ }
693
+ if ( vnode . children . length === 1 ) {
694
+ vnode = vnode . children [ 0 ]
695
+ if ( vnode != null ) continue
696
+ } else {
697
+ for ( var i = 0 ; i < vnode . children . length ; i ++ ) {
698
+ var child = vnode . children [ i ]
699
+ if ( child != null ) removeChild ( parent , child )
700
+ }
625
701
}
626
702
}
703
+ break
627
704
}
628
705
}
629
706
function onremove ( vnode ) {
0 commit comments