|
|
|
@ -14,7 +14,7 @@ export function end_hydrating() {
|
|
|
|
|
type NodeEx = Node & {
|
|
|
|
|
claim_order?: number,
|
|
|
|
|
hydrate_init? : true,
|
|
|
|
|
actual_end_child?: Node,
|
|
|
|
|
actual_end_child?: NodeEx,
|
|
|
|
|
childNodes: NodeListOf<NodeEx>,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -37,8 +37,20 @@ function init_hydrate(target: NodeEx) {
|
|
|
|
|
|
|
|
|
|
type NodeEx2 = NodeEx & {claim_order: number};
|
|
|
|
|
|
|
|
|
|
// We know that all children have claim_order values since the unclaimed have been detached
|
|
|
|
|
const children = target.childNodes as NodeListOf<NodeEx2>;
|
|
|
|
|
// We know that all children have claim_order values since the unclaimed have been detached if target is not <head>
|
|
|
|
|
let children: ArrayLike<NodeEx2> = target.childNodes as NodeListOf<NodeEx2>;
|
|
|
|
|
|
|
|
|
|
// If target is <head>, there may be children without claim_order
|
|
|
|
|
if (target.nodeName === 'HEAD') {
|
|
|
|
|
const myChildren = [];
|
|
|
|
|
for (let i = 0; i < children.length; i++) {
|
|
|
|
|
const node = children[i];
|
|
|
|
|
if (node.claim_order !== undefined) {
|
|
|
|
|
myChildren.push(node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
children = myChildren;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Reorder claimed children optimally.
|
|
|
|
@ -70,7 +82,8 @@ function init_hydrate(target: NodeEx) {
|
|
|
|
|
// Find the largest subsequence length such that it ends in a value less than our current value
|
|
|
|
|
|
|
|
|
|
// upper_bound returns first greater value, so we subtract one
|
|
|
|
|
const seqLen = upper_bound(1, longest + 1, idx => children[m[idx]].claim_order, current) - 1;
|
|
|
|
|
// with fast path for when we are on the current longest subsequence
|
|
|
|
|
const seqLen = ((longest > 0 && children[m[longest]].claim_order <= current) ? longest + 1 : upper_bound(1, longest, idx => children[m[idx]].claim_order, current)) - 1;
|
|
|
|
|
|
|
|
|
|
p[i] = m[seqLen] + 1;
|
|
|
|
|
|
|
|
|
@ -119,8 +132,17 @@ export function append(target: NodeEx, node: NodeEx) {
|
|
|
|
|
if ((target.actual_end_child === undefined) || ((target.actual_end_child !== null) && (target.actual_end_child.parentElement !== target))) {
|
|
|
|
|
target.actual_end_child = target.firstChild;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Skip nodes of undefined ordering
|
|
|
|
|
while ((target.actual_end_child !== null) && (target.actual_end_child.claim_order === undefined)) {
|
|
|
|
|
target.actual_end_child = target.actual_end_child.nextSibling;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (node !== target.actual_end_child) {
|
|
|
|
|
target.insertBefore(node, target.actual_end_child);
|
|
|
|
|
// We only insert if the ordering of this node should be modified or the parent node is not target
|
|
|
|
|
if (node.claim_order !== undefined || node.parentNode !== target) {
|
|
|
|
|
target.insertBefore(node, target.actual_end_child);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
target.actual_end_child = node.nextSibling;
|
|
|
|
|
}
|
|
|
|
@ -304,11 +326,15 @@ export function children(element: Element) {
|
|
|
|
|
return Array.from(element.childNodes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function claim_node<R extends ChildNodeEx>(nodes: ChildNodeArray, predicate: (node: ChildNodeEx) => node is R, processNode: (node: ChildNodeEx) => void, createNode: () => R, dontUpdateLastIndex: boolean = false) {
|
|
|
|
|
// Try to find nodes in an order such that we lengthen the longest increasing subsequence
|
|
|
|
|
function init_claim_info(nodes: ChildNodeArray) {
|
|
|
|
|
if (nodes.claim_info === undefined) {
|
|
|
|
|
nodes.claim_info = {last_index: 0, total_claimed: 0};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function claim_node<R extends ChildNodeEx>(nodes: ChildNodeArray, predicate: (node: ChildNodeEx) => node is R, processNode: (node: ChildNodeEx) => ChildNodeEx | undefined, createNode: () => R, dontUpdateLastIndex: boolean = false) {
|
|
|
|
|
// Try to find nodes in an order such that we lengthen the longest increasing subsequence
|
|
|
|
|
init_claim_info(nodes);
|
|
|
|
|
|
|
|
|
|
const resultNode = (() => {
|
|
|
|
|
// We first try to find an element after the previous one
|
|
|
|
@ -316,9 +342,13 @@ function claim_node<R extends ChildNodeEx>(nodes: ChildNodeArray, predicate: (no
|
|
|
|
|
const node = nodes[i];
|
|
|
|
|
|
|
|
|
|
if (predicate(node)) {
|
|
|
|
|
processNode(node);
|
|
|
|
|
const replacement = processNode(node);
|
|
|
|
|
|
|
|
|
|
nodes.splice(i, 1);
|
|
|
|
|
if (replacement === undefined) {
|
|
|
|
|
nodes.splice(i, 1);
|
|
|
|
|
} else {
|
|
|
|
|
nodes[i] = replacement;
|
|
|
|
|
}
|
|
|
|
|
if (!dontUpdateLastIndex) {
|
|
|
|
|
nodes.claim_info.last_index = i;
|
|
|
|
|
}
|
|
|
|
@ -333,12 +363,16 @@ function claim_node<R extends ChildNodeEx>(nodes: ChildNodeArray, predicate: (no
|
|
|
|
|
const node = nodes[i];
|
|
|
|
|
|
|
|
|
|
if (predicate(node)) {
|
|
|
|
|
processNode(node);
|
|
|
|
|
const replacement = processNode(node);
|
|
|
|
|
|
|
|
|
|
nodes.splice(i, 1);
|
|
|
|
|
if (replacement === undefined) {
|
|
|
|
|
nodes.splice(i, 1);
|
|
|
|
|
} else {
|
|
|
|
|
nodes[i] = replacement;
|
|
|
|
|
}
|
|
|
|
|
if (!dontUpdateLastIndex) {
|
|
|
|
|
nodes.claim_info.last_index = i;
|
|
|
|
|
} else {
|
|
|
|
|
} else if (replacement === undefined) {
|
|
|
|
|
// Since we spliced before the last_index, we decrease it
|
|
|
|
|
nodes.claim_info.last_index--;
|
|
|
|
|
}
|
|
|
|
@ -368,6 +402,7 @@ export function claim_element(nodes: ChildNodeArray, name: string, attributes: {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
remove.forEach(v => node.removeAttribute(v));
|
|
|
|
|
return undefined;
|
|
|
|
|
},
|
|
|
|
|
() => svg ? svg_element(name as keyof SVGElementTagNameMap) : element(name as keyof HTMLElementTagNameMap)
|
|
|
|
|
);
|
|
|
|
@ -378,7 +413,14 @@ export function claim_text(nodes: ChildNodeArray, data) {
|
|
|
|
|
nodes,
|
|
|
|
|
(node: ChildNode): node is Text => node.nodeType === 3,
|
|
|
|
|
(node: Text) => {
|
|
|
|
|
node.data = '' + data;
|
|
|
|
|
const dataStr = '' + data;
|
|
|
|
|
if (node.data.startsWith(dataStr)) {
|
|
|
|
|
if (node.data.length !== dataStr.length) {
|
|
|
|
|
return node.splitText(dataStr.length);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
node.data = dataStr;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
() => text(data),
|
|
|
|
|
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) {
|
|
|
|
|
if (start_index === end_index) {
|
|
|
|
|
return new HtmlTag();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
init_claim_info(nodes);
|
|
|
|
|
const html_tag_nodes = nodes.splice(start_index, end_index + 1);
|
|
|
|
|
detach(html_tag_nodes[0]);
|
|
|
|
|
detach(html_tag_nodes[html_tag_nodes.length - 1]);
|
|
|
|
|
return new HtmlTag(html_tag_nodes.slice(1, html_tag_nodes.length - 1));
|
|
|
|
|
const claimed_nodes = html_tag_nodes.slice(1, html_tag_nodes.length - 1);
|
|
|
|
|
for (const n of claimed_nodes) {
|
|
|
|
|
n.claim_order = nodes.claim_info.total_claimed;
|
|
|
|
|
nodes.claim_info.total_claimed += 1;
|
|
|
|
|
}
|
|
|
|
|
return new HtmlTag(claimed_nodes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function set_data(text, data) {
|
|
|
|
@ -535,7 +584,7 @@ export function custom_event<T=any>(type: string, detail?: T, bubbles: boolean =
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function query_selector_all(selector: string, parent: HTMLElement = document.body) {
|
|
|
|
|
return Array.from(parent.querySelectorAll(selector));
|
|
|
|
|
return Array.from(parent.querySelectorAll(selector)) as ChildNodeArray;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class HtmlTag {
|
|
|
|
|