diff --git a/fastn-js/ftd.html b/fastn-js/ftd.html deleted file mode 100644 index 5d79577136..0000000000 --- a/fastn-js/ftd.html +++ /dev/null @@ -1,26 +0,0 @@ - -
- - - - - - - -__html_body__ - - - - - diff --git a/fastn-js/js/dom.js b/fastn-js/js/dom.js index cae4c94ce1..f6b15f5169 100644 --- a/fastn-js/js/dom.js +++ b/fastn-js/js/dom.js @@ -812,7 +812,7 @@ class Node2 { return this.#parent; } removeAllFaviconLinks() { - if (hydrating || rerender) { + if (doubleBuffering) { const links = document.head.querySelectorAll('link[rel="shortcut icon"]'); links.forEach( link => { link.parentNode.removeChild(link); @@ -821,7 +821,7 @@ class Node2 { } setFavicon(url) { - if (hydrating || rerender) { + if (doubleBuffering) { if (url instanceof fastn.recordInstanceClass) url = url.get('src'); while (true) { if (url instanceof fastn.mutableClass) url = url.get(); @@ -889,7 +889,7 @@ class Node2 { } updatePositionForNodeById(node_id, value) { if (!ssr) { - const target_node = document.querySelector(`[id="${node_id}"]`); + const target_node = fastnVirtual.root.querySelector(`[id="${node_id}"]`); if (!fastn_utils.isNull(target_node)) target_node.style['position'] = value; } @@ -908,7 +908,7 @@ class Node2 { } } updateMetaTitle(value) { - if (!ssr && (hydrating || rerender)) { + if (!ssr && doubleBuffering) { if (!fastn_utils.isNull(value)) window.document.title = value; } } @@ -917,7 +917,7 @@ class Node2 { this.removeMetaTagByName(name); return; } - if (!ssr && (hydrating || rerender)) { + if (!ssr && doubleBuffering) { const metaTag = window.document.createElement('meta'); metaTag.setAttribute('name', name); metaTag.setAttribute('content', value); @@ -929,7 +929,7 @@ class Node2 { this.removeMetaTagByProperty(property); return; } - if (!ssr && (hydrating || rerender)) { + if (!ssr && doubleBuffering) { const metaTag = window.document.createElement('meta'); metaTag.setAttribute('property', property); metaTag.setAttribute('content', value); @@ -937,7 +937,7 @@ class Node2 { } } removeMetaTagByName(name) { - if (!ssr && (hydrating || rerender)) { + if (!ssr && doubleBuffering) { const metaTags = document.getElementsByTagName('meta'); for (let i = 0; i < metaTags.length; i++) { const metaTag = metaTags[i]; @@ -949,7 +949,7 @@ class Node2 { } } removeMetaTagByProperty(property) { - if (!ssr && (hydrating || rerender)) { + if (!ssr && doubleBuffering) { const metaTags = document.getElementsByTagName('meta'); for (let i = 0; i < metaTags.length; i++) { const metaTag = metaTags[i]; @@ -978,7 +978,7 @@ class Node2 { const obj = { property, value }; if (value === undefined) { - if (!ssr && !hydrating) { + if (!ssr) { for (const className of this.#node.classList.values()) { if (className.startsWith(`${propertyShort}-`)) { this.#node.classList.remove(className); @@ -989,7 +989,7 @@ class Node2 { return cls; } - if (!ssr && !hydrating) { + if (!ssr) { if (!!className) { if (!fastn_dom.classes[cssClass]) { fastn_dom.classes[cssClass] = fastn_dom.classes[cssClass] || obj; @@ -1328,7 +1328,7 @@ class Node2 { } } attachExternalCss(css) { - if (hydrating || rerender) { + if (doubleBuffering) { let css_tag = document.createElement('link'); css_tag.rel = 'stylesheet'; css_tag.type = 'text/css'; @@ -1342,7 +1342,7 @@ class Node2 { } } attachExternalJs(js) { - if (hydrating || rerender) { + if (doubleBuffering) { let js_tag = document.createElement('script'); js_tag.src = js; @@ -2161,14 +2161,12 @@ class Node2 { this.#rawInnerValue = staticValue; } else if (kind === fastn_dom.PropertyKind.StringValue) { this.#rawInnerValue = staticValue; - if (!hydrating || this.#node.innerHTML === "undefined") { - staticValue = fastn_utils.markdown_inline(fastn_utils.escapeHtmlInMarkdown(staticValue)); - } else { - staticValue = this.#node.innerHTML; - } + staticValue = fastn_utils.markdown_inline(fastn_utils.escapeHtmlInMarkdown(staticValue)); staticValue = fastn_utils.process_post_markdown(this.#node, staticValue); if(!fastn_utils.isNull(staticValue)) { this.#node.innerHTML = staticValue; + } else { + this.#node.innerHTML = ""; } } else { throw ("invalid fastn_dom.PropertyKind: " + kind); diff --git a/fastn-js/js/ftd.js b/fastn-js/js/ftd.js index 2fefbab34f..63bea9a431 100644 --- a/fastn-js/js/ftd.js +++ b/fastn-js/js/ftd.js @@ -236,7 +236,7 @@ const ftd = (function() { }, get(key) { key = this._get_key(key); - if(ssr && !hydrating) { + if(ssr) { return; } const item = localStorage.getItem(key); diff --git a/fastn-js/js/utils.js b/fastn-js/js/utils.js index a205e63eaa..eea18da5b1 100644 --- a/fastn-js/js/utils.js +++ b/fastn-js/js/utils.js @@ -46,7 +46,7 @@ let fastn_utils = { return [node, css, attributes]; }, createStyle(cssClass, obj) { - if (rerender) { + if (doubleBuffering) { fastn_dom.styleClasses = `${fastn_dom.styleClasses}${getClassAsString(cssClass, obj)}\n`; } else { let styles = document.getElementById('styles'); @@ -336,7 +336,7 @@ let fastn_utils = { }, createNodeHelper(node, classes, attributes) { let tagName = node; - let element = fastn_virtual.document.createElement(node); + let element = fastnVirtual.document.createElement(node); for (let key in attributes) { element.setAttribute(key, attributes[key]) } @@ -525,6 +525,24 @@ let fastn_utils = { } return args; }, + + /** + * Replaces the children of `document.body` with the children from + * newChildrenWrapper and updates the styles based on the + * `fastn_dom.styleClasses`. + * + * @param {HTMLElement} newChildrenWrapper - The wrapper element + * containing the new children. + */ + replaceBodyStyleAndChildren(newChildrenWrapper) { + // Update styles based on `fastn_dom.styleClasses` + let styles = document.getElementById("styles"); + styles.innerHTML = fastn_dom.styleClasses; + + // Replace the children of document.body with the children from + // newChildrenWrapper + fastn_utils.private.replaceChildren(document.body, newChildrenWrapper); + }, } @@ -640,6 +658,33 @@ fastn_utils.private = { } return text; }, + + /** + * Replaces the children of a parent element with the children from a + * new children wrapper. + * + * @param {HTMLElement} parent - The parent element whose children will + * be replaced. + * @param {HTMLElement} newChildrenWrapper - The wrapper element + * containing the new children. + * @returns {void} + */ + replaceChildren(parent, newChildrenWrapper) { + // Remove existing children of the parent + var children = parent.children; + // Loop through the direct children and remove those with tagName 'div' + for (var i = children.length - 1; i >= 0; i--) { + var child = children[i]; + if (child.tagName === 'DIV') { + parent.removeChild(child); + } + } + + // Cut and append the children from newChildrenWrapper to the parent + while (newChildrenWrapper.firstChild) { + parent.appendChild(newChildrenWrapper.firstChild); + } + } } diff --git a/fastn-js/js/virtual.js b/fastn-js/js/virtual.js index 8ff4bb3b6b..8c4c29d2e7 100644 --- a/fastn-js/js/virtual.js +++ b/fastn-js/js/virtual.js @@ -1,9 +1,8 @@ -let fastn_virtual = {} +let fastnVirtual = {} let id_counter = 0; -let hydrating = false; let ssr = false; -let rerender = false; +let doubleBuffering = false; class ClassList { #classes = []; @@ -124,28 +123,14 @@ class Document2 { if (fastn_utils.isWrapperNode(tagName)) { return window.document.createComment(fastn_dom.commentMessage); } - if (hydrating) { - let node = this.getElementByDataID(id_counter); - if (fastn_utils.isCommentNode(tagName)) { - let comment= window.document.createComment(fastn_dom.commentMessage); - node.parentNode.replaceChild(comment, node); - return comment; - } - return node; - } else { - if (fastn_utils.isCommentNode(tagName)) { - return window.document.createComment(fastn_dom.commentMessage); - } - return window.document.createElement(tagName); + if (fastn_utils.isCommentNode(tagName)) { + return window.document.createComment(fastn_dom.commentMessage); } - } - - getElementByDataID(id) { - return window.document.querySelector(`[data-id=\"${id}\"]`); + return window.document.createElement(tagName); } } -fastn_virtual.document = new Document2(); +fastnVirtual.document = new Document2(); function addClosureToBreakpointWidth() { let closure = fastn.closureWithoutExecute(function() { @@ -161,39 +146,22 @@ function addClosureToBreakpointWidth() { ftd.breakpoint_width.addClosure(closure); } -fastn_virtual.hydrate = function(main) { +fastnVirtual.doubleBuffer = function(main) { addClosureToBreakpointWidth(); + let parent = document.createElement("div"); let current_device = ftd.get_device(); - let found_device = ftd.device.get(); - if (current_device !== found_device) { - rerender = true - ftd.device = fastn.mutable(current_device); - let styles = document.getElementById("styles"); - styles.innerText = ""; - var children = document.body.children; - // Loop through the direct children and remove those with tagName 'div' - for (var i = children.length - 1; i >= 0; i--) { - var child = children[i]; - if (child.tagName === 'DIV') { - document.body.removeChild(child); - } - } - - main(document.body); - rerender = false; - styles.innerHTML = fastn_dom.styleClasses; - return; - } - hydrating = true; - let body = fastn_virtual.document.createElement("body"); - main(body); - id_counter = 0; - hydrating = false; + ftd.device = fastn.mutable(current_device); + doubleBuffering = true; + fastnVirtual.root = parent; + main(parent); + fastn_utils.replaceBodyStyleAndChildren(parent) + doubleBuffering = false; + fastnVirtual.root = document.body; } -fastn_virtual.ssr = function(main) { +fastnVirtual.ssr = function(main) { ssr = true; - let body = fastn_virtual.document.createElement("body"); + let body = fastnVirtual.document.createElement("body"); main(body) ssr = false; id_counter = 0; diff --git a/fastn-js/src/main.rs b/fastn-js/src/main.rs index 42bcf38d16..771b71d21f 100644 --- a/fastn-js/src/main.rs +++ b/fastn-js/src/main.rs @@ -17,7 +17,7 @@ fn js() -> &'static str { i.done(); } - fastn_virtual.ssr(main) + fastnVirtual.ssr(main) "# } diff --git a/fastn-js/src/ssr.rs b/fastn-js/src/ssr.rs index f4d6cba9e4..e34b39c90b 100644 --- a/fastn-js/src/ssr.rs +++ b/fastn-js/src/ssr.rs @@ -30,7 +30,7 @@ pub fn ssr(ast: &[fastn_js::Ast]) -> String { parenti0.setProperty(fastn_dom.PropertyKind.Height, fastn_dom.Resizing.FillContainer, inherited); main(parenti0); }}; - fastn_virtual.ssr(main_wrapper);", fastn_js::to_js(ast, + fastnVirtual.ssr(main_wrapper);", fastn_js::to_js(ast, "foo")); ssr_str(&js) } @@ -44,7 +44,7 @@ pub fn ssr_with_js_string(package_name: &str, js: &str) -> String { parenti0.setProperty(fastn_dom.PropertyKind.Height, fastn_dom.Resizing.FillContainer, inherited); main(parenti0); }}; - fastn_virtual.ssr(main_wrapper);", package_name, js); + fastnVirtual.ssr(main_wrapper);", package_name, js); ssr_str(&js) } diff --git a/fastn-js/tests/01-basic.html b/fastn-js/tests/01-basic.html index 92db3f1d5e..c33aa2945e 100644 --- a/fastn-js/tests/01-basic.html +++ b/fastn-js/tests/01-basic.html @@ -16,9 +16,9 @@ i.setProperty(fastn_dom.PropertyKind.IntegerValue, index$num); } - main(fastn_virtual.document.createElement("body")) + main(fastnVirtual.document.createElement("body")) - // document.body.innerHTML = fastn_virtual.ssr(main); + // document.body.innerHTML = fastnVirtual.ssr(main); // fastn_virtual.hydrate(main); })(); diff --git a/fastn-js/tests/02-basic-event.html b/fastn-js/tests/02-basic-event.html index 48c385fab0..33167d7406 100644 --- a/fastn-js/tests/02-basic-event.html +++ b/fastn-js/tests/02-basic-event.html @@ -19,7 +19,7 @@ }); } - main(fastn_virtual.document.createElement("body")) + main(fastnVirtual.document.createElement("body")) })();