Merge branch 'dev'
@@ -13,4 +13,4 @@ - npm update
install: - npm install script: -- npm run coverage+- npm run coveralls
@@ -19,7 +19,7 @@ ### I'm sold! Where can I get it?
Here, look, it's just one file: -<a href="https://raw.githubusercontent.com/h3rald/h3/v0.3.0/h3.js" target="_blank" class="button primary">Download v0.3.0 (Cunning Cardassian)</a> +<a href="https://raw.githubusercontent.com/h3rald/h3/v0.4.0/h3.js" target="_blank" class="button primary">Download v0.4.0 (Dedicated Denobulan)</a> Yes there is also a [NPM package](https://www.npmjs.com/package/@h3rald/h3) if you want to use it with WebPack and similar, but let me repeat: _it's just one file_.
@@ -1,6 +1,16 @@
const h3 = require("../h3.js").default; describe("h3", () => { + beforeEach(() => { + jest + .spyOn(window, "requestAnimationFrame") + .mockImplementation((cb) => cb()); + }); + + afterEach(() => { + window.requestAnimationFrame.mockRestore(); + }); + it("should support a way to discriminate functions and objects", () => { const v1 = h3("div", { onclick: () => true }); const v2 = h3("div", { onclick: () => true });@@ -63,7 +73,10 @@ });
it("should remove null/false/undefined children", () => { const v1 = h3("div", [false, "test", undefined, null, ""]); - expect(v1.children).toEqual([h3({ type: "#text", value: "test" }), h3({ type: "#text", value: "" })]); + expect(v1.children).toEqual([ + h3({ type: "#text", value: "test" }), + h3({ type: "#text", value: "" }), + ]); }); it("should support the creation of nodes with a single child node", () => {
@@ -68,6 +68,15 @@ expect(node.childNodes[1].constructor).toEqual(HTMLLIElement);
expect(node.childNodes[0].childNodes[0].data).toEqual("test1"); }); + it("should handle boolean attributes when redrawing", () => { + const vnode1 = h3("input", { type: "checkbox", checked: true }); + const node = vnode1.render(); + expect(node.checked).toEqual(true); + const vnode = h3("input", { type: "checkbox", checked: false }); + vnode1.redraw({ node, vnode }); + expect(node.checked).toEqual(false); + }); + it("should provide a render method able to render element nodes with a value", () => { const vnode = h3("input", { value: "test" }); const createElement = jest.spyOn(document, "createElement");
@@ -7261,7 +7261,7 @@ <li><a href="#Components">Components</a></li>
<li><a href="#Store">Store</a></li> <li><a href="#Modules">Modules</a></li> <li><a href="#Router">Router</a></li> - <li><a href="#Sequence-Diagram">Sequence Diagram</a></li> + <li><a href="#How-everything-works...">How everything works...</a></li> </ul> </li> <li><a href="#Tutorial">Tutorial</a>@@ -7285,10 +7285,10 @@ <li><a href="#Render-child-components">Render child components</a></li>
<li><a href="#Special-attributes">Special attributes</a></li> </ul> </li> - <li><a href="#h3.dispatch(message:-string,-data:-any)">h3.dispatch(message: string, data: any)</a></li> + <li><a href="#h3.dispatch(event:-string,-data:-any)">h3.dispatch(event: string, data: any)</a></li> <li><a href="#h3.init(config:-object)">h3.init(config: object)</a></li> <li><a href="#h3.navigateTo(path:-string,-params:-object)">h3.navigateTo(path: string, params: object)</a></li> - <li><a href="#h3.on(message:-string,-handler:-function)">h3.on(message: string, handler: function)</a></li> + <li><a href="#h3.on(event:-string,-handler:-function)">h3.on(event: string, handler: function)</a></li> <li><a href="#h3.redraw()">h3.redraw()</a></li> <li><a href="#h3.route">h3.route</a></li> <li><a href="#h3.state">h3.state</a></li>@@ -7331,7 +7331,7 @@ <h3>I’m sold! Where can I get it?<a href="#document-top" title="Go to top"></a></h3>
<p>Here, look, it’s just one file:</p> -<p><a href="https://raw.githubusercontent.com/h3rald/h3/v0.3.0/h3.js" target="_blank" class="button primary">Download v0.3.0 (Cunning Cardassian)</a></p> +<p><a href="https://raw.githubusercontent.com/h3rald/h3/v0.4.0/h3.js" target="_blank" class="button primary">Download v0.4.0 (Dedicated Denobulan)</a></p> <p>Yes there is also a <a href="https://www.npmjs.com/package/@h3rald/h3">NPM package</a> if you want to use it with WebPack and similar, but let me repeat: <em>it’s just one file</em>.</p>@@ -7475,14 +7475,14 @@
<a name="Store"></a> <h3>Store<a href="#document-top" title="Go to top"></a></h3> -<p>H3 essentially uses something very, <em>very</em> similar to <a href="https://github.com/storeon/storeon">Storeon</a> for state management <em>and</em> also as a very simple client-side message dispatcher/subscriber (seriously, it is virtually the same code as Storeon). Typically you’ll only use the default store created by H3 upon initialization, and you’ll use the <code>h3.dispatch()</code> and <code>h3.on()</code> methods to dispatch and subscribe to actions (messages).</p> +<p>H3 essentially uses something very, <em>very</em> similar to <a href="https://github.com/storeon/storeon">Storeon</a> for state management <em>and</em> also as a very simple client-side event dispatcher/subscriber (seriously, it is virtually the same code as Storeon). Typically you’ll only use the default store created by H3 upon initialization, and you’ll use the <code>h3.dispatch()</code> and <code>h3.on()</code> methods to dispatch and subscribe to events.</p> <p>The current application state is accessible via the <code>h3.state</code> property.</p> <a name="Modules"></a> <h3>Modules<a href="#document-top" title="Go to top"></a></h3> -<p>The <code>h3.init()</code> method takes an array of <em>modules</em> that can be used to manipulate the application state when specific messages are received. A simple module looks like this:</p> +<p>The <code>h3.init()</code> method takes an array of <em>modules</em> that can be used to manipulate the application state when specific events are received. A simple module looks like this:</p> <pre><code class="js">const error = () => { h3.on("$init", () => ({ displayEmptyTodoError: false }));@@ -7491,7 +7491,7 @@ h3.on("error/set", (state) => ({ displayEmptyTodoError: true }));
}; </code></pre> -<p>Essentially a module is just a function that typically is meant to run only once to define one or more message subscriptions. Modules are the place where you should handle state changes in your application.</p> +<p>Essentially a module is just a function that typically is meant to run only once to define one or more event subscriptions. Modules are the place where you should handle state changes in your application.</p> <a name="Router"></a> <h3>Router<a href="#document-top" title="Go to top"></a></h3>@@ -7500,23 +7500,24 @@ <p>H3 comes with a very minimal but fully functional URL fragment router. You create your application routes when initializing your application, and you can navigate to them using ordinary <code>href</code> links or programmatically using the <code>h3.navigateTo</code> method.</p>
<p>The current route is always accessible via the <code>h3.route</code> property.</p> -<a name="Sequence-Diagram"></a> -<h3>Sequence Diagram<a href="#document-top" title="Go to top"></a></h3> +<a name="How-everything-works..."></a> +<h3>How everything works…<a href="#document-top" title="Go to top"></a></h3> <p>The following sequence diagram summarizes how H3 works, from its initialization to the redraw and navigation phases.</p> -<p><img src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBjb250ZW50U2NyaXB0VHlwZT0iYXBwbGljYXRpb24vZWNtYXNjcmlwdCIgY29udGVudFN0eWxlVHlwZT0idGV4dC9jc3MiIGhlaWdodD0iODgwcHgiIHByZXNlcnZlQXNwZWN0UmF0aW89Im5vbmUiIHN0eWxlPSJ3aWR0aDo3ODdweDtoZWlnaHQ6ODgwcHg7IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA3ODcgODgwIiB3aWR0aD0iNzg3cHgiIHpvb21BbmRQYW49Im1hZ25pZnkiPjxkZWZzPjxmaWx0ZXIgaGVpZ2h0PSIzMDAlIiBpZD0iZjh2eHV6Y3h2dHV1NSIgd2lkdGg9IjMwMCUiIHg9Ii0xIiB5PSItMSI+PGZlR2F1c3NpYW5CbHVyIHJlc3VsdD0iYmx1ck91dCIgc3RkRGV2aWF0aW9uPSIyLjAiLz48ZmVDb2xvck1hdHJpeCBpbj0iYmx1ck91dCIgcmVzdWx0PSJibHVyT3V0MiIgdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIC40IDAiLz48ZmVPZmZzZXQgZHg9IjQuMCIgZHk9IjQuMCIgaW49ImJsdXJPdXQyIiByZXN1bHQ9ImJsdXJPdXQzIi8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iYmx1ck91dDMiIG1vZGU9Im5vcm1hbCIvPjwvZmlsdGVyPjwvZGVmcz48Zz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxOCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSIyMDgiIHg9IjI4OC43NSIgeT0iMjYuNzA4Ij5IMyBTZXF1ZW5jZSBEaWFncmFtPC90ZXh0PjxyZWN0IGZpbGw9IiNGRkZGRkYiIGZpbHRlcj0idXJsKCNmOHZ4dXpjeHZ0dXU1KSIgaGVpZ2h0PSIxMDQuNTMxMyIgc3R5bGU9InN0cm9rZTogIzAwMDAwMDsgc3Ryb2tlLXdpZHRoOiAyLjA7IiB3aWR0aD0iNTA2LjUiIHg9IjE4Mi41IiB5PSI0NTAuNzEwOSIvPjxyZWN0IGZpbGw9IiNGRkZGRkYiIGZpbHRlcj0idXJsKCNmOHZ4dXpjeHZ0dXU1KSIgaGVpZ2h0PSIyMjEuMDYyNSIgc3R5bGU9InN0cm9rZTogIzAwMDAwMDsgc3Ryb2tlLXdpZHRoOiAyLjA7IiB3aWR0aD0iNjc2IiB4PSIxMyIgeT0iNTY5LjI0MjIiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsgc3Ryb2tlLWRhc2hhcnJheTogNS4wLDUuMDsiIHgxPSI2NCIgeDI9IjY0IiB5MT0iMTAwLjI1IiB5Mj0iODA3LjMwNDciLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsgc3Ryb2tlLWRhc2hhcnJheTogNS4wLDUuMDsiIHgxPSIyMzYuNSIgeDI9IjIzNi41IiB5MT0iMTAwLjI1IiB5Mj0iODA3LjMwNDciLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsgc3Ryb2tlLWRhc2hhcnJheTogNS4wLDUuMDsiIHgxPSIzNzkuNSIgeDI9IjM3OS41IiB5MT0iMTAwLjI1IiB5Mj0iODA3LjMwNDciLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsgc3Ryb2tlLWRhc2hhcnJheTogNS4wLDUuMDsiIHgxPSI0OTMiIHgyPSI0OTMiIHkxPSIxMDAuMjUiIHkyPSI4MDcuMzA0NyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyBzdHJva2UtZGFzaGFycmF5OiA1LjAsNS4wOyIgeDE9IjY1NiIgeDI9IjY1NiIgeTE9IjEwMC4yNSIgeTI9IjgwNy4zMDQ3Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iNzciIHg9IjIzIiB5PSI5Ni45NDgyIj5BcHBsaWNhdGlvbjwvdGV4dD48ZWxsaXBzZSBjeD0iNjQuNSIgY3k9IjY3Ljk1MzEiIGZpbGw9IiNGRUZFQ0UiIGZpbHRlcj0idXJsKCNmOHZ4dXpjeHZ0dXU1KSIgcng9IjEyIiByeT0iMTIiIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMi4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMi4wOyIgeDE9IjUyLjUiIHgyPSI3Ni41IiB5MT0iODEuOTUzMSIgeTI9IjgxLjk1MzEiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSI3NyIgeD0iMjMiIHk9IjgxOS4yOTk4Ij5BcHBsaWNhdGlvbjwvdGV4dD48ZWxsaXBzZSBjeD0iNjQuNSIgY3k9IjgzOC42MDE2IiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjh2eHV6Y3h2dHV1NSkiIHJ4PSIxMiIgcnk9IjEyIiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDIuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDIuMDsiIHgxPSI1Mi41IiB4Mj0iNzYuNSIgeTE9Ijg1Mi42MDE2IiB5Mj0iODUyLjYwMTYiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSI4MiIgeD0iMTkyLjUiIHk9Ijk2Ljk0ODIiPkNvbXBvbmVudDwvdGV4dD48ZWxsaXBzZSBjeD0iMjM2LjUiIGN5PSI2Ny45NTMxIiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjh2eHV6Y3h2dHV1NSkiIHJ4PSIxMiIgcnk9IjEyIiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDIuMDsiLz48cG9seWdvbiBmaWxsPSIjQTgwMDM2IiBwb2ludHM9IjIzMi41LDU1Ljk1MzEsMjM4LjUsNTAuOTUzMSwyMzYuNSw1NS45NTMxLDIzOC41LDYwLjk1MzEsMjMyLjUsNTUuOTUzMSIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iODIiIHg9IjE5Mi41IiB5PSI4MTkuMjk5OCI+Q29tcG9uZW50PC90ZXh0PjxlbGxpcHNlIGN4PSIyMzYuNSIgY3k9IjgzOC42MDE2IiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjh2eHV6Y3h2dHV1NSkiIHJ4PSIxMiIgcnk9IjEyIiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDIuMDsiLz48cG9seWdvbiBmaWxsPSIjQTgwMDM2IiBwb2ludHM9IjIzMi41LDgyNi42MDE2LDIzOC41LDgyMS42MDE2LDIzNi41LDgyNi42MDE2LDIzOC41LDgzMS42MDE2LDIzMi41LDgyNi42MDE2IiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSIxOCIgeD0iMzY3LjUiIHk9Ijk2Ljk0ODIiPkgzPC90ZXh0PjxlbGxpcHNlIGN4PSIzNzkuNSIgY3k9IjY3Ljk1MzEiIGZpbGw9IiNGRUZFQ0UiIGZpbHRlcj0idXJsKCNmOHZ4dXpjeHZ0dXU1KSIgcng9IjEyIiByeT0iMTIiIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMi4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMi4wOyIgeDE9IjM2Ny41IiB4Mj0iMzkxLjUiIHkxPSI4MS45NTMxIiB5Mj0iODEuOTUzMSIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9IjE4IiB4PSIzNjcuNSIgeT0iODE5LjI5OTgiPkgzPC90ZXh0PjxlbGxpcHNlIGN4PSIzNzkuNSIgY3k9IjgzOC42MDE2IiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjh2eHV6Y3h2dHV1NSkiIHJ4PSIxMiIgcnk9IjEyIiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDIuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDIuMDsiIHgxPSIzNjcuNSIgeDI9IjM5MS41IiB5MT0iODUyLjYwMTYiIHkyPSI4NTIuNjAxNiIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9IjQ3IiB4PSI0NjciIHk9Ijk2Ljk0ODIiPlJvdXRlcjwvdGV4dD48ZWxsaXBzZSBjeD0iNDkzLjUiIGN5PSI2Ny45NTMxIiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjh2eHV6Y3h2dHV1NSkiIHJ4PSIxMiIgcnk9IjEyIiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDIuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDIuMDsiIHgxPSI0ODEuNSIgeDI9IjUwNS41IiB5MT0iODEuOTUzMSIgeTI9IjgxLjk1MzEiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSI0NyIgeD0iNDY3IiB5PSI4MTkuMjk5OCI+Um91dGVyPC90ZXh0PjxlbGxpcHNlIGN4PSI0OTMuNSIgY3k9IjgzOC42MDE2IiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjh2eHV6Y3h2dHV1NSkiIHJ4PSIxMiIgcnk9IjEyIiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDIuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDIuMDsiIHgxPSI0ODEuNSIgeDI9IjUwNS41IiB5MT0iODUyLjYwMTYiIHkyPSI4NTIuNjAxNiIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9IjM5IiB4PSI2MzQiIHk9Ijk2Ljk0ODIiPlN0b3JlPC90ZXh0PjxwYXRoIGQ9Ik02MzguNSw0Ny45NTMxIEM2MzguNSwzNy45NTMxIDY1Ni41LDM3Ljk1MzEgNjU2LjUsMzcuOTUzMSBDNjU2LjUsMzcuOTUzMSA2NzQuNSwzNy45NTMxIDY3NC41LDQ3Ljk1MzEgTDY3NC41LDczLjk1MzEgQzY3NC41LDgzLjk1MzEgNjU2LjUsODMuOTUzMSA2NTYuNSw4My45NTMxIEM2NTYuNSw4My45NTMxIDYzOC41LDgzLjk1MzEgNjM4LjUsNzMuOTUzMSBMNjM4LjUsNDcuOTUzMSAiIGZpbGw9IiNGRUZFQ0UiIGZpbHRlcj0idXJsKCNmOHZ4dXpjeHZ0dXU1KSIgc3R5bGU9InN0cm9rZTogIzAwMDAwMDsgc3Ryb2tlLXdpZHRoOiAxLjU7Ii8+PHBhdGggZD0iTTYzOC41LDQ3Ljk1MzEgQzYzOC41LDU3Ljk1MzEgNjU2LjUsNTcuOTUzMSA2NTYuNSw1Ny45NTMxIEM2NTYuNSw1Ny45NTMxIDY3NC41LDU3Ljk1MzEgNjc0LjUsNDcuOTUzMSAiIGZpbGw9Im5vbmUiIHN0eWxlPSJzdHJva2U6ICMwMDAwMDA7IHN0cm9rZS13aWR0aDogMS41OyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9IjM5IiB4PSI2MzQiIHk9IjgxOS4yOTk4Ij5TdG9yZTwvdGV4dD48cGF0aCBkPSJNNjM4LjUsODMyLjYwMTYgQzYzOC41LDgyMi42MDE2IDY1Ni41LDgyMi42MDE2IDY1Ni41LDgyMi42MDE2IEM2NTYuNSw4MjIuNjAxNiA2NzQuNSw4MjIuNjAxNiA2NzQuNSw4MzIuNjAxNiBMNjc0LjUsODU4LjYwMTYgQzY3NC41LDg2OC42MDE2IDY1Ni41LDg2OC42MDE2IDY1Ni41LDg2OC42MDE2IEM2NTYuNSw4NjguNjAxNiA2MzguNSw4NjguNjAxNiA2MzguNSw4NTguNjAxNiBMNjM4LjUsODMyLjYwMTYgIiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjh2eHV6Y3h2dHV1NSkiIHN0eWxlPSJzdHJva2U6ICMwMDAwMDA7IHN0cm9rZS13aWR0aDogMS41OyIvPjxwYXRoIGQ9Ik02MzguNSw4MzIuNjAxNiBDNjM4LjUsODQyLjYwMTYgNjU2LjUsODQyLjYwMTYgNjU2LjUsODQyLjYwMTYgQzY1Ni41LDg0Mi42MDE2IDY3NC41LDg0Mi42MDE2IDY3NC41LDgzMi42MDE2ICIgZmlsbD0ibm9uZSIgc3R5bGU9InN0cm9rZTogIzAwMDAwMDsgc3Ryb2tlLXdpZHRoOiAxLjU7Ii8+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSIzNjcuNSwxMjcuMzgyOCwzNzcuNSwxMzEuMzgyOCwzNjcuNSwxMzUuMzgyOCwzNzEuNSwxMzEuMzgyOCIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IHN0cm9rZS1kYXNoYXJyYXk6IDIuMCwyLjA7IiB4MT0iNjQuNSIgeDI9IjM3My41IiB5MT0iMTMxLjM4MjgiIHkyPSIxMzEuMzgyOCIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBmb250LXdlaWdodD0iYm9sZCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSI1OCIgeD0iNzEuNSIgeT0iMTI2LjMxNjkiPmgzLmluaXQoKTwvdGV4dD48cG9seWdvbiBmaWxsPSIjQTgwMDM2IiBwb2ludHM9IjY0NC41LDE1Ni41MTU2LDY1NC41LDE2MC41MTU2LDY0NC41LDE2NC41MTU2LDY0OC41LDE2MC41MTU2IiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiIHgxPSIzNzkuNSIgeDI9IjY1MC41IiB5MT0iMTYwLjUxNTYiIHkyPSIxNjAuNTE1NiIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBmb250LXN0eWxlPSJpdGFsaWMiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iNTYiIHg9IjM4Ni41IiB5PSIxNTUuNDQ5NyI+aW5pdGlhbGl6ZTwvdGV4dD48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiIHgxPSI2NTYuNSIgeDI9IjY5OC41IiB5MT0iMTg5LjY0ODQiIHkyPSIxODkuNjQ4NCIvPjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIgeDE9IjY5OC41IiB4Mj0iNjk4LjUiIHkxPSIxODkuNjQ4NCIgeTI9IjIwMi42NDg0Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iNjU3LjUiIHgyPSI2OTguNSIgeTE9IjIwMi42NDg0IiB5Mj0iMjAyLjY0ODQiLz48cG9seWdvbiBmaWxsPSIjQTgwMDM2IiBwb2ludHM9IjY2Ny41LDE5OC42NDg0LDY1Ny41LDIwMi42NDg0LDY2Ny41LDIwNi42NDg0LDY2My41LDIwMi42NDg0IiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgZm9udC1zdHlsZT0iaXRhbGljIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9IjExMiIgeD0iNjYzLjUiIHk9IjE4NC41ODI1Ij5leGVjdXRlIG1vZHVsZXM8L3RleHQ+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSI2NDQuNSwyMjcuNzgxMyw2NTQuNSwyMzEuNzgxMyw2NDQuNSwyMzUuNzgxMyw2NDguNSwyMzEuNzgxMyIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iMzc5LjUiIHgyPSI2NTAuNSIgeTE9IjIzMS43ODEzIiB5Mj0iMjMxLjc4MTMiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSI5MSIgeD0iMzg2LjUiIHk9IjIyNi43MTUzIj5kaXNwYXRjaCgkaW5pdCk8L3RleHQ+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSI3NS41LDI1Ni45MTQxLDY1LjUsMjYwLjkxNDEsNzUuNSwyNjQuOTE0MSw3MS41LDI2MC45MTQxIiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiIHgxPSI2OS41IiB4Mj0iMzc4LjUiIHkxPSIyNjAuOTE0MSIgeTI9IjI2MC45MTQxIi8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iNjQiIHg9IjgxLjUiIHk9IjI1NS44NDgxIj5wcmVTdGFydCgpPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iNDgxLjUsMjg2LjA0NjksNDkxLjUsMjkwLjA0NjksNDgxLjUsMjk0LjA0NjksNDg1LjUsMjkwLjA0NjkiIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIgeDE9IjM3OS41IiB4Mj0iNDg3LjUiIHkxPSIyOTAuMDQ2OSIgeTI9IjI5MC4wNDY5Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGZvbnQtc3R5bGU9Iml0YWxpYyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSI1NiIgeD0iMzg2LjUiIHk9IjI4NC45ODEiPmluaXRpYWxpemU8L3RleHQ+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSI0ODEuNSwzMTUuMTc5Nyw0OTEuNSwzMTkuMTc5Nyw0ODEuNSwzMjMuMTc5Nyw0ODUuNSwzMTkuMTc5NyIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iMzc5LjUiIHgyPSI0ODcuNSIgeTE9IjMxOS4xNzk3IiB5Mj0iMzE5LjE3OTciLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSI0MCIgeD0iMzg2LjUiIHk9IjMxNC4xMTM4Ij5zdGFydCgpPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iMjQ3LjUsMzQ0LjMxMjUsMjM3LjUsMzQ4LjMxMjUsMjQ3LjUsMzUyLjMxMjUsMjQzLjUsMzQ4LjMxMjUiIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIgeDE9IjI0MS41IiB4Mj0iNDkyLjUiIHkxPSIzNDguMzEyNSIgeTI9IjM0OC4zMTI1Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iNTQiIHg9IjI1My41IiB5PSIzNDMuMjQ2NiI+cmVuZGVyKCk8L3RleHQ+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSI2NDQuNSwzNzMuNDQ1Myw2NTQuNSwzNzcuNDQ1Myw2NDQuNSwzODEuNDQ1Myw2NDguNSwzNzcuNDQ1MyIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iNDkzLjUiIHgyPSI2NTAuNSIgeTE9IjM3Ny40NDUzIiB5Mj0iMzc3LjQ0NTMiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSIxMTgiIHg9IjUwMC41IiB5PSIzNzIuMzc5NCI+ZGlzcGF0Y2goJHJlZHJhdyk8L3RleHQ+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSI2NDQuNSw0MDIuNTc4MSw2NTQuNSw0MDYuNTc4MSw2NDQuNSw0MTAuNTc4MSw2NDguNSw0MDYuNTc4MSIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iNDkzLjUiIHgyPSI2NTAuNSIgeTE9IjQwNi41NzgxIiB5Mj0iNDA2LjU3ODEiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSIxMzkiIHg9IjUwMC41IiB5PSI0MDEuNTEyMiI+ZGlzcGF0Y2goJG5hdmlnYXRpb24pPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iNzUuNSw0MzEuNzEwOSw2NS41LDQzNS43MTA5LDc1LjUsNDM5LjcxMDksNzEuNSw0MzUuNzEwOSIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iNjkuNSIgeDI9IjM3OC41IiB5MT0iNDM1LjcxMDkiIHkyPSI0MzUuNzEwOSIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9IjcwIiB4PSI4MS41IiB5PSI0MzAuNjQ1Ij5wb3N0U3RhcnQoKTwvdGV4dD48cGF0aCBkPSJNMTgyLjUsNDUwLjcxMDkgTDI4MC41LDQ1MC43MTA5IEwyODAuNSw0NTcuNzEwOSBMMjcwLjUsNDY3LjcxMDkgTDE4Mi41LDQ2Ny43MTA5IEwxODIuNSw0NTAuNzEwOSAiIGZpbGw9IiNFRUVFRUUiIHN0eWxlPSJzdHJva2U6ICMwMDAwMDA7IHN0cm9rZS13aWR0aDogMS4wOyIvPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iMTA0LjUzMTMiIHN0eWxlPSJzdHJva2U6ICMwMDAwMDA7IHN0cm9rZS13aWR0aDogMi4wOyIgd2lkdGg9IjUwNi41IiB4PSIxODIuNSIgeT0iNDUwLjcxMDkiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgZm9udC13ZWlnaHQ9ImJvbGQiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iNTMiIHg9IjE5Ny41IiB5PSI0NjMuNzc3OCI+cmVkcmF3PC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iMzY3LjUsNDg0Ljk3NjYsMzc3LjUsNDg4Ljk3NjYsMzY3LjUsNDkyLjk3NjYsMzcxLjUsNDg4Ljk3NjYiIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIgeDE9IjIzNi41IiB4Mj0iMzczLjUiIHkxPSI0ODguOTc2NiIgeTI9IjQ4OC45NzY2Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGZvbnQtd2VpZ2h0PSJib2xkIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9Ijg2IiB4PSIyNDMuNSIgeT0iNDgzLjkxMDYiPmgzLnJlZHJhdygpPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iMjQ3LjUsNTE0LjEwOTQsMjM3LjUsNTE4LjEwOTQsMjQ3LjUsNTIyLjEwOTQsMjQzLjUsNTE4LjEwOTQiIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIgeDE9IjI0MS41IiB4Mj0iMzc4LjUiIHkxPSI1MTguMTA5NCIgeTI9IjUxOC4xMDk0Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iNTYiIHg9IjI1My41IiB5PSI1MTMuMDQzNSI+cmVkcmF3KCk8L3RleHQ+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSI2NDQuNSw1NDMuMjQyMiw2NTQuNSw1NDcuMjQyMiw2NDQuNSw1NTEuMjQyMiw2NDguNSw1NDcuMjQyMiIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iMzc5LjUiIHgyPSI2NTAuNSIgeTE9IjU0Ny4yNDIyIiB5Mj0iNTQ3LjI0MjIiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSIxMTgiIHg9IjM4Ni41IiB5PSI1NDIuMTc2MyI+ZGlzcGF0Y2goJHJlZHJhdyk8L3RleHQ+PHBhdGggZD0iTTEzLDU2OS4yNDIyIEwxMzcsNTY5LjI0MjIgTDEzNyw1NzYuMjQyMiBMMTI3LDU4Ni4yNDIyIEwxMyw1ODYuMjQyMiBMMTMsNTY5LjI0MjIgIiBmaWxsPSIjRUVFRUVFIiBzdHlsZT0ic3Ryb2tlOiAjMDAwMDAwOyBzdHJva2Utd2lkdGg6IDEuMDsiLz48cmVjdCBmaWxsPSJub25lIiBoZWlnaHQ9IjIyMS4wNjI1IiBzdHlsZT0ic3Ryb2tlOiAjMDAwMDAwOyBzdHJva2Utd2lkdGg6IDIuMDsiIHdpZHRoPSI2NzYiIHg9IjEzIiB5PSI1NjkuMjQyMiIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBmb250LXdlaWdodD0iYm9sZCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSI3OSIgeD0iMjgiIHk9IjU4Mi4zMDkxIj5uYXZpZ2F0aW9uPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iMzY3LjUsNjAzLjUwNzgsMzc3LjUsNjA3LjUwNzgsMzY3LjUsNjExLjUwNzgsMzcxLjUsNjA3LjUwNzgiIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIgeDE9IjIzNi41IiB4Mj0iMzczLjUiIHkxPSI2MDcuNTA3OCIgeTI9IjYwNy41MDc4Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGZvbnQtd2VpZ2h0PSJib2xkIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9IjExOSIgeD0iMjQzLjUiIHk9IjYwMi40NDE5Ij5oMy5uYXZpZ2F0ZVRvKCk8L3RleHQ+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSI0ODEuNSw2MzIuNjQwNiw0OTEuNSw2MzYuNjQwNiw0ODEuNSw2NDAuNjQwNiw0ODUuNSw2MzYuNjQwNiIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iMzc5LjUiIHgyPSI0ODcuNSIgeTE9IjYzNi42NDA2IiB5Mj0iNjM2LjY0MDYiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSI5MCIgeD0iMzg2LjUiIHk9IjYzMS41NzQ3Ij5wcm9jZXNzUGF0aCgpPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iNzUuNSw2NjEuNzczNCw2NS41LDY2NS43NzM0LDc1LjUsNjY5Ljc3MzQsNzEuNSw2NjUuNzczNCIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iNjkuNSIgeDI9IjQ5Mi41IiB5MT0iNjY1Ljc3MzQiIHkyPSI2NjUuNzczNCIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBmb250LXN0eWxlPSJpdGFsaWMiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iMTQ4IiB4PSI4MS41IiB5PSI2NjAuNzA3NSI+cmVtb3ZlIGFsbCBET00gbm9kZXM8L3RleHQ+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSIyMjQuNSw2OTAuOTA2MywyMzQuNSw2OTQuOTA2MywyMjQuNSw2OTguOTA2MywyMjguNSw2OTQuOTA2MyIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iNjQuNSIgeDI9IjIzMC41IiB5MT0iNjk0LjkwNjMiIHkyPSI2OTQuOTA2MyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBmb250LXN0eWxlPSJpdGFsaWMiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iMTQ4IiB4PSI3MS41IiB5PSI2ODkuODQwMyI+cmVtb3ZlIGFsbCBET00gbm9kZXM8L3RleHQ+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSIyNDcuNSw3MjAuMDM5MSwyMzcuNSw3MjQuMDM5MSwyNDcuNSw3MjguMDM5MSwyNDMuNSw3MjQuMDM5MSIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iMjQxLjUiIHgyPSI0OTIuNSIgeTE9IjcyNC4wMzkxIiB5Mj0iNzI0LjAzOTEiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSI1NCIgeD0iMjUzLjUiIHk9IjcxOC45NzMxIj5yZW5kZXIoKTwvdGV4dD48cG9seWdvbiBmaWxsPSIjQTgwMDM2IiBwb2ludHM9IjY0NC41LDc0OS4xNzE5LDY1NC41LDc1My4xNzE5LDY0NC41LDc1Ny4xNzE5LDY0OC41LDc1My4xNzE5IiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiIHgxPSI0OTMuNSIgeDI9IjY1MC41IiB5MT0iNzUzLjE3MTkiIHkyPSI3NTMuMTcxOSIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9IjExOCIgeD0iNTAwLjUiIHk9Ijc0OC4xMDYiPmRpc3BhdGNoKCRyZWRyYXcpPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iNjQ0LjUsNzc4LjMwNDcsNjU0LjUsNzgyLjMwNDcsNjQ0LjUsNzg2LjMwNDcsNjQ4LjUsNzgyLjMwNDciIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIgeDE9IjQ5My41IiB4Mj0iNjUwLjUiIHkxPSI3ODIuMzA0NyIgeTI9Ijc4Mi4zMDQ3Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iMTM5IiB4PSI1MDAuNSIgeT0iNzc3LjIzODgiPmRpc3BhdGNoKCRuYXZpZ2F0aW9uKTwvdGV4dD48IS0tTUQ1PVszNTJkNDMxYjJlY2QyZjllZTQ4YTdkNmVmYzg5MDQzNl0KQHN0YXJ0dW1sCnRpdGxlIEgzIFNlcXVlbmNlIERpYWdyYW0KCmVudGl0eSBBcHBsaWNhdGlvbgpjb250cm9sIENvbXBvbmVudAplbnRpdHkgSDMKZW50aXR5IFJvdXRlcgpkYXRhYmFzZSBTdG9yZQoKQXBwbGljYXRpb24gLSAtPiBIMyA6IDxiPmgzLmluaXQoKTwvYj4KSDMgLT4gU3RvcmUgOiAvL2luaXRpYWxpemUvLwpTdG9yZSAtPiBTdG9yZSA6IC8vZXhlY3V0ZSBtb2R1bGVzLy8KSDMgLT4gU3RvcmUgOiBkaXNwYXRjaCgkaW5pdCkKSDMgLT4gQXBwbGljYXRpb24gOiBwcmVTdGFydCgpCkgzIC0+IFJvdXRlciA6IC8vaW5pdGlhbGl6ZS8vCkgzIC0+IFJvdXRlciA6IHN0YXJ0KCkKUm91dGVyIC0+IENvbXBvbmVudCA6IHJlbmRlcigpClJvdXRlciAtPiBTdG9yZTogZGlzcGF0Y2goJHJlZHJhdykKUm91dGVyIC0+IFN0b3JlOiBkaXNwYXRjaCgkbmF2aWdhdGlvbikKSDMgLT4gQXBwbGljYXRpb24gOiBwb3N0U3RhcnQoKQoKZ3JvdXAgcmVkcmF3CiAgICBDb21wb25lbnQgLT4gSDMgOiA8Yj5oMy5yZWRyYXcoKTwvYj4KICAgIEgzIC0+IENvbXBvbmVudCA6IHJlZHJhdygpCiAgICBIMyAtPiBTdG9yZTogZGlzcGF0Y2goJHJlZHJhdykKZW5kCgpncm91cCBuYXZpZ2F0aW9uCiAgICBDb21wb25lbnQgLT4gSDMgOiA8Yj5oMy5uYXZpZ2F0ZVRvKCk8L2I+CiAgICBIMyAtPiBSb3V0ZXIgOiBwcm9jZXNzUGF0aCgpCiAgICBSb3V0ZXIgLT4gQXBwbGljYXRpb24gOiAvL3JlbW92ZSBhbGwgRE9NIG5vZGVzLy8KICAgIEFwcGxpY2F0aW9uIC0+IENvbXBvbmVudCA6IC8vcmVtb3ZlIGFsbCBET00gbm9kZXMvLwogICAgUm91dGVyIC0+IENvbXBvbmVudCA6IHJlbmRlcigpCiAgICBSb3V0ZXIgLT4gU3RvcmU6IGRpc3BhdGNoKCRyZWRyYXcpCiAgICBSb3V0ZXIgLT4gU3RvcmU6IGRpc3BhdGNoKCRuYXZpZ2F0aW9uKQplbmQKQGVuZHVtbAoKUGxhbnRVTUwgdmVyc2lvbiAxLjIwMjAuMDhiZXRhMShVbmtub3duIGNvbXBpbGUgdGltZSkKKEdQTCBzb3VyY2UgZGlzdHJpYnV0aW9uKQpKYXZhIFJ1bnRpbWU6IEphdmEoVE0pIFNFIFJ1bnRpbWUgRW52aXJvbm1lbnQKSlZNOiBKYXZhIEhvdFNwb3QoVE0pIDY0LUJpdCBTZXJ2ZXIgVk0KSmF2YSBWZXJzaW9uOiAxNC4wLjErNwpPcGVyYXRpbmcgU3lzdGVtOiBMaW51eApEZWZhdWx0IEVuY29kaW5nOiBVVEYtOApMYW5ndWFnZTogZW4KQ291bnRyeTogVVMKLS0+PC9nPjwvc3ZnPg==" alt="Sequence Diagram" /></p> +<p><img src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBjb250ZW50U2NyaXB0VHlwZT0iYXBwbGljYXRpb24vZWNtYXNjcmlwdCIgY29udGVudFN0eWxlVHlwZT0idGV4dC9jc3MiIGhlaWdodD0iODgwcHgiIHByZXNlcnZlQXNwZWN0UmF0aW89Im5vbmUiIHN0eWxlPSJ3aWR0aDo3OTBweDtoZWlnaHQ6ODgwcHg7IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA3OTAgODgwIiB3aWR0aD0iNzkwcHgiIHpvb21BbmRQYW49Im1hZ25pZnkiPjxkZWZzPjxmaWx0ZXIgaGVpZ2h0PSIzMDAlIiBpZD0iZjFkYmh5Y2Z5NTBvemgiIHdpZHRoPSIzMDAlIiB4PSItMSIgeT0iLTEiPjxmZUdhdXNzaWFuQmx1ciByZXN1bHQ9ImJsdXJPdXQiIHN0ZERldmlhdGlvbj0iMi4wIi8+PGZlQ29sb3JNYXRyaXggaW49ImJsdXJPdXQiIHJlc3VsdD0iYmx1ck91dDIiIHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAuNCAwIi8+PGZlT2Zmc2V0IGR4PSI0LjAiIGR5PSI0LjAiIGluPSJibHVyT3V0MiIgcmVzdWx0PSJibHVyT3V0MyIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImJsdXJPdXQzIiBtb2RlPSJub3JtYWwiLz48L2ZpbHRlcj48L2RlZnM+PGc+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTgiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iMjA1IiB4PSIyOTEuNzUiIHk9IjI2LjcwOCI+SDMgU2VxdWVuY2UgRGlhZ3JhbTwvdGV4dD48cmVjdCBmaWxsPSIjRkZGRkZGIiBmaWx0ZXI9InVybCgjZjFkYmh5Y2Z5NTBvemgpIiBoZWlnaHQ9IjEwNC41MzEzIiBzdHlsZT0ic3Ryb2tlOiAjMDAwMDAwOyBzdHJva2Utd2lkdGg6IDIuMDsiIHdpZHRoPSI1MDkiIHg9IjE4My41IiB5PSI0NTAuNzEwOSIvPjxyZWN0IGZpbGw9IiNGRkZGRkYiIGZpbHRlcj0idXJsKCNmMWRiaHljZnk1MG96aCkiIGhlaWdodD0iMjIxLjA2MjUiIHN0eWxlPSJzdHJva2U6ICMwMDAwMDA7IHN0cm9rZS13aWR0aDogMi4wOyIgd2lkdGg9IjY3OS41IiB4PSIxMyIgeT0iNTY5LjI0MjIiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsgc3Ryb2tlLWRhc2hhcnJheTogNS4wLDUuMDsiIHgxPSI2NyIgeDI9IjY3IiB5MT0iMTAwLjI1IiB5Mj0iODA3LjMwNDciLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsgc3Ryb2tlLWRhc2hhcnJheTogNS4wLDUuMDsiIHgxPSIyMzkuNSIgeDI9IjIzOS41IiB5MT0iMTAwLjI1IiB5Mj0iODA3LjMwNDciLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsgc3Ryb2tlLWRhc2hhcnJheTogNS4wLDUuMDsiIHgxPSIzODMuNSIgeDI9IjM4My41IiB5MT0iMTAwLjI1IiB5Mj0iODA3LjMwNDciLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsgc3Ryb2tlLWRhc2hhcnJheTogNS4wLDUuMDsiIHgxPSI0OTYiIHgyPSI0OTYiIHkxPSIxMDAuMjUiIHkyPSI4MDcuMzA0NyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyBzdHJva2UtZGFzaGFycmF5OiA1LjAsNS4wOyIgeDE9IjY1OS41IiB4Mj0iNjU5LjUiIHkxPSIxMDAuMjUiIHkyPSI4MDcuMzA0NyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9IjgzIiB4PSIyMyIgeT0iOTYuOTQ4MiI+QXBwbGljYXRpb248L3RleHQ+PGVsbGlwc2UgY3g9IjY3LjUiIGN5PSI2Ny45NTMxIiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjFkYmh5Y2Z5NTBvemgpIiByeD0iMTIiIHJ5PSIxMiIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAyLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAyLjA7IiB4MT0iNTUuNSIgeDI9Ijc5LjUiIHkxPSI4MS45NTMxIiB5Mj0iODEuOTUzMSIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9IjgzIiB4PSIyMyIgeT0iODE5LjI5OTgiPkFwcGxpY2F0aW9uPC90ZXh0PjxlbGxpcHNlIGN4PSI2Ny41IiBjeT0iODM4LjYwMTYiIGZpbGw9IiNGRUZFQ0UiIGZpbHRlcj0idXJsKCNmMWRiaHljZnk1MG96aCkiIHJ4PSIxMiIgcnk9IjEyIiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDIuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDIuMDsiIHgxPSI1NS41IiB4Mj0iNzkuNSIgeTE9Ijg1Mi42MDE2IiB5Mj0iODUyLjYwMTYiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSI4NiIgeD0iMTkzLjUiIHk9Ijk2Ljk0ODIiPkNvbXBvbmVudDwvdGV4dD48ZWxsaXBzZSBjeD0iMjM5LjUiIGN5PSI2Ny45NTMxIiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjFkYmh5Y2Z5NTBvemgpIiByeD0iMTIiIHJ5PSIxMiIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAyLjA7Ii8+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSIyMzUuNSw1NS45NTMxLDI0MS41LDUwLjk1MzEsMjM5LjUsNTUuOTUzMSwyNDEuNSw2MC45NTMxLDIzNS41LDU1Ljk1MzEiIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9Ijg2IiB4PSIxOTMuNSIgeT0iODE5LjI5OTgiPkNvbXBvbmVudDwvdGV4dD48ZWxsaXBzZSBjeD0iMjM5LjUiIGN5PSI4MzguNjAxNiIgZmlsbD0iI0ZFRkVDRSIgZmlsdGVyPSJ1cmwoI2YxZGJoeWNmeTUwb3poKSIgcng9IjEyIiByeT0iMTIiIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMi4wOyIvPjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iMjM1LjUsODI2LjYwMTYsMjQxLjUsODIxLjYwMTYsMjM5LjUsODI2LjYwMTYsMjQxLjUsODMxLjYwMTYsMjM1LjUsODI2LjYwMTYiIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9IjE5IiB4PSIzNzEiIHk9Ijk2Ljk0ODIiPkgzPC90ZXh0PjxlbGxpcHNlIGN4PSIzODMuNSIgY3k9IjY3Ljk1MzEiIGZpbGw9IiNGRUZFQ0UiIGZpbHRlcj0idXJsKCNmMWRiaHljZnk1MG96aCkiIHJ4PSIxMiIgcnk9IjEyIiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDIuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDIuMDsiIHgxPSIzNzEuNSIgeDI9IjM5NS41IiB5MT0iODEuOTUzMSIgeTI9IjgxLjk1MzEiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSIxOSIgeD0iMzcxIiB5PSI4MTkuMjk5OCI+SDM8L3RleHQ+PGVsbGlwc2UgY3g9IjM4My41IiBjeT0iODM4LjYwMTYiIGZpbGw9IiNGRUZFQ0UiIGZpbHRlcj0idXJsKCNmMWRiaHljZnk1MG96aCkiIHJ4PSIxMiIgcnk9IjEyIiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDIuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDIuMDsiIHgxPSIzNzEuNSIgeDI9IjM5NS41IiB5MT0iODUyLjYwMTYiIHkyPSI4NTIuNjAxNiIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9IjQ5IiB4PSI0NjkiIHk9Ijk2Ljk0ODIiPlJvdXRlcjwvdGV4dD48ZWxsaXBzZSBjeD0iNDk2LjUiIGN5PSI2Ny45NTMxIiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjFkYmh5Y2Z5NTBvemgpIiByeD0iMTIiIHJ5PSIxMiIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAyLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAyLjA7IiB4MT0iNDg0LjUiIHgyPSI1MDguNSIgeTE9IjgxLjk1MzEiIHkyPSI4MS45NTMxIi8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iNDkiIHg9IjQ2OSIgeT0iODE5LjI5OTgiPlJvdXRlcjwvdGV4dD48ZWxsaXBzZSBjeD0iNDk2LjUiIGN5PSI4MzguNjAxNiIgZmlsbD0iI0ZFRkVDRSIgZmlsdGVyPSJ1cmwoI2YxZGJoeWNmeTUwb3poKSIgcng9IjEyIiByeT0iMTIiIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMi4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMi4wOyIgeDE9IjQ4NC41IiB4Mj0iNTA4LjUiIHkxPSI4NTIuNjAxNiIgeTI9Ijg1Mi42MDE2Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iNDAiIHg9IjYzNi41IiB5PSI5Ni45NDgyIj5TdG9yZTwvdGV4dD48cGF0aCBkPSJNNjQxLjUsNDcuOTUzMSBDNjQxLjUsMzcuOTUzMSA2NTkuNSwzNy45NTMxIDY1OS41LDM3Ljk1MzEgQzY1OS41LDM3Ljk1MzEgNjc3LjUsMzcuOTUzMSA2NzcuNSw0Ny45NTMxIEw2NzcuNSw3My45NTMxIEM2NzcuNSw4My45NTMxIDY1OS41LDgzLjk1MzEgNjU5LjUsODMuOTUzMSBDNjU5LjUsODMuOTUzMSA2NDEuNSw4My45NTMxIDY0MS41LDczLjk1MzEgTDY0MS41LDQ3Ljk1MzEgIiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjFkYmh5Y2Z5NTBvemgpIiBzdHlsZT0ic3Ryb2tlOiAjMDAwMDAwOyBzdHJva2Utd2lkdGg6IDEuNTsiLz48cGF0aCBkPSJNNjQxLjUsNDcuOTUzMSBDNjQxLjUsNTcuOTUzMSA2NTkuNSw1Ny45NTMxIDY1OS41LDU3Ljk1MzEgQzY1OS41LDU3Ljk1MzEgNjc3LjUsNTcuOTUzMSA2NzcuNSw0Ny45NTMxICIgZmlsbD0ibm9uZSIgc3R5bGU9InN0cm9rZTogIzAwMDAwMDsgc3Ryb2tlLXdpZHRoOiAxLjU7Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iNDAiIHg9IjYzNi41IiB5PSI4MTkuMjk5OCI+U3RvcmU8L3RleHQ+PHBhdGggZD0iTTY0MS41LDgzMi42MDE2IEM2NDEuNSw4MjIuNjAxNiA2NTkuNSw4MjIuNjAxNiA2NTkuNSw4MjIuNjAxNiBDNjU5LjUsODIyLjYwMTYgNjc3LjUsODIyLjYwMTYgNjc3LjUsODMyLjYwMTYgTDY3Ny41LDg1OC42MDE2IEM2NzcuNSw4NjguNjAxNiA2NTkuNSw4NjguNjAxNiA2NTkuNSw4NjguNjAxNiBDNjU5LjUsODY4LjYwMTYgNjQxLjUsODY4LjYwMTYgNjQxLjUsODU4LjYwMTYgTDY0MS41LDgzMi42MDE2ICIgZmlsbD0iI0ZFRkVDRSIgZmlsdGVyPSJ1cmwoI2YxZGJoeWNmeTUwb3poKSIgc3R5bGU9InN0cm9rZTogIzAwMDAwMDsgc3Ryb2tlLXdpZHRoOiAxLjU7Ii8+PHBhdGggZD0iTTY0MS41LDgzMi42MDE2IEM2NDEuNSw4NDIuNjAxNiA2NTkuNSw4NDIuNjAxNiA2NTkuNSw4NDIuNjAxNiBDNjU5LjUsODQyLjYwMTYgNjc3LjUsODQyLjYwMTYgNjc3LjUsODMyLjYwMTYgIiBmaWxsPSJub25lIiBzdHlsZT0ic3Ryb2tlOiAjMDAwMDAwOyBzdHJva2Utd2lkdGg6IDEuNTsiLz48cG9seWdvbiBmaWxsPSIjQTgwMDM2IiBwb2ludHM9IjM3MS41LDEyNy4zODI4LDM4MS41LDEzMS4zODI4LDM3MS41LDEzNS4zODI4LDM3NS41LDEzMS4zODI4IiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsgc3Ryb2tlLWRhc2hhcnJheTogMi4wLDIuMDsiIHgxPSI2Ny41IiB4Mj0iMzc3LjUiIHkxPSIxMzEuMzgyOCIgeTI9IjEzMS4zODI4Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGZvbnQtd2VpZ2h0PSJib2xkIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9IjU5IiB4PSI3NC41IiB5PSIxMjYuMzE2OSI+aDMuaW5pdCgpPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iNjQ3LjUsMTU2LjUxNTYsNjU3LjUsMTYwLjUxNTYsNjQ3LjUsMTY0LjUxNTYsNjUxLjUsMTYwLjUxNTYiIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIgeDE9IjM4My41IiB4Mj0iNjUzLjUiIHkxPSIxNjAuNTE1NiIgeTI9IjE2MC41MTU2Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGZvbnQtc3R5bGU9Iml0YWxpYyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSI1NiIgeD0iMzkwLjUiIHk9IjE1NS40NDk3Ij5pbml0aWFsaXplPC90ZXh0PjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIgeDE9IjY1OS41IiB4Mj0iNzAxLjUiIHkxPSIxODkuNjQ4NCIgeTI9IjE4OS42NDg0Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iNzAxLjUiIHgyPSI3MDEuNSIgeTE9IjE4OS42NDg0IiB5Mj0iMjAyLjY0ODQiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiIHgxPSI2NjAuNSIgeDI9IjcwMS41IiB5MT0iMjAyLjY0ODQiIHkyPSIyMDIuNjQ4NCIvPjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iNjcwLjUsMTk4LjY0ODQsNjYwLjUsMjAyLjY0ODQsNjcwLjUsMjA2LjY0ODQsNjY2LjUsMjAyLjY0ODQiIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBmb250LXN0eWxlPSJpdGFsaWMiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iMTEyIiB4PSI2NjYuNSIgeT0iMTg0LjU4MjUiPmV4ZWN1dGUgbW9kdWxlczwvdGV4dD48cG9seWdvbiBmaWxsPSIjQTgwMDM2IiBwb2ludHM9IjY0Ny41LDIyNy43ODEzLDY1Ny41LDIzMS43ODEzLDY0Ny41LDIzNS43ODEzLDY1MS41LDIzMS43ODEzIiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiIHgxPSIzODMuNSIgeDI9IjY1My41IiB5MT0iMjMxLjc4MTMiIHkyPSIyMzEuNzgxMyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9IjkxIiB4PSIzOTAuNSIgeT0iMjI2LjcxNTMiPmRpc3BhdGNoKCRpbml0KTwvdGV4dD48cG9seWdvbiBmaWxsPSIjQTgwMDM2IiBwb2ludHM9Ijc4LjUsMjU2LjkxNDEsNjguNSwyNjAuOTE0MSw3OC41LDI2NC45MTQxLDc0LjUsMjYwLjkxNDEiIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIgeDE9IjcyLjUiIHgyPSIzODIuNSIgeTE9IjI2MC45MTQxIiB5Mj0iMjYwLjkxNDEiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSI2MiIgeD0iODQuNSIgeT0iMjU1Ljg0ODEiPnByZVN0YXJ0KCk8L3RleHQ+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSI0ODQuNSwyODYuMDQ2OSw0OTQuNSwyOTAuMDQ2OSw0ODQuNSwyOTQuMDQ2OSw0ODguNSwyOTAuMDQ2OSIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iMzgzLjUiIHgyPSI0OTAuNSIgeTE9IjI5MC4wNDY5IiB5Mj0iMjkwLjA0NjkiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgZm9udC1zdHlsZT0iaXRhbGljIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9IjU2IiB4PSIzOTAuNSIgeT0iMjg0Ljk4MSI+aW5pdGlhbGl6ZTwvdGV4dD48cG9seWdvbiBmaWxsPSIjQTgwMDM2IiBwb2ludHM9IjQ4NC41LDMxNS4xNzk3LDQ5NC41LDMxOS4xNzk3LDQ4NC41LDMyMy4xNzk3LDQ4OC41LDMxOS4xNzk3IiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiIHgxPSIzODMuNSIgeDI9IjQ5MC41IiB5MT0iMzE5LjE3OTciIHkyPSIzMTkuMTc5NyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9IjQwIiB4PSIzOTAuNSIgeT0iMzE0LjExMzgiPnN0YXJ0KCk8L3RleHQ+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSI2NDcuNSwzNDQuMzEyNSw2NTcuNSwzNDguMzEyNSw2NDcuNSwzNTIuMzEyNSw2NTEuNSwzNDguMzEyNSIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iNDk2LjUiIHgyPSI2NTMuNSIgeTE9IjM0OC4zMTI1IiB5Mj0iMzQ4LjMxMjUiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSIxMzkiIHg9IjUwMy41IiB5PSIzNDMuMjQ2NiI+ZGlzcGF0Y2goJG5hdmlnYXRpb24pPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iMjUwLjUsMzczLjQ0NTMsMjQwLjUsMzc3LjQ0NTMsMjUwLjUsMzgxLjQ0NTMsMjQ2LjUsMzc3LjQ0NTMiIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIgeDE9IjI0NC41IiB4Mj0iNDk1LjUiIHkxPSIzNzcuNDQ1MyIgeTI9IjM3Ny40NDUzIi8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iNTIiIHg9IjI1Ni41IiB5PSIzNzIuMzc5NCI+cmVuZGVyKCk8L3RleHQ+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSI2NDcuNSw0MDIuNTc4MSw2NTcuNSw0MDYuNTc4MSw2NDcuNSw0MTAuNTc4MSw2NTEuNSw0MDYuNTc4MSIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iNDk2LjUiIHgyPSI2NTMuNSIgeTE9IjQwNi41NzgxIiB5Mj0iNDA2LjU3ODEiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSIxMTciIHg9IjUwMy41IiB5PSI0MDEuNTEyMiI+ZGlzcGF0Y2goJHJlZHJhdyk8L3RleHQ+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSI3OC41LDQzMS43MTA5LDY4LjUsNDM1LjcxMDksNzguNSw0MzkuNzEwOSw3NC41LDQzNS43MTA5IiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiIHgxPSI3Mi41IiB4Mj0iMzgyLjUiIHkxPSI0MzUuNzEwOSIgeTI9IjQzNS43MTA5Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iNjkiIHg9Ijg0LjUiIHk9IjQzMC42NDUiPnBvc3RTdGFydCgpPC90ZXh0PjxwYXRoIGQ9Ik0xODMuNSw0NTAuNzEwOSBMMjgxLjUsNDUwLjcxMDkgTDI4MS41LDQ1Ny43MTA5IEwyNzEuNSw0NjcuNzEwOSBMMTgzLjUsNDY3LjcxMDkgTDE4My41LDQ1MC43MTA5ICIgZmlsbD0iI0VFRUVFRSIgc3R5bGU9InN0cm9rZTogIzAwMDAwMDsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PHJlY3QgZmlsbD0ibm9uZSIgaGVpZ2h0PSIxMDQuNTMxMyIgc3R5bGU9InN0cm9rZTogIzAwMDAwMDsgc3Ryb2tlLXdpZHRoOiAyLjA7IiB3aWR0aD0iNTA5IiB4PSIxODMuNSIgeT0iNDUwLjcxMDkiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgZm9udC13ZWlnaHQ9ImJvbGQiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iNTMiIHg9IjE5OC41IiB5PSI0NjMuNzc3OCI+cmVkcmF3PC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iMzcxLjUsNDg0Ljk3NjYsMzgxLjUsNDg4Ljk3NjYsMzcxLjUsNDkyLjk3NjYsMzc1LjUsNDg4Ljk3NjYiIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIgeDE9IjIzOS41IiB4Mj0iMzc3LjUiIHkxPSI0ODguOTc2NiIgeTI9IjQ4OC45NzY2Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGZvbnQtd2VpZ2h0PSJib2xkIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9Ijg3IiB4PSIyNDYuNSIgeT0iNDgzLjkxMDYiPmgzLnJlZHJhdygpPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iMjUwLjUsNTE0LjEwOTQsMjQwLjUsNTE4LjEwOTQsMjUwLjUsNTIyLjEwOTQsMjQ2LjUsNTE4LjEwOTQiIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIgeDE9IjI0NC41IiB4Mj0iMzgyLjUiIHkxPSI1MTguMTA5NCIgeTI9IjUxOC4xMDk0Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iNTUiIHg9IjI1Ni41IiB5PSI1MTMuMDQzNSI+cmVkcmF3KCk8L3RleHQ+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSI2NDcuNSw1NDMuMjQyMiw2NTcuNSw1NDcuMjQyMiw2NDcuNSw1NTEuMjQyMiw2NTEuNSw1NDcuMjQyMiIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iMzgzLjUiIHgyPSI2NTMuNSIgeTE9IjU0Ny4yNDIyIiB5Mj0iNTQ3LjI0MjIiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSIxMTciIHg9IjM5MC41IiB5PSI1NDIuMTc2MyI+ZGlzcGF0Y2goJHJlZHJhdyk8L3RleHQ+PHBhdGggZD0iTTEzLDU2OS4yNDIyIEwxMzcsNTY5LjI0MjIgTDEzNyw1NzYuMjQyMiBMMTI3LDU4Ni4yNDIyIEwxMyw1ODYuMjQyMiBMMTMsNTY5LjI0MjIgIiBmaWxsPSIjRUVFRUVFIiBzdHlsZT0ic3Ryb2tlOiAjMDAwMDAwOyBzdHJva2Utd2lkdGg6IDEuMDsiLz48cmVjdCBmaWxsPSJub25lIiBoZWlnaHQ9IjIyMS4wNjI1IiBzdHlsZT0ic3Ryb2tlOiAjMDAwMDAwOyBzdHJva2Utd2lkdGg6IDIuMDsiIHdpZHRoPSI2NzkuNSIgeD0iMTMiIHk9IjU2OS4yNDIyIi8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGZvbnQtd2VpZ2h0PSJib2xkIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9Ijc5IiB4PSIyOCIgeT0iNTgyLjMwOTEiPm5hdmlnYXRpb248L3RleHQ+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSIzNzEuNSw2MDMuNTA3OCwzODEuNSw2MDcuNTA3OCwzNzEuNSw2MTEuNTA3OCwzNzUuNSw2MDcuNTA3OCIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iMjM5LjUiIHgyPSIzNzcuNSIgeTE9IjYwNy41MDc4IiB5Mj0iNjA3LjUwNzgiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgZm9udC13ZWlnaHQ9ImJvbGQiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iMTIwIiB4PSIyNDYuNSIgeT0iNjAyLjQ0MTkiPmgzLm5hdmlnYXRlVG8oKTwvdGV4dD48cG9seWdvbiBmaWxsPSIjQTgwMDM2IiBwb2ludHM9IjQ4NC41LDYzMi42NDA2LDQ5NC41LDYzNi42NDA2LDQ4NC41LDY0MC42NDA2LDQ4OC41LDYzNi42NDA2IiBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiAjQTgwMDM2OyBzdHJva2Utd2lkdGg6IDEuMDsiIHgxPSIzODMuNSIgeDI9IjQ5MC41IiB5MT0iNjM2LjY0MDYiIHkyPSI2MzYuNjQwNiIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiIHRleHRMZW5ndGg9Ijg5IiB4PSIzOTAuNSIgeT0iNjMxLjU3NDciPnByb2Nlc3NQYXRoKCk8L3RleHQ+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSI2NDcuNSw2NjEuNzczNCw2NTcuNSw2NjUuNzczNCw2NDcuNSw2NjkuNzczNCw2NTEuNSw2NjUuNzczNCIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iNDk2LjUiIHgyPSI2NTMuNSIgeTE9IjY2NS43NzM0IiB5Mj0iNjY1Ljc3MzQiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSIxMzkiIHg9IjUwMy41IiB5PSI2NjAuNzA3NSI+ZGlzcGF0Y2goJG5hdmlnYXRpb24pPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iNzguNSw2OTAuOTA2Myw2OC41LDY5NC45MDYzLDc4LjUsNjk4LjkwNjMsNzQuNSw2OTQuOTA2MyIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iNzIuNSIgeDI9IjQ5NS41IiB5MT0iNjk0LjkwNjMiIHkyPSI2OTQuOTA2MyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBmb250LXN0eWxlPSJpdGFsaWMiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iMTQ4IiB4PSI4NC41IiB5PSI2ODkuODQwMyI+cmVtb3ZlIGFsbCBET00gbm9kZXM8L3RleHQ+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSIyMjcuNSw3MjAuMDM5MSwyMzcuNSw3MjQuMDM5MSwyMjcuNSw3MjguMDM5MSwyMzEuNSw3MjQuMDM5MSIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iNjcuNSIgeDI9IjIzMy41IiB5MT0iNzI0LjAzOTEiIHkyPSI3MjQuMDM5MSIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBmb250LXN0eWxlPSJpdGFsaWMiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iMTQ4IiB4PSI3NC41IiB5PSI3MTguOTczMSI+cmVtb3ZlIGFsbCBET00gbm9kZXM8L3RleHQ+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSIyNTAuNSw3NDkuMTcxOSwyNDAuNSw3NTMuMTcxOSwyNTAuNSw3NTcuMTcxOSwyNDYuNSw3NTMuMTcxOSIgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTogI0E4MDAzNjsgc3Ryb2tlLXdpZHRoOiAxLjA7IiB4MT0iMjQ0LjUiIHgyPSI0OTUuNSIgeTE9Ijc1My4xNzE5IiB5Mj0iNzUzLjE3MTkiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nQW5kR2x5cGhzIiB0ZXh0TGVuZ3RoPSI1MiIgeD0iMjU2LjUiIHk9Ijc0OC4xMDYiPnJlbmRlcigpPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iNjQ3LjUsNzc4LjMwNDcsNjU3LjUsNzgyLjMwNDcsNjQ3LjUsNzg2LjMwNDcsNjUxLjUsNzgyLjMwNDciIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6ICNBODAwMzY7IHN0cm9rZS13aWR0aDogMS4wOyIgeDE9IjQ5Ni41IiB4Mj0iNjUzLjUiIHkxPSI3ODIuMzA0NyIgeTI9Ijc4Mi4zMDQ3Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZ0FuZEdseXBocyIgdGV4dExlbmd0aD0iMTE3IiB4PSI1MDMuNSIgeT0iNzc3LjIzODgiPmRpc3BhdGNoKCRyZWRyYXcpPC90ZXh0PjwhLS1NRDU9W2NkMWRlNTI0OTZmZmMwMTdhODdlNjI3M2NjZjg3YjIzXQpAc3RhcnR1bWwKdGl0bGUgSDMgU2VxdWVuY2UgRGlhZ3JhbQoKZW50aXR5IEFwcGxpY2F0aW9uCmNvbnRyb2wgQ29tcG9uZW50CmVudGl0eSBIMwplbnRpdHkgUm91dGVyCmRhdGFiYXNlIFN0b3JlCgpBcHBsaWNhdGlvbiAtIC0+IEgzIDogPGI+aDMuaW5pdCgpPC9iPgpIMyAtPiBTdG9yZSA6IC8vaW5pdGlhbGl6ZS8vClN0b3JlIC0+IFN0b3JlIDogLy9leGVjdXRlIG1vZHVsZXMvLwpIMyAtPiBTdG9yZSA6IGRpc3BhdGNoKCRpbml0KQpIMyAtPiBBcHBsaWNhdGlvbiA6IHByZVN0YXJ0KCkKSDMgLT4gUm91dGVyIDogLy9pbml0aWFsaXplLy8KSDMgLT4gUm91dGVyIDogc3RhcnQoKQpSb3V0ZXIgLT4gU3RvcmU6IGRpc3BhdGNoKCRuYXZpZ2F0aW9uKQpSb3V0ZXIgLT4gQ29tcG9uZW50IDogcmVuZGVyKCkKUm91dGVyIC0+IFN0b3JlOiBkaXNwYXRjaCgkcmVkcmF3KQoKSDMgLT4gQXBwbGljYXRpb24gOiBwb3N0U3RhcnQoKQoKZ3JvdXAgcmVkcmF3CiAgICBDb21wb25lbnQgLT4gSDMgOiA8Yj5oMy5yZWRyYXcoKTwvYj4KICAgIEgzIC0+IENvbXBvbmVudCA6IHJlZHJhdygpCiAgICBIMyAtPiBTdG9yZTogZGlzcGF0Y2goJHJlZHJhdykKZW5kCgpncm91cCBuYXZpZ2F0aW9uCiAgICBDb21wb25lbnQgLT4gSDMgOiA8Yj5oMy5uYXZpZ2F0ZVRvKCk8L2I+CiAgICBIMyAtPiBSb3V0ZXIgOiBwcm9jZXNzUGF0aCgpCiAgICBSb3V0ZXIgLT4gU3RvcmU6IGRpc3BhdGNoKCRuYXZpZ2F0aW9uKQogICAgUm91dGVyIC0+IEFwcGxpY2F0aW9uIDogLy9yZW1vdmUgYWxsIERPTSBub2Rlcy8vCiAgICBBcHBsaWNhdGlvbiAtPiBDb21wb25lbnQgOiAvL3JlbW92ZSBhbGwgRE9NIG5vZGVzLy8KICAgIFJvdXRlciAtPiBDb21wb25lbnQgOiByZW5kZXIoKQogICAgUm91dGVyIC0+IFN0b3JlOiBkaXNwYXRjaCgkcmVkcmF3KQogICAgCmVuZApAZW5kdW1sCgpQbGFudFVNTCB2ZXJzaW9uIDEuMjAyMC4wOWJldGExNyhVbmtub3duIGNvbXBpbGUgdGltZSkKKEdQTCBzb3VyY2UgZGlzdHJpYnV0aW9uKQpKYXZhIFJ1bnRpbWU6IEphdmEoVE0pIFNFIFJ1bnRpbWUgRW52aXJvbm1lbnQKSlZNOiBKYXZhIEhvdFNwb3QoVE0pIDY0LUJpdCBTZXJ2ZXIgVk0KSmF2YSBWZXJzaW9uOiAxNC4wLjErNwpPcGVyYXRpbmcgU3lzdGVtOiBMaW51eApEZWZhdWx0IEVuY29kaW5nOiBVVEYtOApMYW5ndWFnZTogZW4KQ291bnRyeTogVVMKLS0+PC9nPjwvc3ZnPg==" alt="Sequence Diagram" /></p> <p>When the <code>h3.init()</code> method is called at application level, the following operations are performed in sequence:</p> <ol> <li>The <em>Store</em> is created and initialized.</li> <li>Any <em>Module</em> specified when calling <code>h3.init()</code> is executed.</li> -<li>The <strong>$init</strong> message is dispatched.</li> +<li>The <strong>$init</strong> event is dispatched.</li> <li>The <em>preStart</em> function (if specified when calling <code>h3.init()</code>) is executed.</li> <li>The <em>Router</em> is initialized and started.</li> +<li>The <strong>$navigation</strong> event is dispatched.</li> <li>All <em>Components</em> matching the current route are rendered for the first time.</li> -<li>The <strong>$redraw</strong> and <strong>$navigation</strong> messages are dispatched.</li> +<li>The <strong>$redraw</strong> event is dispatched.</li> </ol>@@ -7524,7 +7525,7 @@ <p>Then, whenever the <code>h3.redraw()</code> method is called (typically within a component):</p>
<ol> <li>The whole application is redrawn, i.e. every <em>Component</em> currently rendered on the page is redrawn.</li> -<li>The <strong>$redraw</strong> message is dispatched.</li> +<li>The <strong>$redraw</strong> event is dispatched.</li> </ol>@@ -7533,8 +7534,9 @@
<ol> <li>The <em>Router</em> processes the new path and determine which component to render based on the routing configuration.</li> <li>All DOM nodes within the scope of the routing are removed, all components are removed.</li> +<li>The <strong>$navigation</strong> event is dispatched.</li> <li>The <em>Component</em> matching the new route is rendered.</li> -<li>The <strong>$redraw</strong> and <strong>$navigation</strong> messages are dispatched.</li> +<li>The <strong>$redraw</strong> event is dispatched.</li> </ol>@@ -7662,8 +7664,8 @@ h3("a.logo.col-sm-1", { href: "#/" }, [
h3("img", { alt: "H3", src: "images/h3.svg" }), ]), h3("div.version.col-sm.col-md", [ - h3("div.version-number", "v0.3.0"), - h3("div.version-label", "“Cunning Cardassian“"), + h3("div.version-number", "v0.4.0"), + h3("div.version-label", "“Dedicated Denobulan“"), ]), h3("label.drawer-toggle.button.col-sm-last", { for: "drawer-control" }), ]);@@ -7851,21 +7853,21 @@ <li>The special <code>$html</code> attribute can be used to set the <code>innerHTML</code> property of the resulting HTML element. Use only if you know what you are doing!</li>
</ul> -<a name="h3.dispatch(message:-string,-data:-any)"></a> -<h3>h3.dispatch(message: string, data: any)<a href="#document-top" title="Go to top"></a></h3> +<a name="h3.dispatch(event:-string,-data:-any)"></a> +<h3>h3.dispatch(event: string, data: any)<a href="#document-top" title="Go to top"></a></h3> -<p>Dispatches a message and optionally some data. Messages are typically handled centrally by modules.</p> +<p>Dispatches a event and optionally some data. Messages are typically handled centrally by modules.</p> <pre><code class="js">h3.dispatch("settings/set", { logging: true }); </code></pre> -<p>A message name can be anything, but keep in mind that the following names (and typically any name starting with <code>$</code>) are reserved for framework use:</p> +<p>A event name can be any string, but keep in mind that the following names (and typically any name starting with <code>$</code>) are reserved for framework use:</p> <ul> <li><code>$init</code> — Dispatched when the application is initialized. Useful to initialize application state.</li> <li><code>$redraw</code> — Dispatched after an application redraw is triggered.</li> <li><code>$navigation</code> — Dispatched after a navigation occurs.</li> -<li><code>$log</code> — Dispatched after <em>any</em> message (except <code>$log</code> iself) is dispatched. Very useful for debugging.</li> +<li><code>$log</code> — Dispatched after <em>any</em> event (except <code>$log</code> iself) is dispatched. Very useful for debugging.</li> </ul>@@ -7904,10 +7906,10 @@
<pre><code class="js">h3.navigateTo("/posts/", {orderBy: 'date', direction: 'desc'}); </code></pre> -<a name="h3.on(message:-string,-handler:-function)"></a> -<h3>h3.on(message: string, handler: function)<a href="#document-top" title="Go to top"></a></h3> +<a name="h3.on(event:-string,-handler:-function)"></a> +<h3>h3.on(event: string, handler: function)<a href="#document-top" title="Go to top"></a></h3> -<p>Subscribes to the specified message and executes the specified handler function whenever the message is dispatches. Returns a function that can be used to delete the subscription.</p> +<p>Subscribes to the specified event and executes the specified handler function whenever the event is dispatched. Returns a function that can be used to delete the subscription.</p> <p>Subscriptions should be typically managed in modules rather than in components: a component gets rendered several times and subscriptions <em>must</em> be properly cleaned up to avoid memory leaks.</p>@@ -7941,7 +7943,7 @@
<a name="h3.state"></a> <h3>h3.state<a href="#document-top" title="Go to top"></a></h3> -<p>A read-only property containing the current application state. The state is a plain object, but its properties should only be modified using message subscription handlers.</p> +<p>A read-only property containing the current application state. The state is a plain object, but its properties should only be modified using event subscription handlers.</p> <a name="About"></a> <h2>About<a href="#document-top" title="Go to top"></a></h2>@@ -7956,7 +7958,7 @@
<a name="Why-the-weird-release-labels?"></a> <h3>Why the weird release labels?<a href="#document-top" title="Go to top"></a></h3> -<p>Ubuntu started <a href="https://wiki.ubuntu.com/DevelopmentCodeNames">naming their releases after animals</a> in alphabetical order… In a similar way, H3 releases are named after <a href="https://en.wikipedia.org/wiki/List_of_Star_Trek_races">Star Trek races</a>.</p> +<p>Ubuntu started <a href="https://wiki.ubuntu.com/DevelopmentCodeNames">naming their releases after animals</a> in alphabetical order… In a similar way, H3 releases are named after <a href="https://memory-alpha.fandom.com/wiki/Species">Star Trek species</a>.</p> <a name="A-brief-history-of-H3"></a> <h3>A brief history of H3<a href="#document-top" title="Go to top"></a></h3>@@ -7999,7 +8001,7 @@ </ul>
</div> <div id="footer"> - <p><span class="copy"></span> Fabio Cevasco – May 1, 2020</p> + <p><span class="copy"></span> Fabio Cevasco – May 9, 2020</p> <p><span>Powered by</span> <a href="https://h3rald.com/hastyscribe"><span class="hastyscribe"></span></a></p> </div> </div>
@@ -5,6 +5,18 @@ * This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree. * */ +const checkProperties = (obj1, obj2) => { + for (const key in obj1) { + if (!(key in obj2)) { + return false; + } + if (!equal(obj1[key], obj2[key])) { + return false; + } + } + return true; +}; + const equal = (obj1, obj2) => { if ( (obj1 === null && obj2 === null) ||@@ -44,18 +56,7 @@ }
} return true; } - function checkProperties(obj1, obj2) { - for (const key in obj1) { - if (!(key in obj2)) { - return false; - } - if (!equal(obj1[key], obj2[key])) { - return false; - } - } - return true; - } - return checkProperties(obj1, obj2) && checkProperties(obj2, obj1); + return checkProperties(obj1, obj2); // && checkProperties(obj2, obj1); }; const selectorRegex = /^([a-z0-9:_=-]+)(#[a-z0-9:_=-]+)?(\..+)?$/i;@@ -267,6 +268,10 @@ const a = document.createAttribute(attr);
a.value = this.attributes[attr]; node.setAttributeNode(a); } + // Handle boolean attributes + if (this.attributes[attr] === false) { + node[attr] = false; + } }); // Event Listeners Object.keys(this.eventListeners).forEach((event) => {@@ -364,6 +369,9 @@ }
// Attributes if (!equal(oldvnode.attributes, newvnode.attributes)) { Object.keys(oldvnode.attributes).forEach((a) => { + if (newvnode.attributes[a] === false) { + node[a] = false; + } if (!newvnode.attributes[a]) { node.removeAttribute(a); } else if (@@ -544,8 +552,10 @@ }
setRedraw(vnode) { this.redraw = () => { - const fn = this.routes[this.route.def]; - vnode.redraw({ node: this.element.childNodes[0], vnode: fn() }); + vnode.redraw({ + node: this.element.childNodes[0], + vnode: this.routes[this.route.def](), + }); this.store.dispatch("$redraw"); }; }@@ -586,6 +596,7 @@ }
if (!this.route) { throw new Error(`[Router] No route matches '${fragment}'`); } + this.store.dispatch("$navigation", this.route); // Display View while (this.element.firstChild) { this.element.removeChild(this.element.firstChild);@@ -594,8 +605,7 @@ const vnode = this.routes[this.route.def]();
this.element.appendChild(vnode.render()); this.setRedraw(vnode); window.scrollTo(0, 0); - this.store.dispatch("$redraw", this.route); - this.store.dispatch("$navigation", this.route); + this.store.dispatch("$redraw"); }; processPath(); window.addEventListener("hashchange", processPath);
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="880px" preserveAspectRatio="none" style="width:787px;height:880px;" version="1.1" viewBox="0 0 787 880" width="787px" zoomAndPan="magnify"><defs><filter height="300%" id="f8vxuzcxvtuu5" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><text fill="#000000" font-family="sans-serif" font-size="18" lengthAdjust="spacingAndGlyphs" textLength="208" x="288.75" y="26.708">H3 Sequence Diagram</text><rect fill="#FFFFFF" filter="url(#f8vxuzcxvtuu5)" height="104.5313" style="stroke: #000000; stroke-width: 2.0;" width="506.5" x="182.5" y="450.7109"/><rect fill="#FFFFFF" filter="url(#f8vxuzcxvtuu5)" height="221.0625" style="stroke: #000000; stroke-width: 2.0;" width="676" x="13" y="569.2422"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="64" x2="64" y1="100.25" y2="807.3047"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="236.5" x2="236.5" y1="100.25" y2="807.3047"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="379.5" x2="379.5" y1="100.25" y2="807.3047"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="493" x2="493" y1="100.25" y2="807.3047"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="656" x2="656" y1="100.25" y2="807.3047"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="77" x="23" y="96.9482">Application</text><ellipse cx="64.5" cy="67.9531" fill="#FEFECE" filter="url(#f8vxuzcxvtuu5)" rx="12" ry="12" style="stroke: #A80036; stroke-width: 2.0;"/><line style="stroke: #A80036; stroke-width: 2.0;" x1="52.5" x2="76.5" y1="81.9531" y2="81.9531"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="77" x="23" y="819.2998">Application</text><ellipse cx="64.5" cy="838.6016" fill="#FEFECE" filter="url(#f8vxuzcxvtuu5)" rx="12" ry="12" style="stroke: #A80036; stroke-width: 2.0;"/><line style="stroke: #A80036; stroke-width: 2.0;" x1="52.5" x2="76.5" y1="852.6016" y2="852.6016"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="82" x="192.5" y="96.9482">Component</text><ellipse cx="236.5" cy="67.9531" fill="#FEFECE" filter="url(#f8vxuzcxvtuu5)" rx="12" ry="12" style="stroke: #A80036; stroke-width: 2.0;"/><polygon fill="#A80036" points="232.5,55.9531,238.5,50.9531,236.5,55.9531,238.5,60.9531,232.5,55.9531" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="82" x="192.5" y="819.2998">Component</text><ellipse cx="236.5" cy="838.6016" fill="#FEFECE" filter="url(#f8vxuzcxvtuu5)" rx="12" ry="12" style="stroke: #A80036; stroke-width: 2.0;"/><polygon fill="#A80036" points="232.5,826.6016,238.5,821.6016,236.5,826.6016,238.5,831.6016,232.5,826.6016" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="18" x="367.5" y="96.9482">H3</text><ellipse cx="379.5" cy="67.9531" fill="#FEFECE" filter="url(#f8vxuzcxvtuu5)" rx="12" ry="12" style="stroke: #A80036; stroke-width: 2.0;"/><line style="stroke: #A80036; stroke-width: 2.0;" x1="367.5" x2="391.5" y1="81.9531" y2="81.9531"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="18" x="367.5" y="819.2998">H3</text><ellipse cx="379.5" cy="838.6016" fill="#FEFECE" filter="url(#f8vxuzcxvtuu5)" rx="12" ry="12" style="stroke: #A80036; stroke-width: 2.0;"/><line style="stroke: #A80036; stroke-width: 2.0;" x1="367.5" x2="391.5" y1="852.6016" y2="852.6016"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="47" x="467" y="96.9482">Router</text><ellipse cx="493.5" cy="67.9531" fill="#FEFECE" filter="url(#f8vxuzcxvtuu5)" rx="12" ry="12" style="stroke: #A80036; stroke-width: 2.0;"/><line style="stroke: #A80036; stroke-width: 2.0;" x1="481.5" x2="505.5" y1="81.9531" y2="81.9531"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="47" x="467" y="819.2998">Router</text><ellipse cx="493.5" cy="838.6016" fill="#FEFECE" filter="url(#f8vxuzcxvtuu5)" rx="12" ry="12" style="stroke: #A80036; stroke-width: 2.0;"/><line style="stroke: #A80036; stroke-width: 2.0;" x1="481.5" x2="505.5" y1="852.6016" y2="852.6016"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="39" x="634" y="96.9482">Store</text><path d="M638.5,47.9531 C638.5,37.9531 656.5,37.9531 656.5,37.9531 C656.5,37.9531 674.5,37.9531 674.5,47.9531 L674.5,73.9531 C674.5,83.9531 656.5,83.9531 656.5,83.9531 C656.5,83.9531 638.5,83.9531 638.5,73.9531 L638.5,47.9531 " fill="#FEFECE" filter="url(#f8vxuzcxvtuu5)" style="stroke: #000000; stroke-width: 1.5;"/><path d="M638.5,47.9531 C638.5,57.9531 656.5,57.9531 656.5,57.9531 C656.5,57.9531 674.5,57.9531 674.5,47.9531 " fill="none" style="stroke: #000000; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="39" x="634" y="819.2998">Store</text><path d="M638.5,832.6016 C638.5,822.6016 656.5,822.6016 656.5,822.6016 C656.5,822.6016 674.5,822.6016 674.5,832.6016 L674.5,858.6016 C674.5,868.6016 656.5,868.6016 656.5,868.6016 C656.5,868.6016 638.5,868.6016 638.5,858.6016 L638.5,832.6016 " fill="#FEFECE" filter="url(#f8vxuzcxvtuu5)" style="stroke: #000000; stroke-width: 1.5;"/><path d="M638.5,832.6016 C638.5,842.6016 656.5,842.6016 656.5,842.6016 C656.5,842.6016 674.5,842.6016 674.5,832.6016 " fill="none" style="stroke: #000000; stroke-width: 1.5;"/><polygon fill="#A80036" points="367.5,127.3828,377.5,131.3828,367.5,135.3828,371.5,131.3828" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 2.0,2.0;" x1="64.5" x2="373.5" y1="131.3828" y2="131.3828"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="58" x="71.5" y="126.3169">h3.init()</text><polygon fill="#A80036" points="644.5,156.5156,654.5,160.5156,644.5,164.5156,648.5,160.5156" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="379.5" x2="650.5" y1="160.5156" y2="160.5156"/><text fill="#000000" font-family="sans-serif" font-size="13" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="56" x="386.5" y="155.4497">initialize</text><line style="stroke: #A80036; stroke-width: 1.0;" x1="656.5" x2="698.5" y1="189.6484" y2="189.6484"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="698.5" x2="698.5" y1="189.6484" y2="202.6484"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="657.5" x2="698.5" y1="202.6484" y2="202.6484"/><polygon fill="#A80036" points="667.5,198.6484,657.5,202.6484,667.5,206.6484,663.5,202.6484" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="112" x="663.5" y="184.5825">execute modules</text><polygon fill="#A80036" points="644.5,227.7813,654.5,231.7813,644.5,235.7813,648.5,231.7813" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="379.5" x2="650.5" y1="231.7813" y2="231.7813"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="91" x="386.5" y="226.7153">dispatch($init)</text><polygon fill="#A80036" points="75.5,256.9141,65.5,260.9141,75.5,264.9141,71.5,260.9141" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="69.5" x2="378.5" y1="260.9141" y2="260.9141"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="64" x="81.5" y="255.8481">preStart()</text><polygon fill="#A80036" points="481.5,286.0469,491.5,290.0469,481.5,294.0469,485.5,290.0469" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="379.5" x2="487.5" y1="290.0469" y2="290.0469"/><text fill="#000000" font-family="sans-serif" font-size="13" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="56" x="386.5" y="284.981">initialize</text><polygon fill="#A80036" points="481.5,315.1797,491.5,319.1797,481.5,323.1797,485.5,319.1797" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="379.5" x2="487.5" y1="319.1797" y2="319.1797"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="40" x="386.5" y="314.1138">start()</text><polygon fill="#A80036" points="247.5,344.3125,237.5,348.3125,247.5,352.3125,243.5,348.3125" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="241.5" x2="492.5" y1="348.3125" y2="348.3125"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="54" x="253.5" y="343.2466">render()</text><polygon fill="#A80036" points="644.5,373.4453,654.5,377.4453,644.5,381.4453,648.5,377.4453" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="493.5" x2="650.5" y1="377.4453" y2="377.4453"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="118" x="500.5" y="372.3794">dispatch($redraw)</text><polygon fill="#A80036" points="644.5,402.5781,654.5,406.5781,644.5,410.5781,648.5,406.5781" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="493.5" x2="650.5" y1="406.5781" y2="406.5781"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="139" x="500.5" y="401.5122">dispatch($navigation)</text><polygon fill="#A80036" points="75.5,431.7109,65.5,435.7109,75.5,439.7109,71.5,435.7109" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="69.5" x2="378.5" y1="435.7109" y2="435.7109"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="70" x="81.5" y="430.645">postStart()</text><path d="M182.5,450.7109 L280.5,450.7109 L280.5,457.7109 L270.5,467.7109 L182.5,467.7109 L182.5,450.7109 " fill="#EEEEEE" style="stroke: #000000; stroke-width: 1.0;"/><rect fill="none" height="104.5313" style="stroke: #000000; stroke-width: 2.0;" width="506.5" x="182.5" y="450.7109"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="53" x="197.5" y="463.7778">redraw</text><polygon fill="#A80036" points="367.5,484.9766,377.5,488.9766,367.5,492.9766,371.5,488.9766" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="236.5" x2="373.5" y1="488.9766" y2="488.9766"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="86" x="243.5" y="483.9106">h3.redraw()</text><polygon fill="#A80036" points="247.5,514.1094,237.5,518.1094,247.5,522.1094,243.5,518.1094" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="241.5" x2="378.5" y1="518.1094" y2="518.1094"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="56" x="253.5" y="513.0435">redraw()</text><polygon fill="#A80036" points="644.5,543.2422,654.5,547.2422,644.5,551.2422,648.5,547.2422" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="379.5" x2="650.5" y1="547.2422" y2="547.2422"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="118" x="386.5" y="542.1763">dispatch($redraw)</text><path d="M13,569.2422 L137,569.2422 L137,576.2422 L127,586.2422 L13,586.2422 L13,569.2422 " fill="#EEEEEE" style="stroke: #000000; stroke-width: 1.0;"/><rect fill="none" height="221.0625" style="stroke: #000000; stroke-width: 2.0;" width="676" x="13" y="569.2422"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="79" x="28" y="582.3091">navigation</text><polygon fill="#A80036" points="367.5,603.5078,377.5,607.5078,367.5,611.5078,371.5,607.5078" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="236.5" x2="373.5" y1="607.5078" y2="607.5078"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="119" x="243.5" y="602.4419">h3.navigateTo()</text><polygon fill="#A80036" points="481.5,632.6406,491.5,636.6406,481.5,640.6406,485.5,636.6406" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="379.5" x2="487.5" y1="636.6406" y2="636.6406"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="90" x="386.5" y="631.5747">processPath()</text><polygon fill="#A80036" points="75.5,661.7734,65.5,665.7734,75.5,669.7734,71.5,665.7734" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="69.5" x2="492.5" y1="665.7734" y2="665.7734"/><text fill="#000000" font-family="sans-serif" font-size="13" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="148" x="81.5" y="660.7075">remove all DOM nodes</text><polygon fill="#A80036" points="224.5,690.9063,234.5,694.9063,224.5,698.9063,228.5,694.9063" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="64.5" x2="230.5" y1="694.9063" y2="694.9063"/><text fill="#000000" font-family="sans-serif" font-size="13" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="148" x="71.5" y="689.8403">remove all DOM nodes</text><polygon fill="#A80036" points="247.5,720.0391,237.5,724.0391,247.5,728.0391,243.5,724.0391" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="241.5" x2="492.5" y1="724.0391" y2="724.0391"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="54" x="253.5" y="718.9731">render()</text><polygon fill="#A80036" points="644.5,749.1719,654.5,753.1719,644.5,757.1719,648.5,753.1719" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="493.5" x2="650.5" y1="753.1719" y2="753.1719"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="118" x="500.5" y="748.106">dispatch($redraw)</text><polygon fill="#A80036" points="644.5,778.3047,654.5,782.3047,644.5,786.3047,648.5,782.3047" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="493.5" x2="650.5" y1="782.3047" y2="782.3047"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="139" x="500.5" y="777.2388">dispatch($navigation)</text><!--MD5=[352d431b2ecd2f9ee48a7d6efc890436] +<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="880px" preserveAspectRatio="none" style="width:790px;height:880px;" version="1.1" viewBox="0 0 790 880" width="790px" zoomAndPan="magnify"><defs><filter height="300%" id="f1dbhycfy50ozh" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><text fill="#000000" font-family="sans-serif" font-size="18" lengthAdjust="spacingAndGlyphs" textLength="205" x="291.75" y="26.708">H3 Sequence Diagram</text><rect fill="#FFFFFF" filter="url(#f1dbhycfy50ozh)" height="104.5313" style="stroke: #000000; stroke-width: 2.0;" width="509" x="183.5" y="450.7109"/><rect fill="#FFFFFF" filter="url(#f1dbhycfy50ozh)" height="221.0625" style="stroke: #000000; stroke-width: 2.0;" width="679.5" x="13" y="569.2422"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="67" x2="67" y1="100.25" y2="807.3047"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="239.5" x2="239.5" y1="100.25" y2="807.3047"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="383.5" x2="383.5" y1="100.25" y2="807.3047"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="496" x2="496" y1="100.25" y2="807.3047"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="659.5" x2="659.5" y1="100.25" y2="807.3047"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="83" x="23" y="96.9482">Application</text><ellipse cx="67.5" cy="67.9531" fill="#FEFECE" filter="url(#f1dbhycfy50ozh)" rx="12" ry="12" style="stroke: #A80036; stroke-width: 2.0;"/><line style="stroke: #A80036; stroke-width: 2.0;" x1="55.5" x2="79.5" y1="81.9531" y2="81.9531"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="83" x="23" y="819.2998">Application</text><ellipse cx="67.5" cy="838.6016" fill="#FEFECE" filter="url(#f1dbhycfy50ozh)" rx="12" ry="12" style="stroke: #A80036; stroke-width: 2.0;"/><line style="stroke: #A80036; stroke-width: 2.0;" x1="55.5" x2="79.5" y1="852.6016" y2="852.6016"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="86" x="193.5" y="96.9482">Component</text><ellipse cx="239.5" cy="67.9531" fill="#FEFECE" filter="url(#f1dbhycfy50ozh)" rx="12" ry="12" style="stroke: #A80036; stroke-width: 2.0;"/><polygon fill="#A80036" points="235.5,55.9531,241.5,50.9531,239.5,55.9531,241.5,60.9531,235.5,55.9531" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="86" x="193.5" y="819.2998">Component</text><ellipse cx="239.5" cy="838.6016" fill="#FEFECE" filter="url(#f1dbhycfy50ozh)" rx="12" ry="12" style="stroke: #A80036; stroke-width: 2.0;"/><polygon fill="#A80036" points="235.5,826.6016,241.5,821.6016,239.5,826.6016,241.5,831.6016,235.5,826.6016" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="19" x="371" y="96.9482">H3</text><ellipse cx="383.5" cy="67.9531" fill="#FEFECE" filter="url(#f1dbhycfy50ozh)" rx="12" ry="12" style="stroke: #A80036; stroke-width: 2.0;"/><line style="stroke: #A80036; stroke-width: 2.0;" x1="371.5" x2="395.5" y1="81.9531" y2="81.9531"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="19" x="371" y="819.2998">H3</text><ellipse cx="383.5" cy="838.6016" fill="#FEFECE" filter="url(#f1dbhycfy50ozh)" rx="12" ry="12" style="stroke: #A80036; stroke-width: 2.0;"/><line style="stroke: #A80036; stroke-width: 2.0;" x1="371.5" x2="395.5" y1="852.6016" y2="852.6016"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="49" x="469" y="96.9482">Router</text><ellipse cx="496.5" cy="67.9531" fill="#FEFECE" filter="url(#f1dbhycfy50ozh)" rx="12" ry="12" style="stroke: #A80036; stroke-width: 2.0;"/><line style="stroke: #A80036; stroke-width: 2.0;" x1="484.5" x2="508.5" y1="81.9531" y2="81.9531"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="49" x="469" y="819.2998">Router</text><ellipse cx="496.5" cy="838.6016" fill="#FEFECE" filter="url(#f1dbhycfy50ozh)" rx="12" ry="12" style="stroke: #A80036; stroke-width: 2.0;"/><line style="stroke: #A80036; stroke-width: 2.0;" x1="484.5" x2="508.5" y1="852.6016" y2="852.6016"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="40" x="636.5" y="96.9482">Store</text><path d="M641.5,47.9531 C641.5,37.9531 659.5,37.9531 659.5,37.9531 C659.5,37.9531 677.5,37.9531 677.5,47.9531 L677.5,73.9531 C677.5,83.9531 659.5,83.9531 659.5,83.9531 C659.5,83.9531 641.5,83.9531 641.5,73.9531 L641.5,47.9531 " fill="#FEFECE" filter="url(#f1dbhycfy50ozh)" style="stroke: #000000; stroke-width: 1.5;"/><path d="M641.5,47.9531 C641.5,57.9531 659.5,57.9531 659.5,57.9531 C659.5,57.9531 677.5,57.9531 677.5,47.9531 " fill="none" style="stroke: #000000; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="40" x="636.5" y="819.2998">Store</text><path d="M641.5,832.6016 C641.5,822.6016 659.5,822.6016 659.5,822.6016 C659.5,822.6016 677.5,822.6016 677.5,832.6016 L677.5,858.6016 C677.5,868.6016 659.5,868.6016 659.5,868.6016 C659.5,868.6016 641.5,868.6016 641.5,858.6016 L641.5,832.6016 " fill="#FEFECE" filter="url(#f1dbhycfy50ozh)" style="stroke: #000000; stroke-width: 1.5;"/><path d="M641.5,832.6016 C641.5,842.6016 659.5,842.6016 659.5,842.6016 C659.5,842.6016 677.5,842.6016 677.5,832.6016 " fill="none" style="stroke: #000000; stroke-width: 1.5;"/><polygon fill="#A80036" points="371.5,127.3828,381.5,131.3828,371.5,135.3828,375.5,131.3828" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 2.0,2.0;" x1="67.5" x2="377.5" y1="131.3828" y2="131.3828"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="59" x="74.5" y="126.3169">h3.init()</text><polygon fill="#A80036" points="647.5,156.5156,657.5,160.5156,647.5,164.5156,651.5,160.5156" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="383.5" x2="653.5" y1="160.5156" y2="160.5156"/><text fill="#000000" font-family="sans-serif" font-size="13" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="56" x="390.5" y="155.4497">initialize</text><line style="stroke: #A80036; stroke-width: 1.0;" x1="659.5" x2="701.5" y1="189.6484" y2="189.6484"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="701.5" x2="701.5" y1="189.6484" y2="202.6484"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="660.5" x2="701.5" y1="202.6484" y2="202.6484"/><polygon fill="#A80036" points="670.5,198.6484,660.5,202.6484,670.5,206.6484,666.5,202.6484" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="112" x="666.5" y="184.5825">execute modules</text><polygon fill="#A80036" points="647.5,227.7813,657.5,231.7813,647.5,235.7813,651.5,231.7813" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="383.5" x2="653.5" y1="231.7813" y2="231.7813"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="91" x="390.5" y="226.7153">dispatch($init)</text><polygon fill="#A80036" points="78.5,256.9141,68.5,260.9141,78.5,264.9141,74.5,260.9141" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="72.5" x2="382.5" y1="260.9141" y2="260.9141"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="62" x="84.5" y="255.8481">preStart()</text><polygon fill="#A80036" points="484.5,286.0469,494.5,290.0469,484.5,294.0469,488.5,290.0469" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="383.5" x2="490.5" y1="290.0469" y2="290.0469"/><text fill="#000000" font-family="sans-serif" font-size="13" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="56" x="390.5" y="284.981">initialize</text><polygon fill="#A80036" points="484.5,315.1797,494.5,319.1797,484.5,323.1797,488.5,319.1797" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="383.5" x2="490.5" y1="319.1797" y2="319.1797"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="40" x="390.5" y="314.1138">start()</text><polygon fill="#A80036" points="647.5,344.3125,657.5,348.3125,647.5,352.3125,651.5,348.3125" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="496.5" x2="653.5" y1="348.3125" y2="348.3125"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="139" x="503.5" y="343.2466">dispatch($navigation)</text><polygon fill="#A80036" points="250.5,373.4453,240.5,377.4453,250.5,381.4453,246.5,377.4453" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="244.5" x2="495.5" y1="377.4453" y2="377.4453"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="52" x="256.5" y="372.3794">render()</text><polygon fill="#A80036" points="647.5,402.5781,657.5,406.5781,647.5,410.5781,651.5,406.5781" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="496.5" x2="653.5" y1="406.5781" y2="406.5781"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="117" x="503.5" y="401.5122">dispatch($redraw)</text><polygon fill="#A80036" points="78.5,431.7109,68.5,435.7109,78.5,439.7109,74.5,435.7109" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="72.5" x2="382.5" y1="435.7109" y2="435.7109"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="69" x="84.5" y="430.645">postStart()</text><path d="M183.5,450.7109 L281.5,450.7109 L281.5,457.7109 L271.5,467.7109 L183.5,467.7109 L183.5,450.7109 " fill="#EEEEEE" style="stroke: #000000; stroke-width: 1.0;"/><rect fill="none" height="104.5313" style="stroke: #000000; stroke-width: 2.0;" width="509" x="183.5" y="450.7109"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="53" x="198.5" y="463.7778">redraw</text><polygon fill="#A80036" points="371.5,484.9766,381.5,488.9766,371.5,492.9766,375.5,488.9766" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="239.5" x2="377.5" y1="488.9766" y2="488.9766"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="87" x="246.5" y="483.9106">h3.redraw()</text><polygon fill="#A80036" points="250.5,514.1094,240.5,518.1094,250.5,522.1094,246.5,518.1094" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="244.5" x2="382.5" y1="518.1094" y2="518.1094"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="55" x="256.5" y="513.0435">redraw()</text><polygon fill="#A80036" points="647.5,543.2422,657.5,547.2422,647.5,551.2422,651.5,547.2422" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="383.5" x2="653.5" y1="547.2422" y2="547.2422"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="117" x="390.5" y="542.1763">dispatch($redraw)</text><path d="M13,569.2422 L137,569.2422 L137,576.2422 L127,586.2422 L13,586.2422 L13,569.2422 " fill="#EEEEEE" style="stroke: #000000; stroke-width: 1.0;"/><rect fill="none" height="221.0625" style="stroke: #000000; stroke-width: 2.0;" width="679.5" x="13" y="569.2422"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="79" x="28" y="582.3091">navigation</text><polygon fill="#A80036" points="371.5,603.5078,381.5,607.5078,371.5,611.5078,375.5,607.5078" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="239.5" x2="377.5" y1="607.5078" y2="607.5078"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="120" x="246.5" y="602.4419">h3.navigateTo()</text><polygon fill="#A80036" points="484.5,632.6406,494.5,636.6406,484.5,640.6406,488.5,636.6406" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="383.5" x2="490.5" y1="636.6406" y2="636.6406"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="89" x="390.5" y="631.5747">processPath()</text><polygon fill="#A80036" points="647.5,661.7734,657.5,665.7734,647.5,669.7734,651.5,665.7734" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="496.5" x2="653.5" y1="665.7734" y2="665.7734"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="139" x="503.5" y="660.7075">dispatch($navigation)</text><polygon fill="#A80036" points="78.5,690.9063,68.5,694.9063,78.5,698.9063,74.5,694.9063" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="72.5" x2="495.5" y1="694.9063" y2="694.9063"/><text fill="#000000" font-family="sans-serif" font-size="13" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="148" x="84.5" y="689.8403">remove all DOM nodes</text><polygon fill="#A80036" points="227.5,720.0391,237.5,724.0391,227.5,728.0391,231.5,724.0391" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="67.5" x2="233.5" y1="724.0391" y2="724.0391"/><text fill="#000000" font-family="sans-serif" font-size="13" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="148" x="74.5" y="718.9731">remove all DOM nodes</text><polygon fill="#A80036" points="250.5,749.1719,240.5,753.1719,250.5,757.1719,246.5,753.1719" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="244.5" x2="495.5" y1="753.1719" y2="753.1719"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="52" x="256.5" y="748.106">render()</text><polygon fill="#A80036" points="647.5,778.3047,657.5,782.3047,647.5,786.3047,651.5,782.3047" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="496.5" x2="653.5" y1="782.3047" y2="782.3047"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="117" x="503.5" y="777.2388">dispatch($redraw)</text><!--MD5=[cd1de52496ffc017a87e6273ccf87b23] @startuml title H3 Sequence Diagram@@ -15,9 +15,10 @@ H3 -> Store : dispatch($init)
H3 -> Application : preStart() H3 -> Router : //initialize// H3 -> Router : start() +Router -> Store: dispatch($navigation) Router -> Component : render() Router -> Store: dispatch($redraw) -Router -> Store: dispatch($navigation) + H3 -> Application : postStart() group redraw@@ -29,15 +30,16 @@
group navigation Component -> H3 : <b>h3.navigateTo()</b> H3 -> Router : processPath() + Router -> Store: dispatch($navigation) Router -> Application : //remove all DOM nodes// Application -> Component : //remove all DOM nodes// Router -> Component : render() Router -> Store: dispatch($redraw) - Router -> Store: dispatch($navigation) + end @enduml -PlantUML version 1.2020.08beta1(Unknown compile time) +PlantUML version 1.2020.09beta17(Unknown compile time) (GPL source distribution) Java Runtime: Java(TM) SE Runtime Environment JVM: Java HotSpot(TM) 64-Bit Server VM
@@ -44,8 +44,8 @@ h3("a.logo.col-sm-1", { href: "#/" }, [
h3("img", { alt: "H3", src: "images/h3.svg" }), ]), h3("div.version.col-sm.col-md", [ - h3("div.version-number", "v0.3.0"), - h3("div.version-label", "“Cunning Cardassian“"), + h3("div.version-number", "v0.4.0"), + h3("div.version-label", "“Dedicated Denobulan“"), ]), h3("label.drawer-toggle.button.col-sm-last", { for: "drawer-control" }), ]);
@@ -5,6 +5,18 @@ * This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree. * */ +const checkProperties = (obj1, obj2) => { + for (const key in obj1) { + if (!(key in obj2)) { + return false; + } + if (!equal(obj1[key], obj2[key])) { + return false; + } + } + return true; +}; + const equal = (obj1, obj2) => { if ( (obj1 === null && obj2 === null) ||@@ -44,18 +56,7 @@ }
} return true; } - function checkProperties(obj1, obj2) { - for (const key in obj1) { - if (!(key in obj2)) { - return false; - } - if (!equal(obj1[key], obj2[key])) { - return false; - } - } - return true; - } - return checkProperties(obj1, obj2) && checkProperties(obj2, obj1); + return checkProperties(obj1, obj2); // && checkProperties(obj2, obj1); }; const selectorRegex = /^([a-z0-9:_=-]+)(#[a-z0-9:_=-]+)?(\..+)?$/i;@@ -267,6 +268,10 @@ const a = document.createAttribute(attr);
a.value = this.attributes[attr]; node.setAttributeNode(a); } + // Handle boolean attributes + if (this.attributes[attr] === false) { + node[attr] = false; + } }); // Event Listeners Object.keys(this.eventListeners).forEach((event) => {@@ -364,6 +369,9 @@ }
// Attributes if (!equal(oldvnode.attributes, newvnode.attributes)) { Object.keys(oldvnode.attributes).forEach((a) => { + if (newvnode.attributes[a] === false) { + node[a] = false; + } if (!newvnode.attributes[a]) { node.removeAttribute(a); } else if (@@ -544,8 +552,10 @@ }
setRedraw(vnode) { this.redraw = () => { - const fn = this.routes[this.route.def]; - vnode.redraw({ node: this.element.childNodes[0], vnode: fn() }); + vnode.redraw({ + node: this.element.childNodes[0], + vnode: this.routes[this.route.def](), + }); this.store.dispatch("$redraw"); }; }@@ -586,6 +596,7 @@ }
if (!this.route) { throw new Error(`[Router] No route matches '${fragment}'`); } + this.store.dispatch("$navigation", this.route); // Display View while (this.element.firstChild) { this.element.removeChild(this.element.firstChild);@@ -594,8 +605,7 @@ const vnode = this.routes[this.route.def]();
this.element.appendChild(vnode.render()); this.setRedraw(vnode); window.scrollTo(0, 0); - this.store.dispatch("$redraw", this.route); - this.store.dispatch("$navigation", this.route); + this.store.dispatch("$redraw"); }; processPath(); window.addEventListener("hashchange", processPath);
@@ -8,7 +8,7 @@ Well, because I typically use [H3RALD](https://h3rald.com) as my handle online... **h** was already used by [HyperScript](https://github.com/hyperhype/hyperscript) so... **h3** was the next best thing to use as a constructor for Virtual DOM Nodes.
### Why the weird release labels? -Ubuntu started [naming their releases after animals](https://wiki.ubuntu.com/DevelopmentCodeNames) in alphabetical order... In a similar way, H3 releases are named after [Star Trek races](https://en.wikipedia.org/wiki/List_of_Star_Trek_races). +Ubuntu started [naming their releases after animals](https://wiki.ubuntu.com/DevelopmentCodeNames) in alphabetical order... In a similar way, H3 releases are named after [Star Trek species](https://memory-alpha.fandom.com/wiki/Species). ### A brief history of H3
@@ -115,20 +115,20 @@ * The `data` attribute can be set to a simple object containing [data attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes).
* The special `$key` attribute can be used to guarantee the uniqueness of two VNodes and it will not be translated into an HTML attribute. * The special `$html` attribute can be used to set the `innerHTML` property of the resulting HTML element. Use only if you know what you are doing! -### h3.dispatch(message: string, data: any) +### h3.dispatch(event: string, data: any) -Dispatches a message and optionally some data. Messages are typically handled centrally by modules. +Dispatches a event and optionally some data. Messages are typically handled centrally by modules. ```js h3.dispatch("settings/set", { logging: true }); ``` -A message name can be anything, but keep in mind that the following names (and typically any name starting with `$`) are reserved for framework use: +A event name can be any string, but keep in mind that the following names (and typically any name starting with `$`) are reserved for framework use: * `$init` — Dispatched when the application is initialized. Useful to initialize application state. * `$redraw` — Dispatched after an application redraw is triggered. * `$navigation` — Dispatched after a navigation occurs. -* `$log` — Dispatched after *any* message (except `$log` iself) is dispatched. Very useful for debugging. +* `$log` — Dispatched after *any* event (except `$log` iself) is dispatched. Very useful for debugging. ### h3.init(config: object)@@ -162,9 +162,9 @@ ```js
h3.navigateTo("/posts/", {orderBy: 'date', direction: 'desc'}); ``` -### h3.on(message: string, handler: function) +### h3.on(event: string, handler: function) -Subscribes to the specified message and executes the specified handler function whenever the message is dispatches. Returns a function that can be used to delete the subscription. +Subscribes to the specified event and executes the specified handler function whenever the event is dispatched. Returns a function that can be used to delete the subscription. Subscriptions should be typically managed in modules rather than in components: a component gets rendered several times and subscriptions *must* be properly cleaned up to avoid memory leaks.@@ -193,4 +193,4 @@ * **params** — An object containing the query string parameters, e.g. `{comments: "yet"}`
### h3.state -A read-only property containing the current application state. The state is a plain object, but its properties should only be modified using message subscription handlers. +A read-only property containing the current application state. The state is a plain object, but its properties should only be modified using event subscription handlers.
@@ -53,13 +53,13 @@ ```
### Store -H3 essentially uses something very, *very* similar to [Storeon](https://github.com/storeon/storeon) for state management *and* also as a very simple client-side message dispatcher/subscriber (seriously, it is virtually the same code as Storeon). Typically you'll only use the default store created by H3 upon initialization, and you'll use the `h3.dispatch()` and `h3.on()` methods to dispatch and subscribe to actions (messages). +H3 essentially uses something very, *very* similar to [Storeon](https://github.com/storeon/storeon) for state management *and* also as a very simple client-side event dispatcher/subscriber (seriously, it is virtually the same code as Storeon). Typically you'll only use the default store created by H3 upon initialization, and you'll use the `h3.dispatch()` and `h3.on()` methods to dispatch and subscribe to events. The current application state is accessible via the `h3.state` property. ### Modules -The `h3.init()` method takes an array of *modules* that can be used to manipulate the application state when specific messages are received. A simple module looks like this: +The `h3.init()` method takes an array of *modules* that can be used to manipulate the application state when specific events are received. A simple module looks like this: ```js const error = () => {@@ -69,7 +69,7 @@ h3.on("error/set", (state) => ({ displayEmptyTodoError: true }));
}; ``` -Essentially a module is just a function that typically is meant to run only once to define one or more message subscriptions. Modules are the place where you should handle state changes in your application. +Essentially a module is just a function that typically is meant to run only once to define one or more event subscriptions. Modules are the place where you should handle state changes in your application. ### Router@@ -77,7 +77,7 @@ H3 comes with a very minimal but fully functional URL fragment router. You create your application routes when initializing your application, and you can navigate to them using ordinary `href` links or programmatically using the `h3.navigateTo` method.
The current route is always accessible via the `h3.route` property. -### Sequence Diagram +### How everything works... The following sequence diagram summarizes how H3 works, from its initialization to the redraw and navigation phases.@@ -87,22 +87,24 @@ When the `h3.init()` method is called at application level, the following operations are performed in sequence:
1. The *Store* is created and initialized. 2. Any *Module* specified when calling `h3.init()` is executed. -3. The **$init** message is dispatched. +3. The **$init** event is dispatched. 4. The *preStart* function (if specified when calling `h3.init()`) is executed. 5. The *Router* is initialized and started. -6. All *Components* matching the current route are rendered for the first time. -7. The **$redraw** and **$navigation** messages are dispatched. +6. The **$navigation** event is dispatched. +7. All *Components* matching the current route are rendered for the first time. +8. The **$redraw** event is dispatched. Then, whenever the `h3.redraw()` method is called (typically within a component): 1. The whole application is redrawn, i.e. every *Component* currently rendered on the page is redrawn. -2. The **$redraw** message is dispatched. +2. The **$redraw** event is dispatched. Similarly, whenever the `h3.navigateTo()` method is called (typically within a component), or the URL fragment changes: 1. The *Router* processes the new path and determine which component to render based on the routing configuration. 2. All DOM nodes within the scope of the routing are removed, all components are removed. -3. The *Component* matching the new route is rendered. -4. The **$redraw** and **$navigation** messages are dispatched. +3. The **$navigation** event is dispatched. +4. The *Component* matching the new route is rendered. +5. The **$redraw** event is dispatched. And that's it. The whole idea is to make the system extremely *simple* and *predictable* — which means everything should be very easy to debug, too.
@@ -12,7 +12,7 @@ ### I'm sold! Where can I get it?
Here, look, it's just one file: -<a href="https://raw.githubusercontent.com/h3rald/h3/v0.3.0/h3.js" target="_blank" class="button primary">Download v0.3.0 (Cunning Cardassian)</a> +<a href="https://raw.githubusercontent.com/h3rald/h3/v0.4.0/h3.js" target="_blank" class="button primary">Download v0.4.0 (Dedicated Denobulan)</a> Yes there is also a [NPM package](https://www.npmjs.com/package/@h3rald/h3) if you want to use it with WebPack and similar, but let me repeat: _it's just one file_.
@@ -118,8 +118,8 @@ h3("a.logo.col-sm-1", { href: "#/" }, [
h3("img", { alt: "H3", src: "images/h3.svg" }), ]), h3("div.version.col-sm.col-md", [ - h3("div.version-number", "v0.3.0"), - h3("div.version-label", "“Cunning Cardassian“"), + h3("div.version-number", "v0.4.0"), + h3("div.version-label", "“Dedicated Denobulan“"), ]), h3("label.drawer-toggle.button.col-sm-last", { for: "drawer-control" }), ]);
@@ -1,40 +0,0 @@
-@startuml - -title H3 Sequence Diagram - -entity Application -control Component -entity H3 -entity Router -database Store - -Application --> H3 : <b>h3.init()</b> -H3 -> Store : //initialize// -Store -> Store : //execute modules// -H3 -> Store : dispatch($init) -H3 -> Application : preStart() -H3 -> Router : //initialize// -H3 -> Router : start() -Router -> Component : render() -Router -> Store: dispatch($redraw) -Router -> Store: dispatch($navigation) -H3 -> Application : postStart() - -group redraw - Component -> H3 : <b>h3.redraw()</b> - H3 -> Component : redraw() - H3 -> Store: dispatch($redraw) -end - -group navigation - Component -> H3 : <b>h3.navigateTo()</b> - H3 -> Router : processPath() - Router -> Application : //remove all DOM nodes// - Application -> Component : //remove all DOM nodes// - Router -> Component : render() - Router -> Store: dispatch($redraw) - Router -> Store: dispatch($navigation) -end - - -@enduml
@@ -5,6 +5,18 @@ * This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree. * */ +const checkProperties = (obj1, obj2) => { + for (const key in obj1) { + if (!(key in obj2)) { + return false; + } + if (!equal(obj1[key], obj2[key])) { + return false; + } + } + return true; +}; + const equal = (obj1, obj2) => { if ( (obj1 === null && obj2 === null) ||@@ -44,18 +56,7 @@ }
} return true; } - function checkProperties(obj1, obj2) { - for (const key in obj1) { - if (!(key in obj2)) { - return false; - } - if (!equal(obj1[key], obj2[key])) { - return false; - } - } - return true; - } - return checkProperties(obj1, obj2) && checkProperties(obj2, obj1); + return checkProperties(obj1, obj2); // && checkProperties(obj2, obj1); }; const selectorRegex = /^([a-z0-9:_=-]+)(#[a-z0-9:_=-]+)?(\..+)?$/i;@@ -267,6 +268,10 @@ const a = document.createAttribute(attr);
a.value = this.attributes[attr]; node.setAttributeNode(a); } + // Handle boolean attributes + if (this.attributes[attr] === false) { + node[attr] = false; + } }); // Event Listeners Object.keys(this.eventListeners).forEach((event) => {@@ -364,6 +369,9 @@ }
// Attributes if (!equal(oldvnode.attributes, newvnode.attributes)) { Object.keys(oldvnode.attributes).forEach((a) => { + if (newvnode.attributes[a] === false) { + node[a] = false; + } if (!newvnode.attributes[a]) { node.removeAttribute(a); } else if (@@ -544,8 +552,10 @@ }
setRedraw(vnode) { this.redraw = () => { - const fn = this.routes[this.route.def]; - vnode.redraw({ node: this.element.childNodes[0], vnode: fn() }); + vnode.redraw({ + node: this.element.childNodes[0], + vnode: this.routes[this.route.def](), + }); this.store.dispatch("$redraw"); }; }@@ -586,6 +596,7 @@ }
if (!this.route) { throw new Error(`[Router] No route matches '${fragment}'`); } + this.store.dispatch("$navigation", this.route); // Display View while (this.element.firstChild) { this.element.removeChild(this.element.firstChild);@@ -594,8 +605,7 @@ const vnode = this.routes[this.route.def]();
this.element.appendChild(vnode.render()); this.setRedraw(vnode); window.scrollTo(0, 0); - this.store.dispatch("$redraw", this.route); - this.store.dispatch("$navigation", this.route); + this.store.dispatch("$redraw"); }; processPath(); window.addEventListener("hashchange", processPath);
@@ -1,16 +1,17 @@
{ "name": "@h3rald/h3", - "version": "0.3.0", - "versionName": "Cunning Cardassian", + "version": "0.4.0", + "versionName": "Dedicated Denobulan", "description": "A tiny, extremely minimalist JavaScript microframework.", "main": "h3.js", "scripts": { "test": "jest", - "coverage": "jest --env=jsdom --coverage && cat ./coverage/lcov.info | coveralls", + "coverage": "jest --coverage ", + "coveralls": "npm run coverage && cat ./coverage/lcov.info | coveralls", "copy": "cp h3.js docs/js/h3.js && cp h3.js docs/example/assets/js/h3.js", "release": "node scripts/release.js", "guide": "hastyscribe docs/H3_DeveloperGuide.md", - "update": "npm run release && npm run copy && npm run guide" + "regenerate": "npm run release && npm run copy && npm run guide" }, "repository": { "type": "git",