@@ -14,7 +14,7 @@ export function end_hydrating() {
14
14
type NodeEx = Node & {
15
15
claim_order ?: number ,
16
16
hydrate_init ? : true ,
17
- actual_end_child ?: Node ,
17
+ actual_end_child ?: NodeEx ,
18
18
childNodes : NodeListOf < NodeEx > ,
19
19
} ;
20
20
@@ -37,8 +37,20 @@ function init_hydrate(target: NodeEx) {
37
37
38
38
type NodeEx2 = NodeEx & { claim_order : number } ;
39
39
40
- // We know that all children have claim_order values since the unclaimed have been detached
41
- const children = target . childNodes as NodeListOf < NodeEx2 > ;
40
+ // We know that all children have claim_order values since the unclaimed have been detached if target is not <head>
41
+ let children : ArrayLike < NodeEx2 > = target . childNodes as NodeListOf < NodeEx2 > ;
42
+
43
+ // If target is <head>, there may be children without claim_order
44
+ if ( target . nodeName === 'HEAD' ) {
45
+ const myChildren = [ ] ;
46
+ for ( let i = 0 ; i < children . length ; i ++ ) {
47
+ const node = children [ i ] ;
48
+ if ( node . claim_order !== undefined ) {
49
+ myChildren . push ( node ) ;
50
+ }
51
+ }
52
+ children = myChildren ;
53
+ }
42
54
43
55
/*
44
56
* Reorder claimed children optimally.
@@ -70,7 +82,8 @@ function init_hydrate(target: NodeEx) {
70
82
// Find the largest subsequence length such that it ends in a value less than our current value
71
83
72
84
// upper_bound returns first greater value, so we subtract one
73
- const seqLen = upper_bound ( 1 , longest + 1 , idx => children [ m [ idx ] ] . claim_order , current ) - 1 ;
85
+ // with fast path for when we are on the current longest subsequence
86
+ const seqLen = ( ( longest > 0 && children [ m [ longest ] ] . claim_order <= current ) ? longest + 1 : upper_bound ( 1 , longest , idx => children [ m [ idx ] ] . claim_order , current ) ) - 1 ;
74
87
75
88
p [ i ] = m [ seqLen ] + 1 ;
76
89
@@ -119,8 +132,17 @@ export function append(target: NodeEx, node: NodeEx) {
119
132
if ( ( target . actual_end_child === undefined ) || ( ( target . actual_end_child !== null ) && ( target . actual_end_child . parentElement !== target ) ) ) {
120
133
target . actual_end_child = target . firstChild ;
121
134
}
135
+
136
+ // Skip nodes of undefined ordering
137
+ while ( ( target . actual_end_child !== null ) && ( target . actual_end_child . claim_order === undefined ) ) {
138
+ target . actual_end_child = target . actual_end_child . nextSibling ;
139
+ }
140
+
122
141
if ( node !== target . actual_end_child ) {
123
- target . insertBefore ( node , target . actual_end_child ) ;
142
+ // We only insert if the ordering of this node should be modified or the parent node is not target
143
+ if ( node . claim_order !== undefined || node . parentNode !== target ) {
144
+ target . insertBefore ( node , target . actual_end_child ) ;
145
+ }
124
146
} else {
125
147
target . actual_end_child = node . nextSibling ;
126
148
}
@@ -304,21 +326,29 @@ export function children(element: Element) {
304
326
return Array . from ( element . childNodes ) ;
305
327
}
306
328
307
- function claim_node < R extends ChildNodeEx > ( nodes : ChildNodeArray , predicate : ( node : ChildNodeEx ) => node is R , processNode : ( node : ChildNodeEx ) => void , createNode : ( ) => R , dontUpdateLastIndex : boolean = false ) {
308
- // Try to find nodes in an order such that we lengthen the longest increasing subsequence
329
+ function init_claim_info ( nodes : ChildNodeArray ) {
309
330
if ( nodes . claim_info === undefined ) {
310
331
nodes . claim_info = { last_index : 0 , total_claimed : 0 } ;
311
332
}
333
+ }
334
+
335
+ function claim_node < R extends ChildNodeEx > ( nodes : ChildNodeArray , predicate : ( node : ChildNodeEx ) => node is R , processNode : ( node : ChildNodeEx ) => ChildNodeEx | undefined , createNode : ( ) => R , dontUpdateLastIndex : boolean = false ) {
336
+ // Try to find nodes in an order such that we lengthen the longest increasing subsequence
337
+ init_claim_info ( nodes ) ;
312
338
313
339
const resultNode = ( ( ) => {
314
340
// We first try to find an element after the previous one
315
341
for ( let i = nodes . claim_info . last_index ; i < nodes . length ; i ++ ) {
316
342
const node = nodes [ i ] ;
317
343
318
344
if ( predicate ( node ) ) {
319
- processNode ( node ) ;
345
+ const replacement = processNode ( node ) ;
320
346
321
- nodes . splice ( i , 1 ) ;
347
+ if ( replacement === undefined ) {
348
+ nodes . splice ( i , 1 ) ;
349
+ } else {
350
+ nodes [ i ] = replacement ;
351
+ }
322
352
if ( ! dontUpdateLastIndex ) {
323
353
nodes . claim_info . last_index = i ;
324
354
}
@@ -333,12 +363,16 @@ function claim_node<R extends ChildNodeEx>(nodes: ChildNodeArray, predicate: (no
333
363
const node = nodes [ i ] ;
334
364
335
365
if ( predicate ( node ) ) {
336
- processNode ( node ) ;
366
+ const replacement = processNode ( node ) ;
337
367
338
- nodes . splice ( i , 1 ) ;
368
+ if ( replacement === undefined ) {
369
+ nodes . splice ( i , 1 ) ;
370
+ } else {
371
+ nodes [ i ] = replacement ;
372
+ }
339
373
if ( ! dontUpdateLastIndex ) {
340
374
nodes . claim_info . last_index = i ;
341
- } else {
375
+ } else if ( replacement === undefined ) {
342
376
// Since we spliced before the last_index, we decrease it
343
377
nodes . claim_info . last_index -- ;
344
378
}
@@ -368,6 +402,7 @@ export function claim_element(nodes: ChildNodeArray, name: string, attributes: {
368
402
}
369
403
}
370
404
remove . forEach ( v => node . removeAttribute ( v ) ) ;
405
+ return undefined ;
371
406
} ,
372
407
( ) => svg ? svg_element ( name as keyof SVGElementTagNameMap ) : element ( name as keyof HTMLElementTagNameMap )
373
408
) ;
@@ -378,7 +413,14 @@ export function claim_text(nodes: ChildNodeArray, data) {
378
413
nodes ,
379
414
( node : ChildNode ) : node is Text => node . nodeType === 3 ,
380
415
( node : Text ) => {
381
- node . data = '' + data ;
416
+ const dataStr = '' + data ;
417
+ if ( node . data . startsWith ( dataStr ) ) {
418
+ if ( node . data . length !== dataStr . length ) {
419
+ return node . splitText ( dataStr . length ) ;
420
+ }
421
+ } else {
422
+ node . data = dataStr ;
423
+ }
382
424
} ,
383
425
( ) => text ( data ) ,
384
426
true // Text nodes should not update last index since it is likely not worth it to eliminate an increasing subsequence of actual elements
@@ -406,10 +448,17 @@ export function claim_html_tag(nodes) {
406
448
if ( start_index === end_index ) {
407
449
return new HtmlTag ( ) ;
408
450
}
451
+
452
+ init_claim_info ( nodes ) ;
409
453
const html_tag_nodes = nodes . splice ( start_index , end_index + 1 ) ;
410
454
detach ( html_tag_nodes [ 0 ] ) ;
411
455
detach ( html_tag_nodes [ html_tag_nodes . length - 1 ] ) ;
412
- return new HtmlTag ( html_tag_nodes . slice ( 1 , html_tag_nodes . length - 1 ) ) ;
456
+ const claimed_nodes = html_tag_nodes . slice ( 1 , html_tag_nodes . length - 1 ) ;
457
+ for ( const n of claimed_nodes ) {
458
+ n . claim_order = nodes . claim_info . total_claimed ;
459
+ nodes . claim_info . total_claimed += 1 ;
460
+ }
461
+ return new HtmlTag ( claimed_nodes ) ;
413
462
}
414
463
415
464
export function set_data ( text , data ) {
@@ -535,7 +584,7 @@ export function custom_event<T=any>(type: string, detail?: T, bubbles: boolean =
535
584
}
536
585
537
586
export function query_selector_all ( selector : string , parent : HTMLElement = document . body ) {
538
- return Array . from ( parent . querySelectorAll ( selector ) ) ;
587
+ return Array . from ( parent . querySelectorAll ( selector ) ) as ChildNodeArray ;
539
588
}
540
589
541
590
export class HtmlTag {
0 commit comments