all repos — h3 @ 249ae34698c349a227ddd8227b0c9db03b116980

A tiny, extremely minimalist JavaScript microframework.

Fixed redraw algorithm for lists with common nodes in different positions; other fixes.
h3rald h3rald@h3rald.com
Fri, 29 May 2020 17:05:20 +0200
commit

249ae34698c349a227ddd8227b0c9db03b116980

parent

337b82237ed01af96e8cb94cd641b8c5811bf381

M README.mdREADME.md

@@ -46,3 +46,6 @@ ### What if something is broken?

Go fix it! Or at least open an issue on the [Github repo](https://github.com/h3rald/h3), pleasy. +### Can I download a copy of this site as a standalone HTML file? + +What a weird thing to ask... sure you can: [here](/H3_DeveloperGuide.htm)!
M __tests__/vnode.js__tests__/vnode.js

@@ -217,6 +217,15 @@ expect(node.constructor).toEqual(HTMLSpanElement);

expect(container.childNodes[0].constructor).toEqual(HTMLDivElement); }); + it("should provide redraw method to detect position changes in child nodes", () => { + const v1 = h3("ul", [h3("li.a"), h3("li.b"), h3("li.c"), h3("li.d")]); + const v2 = h3("ul", [h3("li.c"), h3("li.b"), h3("li.a"), h3("li.d")]); + const n = v1.render(); + expect(n.childNodes[0].classList[0]).toEqual("a"); + v1.redraw({ node: n, vnode: v2 }); + expect(n.childNodes[0].classList[0]).toEqual("c"); + }); + it("should provide redraw method to detect changed nodes if they have different node types", () => { const oldvnode = h3("span.c", { title: "b" }); const newvnode = h3({ type: "#text", value: "test" });

@@ -255,7 +264,7 @@ expect(node.childNodes[0].getAttribute("id")).toEqual("aaa");

expect(node.childNodes[1].getAttribute("id")).toEqual("ccc"); }); - it("should provide a redraw method able to detect specific changes to style, data, value, attributes and eventListeners", () => { + it("should provide a redraw method able to detect specific changes to style, data, value, attributes, $onrender and eventListeners", () => { const fn = () => false; const oldvnode = h3("input", { style: "margin: auto;",

@@ -275,6 +284,7 @@ label: "test",

placeholder: "test", onkeydown: () => true, onkeypress: () => false, + $onrender: () => true, onhover: () => true, }); const container = document.createElement("div");

@@ -295,7 +305,10 @@

it("should provide a redraw method able to detect changes in child content", () => { const v1 = h3("ul", [h3("li", "a"), h3("li", "b")]); const n1 = v1.render(); - const v2 = h3("ul", { $html: "<li>a</li><li>b</li>" }); + const v2 = h3("ul", { + $html: "<li>a</li><li>b</li>", + $onrender: (node) => node.classList.add("test"), + }); const v3 = h3("ul", [h3("li", "a")]); const v4 = h3("ul", [h3("li", "b")]); const n2 = v2.render();

@@ -304,6 +317,7 @@ expect(n2.childNodes[0].childNodes[0].data).toEqual(

n1.childNodes[0].childNodes[0].data ); v1.redraw({ node: n1, vnode: v2 }); + expect(n1.classList[0]).toEqual("test"); expect(v1).toEqual(v2); v3.redraw({ node: n3, vnode: v4 }); expect(v3).toEqual(v4);
M docs/H3_DeveloperGuide.htmdocs/H3_DeveloperGuide.htm

@@ -8044,7 +8044,7 @@ </ul>

</div> <div id="footer"> - <p><span class="copy"></span> Fabio Cevasco &ndash; May 25, 2020</p> + <p><span class="copy"></span> Fabio Cevasco &ndash; May 29, 2020</p> <p><span>Powered by</span> <a href="https://h3rald.com/hastyscribe"><span class="hastyscribe"></span></a></p> </div> </div>
M docs/example/assets/js/h3.jsdocs/example/assets/js/h3.js

@@ -60,7 +60,7 @@ return checkProperties(obj1, obj2); // && checkProperties(obj2, obj1);

}; let $onrenderCallbacks = []; -const selectorRegex = /^([a-z0-9:_=-]+)(#[a-z0-9:_=-]+)?(\..+)?$/i; +const selectorRegex = /^([a-z][a-z0-9:_=-]*)(#[a-z0-9:_=-]+)?(\.[^ ]+)*$/i; // Virtual Node Implementation with HyperScript-like syntax class VNode {

@@ -436,20 +436,45 @@ }

} return map; } - // Map positions of newvnode children in relation to oldvnode children - let newmap = mapChildren(newvnode, oldvnode); - // Map positions of oldvnode children in relation to newvnode children - let oldmap = mapChildren(oldvnode, newvnode); - let notFoundInOld = newmap.indexOf(-1); - let notFoundInNew = oldmap.indexOf(-1); - if (equal(newmap, oldmap) && notFoundInNew >= 0) { - // Something changed (some nodes are different at the same position) - for (let i = 0; i < newmap.length; i++) { - if (newmap[i] === -1 || oldmap[i] === -1) { - oldvnode.children[i].redraw({ - node: node.childNodes[i], - vnode: newvnode.children[i], - }); + let newmap, oldmap, notFoundInNew, notFoundInOld; + const remap = () => { + // Map positions of newvnode children in relation to oldvnode children + newmap = mapChildren(newvnode, oldvnode); + // Map positions of oldvnode children in relation to newvnode children + oldmap = mapChildren(oldvnode, newvnode); + notFoundInOld = newmap.indexOf(-1); + notFoundInNew = oldmap.indexOf(-1); + }; + remap(); + if (newmap.length === oldmap.length) { + if (equal(newmap, oldmap) && notFoundInNew >= 0) { + // Something changed (some nodes are different at the same position) + for (let i = 0; i < newmap.length; i++) { + if (newmap[i] === -1 || oldmap[i] === -1) { + oldvnode.children[i].redraw({ + node: node.childNodes[i], + vnode: newvnode.children[i], + }); + } + } + } else { + // Nodes in different position (maps have same nodes) + let index = 0; + while (!equal(oldmap, [...Array(oldmap.length).keys()])) { + if (newmap[index] !== index) { + const child = node.childNodes[newmap[index]]; + node.removeChild(child); + node.insertBefore(child, node.childNodes[index]); + const cnode = oldvnode.children[newmap[index]]; + oldvnode.children = oldvnode.children.filter( + (c) => !equal(c, cnode) + ); + oldvnode.children.splice(index, 0, cnode); + remap(); + index = 0; + } else { + index++; + } } } } else {

@@ -487,16 +512,18 @@ 0,

newvnode.children[notFoundInOld] ); } - newmap = mapChildren(newvnode, oldvnode); - oldmap = mapChildren(oldvnode, newvnode); - notFoundInNew = oldmap.indexOf(-1); - notFoundInOld = newmap.indexOf(-1); + remap(); } } + // $onrender + if (!equal(oldvnode.$onrender, newvnode.$onrender)) { + oldvnode.$onrender = newvnode.$onrender; + } // innerHTML - if (newvnode.$html) { + if (oldvnode.$html !== newvnode.$html) { node.innerHTML = newvnode.$html; oldvnode.$html = newvnode.$html; + oldvnode.$onrender && oldvnode.$onrender(node); } } }

@@ -574,6 +601,7 @@ }

async start() { const processPath = async (data) => { + $onrenderCallbacks = []; const oldRoute = this.route; const fragment = (data &&
M docs/js/h3.jsdocs/js/h3.js

@@ -60,7 +60,7 @@ return checkProperties(obj1, obj2); // && checkProperties(obj2, obj1);

}; let $onrenderCallbacks = []; -const selectorRegex = /^([a-z0-9:_=-]+)(#[a-z0-9:_=-]+)?(\..+)?$/i; +const selectorRegex = /^([a-z][a-z0-9:_=-]*)(#[a-z0-9:_=-]+)?(\.[^ ]+)*$/i; // Virtual Node Implementation with HyperScript-like syntax class VNode {

@@ -436,20 +436,45 @@ }

} return map; } - // Map positions of newvnode children in relation to oldvnode children - let newmap = mapChildren(newvnode, oldvnode); - // Map positions of oldvnode children in relation to newvnode children - let oldmap = mapChildren(oldvnode, newvnode); - let notFoundInOld = newmap.indexOf(-1); - let notFoundInNew = oldmap.indexOf(-1); - if (equal(newmap, oldmap) && notFoundInNew >= 0) { - // Something changed (some nodes are different at the same position) - for (let i = 0; i < newmap.length; i++) { - if (newmap[i] === -1 || oldmap[i] === -1) { - oldvnode.children[i].redraw({ - node: node.childNodes[i], - vnode: newvnode.children[i], - }); + let newmap, oldmap, notFoundInNew, notFoundInOld; + const remap = () => { + // Map positions of newvnode children in relation to oldvnode children + newmap = mapChildren(newvnode, oldvnode); + // Map positions of oldvnode children in relation to newvnode children + oldmap = mapChildren(oldvnode, newvnode); + notFoundInOld = newmap.indexOf(-1); + notFoundInNew = oldmap.indexOf(-1); + }; + remap(); + if (newmap.length === oldmap.length) { + if (equal(newmap, oldmap) && notFoundInNew >= 0) { + // Something changed (some nodes are different at the same position) + for (let i = 0; i < newmap.length; i++) { + if (newmap[i] === -1 || oldmap[i] === -1) { + oldvnode.children[i].redraw({ + node: node.childNodes[i], + vnode: newvnode.children[i], + }); + } + } + } else { + // Nodes in different position (maps have same nodes) + let index = 0; + while (!equal(oldmap, [...Array(oldmap.length).keys()])) { + if (newmap[index] !== index) { + const child = node.childNodes[newmap[index]]; + node.removeChild(child); + node.insertBefore(child, node.childNodes[index]); + const cnode = oldvnode.children[newmap[index]]; + oldvnode.children = oldvnode.children.filter( + (c) => !equal(c, cnode) + ); + oldvnode.children.splice(index, 0, cnode); + remap(); + index = 0; + } else { + index++; + } } } } else {

@@ -487,16 +512,18 @@ 0,

newvnode.children[notFoundInOld] ); } - newmap = mapChildren(newvnode, oldvnode); - oldmap = mapChildren(oldvnode, newvnode); - notFoundInNew = oldmap.indexOf(-1); - notFoundInOld = newmap.indexOf(-1); + remap(); } } + // $onrender + if (!equal(oldvnode.$onrender, newvnode.$onrender)) { + oldvnode.$onrender = newvnode.$onrender; + } // innerHTML - if (newvnode.$html) { + if (oldvnode.$html !== newvnode.$html) { node.innerHTML = newvnode.$html; oldvnode.$html = newvnode.$html; + oldvnode.$onrender && oldvnode.$onrender(node); } } }

@@ -574,6 +601,7 @@ }

async start() { const processPath = async (data) => { + $onrenderCallbacks = []; const oldRoute = this.route; const fragment = (data &&
M h3.jsh3.js

@@ -60,7 +60,7 @@ return checkProperties(obj1, obj2); // && checkProperties(obj2, obj1);

}; let $onrenderCallbacks = []; -const selectorRegex = /^([a-z0-9:_=-]+)(#[a-z0-9:_=-]+)?(\..+)?$/i; +const selectorRegex = /^([a-z][a-z0-9:_=-]*)(#[a-z0-9:_=-]+)?(\.[^ ]+)*$/i; // Virtual Node Implementation with HyperScript-like syntax class VNode {

@@ -436,20 +436,45 @@ }

} return map; } - // Map positions of newvnode children in relation to oldvnode children - let newmap = mapChildren(newvnode, oldvnode); - // Map positions of oldvnode children in relation to newvnode children - let oldmap = mapChildren(oldvnode, newvnode); - let notFoundInOld = newmap.indexOf(-1); - let notFoundInNew = oldmap.indexOf(-1); - if (equal(newmap, oldmap) && notFoundInNew >= 0) { - // Something changed (some nodes are different at the same position) - for (let i = 0; i < newmap.length; i++) { - if (newmap[i] === -1 || oldmap[i] === -1) { - oldvnode.children[i].redraw({ - node: node.childNodes[i], - vnode: newvnode.children[i], - }); + let newmap, oldmap, notFoundInNew, notFoundInOld; + const remap = () => { + // Map positions of newvnode children in relation to oldvnode children + newmap = mapChildren(newvnode, oldvnode); + // Map positions of oldvnode children in relation to newvnode children + oldmap = mapChildren(oldvnode, newvnode); + notFoundInOld = newmap.indexOf(-1); + notFoundInNew = oldmap.indexOf(-1); + }; + remap(); + if (newmap.length === oldmap.length) { + if (equal(newmap, oldmap) && notFoundInNew >= 0) { + // Something changed (some nodes are different at the same position) + for (let i = 0; i < newmap.length; i++) { + if (newmap[i] === -1 || oldmap[i] === -1) { + oldvnode.children[i].redraw({ + node: node.childNodes[i], + vnode: newvnode.children[i], + }); + } + } + } else { + // Nodes in different position (maps have same nodes) + let index = 0; + while (!equal(oldmap, [...Array(oldmap.length).keys()])) { + if (newmap[index] !== index) { + const child = node.childNodes[newmap[index]]; + node.removeChild(child); + node.insertBefore(child, node.childNodes[index]); + const cnode = oldvnode.children[newmap[index]]; + oldvnode.children = oldvnode.children.filter( + (c) => !equal(c, cnode) + ); + oldvnode.children.splice(index, 0, cnode); + remap(); + index = 0; + } else { + index++; + } } } } else {

@@ -487,16 +512,18 @@ 0,

newvnode.children[notFoundInOld] ); } - newmap = mapChildren(newvnode, oldvnode); - oldmap = mapChildren(oldvnode, newvnode); - notFoundInNew = oldmap.indexOf(-1); - notFoundInOld = newmap.indexOf(-1); + remap(); } } + // $onrender + if (!equal(oldvnode.$onrender, newvnode.$onrender)) { + oldvnode.$onrender = newvnode.$onrender; + } // innerHTML - if (newvnode.$html) { + if (oldvnode.$html !== newvnode.$html) { node.innerHTML = newvnode.$html; oldvnode.$html = newvnode.$html; + oldvnode.$onrender && oldvnode.$onrender(node); } } }

@@ -574,6 +601,7 @@ }

async start() { const processPath = async (data) => { + $onrenderCallbacks = []; const oldRoute = this.route; const fragment = (data &&
M scripts/release.jsscripts/release.js

@@ -28,8 +28,10 @@ readmeData = readmeData.replace(

/Download v\d+\.\d+\.\d+ \([^)]+\)/, `Download v${pkg.version} (${pkg.versionName})` ); -readmeData = readmeData.replace(/### Can I download(\n|.)+/gm, ""); fs.writeFileSync(readme, readmeData); + +// Remove link to download guide in overview.md +readmeData = readmeData.replace(/### Can I download(\n|\r|.)+/gm, ""); // Remove badges and copy to overview.md const overviewData = readmeData.replace(/[^\*]+\*\*\*\s+/m, "");