diff --git a/shared/static/scripts/sx-browser.js b/shared/static/scripts/sx-browser.js index fa94c44..02e2c52 100644 --- a/shared/static/scripts/sx-browser.js +++ b/shared/static/scripts/sx-browser.js @@ -14,7 +14,7 @@ // ========================================================================= var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } }); - var SX_VERSION = "2026-03-10T10:47:20Z"; + var SX_VERSION = "2026-03-10T14:05:08Z"; function isNil(x) { return x === NIL || x === null || x === undefined; } function isSxTruthy(x) { return x !== false && !isNil(x); } @@ -629,7 +629,7 @@ var callLambda = function(f, args, callerEnv) { return (function() { var params = lambdaParams(f); var local = envMerge(lambdaClosure(f), callerEnv); - return (isSxTruthy((len(args) != len(params))) ? error((String(sxOr(lambdaName(f), "lambda")) + String(" expects ") + String(len(params)) + String(" args, got ") + String(len(args)))) : (forEach(function(pair) { return envSet(local, first(pair), nth(pair, 1)); }, zip(params, args)), makeThunk(lambdaBody(f), local))); + return (isSxTruthy((len(args) > len(params))) ? error((String(sxOr(lambdaName(f), "lambda")) + String(" expects ") + String(len(params)) + String(" args, got ") + String(len(args)))) : (forEach(function(pair) { return envSet(local, first(pair), nth(pair, 1)); }, zip(params, args)), forEach(function(p) { return envSet(local, p, NIL); }, slice(params, len(args))), makeThunk(lambdaBody(f), local))); })(); }; // call-component @@ -1336,7 +1336,7 @@ continue; } else { return NIL; } } }; return (function() { var bodyHtml = renderToHtml(componentBody(island), local); var stateJson = serializeIslandState(kwargs); - return (String("
") + String(bodyHtml) + String("
")); + return (String("") + String(bodyHtml) + String("")); })(); })(); })(); }; @@ -1761,7 +1761,7 @@ return result; }, args); })(); } return (function() { - var container = domCreateElement("div", NIL); + var container = domCreateElement("span", NIL); var disposers = []; domSetAttr(container, "data-sx-island", islandName); return (function() { @@ -2053,7 +2053,7 @@ return (function() { })(); }; // morph-node - var morphNode = function(oldNode, newNode) { return (isSxTruthy(sxOr(domHasAttr(oldNode, "sx-preserve"), domHasAttr(oldNode, "sx-ignore"))) ? NIL : (isSxTruthy(sxOr(!isSxTruthy((domNodeType(oldNode) == domNodeType(newNode))), !isSxTruthy((domNodeName(oldNode) == domNodeName(newNode))))) ? domReplaceChild(domParent(oldNode), domClone(newNode), oldNode) : (isSxTruthy(sxOr((domNodeType(oldNode) == 3), (domNodeType(oldNode) == 8))) ? (isSxTruthy(!isSxTruthy((domTextContent(oldNode) == domTextContent(newNode)))) ? domSetTextContent(oldNode, domTextContent(newNode)) : NIL) : (isSxTruthy((domNodeType(oldNode) == 1)) ? (syncAttrs(oldNode, newNode), (isSxTruthy(!isSxTruthy((isSxTruthy(domIsActiveElement(oldNode)) && domIsInputElement(oldNode)))) ? morphChildren(oldNode, newNode) : NIL)) : NIL)))); }; + var morphNode = function(oldNode, newNode) { return (isSxTruthy(sxOr(domHasAttr(oldNode, "sx-preserve"), domHasAttr(oldNode, "sx-ignore"))) ? NIL : (isSxTruthy((isSxTruthy(domHasAttr(oldNode, "data-sx-island")) && isSxTruthy(isProcessed(oldNode, "island-hydrated")) && isSxTruthy(domHasAttr(newNode, "data-sx-island")) && (domGetAttr(oldNode, "data-sx-island") == domGetAttr(newNode, "data-sx-island")))) ? NIL : (isSxTruthy(sxOr(!isSxTruthy((domNodeType(oldNode) == domNodeType(newNode))), !isSxTruthy((domNodeName(oldNode) == domNodeName(newNode))))) ? domReplaceChild(domParent(oldNode), domClone(newNode), oldNode) : (isSxTruthy(sxOr((domNodeType(oldNode) == 3), (domNodeType(oldNode) == 8))) ? (isSxTruthy(!isSxTruthy((domTextContent(oldNode) == domTextContent(newNode)))) ? domSetTextContent(oldNode, domTextContent(newNode)) : NIL) : (isSxTruthy((domNodeType(oldNode) == 1)) ? (syncAttrs(oldNode, newNode), (isSxTruthy(!isSxTruthy((isSxTruthy(domIsActiveElement(oldNode)) && domIsInputElement(oldNode)))) ? morphChildren(oldNode, newNode) : NIL)) : NIL))))); }; // sync-attrs var syncAttrs = function(oldEl, newEl) { { var _c = domAttrList(newEl); for (var _i = 0; _i < _c.length; _i++) { var attr = _c[_i]; (function() { @@ -2982,7 +2982,10 @@ callExpr.push(dictGet(kwargs, k)); } } // dispose-islands-in var disposeIslandsIn = function(root) { return (isSxTruthy(root) ? (function() { var islands = domQueryAll(root, "[data-sx-island]"); - return (isSxTruthy((isSxTruthy(islands) && !isSxTruthy(isEmpty(islands)))) ? (logInfo((String("disposing ") + String(len(islands)) + String(" island(s)"))), forEach(disposeIsland, islands)) : NIL); + return (isSxTruthy((isSxTruthy(islands) && !isSxTruthy(isEmpty(islands)))) ? (function() { + var toDispose = filter(function(el) { return !isSxTruthy(isProcessed(el, "island-hydrated")); }, islands); + return (isSxTruthy(!isSxTruthy(isEmpty(toDispose))) ? (logInfo((String("disposing ") + String(len(toDispose)) + String(" island(s)"))), forEach(disposeIsland, toDispose)) : NIL); +})() : NIL); })() : NIL); }; // boot-init @@ -3396,8 +3399,9 @@ return (isSxTruthy((_batchDepth == 0)) ? (function() { if (!_hasDom || !el) return function() {}; // Wrap SX lambdas from runtime-evaluated island code into native fns var wrapped = isLambda(handler) - ? function(e) { invoke(handler, e); } + ? function(e) { try { invoke(handler, e); } catch(err) { console.error("[sx-ref] domListen handler error:", name, err); } } : handler; + if (name === "click") logInfo("domListen: click on <" + (el.tagName||"?").toLowerCase() + "> text=" + (el.textContent||"").substring(0,20) + " isLambda=" + isLambda(handler)); el.addEventListener(name, wrapped); return function() { el.removeEventListener(name, wrapped); }; } diff --git a/shared/sx/html.py b/shared/sx/html.py index 8488dc5..381ea16 100644 --- a/shared/sx/html.py +++ b/shared/sx/html.py @@ -414,7 +414,7 @@ def _render_component(comp: Component, args: list, env: dict[str, Any]) -> str: def _render_island(island: Island, args: list, env: dict[str, Any]) -> str: """Render an island as static HTML with hydration attributes. - Produces:
body HTML
+ Produces: body HTML The client hydrates this into a reactive island. """ import json as _json @@ -460,12 +460,12 @@ def _render_island(island: Island, args: list, env: dict[str, Any]) -> str: state_json = _escape_attr(_json.dumps(state, separators=(",", ":"))) if state else "" island_name = _escape_attr(island.name) - parts = [f'
") parts.append(body_html) - parts.append("
") + parts.append("") return "".join(parts) diff --git a/shared/sx/jinja_bridge.py b/shared/sx/jinja_bridge.py index a9e76ac..9da4a6c 100644 --- a/shared/sx/jinja_bridge.py +++ b/shared/sx/jinja_bridge.py @@ -46,6 +46,11 @@ _COMPONENT_ENV: dict[str, Any] = {} # client-side localStorage caching. _COMPONENT_HASH: str = "" +# Raw source of .sx files marked with ;; @client — sent to the browser +# alongside component definitions so define forms (functions, data) are +# available for client-side evaluation (e.g. cssx colour/spacing functions). +_CLIENT_LIBRARY_SOURCES: list[str] = [] + def get_component_env() -> dict[str, Any]: """Return the shared component environment.""" @@ -61,7 +66,7 @@ def _compute_component_hash() -> None: """Recompute _COMPONENT_HASH from all registered Component and Macro definitions.""" global _COMPONENT_HASH from .parser import serialize - parts = [] + parts = list(_CLIENT_LIBRARY_SOURCES) for key in sorted(_COMPONENT_ENV): val = _COMPONENT_ENV[key] if isinstance(val, Island): @@ -96,6 +101,8 @@ def load_sx_dir(directory: str) -> None: """Load all .sx files from a directory and register components. Skips boundary.sx — those are parsed separately by the boundary validator. + Files starting with ``;; @client`` have their source stored for delivery + to the browser (so ``define`` forms are available client-side). """ for filepath in sorted( glob.glob(os.path.join(directory, "**", "*.sx"), recursive=True) @@ -103,7 +110,17 @@ def load_sx_dir(directory: str) -> None: if os.path.basename(filepath) == "boundary.sx": continue with open(filepath, encoding="utf-8") as f: - register_components(f.read()) + source = f.read() + if source.lstrip().startswith(";; @client"): + # Parse and re-serialize to normalize syntax sugar. + # The Python parser accepts ' for quote but the bootstrapped + # client parser uses #' — re-serializing emits (quote x). + from .parser import parse_all, serialize + exprs = parse_all(source) + _CLIENT_LIBRARY_SOURCES.append( + "\n".join(serialize(e) for e in exprs) + ) + register_components(source) # --------------------------------------------------------------------------- @@ -150,6 +167,7 @@ def reload_if_changed() -> None: _logger.info("Changed: %s", fp) t0 = time.monotonic() _COMPONENT_ENV.clear() + _CLIENT_LIBRARY_SOURCES.clear() # Reload SX libraries first (e.g. z3.sx) so reader macros resolve for cb in _reload_callbacks: cb() @@ -368,9 +386,10 @@ def client_components_tag(*names: str) -> str: params_sx = "(" + " ".join(param_strs) + ")" body_sx = serialize(val.body, pretty=True) parts.append(f"(defmacro {val.name} {params_sx} {body_sx})") - if not parts: + if not parts and not _CLIENT_LIBRARY_SOURCES: return "" - source = "\n".join(parts) + all_parts = list(_CLIENT_LIBRARY_SOURCES) + parts + source = "\n".join(all_parts) return f'' @@ -437,10 +456,12 @@ def components_for_page(page_sx: str, service: str | None = None) -> tuple[str, body_sx = serialize(val.body, pretty=True) parts.append(f"(defmacro {val.name} {params_sx} {body_sx})") - if not parts: + if not parts and not _CLIENT_LIBRARY_SOURCES: return "", "" - source = "\n".join(parts) + # Prepend client library sources (define forms) before component defs + all_parts = list(_CLIENT_LIBRARY_SOURCES) + parts + source = "\n".join(all_parts) digest = hashlib.sha256(source.encode()).hexdigest()[:12] return source, digest diff --git a/shared/sx/ref/adapter-dom.sx b/shared/sx/ref/adapter-dom.sx index 92855fe..3ad1acb 100644 --- a/shared/sx/ref/adapter-dom.sx +++ b/shared/sx/ref/adapter-dom.sx @@ -630,7 +630,7 @@ (env-set! local "children" child-frag))) ;; Create the island container element - (let ((container (dom-create-element "div" nil)) + (let ((container (dom-create-element "span" nil)) (disposers (list))) ;; Mark as island diff --git a/shared/sx/ref/adapter-html.sx b/shared/sx/ref/adapter-html.sx index cfe34a7..e17224a 100644 --- a/shared/sx/ref/adapter-html.sx +++ b/shared/sx/ref/adapter-html.sx @@ -349,13 +349,13 @@ (let ((body-html (render-to-html (component-body island) local)) (state-json (serialize-island-state kwargs))) ;; Wrap in container with hydration attributes - (str "
" body-html - "
")))))) + "")))))) ;; -------------------------------------------------------------------------- diff --git a/shared/sx/ref/boot.sx b/shared/sx/ref/boot.sx index bcf4e3d..afb9d55 100644 --- a/shared/sx/ref/boot.sx +++ b/shared/sx/ref/boot.sx @@ -400,12 +400,19 @@ (define dispose-islands-in (fn (root) - ;; Dispose all islands within root before a swap replaces them. + ;; Dispose islands within root, but SKIP hydrated islands — + ;; they may be preserved across morphs. Only dispose islands + ;; that are not currently hydrated (e.g. freshly parsed content + ;; being discarded) or that have been explicitly detached. (when root (let ((islands (dom-query-all root "[data-sx-island]"))) (when (and islands (not (empty? islands))) - (log-info (str "disposing " (len islands) " island(s)")) - (for-each dispose-island islands)))))) + (let ((to-dispose (filter + (fn (el) (not (is-processed? el "island-hydrated"))) + islands))) + (when (not (empty? to-dispose)) + (log-info (str "disposing " (len to-dispose) " island(s)")) + (for-each dispose-island to-dispose)))))))) ;; -------------------------------------------------------------------------- diff --git a/shared/sx/ref/bootstrap_js.py b/shared/sx/ref/bootstrap_js.py index 2328b33..e2aa348 100644 --- a/shared/sx/ref/bootstrap_js.py +++ b/shared/sx/ref/bootstrap_js.py @@ -3,14 +3,14 @@ Bootstrap compiler: reference SX evaluator → JavaScript. Reads the .sx reference specification and emits a standalone JavaScript -evaluator (sx-ref.js) that can be compared against the hand-written sx.js. +evaluator (sx-browser.js) that runs in the browser. The compiler translates the restricted SX subset used in eval.sx/render.sx into idiomatic JavaScript. Platform interface functions are emitted as native JS implementations. Usage: - python bootstrap_js.py > sx-ref.js + python bootstrap_js.py """ from __future__ import annotations @@ -2911,8 +2911,9 @@ PLATFORM_DOM_JS = """ if (!_hasDom || !el) return function() {}; // Wrap SX lambdas from runtime-evaluated island code into native fns var wrapped = isLambda(handler) - ? function(e) { invoke(handler, e); } + ? function(e) { try { invoke(handler, e); } catch(err) { console.error("[sx-ref] domListen handler error:", name, err); } } : handler; + if (name === "click") logInfo("domListen: click on <" + (el.tagName||"?").toLowerCase() + "> text=" + (el.textContent||"").substring(0,20) + " isLambda=" + isLambda(handler)); el.addEventListener(name, wrapped); return function() { el.removeEventListener(name, wrapped); }; } @@ -4365,8 +4366,9 @@ if __name__ == "__main__": help="Comma-separated extensions (continuations). Default: none.") p.add_argument("--spec-modules", help="Comma-separated spec modules (deps). Default: none.") - p.add_argument("--output", "-o", - help="Output file (default: stdout)") + default_output = os.path.join(os.path.dirname(__file__), "..", "..", "static", "scripts", "sx-browser.js") + p.add_argument("--output", "-o", default=default_output, + help="Output file (default: shared/static/scripts/sx-browser.js)") args = p.parse_args() adapters = args.adapters.split(",") if args.adapters else None @@ -4375,13 +4377,10 @@ if __name__ == "__main__": spec_modules = args.spec_modules.split(",") if args.spec_modules else None js = compile_ref_to_js(adapters, modules, extensions, spec_modules) - if args.output: - with open(args.output, "w") as f: - f.write(js) - included = ", ".join(adapters) if adapters else "all" - mods = ", ".join(modules) if modules else "all" - ext_label = ", ".join(extensions) if extensions else "none" - print(f"Wrote {args.output} ({len(js)} bytes, adapters: {included}, modules: {mods}, extensions: {ext_label})", - file=sys.stderr) - else: - print(js) + with open(args.output, "w") as f: + f.write(js) + included = ", ".join(adapters) if adapters else "all" + mods = ", ".join(modules) if modules else "all" + ext_label = ", ".join(extensions) if extensions else "none" + print(f"Wrote {args.output} ({len(js)} bytes, adapters: {included}, modules: {mods}, extensions: {ext_label})", + file=sys.stderr) diff --git a/shared/sx/ref/engine.sx b/shared/sx/ref/engine.sx index e3e42a7..8d01c6c 100644 --- a/shared/sx/ref/engine.sx +++ b/shared/sx/ref/engine.sx @@ -338,6 +338,16 @@ (dom-has-attr? old-node "sx-ignore")) nil + ;; Hydrated island → preserve reactive state across server swaps. + ;; If old and new are the same island (by name), keep the old DOM + ;; with its live signals, effects, and event listeners intact. + (and (dom-has-attr? old-node "data-sx-island") + (is-processed? old-node "island-hydrated") + (dom-has-attr? new-node "data-sx-island") + (= (dom-get-attr old-node "data-sx-island") + (dom-get-attr new-node "data-sx-island"))) + nil + ;; Different node type or tag → replace wholesale (or (not (= (dom-node-type old-node) (dom-node-type new-node))) (not (= (dom-node-name old-node) (dom-node-name new-node)))) diff --git a/shared/sx/ref/sx-ref.js b/shared/sx/ref/sx-ref.js deleted file mode 100644 index fa94c44..0000000 --- a/shared/sx/ref/sx-ref.js +++ /dev/null @@ -1,5382 +0,0 @@ -/** - * sx-ref.js — Generated from reference SX evaluator specification. - * - * Bootstrap-compiled from shared/sx/ref/{eval,render,primitives}.sx - * Compare against hand-written sx.js for correctness verification. - * - * DO NOT EDIT — regenerate with: python bootstrap_js.py - */ -;(function(global) { - "use strict"; - - // ========================================================================= - // Types - // ========================================================================= - - var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } }); - var SX_VERSION = "2026-03-10T10:47:20Z"; - - function isNil(x) { return x === NIL || x === null || x === undefined; } - function isSxTruthy(x) { return x !== false && !isNil(x); } - - function Symbol(name) { this.name = name; } - Symbol.prototype.toString = function() { return this.name; }; - Symbol.prototype._sym = true; - - function Keyword(name) { this.name = name; } - Keyword.prototype.toString = function() { return ":" + this.name; }; - Keyword.prototype._kw = true; - - function Lambda(params, body, closure, name) { - this.params = params; - this.body = body; - this.closure = closure || {}; - this.name = name || null; - } - Lambda.prototype._lambda = true; - - function Component(name, params, hasChildren, body, closure, affinity) { - this.name = name; - this.params = params; - this.hasChildren = hasChildren; - this.body = body; - this.closure = closure || {}; - this.affinity = affinity || "auto"; - } - Component.prototype._component = true; - - function Island(name, params, hasChildren, body, closure) { - this.name = name; - this.params = params; - this.hasChildren = hasChildren; - this.body = body; - this.closure = closure || {}; - } - Island.prototype._island = true; - - function SxSignal(value) { - this.value = value; - this.subscribers = []; - this.deps = []; - } - SxSignal.prototype._signal = true; - - function TrackingCtx(notifyFn) { - this.notifyFn = notifyFn; - this.deps = []; - } - - var _trackingContext = null; - - function Macro(params, restParam, body, closure, name) { - this.params = params; - this.restParam = restParam; - this.body = body; - this.closure = closure || {}; - this.name = name || null; - } - Macro.prototype._macro = true; - - function Thunk(expr, env) { this.expr = expr; this.env = env; } - Thunk.prototype._thunk = true; - - function RawHTML(html) { this.html = html; } - RawHTML.prototype._raw = true; - - function isSym(x) { return x != null && x._sym === true; } - function isKw(x) { return x != null && x._kw === true; } - - function merge() { - var out = {}; - for (var i = 0; i < arguments.length; i++) { - var d = arguments[i]; - if (d) for (var k in d) out[k] = d[k]; - } - return out; - } - - function sxOr() { - for (var i = 0; i < arguments.length; i++) { - if (isSxTruthy(arguments[i])) return arguments[i]; - } - return arguments.length ? arguments[arguments.length - 1] : false; - } - - // ========================================================================= - // Platform interface — JS implementation - // ========================================================================= - - function typeOf(x) { - if (isNil(x)) return "nil"; - if (typeof x === "number") return "number"; - if (typeof x === "string") return "string"; - if (typeof x === "boolean") return "boolean"; - if (x._sym) return "symbol"; - if (x._kw) return "keyword"; - if (x._thunk) return "thunk"; - if (x._lambda) return "lambda"; - if (x._component) return "component"; - if (x._island) return "island"; - if (x._signal) return "signal"; - if (x._macro) return "macro"; - if (x._raw) return "raw-html"; - if (typeof Node !== "undefined" && x instanceof Node) return "dom-node"; - if (Array.isArray(x)) return "list"; - if (typeof x === "object") return "dict"; - return "unknown"; - } - - function symbolName(s) { return s.name; } - function keywordName(k) { return k.name; } - function makeSymbol(n) { return new Symbol(n); } - function makeKeyword(n) { return new Keyword(n); } - - function makeLambda(params, body, env) { return new Lambda(params, body, merge(env)); } - function makeComponent(name, params, hasChildren, body, env, affinity) { - return new Component(name, params, hasChildren, body, merge(env), affinity); - } - function makeMacro(params, restParam, body, env, name) { - return new Macro(params, restParam, body, merge(env), name); - } - function makeThunk(expr, env) { return new Thunk(expr, env); } - - function lambdaParams(f) { return f.params; } - function lambdaBody(f) { return f.body; } - function lambdaClosure(f) { return f.closure; } - function lambdaName(f) { return f.name; } - function setLambdaName(f, n) { f.name = n; } - - function componentParams(c) { return c.params; } - function componentBody(c) { return c.body; } - function componentClosure(c) { return c.closure; } - function componentHasChildren(c) { return c.hasChildren; } - function componentName(c) { return c.name; } - function componentAffinity(c) { return c.affinity || "auto"; } - - function macroParams(m) { return m.params; } - function macroRestParam(m) { return m.restParam; } - function macroBody(m) { return m.body; } - function macroClosure(m) { return m.closure; } - - function isThunk(x) { return x != null && x._thunk === true; } - function thunkExpr(t) { return t.expr; } - function thunkEnv(t) { return t.env; } - - function isCallable(x) { return typeof x === "function" || (x != null && x._lambda === true); } - function isLambda(x) { return x != null && x._lambda === true; } - function isComponent(x) { return x != null && x._component === true; } - function isIsland(x) { return x != null && x._island === true; } - function isMacro(x) { return x != null && x._macro === true; } - function isIdentical(a, b) { return a === b; } - - // Island platform - function makeIsland(name, params, hasChildren, body, env) { - return new Island(name, params, hasChildren, body, merge(env)); - } - - // Signal platform - function makeSignal(value) { return new SxSignal(value); } - function isSignal(x) { return x != null && x._signal === true; } - function signalValue(s) { return s.value; } - function signalSetValue(s, v) { s.value = v; } - function signalSubscribers(s) { return s.subscribers.slice(); } - function signalAddSub(s, fn) { if (s.subscribers.indexOf(fn) < 0) s.subscribers.push(fn); } - function signalRemoveSub(s, fn) { var i = s.subscribers.indexOf(fn); if (i >= 0) s.subscribers.splice(i, 1); } - function signalDeps(s) { return s.deps.slice(); } - function signalSetDeps(s, deps) { s.deps = Array.isArray(deps) ? deps.slice() : []; } - function setTrackingContext(ctx) { _trackingContext = ctx; } - function getTrackingContext() { return _trackingContext || NIL; } - function makeTrackingContext(notifyFn) { return new TrackingCtx(notifyFn); } - function trackingContextDeps(ctx) { return ctx ? ctx.deps : []; } - function trackingContextAddDep(ctx, s) { if (ctx && ctx.deps.indexOf(s) < 0) ctx.deps.push(s); } - function trackingContextNotifyFn(ctx) { return ctx ? ctx.notifyFn : NIL; } - - // invoke — call any callable (native fn or SX lambda) with args. - // Transpiled code emits direct calls f(args) which fail on SX lambdas - // from runtime-evaluated island bodies. invoke dispatches correctly. - function invoke() { - var f = arguments[0]; - var args = Array.prototype.slice.call(arguments, 1); - if (isLambda(f)) return trampoline(callLambda(f, args, lambdaClosure(f))); - if (typeof f === 'function') return f.apply(null, args); - return NIL; - } - - // JSON / dict helpers for island state serialization - function jsonSerialize(obj) { - try { return JSON.stringify(obj); } catch(e) { return "{}"; } - } - function isEmptyDict(d) { - if (!d || typeof d !== "object") return true; - for (var k in d) if (d.hasOwnProperty(k)) return false; - return true; - } - - function envHas(env, name) { return name in env; } - function envGet(env, name) { return env[name]; } - function envSet(env, name, val) { env[name] = val; } - function envExtend(env) { return Object.create(env); } - function envMerge(base, overlay) { - var child = Object.create(base); - if (overlay) for (var k in overlay) if (overlay.hasOwnProperty(k)) child[k] = overlay[k]; - return child; - } - - function dictSet(d, k, v) { d[k] = v; return v; } - function dictGet(d, k) { var v = d[k]; return v !== undefined ? v : NIL; } - - // Render-expression detection — lets the evaluator delegate to the active adapter. - // Matches HTML tags, SVG tags, <>, raw!, ~components, html: prefix, custom elements. - // Placeholder — overridden by transpiled version from render.sx - function isRenderExpr(expr) { return false; } - - // Render dispatch — call the active adapter's render function. - // Set by each adapter when loaded; defaults to identity (no rendering). - var _renderExprFn = null; - function renderExpr(expr, env) { - if (_renderExprFn) return _renderExprFn(expr, env); - // No adapter loaded — just return the expression as-is - return expr; - } - - function stripPrefix(s, prefix) { - return s.indexOf(prefix) === 0 ? s.slice(prefix.length) : s; - } - - function error(msg) { throw new Error(msg); } - function inspect(x) { return JSON.stringify(x); } - - - - // ========================================================================= - // Primitives - // ========================================================================= - - var PRIMITIVES = {}; - - // core.arithmetic - PRIMITIVES["+"] = function() { var s = 0; for (var i = 0; i < arguments.length; i++) s += arguments[i]; return s; }; - PRIMITIVES["-"] = function(a, b) { return arguments.length === 1 ? -a : a - b; }; - PRIMITIVES["*"] = function() { var s = 1; for (var i = 0; i < arguments.length; i++) s *= arguments[i]; return s; }; - PRIMITIVES["/"] = function(a, b) { return a / b; }; - PRIMITIVES["mod"] = function(a, b) { return a % b; }; - PRIMITIVES["inc"] = function(n) { return n + 1; }; - PRIMITIVES["dec"] = function(n) { return n - 1; }; - PRIMITIVES["abs"] = Math.abs; - PRIMITIVES["floor"] = Math.floor; - PRIMITIVES["ceil"] = Math.ceil; - PRIMITIVES["round"] = function(x, n) { - if (n === undefined || n === 0) return Math.round(x); - var f = Math.pow(10, n); return Math.round(x * f) / f; - }; - PRIMITIVES["min"] = Math.min; - PRIMITIVES["max"] = Math.max; - PRIMITIVES["sqrt"] = Math.sqrt; - PRIMITIVES["pow"] = Math.pow; - PRIMITIVES["clamp"] = function(x, lo, hi) { return Math.max(lo, Math.min(hi, x)); }; - - - // core.comparison - PRIMITIVES["="] = function(a, b) { return a === b; }; - PRIMITIVES["!="] = function(a, b) { return a !== b; }; - PRIMITIVES["<"] = function(a, b) { return a < b; }; - PRIMITIVES[">"] = function(a, b) { return a > b; }; - PRIMITIVES["<="] = function(a, b) { return a <= b; }; - PRIMITIVES[">="] = function(a, b) { return a >= b; }; - - - // core.logic - PRIMITIVES["not"] = function(x) { return !isSxTruthy(x); }; - - - // core.predicates - PRIMITIVES["nil?"] = isNil; - PRIMITIVES["number?"] = function(x) { return typeof x === "number"; }; - PRIMITIVES["string?"] = function(x) { return typeof x === "string"; }; - PRIMITIVES["list?"] = Array.isArray; - PRIMITIVES["dict?"] = function(x) { return x !== null && typeof x === "object" && !Array.isArray(x) && !x._sym && !x._kw; }; - PRIMITIVES["empty?"] = function(c) { return isNil(c) || (Array.isArray(c) ? c.length === 0 : typeof c === "string" ? c.length === 0 : Object.keys(c).length === 0); }; - PRIMITIVES["contains?"] = function(c, k) { - if (typeof c === "string") return c.indexOf(String(k)) !== -1; - if (Array.isArray(c)) return c.indexOf(k) !== -1; - return k in c; - }; - PRIMITIVES["odd?"] = function(n) { return n % 2 !== 0; }; - PRIMITIVES["even?"] = function(n) { return n % 2 === 0; }; - PRIMITIVES["zero?"] = function(n) { return n === 0; }; - PRIMITIVES["boolean?"] = function(x) { return x === true || x === false; }; - - - // core.strings - PRIMITIVES["str"] = function() { - var p = []; - for (var i = 0; i < arguments.length; i++) { - var v = arguments[i]; if (isNil(v)) continue; p.push(String(v)); - } - return p.join(""); - }; - PRIMITIVES["upper"] = function(s) { return String(s).toUpperCase(); }; - PRIMITIVES["lower"] = function(s) { return String(s).toLowerCase(); }; - PRIMITIVES["trim"] = function(s) { return String(s).trim(); }; - PRIMITIVES["split"] = function(s, sep) { return String(s).split(sep || " "); }; - PRIMITIVES["join"] = function(sep, coll) { return coll.join(sep); }; - PRIMITIVES["replace"] = function(s, old, nw) { return s.split(old).join(nw); }; - PRIMITIVES["index-of"] = function(s, needle, from) { return String(s).indexOf(needle, from || 0); }; - PRIMITIVES["starts-with?"] = function(s, p) { return String(s).indexOf(p) === 0; }; - PRIMITIVES["ends-with?"] = function(s, p) { var str = String(s); return str.indexOf(p, str.length - p.length) !== -1; }; - PRIMITIVES["slice"] = function(c, a, b) { return b !== undefined ? c.slice(a, b) : c.slice(a); }; - PRIMITIVES["substring"] = function(s, a, b) { return String(s).substring(a, b); }; - PRIMITIVES["string-length"] = function(s) { return String(s).length; }; - PRIMITIVES["string-contains?"] = function(s, sub) { return String(s).indexOf(String(sub)) !== -1; }; - PRIMITIVES["concat"] = function() { - var out = []; - for (var i = 0; i < arguments.length; i++) if (!isNil(arguments[i])) out = out.concat(arguments[i]); - return out; - }; - - - // core.collections - PRIMITIVES["list"] = function() { return Array.prototype.slice.call(arguments); }; - PRIMITIVES["dict"] = function() { - var d = {}; - for (var i = 0; i < arguments.length - 1; i += 2) d[arguments[i]] = arguments[i + 1]; - return d; - }; - PRIMITIVES["range"] = function(a, b, step) { - var r = []; step = step || 1; - for (var i = a; step > 0 ? i < b : i > b; i += step) r.push(i); - return r; - }; - PRIMITIVES["get"] = function(c, k, def) { var v = (c && c[k]); return v !== undefined ? v : (def !== undefined ? def : NIL); }; - PRIMITIVES["len"] = function(c) { return Array.isArray(c) ? c.length : typeof c === "string" ? c.length : Object.keys(c).length; }; - PRIMITIVES["first"] = function(c) { return c && c.length > 0 ? c[0] : NIL; }; - PRIMITIVES["last"] = function(c) { return c && c.length > 0 ? c[c.length - 1] : NIL; }; - PRIMITIVES["rest"] = function(c) { return c ? c.slice(1) : []; }; - PRIMITIVES["nth"] = function(c, n) { return c && n >= 0 && n < c.length ? c[n] : NIL; }; - PRIMITIVES["cons"] = function(x, c) { return [x].concat(c || []); }; - PRIMITIVES["append"] = function(c, x) { return (c || []).concat([x]); }; - PRIMITIVES["append!"] = function(arr, x) { arr.push(x); return arr; }; - PRIMITIVES["chunk-every"] = function(c, n) { - var r = []; for (var i = 0; i < c.length; i += n) r.push(c.slice(i, i + n)); return r; - }; - PRIMITIVES["zip-pairs"] = function(c) { - var r = []; for (var i = 0; i < c.length - 1; i++) r.push([c[i], c[i + 1]]); return r; - }; - PRIMITIVES["reverse"] = function(c) { return Array.isArray(c) ? c.slice().reverse() : String(c).split("").reverse().join(""); }; - PRIMITIVES["flatten"] = function(c) { - var out = []; - function walk(a) { for (var i = 0; i < a.length; i++) Array.isArray(a[i]) ? walk(a[i]) : out.push(a[i]); } - walk(c || []); return out; - }; - - - // core.dict - PRIMITIVES["keys"] = function(d) { return Object.keys(d || {}); }; - PRIMITIVES["vals"] = function(d) { var r = []; for (var k in d) r.push(d[k]); return r; }; - PRIMITIVES["merge"] = function() { - var out = {}; - for (var i = 0; i < arguments.length; i++) { var d = arguments[i]; if (d && !isNil(d)) for (var k in d) out[k] = d[k]; } - return out; - }; - PRIMITIVES["assoc"] = function(d) { - var out = {}; if (d && !isNil(d)) for (var k in d) out[k] = d[k]; - for (var i = 1; i < arguments.length - 1; i += 2) out[arguments[i]] = arguments[i + 1]; - return out; - }; - PRIMITIVES["dissoc"] = function(d) { - var out = {}; for (var k in d) out[k] = d[k]; - for (var i = 1; i < arguments.length; i++) delete out[arguments[i]]; - return out; - }; - PRIMITIVES["dict-set!"] = function(d, k, v) { d[k] = v; return v; }; - PRIMITIVES["has-key?"] = function(d, k) { return d !== null && d !== undefined && k in d; }; - PRIMITIVES["into"] = function(target, coll) { - if (Array.isArray(target)) return Array.isArray(coll) ? coll.slice() : Object.entries(coll); - var r = {}; for (var i = 0; i < coll.length; i++) { var p = coll[i]; if (Array.isArray(p) && p.length >= 2) r[p[0]] = p[1]; } - return r; - }; - - - // stdlib.format - PRIMITIVES["format-decimal"] = function(v, p) { return Number(v).toFixed(p || 2); }; - PRIMITIVES["parse-int"] = function(v, d) { var n = parseInt(v, 10); return isNaN(n) ? (d || 0) : n; }; - PRIMITIVES["format-date"] = function(s, fmt) { - if (!s) return ""; - try { - var d = new Date(s); - if (isNaN(d.getTime())) return String(s); - var months = ["January","February","March","April","May","June","July","August","September","October","November","December"]; - var short_months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; - return fmt.replace(/%-d/g, d.getDate()).replace(/%d/g, ("0"+d.getDate()).slice(-2)) - .replace(/%B/g, months[d.getMonth()]).replace(/%b/g, short_months[d.getMonth()]) - .replace(/%Y/g, d.getFullYear()).replace(/%m/g, ("0"+(d.getMonth()+1)).slice(-2)) - .replace(/%H/g, ("0"+d.getHours()).slice(-2)).replace(/%M/g, ("0"+d.getMinutes()).slice(-2)); - } catch (e) { return String(s); } - }; - PRIMITIVES["parse-datetime"] = function(s) { return s ? String(s) : NIL; }; - - - // stdlib.text - PRIMITIVES["pluralize"] = function(n, s, p) { - if (s || (p && p !== "s")) return n == 1 ? (s || "") : (p || "s"); - return n == 1 ? "" : "s"; - }; - PRIMITIVES["escape"] = function(s) { - return String(s).replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'"); - }; - PRIMITIVES["strip-tags"] = function(s) { return String(s).replace(/<[^>]+>/g, ""); }; - - - // stdlib.debug - PRIMITIVES["assert"] = function(cond, msg) { - if (!isSxTruthy(cond)) throw new Error("Assertion error: " + (msg || "Assertion failed")); - return true; - }; - - - function isPrimitive(name) { return name in PRIMITIVES; } - function getPrimitive(name) { return PRIMITIVES[name]; } - - // Higher-order helpers used by the transpiled code - function map(fn, coll) { return coll.map(fn); } - function mapIndexed(fn, coll) { return coll.map(function(item, i) { return fn(i, item); }); } - function filter(fn, coll) { return coll.filter(function(x) { return isSxTruthy(fn(x)); }); } - function reduce(fn, init, coll) { - var acc = init; - for (var i = 0; i < coll.length; i++) acc = fn(acc, coll[i]); - return acc; - } - function some(fn, coll) { - for (var i = 0; i < coll.length; i++) { var r = fn(coll[i]); if (isSxTruthy(r)) return r; } - return NIL; - } - function forEach(fn, coll) { for (var i = 0; i < coll.length; i++) fn(coll[i]); return NIL; } - function isEvery(fn, coll) { - for (var i = 0; i < coll.length; i++) { if (!isSxTruthy(fn(coll[i]))) return false; } - return true; - } - function mapDict(fn, d) { var r = {}; for (var k in d) r[k] = fn(k, d[k]); return r; } - - // List primitives used directly by transpiled code - var len = PRIMITIVES["len"]; - var first = PRIMITIVES["first"]; - var last = PRIMITIVES["last"]; - var rest = PRIMITIVES["rest"]; - var nth = PRIMITIVES["nth"]; - var cons = PRIMITIVES["cons"]; - var append = PRIMITIVES["append"]; - var isEmpty = PRIMITIVES["empty?"]; - var contains = PRIMITIVES["contains?"]; - var startsWith = PRIMITIVES["starts-with?"]; - var slice = PRIMITIVES["slice"]; - var concat = PRIMITIVES["concat"]; - var str = PRIMITIVES["str"]; - var join = PRIMITIVES["join"]; - var keys = PRIMITIVES["keys"]; - var get = PRIMITIVES["get"]; - var assoc = PRIMITIVES["assoc"]; - var range = PRIMITIVES["range"]; - function zip(a, b) { var r = []; for (var i = 0; i < Math.min(a.length, b.length); i++) r.push([a[i], b[i]]); return r; } - function append_b(arr, x) { arr.push(x); return arr; } - var apply = function(f, args) { - if (isLambda(f)) return trampoline(callLambda(f, args, lambdaClosure(f))); - return f.apply(null, args); - }; - - // Additional primitive aliases used by adapter/engine transpiled code - var split = PRIMITIVES["split"]; - var trim = PRIMITIVES["trim"]; - var upper = PRIMITIVES["upper"]; - var lower = PRIMITIVES["lower"]; - var replace_ = function(s, old, nw) { return s.split(old).join(nw); }; - var endsWith = PRIMITIVES["ends-with?"]; - var parseInt_ = PRIMITIVES["parse-int"]; - var dict_fn = PRIMITIVES["dict"]; - - // HTML rendering helpers - function escapeHtml(s) { - return String(s).replace(/&/g,"&").replace(//g,">").replace(/"/g,"""); - } - function escapeAttr(s) { return escapeHtml(s); } - function rawHtmlContent(r) { return r.html; } - function makeRawHtml(s) { return { _raw: true, html: s }; } - function sxExprSource(x) { return x && x.source ? x.source : String(x); } - - // Placeholders — overridden by transpiled spec from parser.sx / adapter-sx.sx - function serialize(val) { return String(val); } - function isSpecialForm(n) { return false; } - function isHoForm(n) { return false; } - - // processBindings and evalCond — now specced in render.sx, bootstrapped above - - function isDefinitionForm(name) { - return name === "define" || name === "defcomp" || name === "defmacro" || - name === "defstyle" || name === "defhandler"; - } - - function indexOf_(s, ch) { - return typeof s === "string" ? s.indexOf(ch) : -1; - } - - function dictHas(d, k) { return d != null && k in d; } - function dictDelete(d, k) { delete d[k]; } - - function forEachIndexed(fn, coll) { - for (var i = 0; i < coll.length; i++) fn(i, coll[i]); - return NIL; - } - - // ========================================================================= - // Performance overrides — evaluator hot path - // ========================================================================= - - // Override parseKeywordArgs: imperative loop instead of reduce+assoc - parseKeywordArgs = function(rawArgs, env) { - var kwargs = {}; - var children = []; - for (var i = 0; i < rawArgs.length; i++) { - var arg = rawArgs[i]; - if (arg && arg._kw && (i + 1) < rawArgs.length) { - kwargs[arg.name] = trampoline(evalExpr(rawArgs[i + 1], env)); - i++; - } else { - children.push(trampoline(evalExpr(arg, env))); - } - } - return [kwargs, children]; - }; - - // Override callComponent: use prototype chain env, imperative kwarg binding - callComponent = function(comp, rawArgs, env) { - var kwargs = {}; - var children = []; - for (var i = 0; i < rawArgs.length; i++) { - var arg = rawArgs[i]; - if (arg && arg._kw && (i + 1) < rawArgs.length) { - kwargs[arg.name] = trampoline(evalExpr(rawArgs[i + 1], env)); - i++; - } else { - children.push(trampoline(evalExpr(arg, env))); - } - } - var local = Object.create(componentClosure(comp)); - for (var k in env) if (env.hasOwnProperty(k)) local[k] = env[k]; - var params = componentParams(comp); - for (var j = 0; j < params.length; j++) { - var p = params[j]; - local[p] = p in kwargs ? kwargs[p] : NIL; - } - if (componentHasChildren(comp)) { - local["children"] = children; - } - return makeThunk(componentBody(comp), local); - }; - - // ========================================================================= - // Platform interface — Parser - // ========================================================================= - // Character classification derived from the grammar: - // ident-start → [a-zA-Z_~*+\-><=/!?&] - // ident-char → ident-start + [0-9.:\/\[\]#,] - - var _identStartRe = /[a-zA-Z_~*+\-><=/!?&]/; - var _identCharRe = /[a-zA-Z0-9_~*+\-><=/!?.:&/\[\]#,]/; - - function isIdentStart(ch) { return _identStartRe.test(ch); } - function isIdentChar(ch) { return _identCharRe.test(ch); } - function parseNumber(s) { return Number(s); } - function escapeString(s) { - return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\t/g, "\\t"); - } - function sxExprSource(e) { return typeof e === "string" ? e : String(e); } - - - // === Transpiled from eval === - - // trampoline - var trampoline = function(val) { return (function() { - var result = val; - return (isSxTruthy(isThunk(result)) ? trampoline(evalExpr(thunkExpr(result), thunkEnv(result))) : result); -})(); }; - - // eval-expr - var evalExpr = function(expr, env) { return (function() { var _m = typeOf(expr); if (_m == "number") return expr; if (_m == "string") return expr; if (_m == "boolean") return expr; if (_m == "nil") return NIL; if (_m == "symbol") return (function() { - var name = symbolName(expr); - return (isSxTruthy(envHas(env, name)) ? envGet(env, name) : (isSxTruthy(isPrimitive(name)) ? getPrimitive(name) : (isSxTruthy((name == "true")) ? true : (isSxTruthy((name == "false")) ? false : (isSxTruthy((name == "nil")) ? NIL : error((String("Undefined symbol: ") + String(name)))))))); -})(); if (_m == "keyword") return keywordName(expr); if (_m == "dict") return mapDict(function(k, v) { return trampoline(evalExpr(v, env)); }, expr); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? [] : evalList(expr, env)); return expr; })(); }; - - // eval-list - var evalList = function(expr, env) { return (function() { - var head = first(expr); - var args = rest(expr); - return (isSxTruthy(!isSxTruthy(sxOr((typeOf(head) == "symbol"), (typeOf(head) == "lambda"), (typeOf(head) == "list")))) ? map(function(x) { return trampoline(evalExpr(x, env)); }, expr) : (isSxTruthy((typeOf(head) == "symbol")) ? (function() { - var name = symbolName(head); - return (isSxTruthy((name == "if")) ? sfIf(args, env) : (isSxTruthy((name == "when")) ? sfWhen(args, env) : (isSxTruthy((name == "cond")) ? sfCond(args, env) : (isSxTruthy((name == "case")) ? sfCase(args, env) : (isSxTruthy((name == "and")) ? sfAnd(args, env) : (isSxTruthy((name == "or")) ? sfOr(args, env) : (isSxTruthy((name == "let")) ? sfLet(args, env) : (isSxTruthy((name == "let*")) ? sfLet(args, env) : (isSxTruthy((name == "letrec")) ? sfLetrec(args, env) : (isSxTruthy((name == "lambda")) ? sfLambda(args, env) : (isSxTruthy((name == "fn")) ? sfLambda(args, env) : (isSxTruthy((name == "define")) ? sfDefine(args, env) : (isSxTruthy((name == "defcomp")) ? sfDefcomp(args, env) : (isSxTruthy((name == "defisland")) ? sfDefisland(args, env) : (isSxTruthy((name == "defmacro")) ? sfDefmacro(args, env) : (isSxTruthy((name == "defstyle")) ? sfDefstyle(args, env) : (isSxTruthy((name == "defhandler")) ? sfDefhandler(args, env) : (isSxTruthy((name == "defpage")) ? sfDefpage(args, env) : (isSxTruthy((name == "defquery")) ? sfDefquery(args, env) : (isSxTruthy((name == "defaction")) ? sfDefaction(args, env) : (isSxTruthy((name == "begin")) ? sfBegin(args, env) : (isSxTruthy((name == "do")) ? sfBegin(args, env) : (isSxTruthy((name == "quote")) ? sfQuote(args, env) : (isSxTruthy((name == "quasiquote")) ? sfQuasiquote(args, env) : (isSxTruthy((name == "->")) ? sfThreadFirst(args, env) : (isSxTruthy((name == "set!")) ? sfSetBang(args, env) : (isSxTruthy((name == "reset")) ? sfReset(args, env) : (isSxTruthy((name == "shift")) ? sfShift(args, env) : (isSxTruthy((name == "dynamic-wind")) ? sfDynamicWind(args, env) : (isSxTruthy((name == "map")) ? hoMap(args, env) : (isSxTruthy((name == "map-indexed")) ? hoMapIndexed(args, env) : (isSxTruthy((name == "filter")) ? hoFilter(args, env) : (isSxTruthy((name == "reduce")) ? hoReduce(args, env) : (isSxTruthy((name == "some")) ? hoSome(args, env) : (isSxTruthy((name == "every?")) ? hoEvery(args, env) : (isSxTruthy((name == "for-each")) ? hoForEach(args, env) : (isSxTruthy((isSxTruthy(envHas(env, name)) && isMacro(envGet(env, name)))) ? (function() { - var mac = envGet(env, name); - return makeThunk(expandMacro(mac, args, env), env); -})() : (isSxTruthy(isRenderExpr(expr)) ? renderExpr(expr, env) : evalCall(head, args, env))))))))))))))))))))))))))))))))))))))); -})() : evalCall(head, args, env))); -})(); }; - - // eval-call - var evalCall = function(head, args, env) { return (function() { - var f = trampoline(evalExpr(head, env)); - var evaluatedArgs = map(function(a) { return trampoline(evalExpr(a, env)); }, args); - return (isSxTruthy((isSxTruthy(isCallable(f)) && isSxTruthy(!isSxTruthy(isLambda(f))) && isSxTruthy(!isSxTruthy(isComponent(f))) && !isSxTruthy(isIsland(f)))) ? apply(f, evaluatedArgs) : (isSxTruthy(isLambda(f)) ? callLambda(f, evaluatedArgs, env) : (isSxTruthy(isComponent(f)) ? callComponent(f, args, env) : (isSxTruthy(isIsland(f)) ? callComponent(f, args, env) : error((String("Not callable: ") + String(inspect(f)))))))); -})(); }; - - // call-lambda - var callLambda = function(f, args, callerEnv) { return (function() { - var params = lambdaParams(f); - var local = envMerge(lambdaClosure(f), callerEnv); - return (isSxTruthy((len(args) != len(params))) ? error((String(sxOr(lambdaName(f), "lambda")) + String(" expects ") + String(len(params)) + String(" args, got ") + String(len(args)))) : (forEach(function(pair) { return envSet(local, first(pair), nth(pair, 1)); }, zip(params, args)), makeThunk(lambdaBody(f), local))); -})(); }; - - // call-component - var callComponent = function(comp, rawArgs, env) { return (function() { - var parsed = parseKeywordArgs(rawArgs, env); - var kwargs = first(parsed); - var children = nth(parsed, 1); - var local = envMerge(componentClosure(comp), env); - { var _c = componentParams(comp); for (var _i = 0; _i < _c.length; _i++) { var p = _c[_i]; local[p] = sxOr(dictGet(kwargs, p), NIL); } } - if (isSxTruthy(componentHasChildren(comp))) { - local["children"] = children; -} - return makeThunk(componentBody(comp), local); -})(); }; - - // parse-keyword-args - var parseKeywordArgs = function(rawArgs, env) { return (function() { - var kwargs = {}; - var children = []; - var i = 0; - reduce(function(state, arg) { return (function() { - var idx = get(state, "i"); - var skip = get(state, "skip"); - return (isSxTruthy(skip) ? assoc(state, "skip", false, "i", (idx + 1)) : (isSxTruthy((isSxTruthy((typeOf(arg) == "keyword")) && ((idx + 1) < len(rawArgs)))) ? (dictSet(kwargs, keywordName(arg), trampoline(evalExpr(nth(rawArgs, (idx + 1)), env))), assoc(state, "skip", true, "i", (idx + 1))) : (append_b(children, trampoline(evalExpr(arg, env))), assoc(state, "i", (idx + 1))))); -})(); }, {["i"]: 0, ["skip"]: false}, rawArgs); - return [kwargs, children]; -})(); }; - - // sf-if - var sfIf = function(args, env) { return (function() { - var condition = trampoline(evalExpr(first(args), env)); - return (isSxTruthy((isSxTruthy(condition) && !isSxTruthy(isNil(condition)))) ? makeThunk(nth(args, 1), env) : (isSxTruthy((len(args) > 2)) ? makeThunk(nth(args, 2), env) : NIL)); -})(); }; - - // sf-when - var sfWhen = function(args, env) { return (function() { - var condition = trampoline(evalExpr(first(args), env)); - return (isSxTruthy((isSxTruthy(condition) && !isSxTruthy(isNil(condition)))) ? (forEach(function(e) { return trampoline(evalExpr(e, env)); }, slice(args, 1, (len(args) - 1))), makeThunk(last(args), env)) : NIL); -})(); }; - - // sf-cond - var sfCond = function(args, env) { return (isSxTruthy((isSxTruthy((typeOf(first(args)) == "list")) && (len(first(args)) == 2))) ? sfCondScheme(args, env) : sfCondClojure(args, env)); }; - - // sf-cond-scheme - var sfCondScheme = function(clauses, env) { return (isSxTruthy(isEmpty(clauses)) ? NIL : (function() { - var clause = first(clauses); - var test = first(clause); - var body = nth(clause, 1); - return (isSxTruthy(sxOr((isSxTruthy((typeOf(test) == "symbol")) && sxOr((symbolName(test) == "else"), (symbolName(test) == ":else"))), (isSxTruthy((typeOf(test) == "keyword")) && (keywordName(test) == "else")))) ? makeThunk(body, env) : (isSxTruthy(trampoline(evalExpr(test, env))) ? makeThunk(body, env) : sfCondScheme(rest(clauses), env))); -})()); }; - - // sf-cond-clojure - var sfCondClojure = function(clauses, env) { return (isSxTruthy((len(clauses) < 2)) ? NIL : (function() { - var test = first(clauses); - var body = nth(clauses, 1); - return (isSxTruthy(sxOr((isSxTruthy((typeOf(test) == "keyword")) && (keywordName(test) == "else")), (isSxTruthy((typeOf(test) == "symbol")) && sxOr((symbolName(test) == "else"), (symbolName(test) == ":else"))))) ? makeThunk(body, env) : (isSxTruthy(trampoline(evalExpr(test, env))) ? makeThunk(body, env) : sfCondClojure(slice(clauses, 2), env))); -})()); }; - - // sf-case - var sfCase = function(args, env) { return (function() { - var matchVal = trampoline(evalExpr(first(args), env)); - var clauses = rest(args); - return sfCaseLoop(matchVal, clauses, env); -})(); }; - - // sf-case-loop - var sfCaseLoop = function(matchVal, clauses, env) { return (isSxTruthy((len(clauses) < 2)) ? NIL : (function() { - var test = first(clauses); - var body = nth(clauses, 1); - return (isSxTruthy(sxOr((isSxTruthy((typeOf(test) == "keyword")) && (keywordName(test) == "else")), (isSxTruthy((typeOf(test) == "symbol")) && sxOr((symbolName(test) == "else"), (symbolName(test) == ":else"))))) ? makeThunk(body, env) : (isSxTruthy((matchVal == trampoline(evalExpr(test, env)))) ? makeThunk(body, env) : sfCaseLoop(matchVal, slice(clauses, 2), env))); -})()); }; - - // sf-and - var sfAnd = function(args, env) { return (isSxTruthy(isEmpty(args)) ? true : (function() { - var val = trampoline(evalExpr(first(args), env)); - return (isSxTruthy(!isSxTruthy(val)) ? val : (isSxTruthy((len(args) == 1)) ? val : sfAnd(rest(args), env))); -})()); }; - - // sf-or - var sfOr = function(args, env) { return (isSxTruthy(isEmpty(args)) ? false : (function() { - var val = trampoline(evalExpr(first(args), env)); - return (isSxTruthy(val) ? val : sfOr(rest(args), env)); -})()); }; - - // sf-let - var sfLet = function(args, env) { return (isSxTruthy((typeOf(first(args)) == "symbol")) ? sfNamedLet(args, env) : (function() { - var bindings = first(args); - var body = rest(args); - var local = envExtend(env); - (isSxTruthy((isSxTruthy((typeOf(first(bindings)) == "list")) && (len(first(bindings)) == 2))) ? forEach(function(binding) { return (function() { - var vname = (isSxTruthy((typeOf(first(binding)) == "symbol")) ? symbolName(first(binding)) : first(binding)); - return envSet(local, vname, trampoline(evalExpr(nth(binding, 1), local))); -})(); }, bindings) : (function() { - var i = 0; - return reduce(function(acc, pairIdx) { return (function() { - var vname = (isSxTruthy((typeOf(nth(bindings, (pairIdx * 2))) == "symbol")) ? symbolName(nth(bindings, (pairIdx * 2))) : nth(bindings, (pairIdx * 2))); - var valExpr = nth(bindings, ((pairIdx * 2) + 1)); - return envSet(local, vname, trampoline(evalExpr(valExpr, local))); -})(); }, NIL, range(0, (len(bindings) / 2))); -})()); - { var _c = slice(body, 0, (len(body) - 1)); for (var _i = 0; _i < _c.length; _i++) { var e = _c[_i]; trampoline(evalExpr(e, local)); } } - return makeThunk(last(body), local); -})()); }; - - // sf-named-let - var sfNamedLet = function(args, env) { return (function() { - var loopName = symbolName(first(args)); - var bindings = nth(args, 1); - var body = slice(args, 2); - var params = []; - var inits = []; - (isSxTruthy((isSxTruthy((typeOf(first(bindings)) == "list")) && (len(first(bindings)) == 2))) ? forEach(function(binding) { params.push((isSxTruthy((typeOf(first(binding)) == "symbol")) ? symbolName(first(binding)) : first(binding))); -return append_b(inits, nth(binding, 1)); }, bindings) : reduce(function(acc, pairIdx) { return (append_b(params, (isSxTruthy((typeOf(nth(bindings, (pairIdx * 2))) == "symbol")) ? symbolName(nth(bindings, (pairIdx * 2))) : nth(bindings, (pairIdx * 2)))), append_b(inits, nth(bindings, ((pairIdx * 2) + 1)))); }, NIL, range(0, (len(bindings) / 2)))); - return (function() { - var loopBody = (isSxTruthy((len(body) == 1)) ? first(body) : cons(makeSymbol("begin"), body)); - var loopFn = makeLambda(params, loopBody, env); - loopFn.name = loopName; - lambdaClosure(loopFn)[loopName] = loopFn; - return (function() { - var initVals = map(function(e) { return trampoline(evalExpr(e, env)); }, inits); - return callLambda(loopFn, initVals, env); -})(); -})(); -})(); }; - - // sf-lambda - var sfLambda = function(args, env) { return (function() { - var paramsExpr = first(args); - var bodyExprs = rest(args); - var body = (isSxTruthy((len(bodyExprs) == 1)) ? first(bodyExprs) : cons(makeSymbol("begin"), bodyExprs)); - var paramNames = map(function(p) { return (isSxTruthy((typeOf(p) == "symbol")) ? symbolName(p) : p); }, paramsExpr); - return makeLambda(paramNames, body, env); -})(); }; - - // sf-define - var sfDefine = function(args, env) { return (function() { - var nameSym = first(args); - var value = trampoline(evalExpr(nth(args, 1), env)); - if (isSxTruthy((isSxTruthy(isLambda(value)) && isNil(lambdaName(value))))) { - value.name = symbolName(nameSym); -} - env[symbolName(nameSym)] = value; - return value; -})(); }; - - // sf-defcomp - var sfDefcomp = function(args, env) { return (function() { - var nameSym = first(args); - var paramsRaw = nth(args, 1); - var body = last(args); - var compName = stripPrefix(symbolName(nameSym), "~"); - var parsed = parseCompParams(paramsRaw); - var params = first(parsed); - var hasChildren = nth(parsed, 1); - var affinity = defcompKwarg(args, "affinity", "auto"); - return (function() { - var comp = makeComponent(compName, params, hasChildren, body, env, affinity); - env[symbolName(nameSym)] = comp; - return comp; -})(); -})(); }; - - // defcomp-kwarg - var defcompKwarg = function(args, key, default_) { return (function() { - var end = (len(args) - 1); - var result = default_; - { var _c = range(2, end, 1); for (var _i = 0; _i < _c.length; _i++) { var i = _c[_i]; if (isSxTruthy((isSxTruthy((typeOf(nth(args, i)) == "keyword")) && isSxTruthy((keywordName(nth(args, i)) == key)) && ((i + 1) < end)))) { - (function() { - var val = nth(args, (i + 1)); - return (result = (isSxTruthy((typeOf(val) == "keyword")) ? keywordName(val) : val)); -})(); -} } } - return result; -})(); }; - - // parse-comp-params - var parseCompParams = function(paramsExpr) { return (function() { - var params = []; - var hasChildren = false; - var inKey = false; - { var _c = paramsExpr; for (var _i = 0; _i < _c.length; _i++) { var p = _c[_i]; if (isSxTruthy((typeOf(p) == "symbol"))) { - (function() { - var name = symbolName(p); - return (isSxTruthy((name == "&key")) ? (inKey = true) : (isSxTruthy((name == "&rest")) ? (hasChildren = true) : (isSxTruthy((name == "&children")) ? (hasChildren = true) : (isSxTruthy(hasChildren) ? NIL : (isSxTruthy(inKey) ? append_b(params, name) : append_b(params, name)))))); -})(); -} } } - return [params, hasChildren]; -})(); }; - - // sf-defisland - var sfDefisland = function(args, env) { return (function() { - var nameSym = first(args); - var paramsRaw = nth(args, 1); - var body = last(args); - var compName = stripPrefix(symbolName(nameSym), "~"); - var parsed = parseCompParams(paramsRaw); - var params = first(parsed); - var hasChildren = nth(parsed, 1); - return (function() { - var island = makeIsland(compName, params, hasChildren, body, env); - env[symbolName(nameSym)] = island; - return island; -})(); -})(); }; - - // sf-defmacro - var sfDefmacro = function(args, env) { return (function() { - var nameSym = first(args); - var paramsRaw = nth(args, 1); - var body = nth(args, 2); - var parsed = parseMacroParams(paramsRaw); - var params = first(parsed); - var restParam = nth(parsed, 1); - return (function() { - var mac = makeMacro(params, restParam, body, env, symbolName(nameSym)); - env[symbolName(nameSym)] = mac; - return mac; -})(); -})(); }; - - // parse-macro-params - var parseMacroParams = function(paramsExpr) { return (function() { - var params = []; - var restParam = NIL; - reduce(function(state, p) { return (isSxTruthy((isSxTruthy((typeOf(p) == "symbol")) && (symbolName(p) == "&rest"))) ? assoc(state, "in-rest", true) : (isSxTruthy(get(state, "in-rest")) ? ((restParam = (isSxTruthy((typeOf(p) == "symbol")) ? symbolName(p) : p)), state) : (append_b(params, (isSxTruthy((typeOf(p) == "symbol")) ? symbolName(p) : p)), state))); }, {["in-rest"]: false}, paramsExpr); - return [params, restParam]; -})(); }; - - // sf-defstyle - var sfDefstyle = function(args, env) { return (function() { - var nameSym = first(args); - var value = trampoline(evalExpr(nth(args, 1), env)); - env[symbolName(nameSym)] = value; - return value; -})(); }; - - // sf-begin - var sfBegin = function(args, env) { return (isSxTruthy(isEmpty(args)) ? NIL : (forEach(function(e) { return trampoline(evalExpr(e, env)); }, slice(args, 0, (len(args) - 1))), makeThunk(last(args), env))); }; - - // sf-quote - var sfQuote = function(args, env) { return (isSxTruthy(isEmpty(args)) ? NIL : first(args)); }; - - // sf-quasiquote - var sfQuasiquote = function(args, env) { return qqExpand(first(args), env); }; - - // qq-expand - var qqExpand = function(template, env) { return (isSxTruthy(!isSxTruthy((typeOf(template) == "list"))) ? template : (isSxTruthy(isEmpty(template)) ? [] : (function() { - var head = first(template); - return (isSxTruthy((isSxTruthy((typeOf(head) == "symbol")) && (symbolName(head) == "unquote"))) ? trampoline(evalExpr(nth(template, 1), env)) : reduce(function(result, item) { return (isSxTruthy((isSxTruthy((typeOf(item) == "list")) && isSxTruthy((len(item) == 2)) && isSxTruthy((typeOf(first(item)) == "symbol")) && (symbolName(first(item)) == "splice-unquote"))) ? (function() { - var spliced = trampoline(evalExpr(nth(item, 1), env)); - return (isSxTruthy((typeOf(spliced) == "list")) ? concat(result, spliced) : (isSxTruthy(isNil(spliced)) ? result : append(result, spliced))); -})() : append(result, qqExpand(item, env))); }, [], template)); -})())); }; - - // sf-thread-first - var sfThreadFirst = function(args, env) { return (function() { - var val = trampoline(evalExpr(first(args), env)); - return reduce(function(result, form) { return (isSxTruthy((typeOf(form) == "list")) ? (function() { - var f = trampoline(evalExpr(first(form), env)); - var restArgs = map(function(a) { return trampoline(evalExpr(a, env)); }, rest(form)); - var allArgs = cons(result, restArgs); - return (isSxTruthy((isSxTruthy(isCallable(f)) && !isSxTruthy(isLambda(f)))) ? apply(f, allArgs) : (isSxTruthy(isLambda(f)) ? trampoline(callLambda(f, allArgs, env)) : error((String("-> form not callable: ") + String(inspect(f)))))); -})() : (function() { - var f = trampoline(evalExpr(form, env)); - return (isSxTruthy((isSxTruthy(isCallable(f)) && !isSxTruthy(isLambda(f)))) ? f(result) : (isSxTruthy(isLambda(f)) ? trampoline(callLambda(f, [result], env)) : error((String("-> form not callable: ") + String(inspect(f)))))); -})()); }, val, rest(args)); -})(); }; - - // sf-set! - var sfSetBang = function(args, env) { return (function() { - var name = symbolName(first(args)); - var value = trampoline(evalExpr(nth(args, 1), env)); - env[name] = value; - return value; -})(); }; - - // sf-letrec - var sfLetrec = function(args, env) { return (function() { - var bindings = first(args); - var body = rest(args); - var local = envExtend(env); - var names = []; - var valExprs = []; - (isSxTruthy((isSxTruthy((typeOf(first(bindings)) == "list")) && (len(first(bindings)) == 2))) ? forEach(function(binding) { return (function() { - var vname = (isSxTruthy((typeOf(first(binding)) == "symbol")) ? symbolName(first(binding)) : first(binding)); - names.push(vname); - valExprs.push(nth(binding, 1)); - return envSet(local, vname, NIL); -})(); }, bindings) : reduce(function(acc, pairIdx) { return (function() { - var vname = (isSxTruthy((typeOf(nth(bindings, (pairIdx * 2))) == "symbol")) ? symbolName(nth(bindings, (pairIdx * 2))) : nth(bindings, (pairIdx * 2))); - var valExpr = nth(bindings, ((pairIdx * 2) + 1)); - names.push(vname); - valExprs.push(valExpr); - return envSet(local, vname, NIL); -})(); }, NIL, range(0, (len(bindings) / 2)))); - (function() { - var values = map(function(e) { return trampoline(evalExpr(e, local)); }, valExprs); - { var _c = zip(names, values); for (var _i = 0; _i < _c.length; _i++) { var pair = _c[_i]; local[first(pair)] = nth(pair, 1); } } - return forEach(function(val) { return (isSxTruthy(isLambda(val)) ? forEach(function(n) { return envSet(lambdaClosure(val), n, envGet(local, n)); }, names) : NIL); }, values); -})(); - { var _c = slice(body, 0, (len(body) - 1)); for (var _i = 0; _i < _c.length; _i++) { var e = _c[_i]; trampoline(evalExpr(e, local)); } } - return makeThunk(last(body), local); -})(); }; - - // sf-dynamic-wind - var sfDynamicWind = function(args, env) { return (function() { - var before = trampoline(evalExpr(first(args), env)); - var body = trampoline(evalExpr(nth(args, 1), env)); - var after = trampoline(evalExpr(nth(args, 2), env)); - callThunk(before, env); - pushWind(before, after); - return (function() { - var result = callThunk(body, env); - popWind(); - callThunk(after, env); - return result; -})(); -})(); }; - - // expand-macro - var expandMacro = function(mac, rawArgs, env) { return (function() { - var local = envMerge(macroClosure(mac), env); - { var _c = mapIndexed(function(i, p) { return [p, i]; }, macroParams(mac)); for (var _i = 0; _i < _c.length; _i++) { var pair = _c[_i]; local[first(pair)] = (isSxTruthy((nth(pair, 1) < len(rawArgs))) ? nth(rawArgs, nth(pair, 1)) : NIL); } } - if (isSxTruthy(macroRestParam(mac))) { - local[macroRestParam(mac)] = slice(rawArgs, len(macroParams(mac))); -} - return trampoline(evalExpr(macroBody(mac), local)); -})(); }; - - // call-fn - var callFn = function(f, args, env) { return (isSxTruthy(isLambda(f)) ? trampoline(callLambda(f, args, env)) : (isSxTruthy(isCallable(f)) ? apply(f, args) : error((String("Not callable in HO form: ") + String(inspect(f)))))); }; - - // ho-map - var hoMap = function(args, env) { return (function() { - var f = trampoline(evalExpr(first(args), env)); - var coll = trampoline(evalExpr(nth(args, 1), env)); - return map(function(item) { return callFn(f, [item], env); }, coll); -})(); }; - - // ho-map-indexed - var hoMapIndexed = function(args, env) { return (function() { - var f = trampoline(evalExpr(first(args), env)); - var coll = trampoline(evalExpr(nth(args, 1), env)); - return mapIndexed(function(i, item) { return callFn(f, [i, item], env); }, coll); -})(); }; - - // ho-filter - var hoFilter = function(args, env) { return (function() { - var f = trampoline(evalExpr(first(args), env)); - var coll = trampoline(evalExpr(nth(args, 1), env)); - return filter(function(item) { return callFn(f, [item], env); }, coll); -})(); }; - - // ho-reduce - var hoReduce = function(args, env) { return (function() { - var f = trampoline(evalExpr(first(args), env)); - var init = trampoline(evalExpr(nth(args, 1), env)); - var coll = trampoline(evalExpr(nth(args, 2), env)); - return reduce(function(acc, item) { return callFn(f, [acc, item], env); }, init, coll); -})(); }; - - // ho-some - var hoSome = function(args, env) { return (function() { - var f = trampoline(evalExpr(first(args), env)); - var coll = trampoline(evalExpr(nth(args, 1), env)); - return some(function(item) { return callFn(f, [item], env); }, coll); -})(); }; - - // ho-every - var hoEvery = function(args, env) { return (function() { - var f = trampoline(evalExpr(first(args), env)); - var coll = trampoline(evalExpr(nth(args, 1), env)); - return isEvery(function(item) { return callFn(f, [item], env); }, coll); -})(); }; - - // ho-for-each - var hoForEach = function(args, env) { return (function() { - var f = trampoline(evalExpr(first(args), env)); - var coll = trampoline(evalExpr(nth(args, 1), env)); - return forEach(function(item) { return callFn(f, [item], env); }, coll); -})(); }; - - - // === Transpiled from render (core) === - - // HTML_TAGS - var HTML_TAGS = ["html", "head", "body", "title", "meta", "link", "script", "style", "noscript", "header", "nav", "main", "section", "article", "aside", "footer", "h1", "h2", "h3", "h4", "h5", "h6", "hgroup", "div", "p", "blockquote", "pre", "figure", "figcaption", "address", "details", "summary", "a", "span", "em", "strong", "small", "b", "i", "u", "s", "mark", "sub", "sup", "abbr", "cite", "code", "time", "br", "wbr", "hr", "ul", "ol", "li", "dl", "dt", "dd", "table", "thead", "tbody", "tfoot", "tr", "th", "td", "caption", "colgroup", "col", "form", "input", "textarea", "select", "option", "optgroup", "button", "label", "fieldset", "legend", "output", "datalist", "img", "video", "audio", "source", "picture", "canvas", "iframe", "svg", "math", "path", "circle", "ellipse", "rect", "line", "polyline", "polygon", "text", "tspan", "g", "defs", "use", "clipPath", "mask", "pattern", "linearGradient", "radialGradient", "stop", "filter", "feGaussianBlur", "feOffset", "feBlend", "feColorMatrix", "feComposite", "feMerge", "feMergeNode", "feTurbulence", "feComponentTransfer", "feFuncR", "feFuncG", "feFuncB", "feFuncA", "feDisplacementMap", "feFlood", "feImage", "feMorphology", "feSpecularLighting", "feDiffuseLighting", "fePointLight", "feSpotLight", "feDistantLight", "animate", "animateTransform", "foreignObject", "template", "slot", "dialog", "menu"]; - - // VOID_ELEMENTS - var VOID_ELEMENTS = ["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]; - - // BOOLEAN_ATTRS - var BOOLEAN_ATTRS = ["async", "autofocus", "autoplay", "checked", "controls", "default", "defer", "disabled", "formnovalidate", "hidden", "inert", "ismap", "loop", "multiple", "muted", "nomodule", "novalidate", "open", "playsinline", "readonly", "required", "reversed", "selected"]; - - // definition-form? - var isDefinitionForm = function(name) { return sxOr((name == "define"), (name == "defcomp"), (name == "defisland"), (name == "defmacro"), (name == "defstyle"), (name == "defhandler")); }; - - // parse-element-args - var parseElementArgs = function(args, env) { return (function() { - var attrs = {}; - var children = []; - reduce(function(state, arg) { return (function() { - var skip = get(state, "skip"); - return (isSxTruthy(skip) ? assoc(state, "skip", false, "i", (get(state, "i") + 1)) : (isSxTruthy((isSxTruthy((typeOf(arg) == "keyword")) && ((get(state, "i") + 1) < len(args)))) ? (function() { - var val = trampoline(evalExpr(nth(args, (get(state, "i") + 1)), env)); - attrs[keywordName(arg)] = val; - return assoc(state, "skip", true, "i", (get(state, "i") + 1)); -})() : (append_b(children, arg), assoc(state, "i", (get(state, "i") + 1))))); -})(); }, {["i"]: 0, ["skip"]: false}, args); - return [attrs, children]; -})(); }; - - // render-attrs - var renderAttrs = function(attrs) { return join("", map(function(key) { return (function() { - var val = dictGet(attrs, key); - return (isSxTruthy((isSxTruthy(contains(BOOLEAN_ATTRS, key)) && val)) ? (String(" ") + String(key)) : (isSxTruthy((isSxTruthy(contains(BOOLEAN_ATTRS, key)) && !isSxTruthy(val))) ? "" : (isSxTruthy(isNil(val)) ? "" : (String(" ") + String(key) + String("=\"") + String(escapeAttr((String(val)))) + String("\""))))); -})(); }, keys(attrs))); }; - - // eval-cond - var evalCond = function(clauses, env) { return (isSxTruthy((isSxTruthy(!isSxTruthy(isEmpty(clauses))) && isSxTruthy((typeOf(first(clauses)) == "list")) && (len(first(clauses)) == 2))) ? evalCondScheme(clauses, env) : evalCondClojure(clauses, env)); }; - - // eval-cond-scheme - var evalCondScheme = function(clauses, env) { return (isSxTruthy(isEmpty(clauses)) ? NIL : (function() { - var clause = first(clauses); - var test = first(clause); - var body = nth(clause, 1); - return (isSxTruthy(sxOr((isSxTruthy((typeOf(test) == "symbol")) && sxOr((symbolName(test) == "else"), (symbolName(test) == ":else"))), (isSxTruthy((typeOf(test) == "keyword")) && (keywordName(test) == "else")))) ? body : (isSxTruthy(trampoline(evalExpr(test, env))) ? body : evalCondScheme(rest(clauses), env))); -})()); }; - - // eval-cond-clojure - var evalCondClojure = function(clauses, env) { return (isSxTruthy((len(clauses) < 2)) ? NIL : (function() { - var test = first(clauses); - var body = nth(clauses, 1); - return (isSxTruthy(sxOr((isSxTruthy((typeOf(test) == "keyword")) && (keywordName(test) == "else")), (isSxTruthy((typeOf(test) == "symbol")) && sxOr((symbolName(test) == "else"), (symbolName(test) == ":else"))))) ? body : (isSxTruthy(trampoline(evalExpr(test, env))) ? body : evalCondClojure(slice(clauses, 2), env))); -})()); }; - - // process-bindings - var processBindings = function(bindings, env) { return (function() { - var local = merge(env); - { var _c = bindings; for (var _i = 0; _i < _c.length; _i++) { var pair = _c[_i]; if (isSxTruthy((isSxTruthy((typeOf(pair) == "list")) && (len(pair) >= 2)))) { - (function() { - var name = (isSxTruthy((typeOf(first(pair)) == "symbol")) ? symbolName(first(pair)) : (String(first(pair)))); - return envSet(local, name, trampoline(evalExpr(nth(pair, 1), local))); -})(); -} } } - return local; -})(); }; - - // is-render-expr? - var isRenderExpr = function(expr) { return (isSxTruthy(sxOr(!isSxTruthy((typeOf(expr) == "list")), isEmpty(expr))) ? false : (function() { - var h = first(expr); - return (isSxTruthy(!isSxTruthy((typeOf(h) == "symbol"))) ? false : (function() { - var n = symbolName(h); - return sxOr((n == "<>"), (n == "raw!"), startsWith(n, "~"), startsWith(n, "html:"), contains(HTML_TAGS, n), (isSxTruthy((indexOf_(n, "-") > 0)) && isSxTruthy((len(expr) > 1)) && (typeOf(nth(expr, 1)) == "keyword"))); -})()); -})()); }; - - - // === Transpiled from parser === - - // sx-parse - var sxParse = function(source) { return (function() { - var pos = 0; - var lenSrc = len(source); - var skipComment = function() { while(true) { if (isSxTruthy((isSxTruthy((pos < lenSrc)) && !isSxTruthy((nth(source, pos) == "\n"))))) { pos = (pos + 1); -continue; } else { return NIL; } } }; - var skipWs = function() { while(true) { if (isSxTruthy((pos < lenSrc))) { { var ch = nth(source, pos); -if (isSxTruthy(sxOr((ch == " "), (ch == "\t"), (ch == "\n"), (ch == "\r")))) { pos = (pos + 1); -continue; } else if (isSxTruthy((ch == ";"))) { pos = (pos + 1); -skipComment(); -continue; } else { return NIL; } } } else { return NIL; } } }; - var readString = function() { pos = (pos + 1); -return (function() { - var buf = ""; - var readStrLoop = function() { while(true) { if (isSxTruthy((pos >= lenSrc))) { return error("Unterminated string"); } else { { var ch = nth(source, pos); -if (isSxTruthy((ch == "\""))) { pos = (pos + 1); -return NIL; } else if (isSxTruthy((ch == "\\"))) { pos = (pos + 1); -{ var esc = nth(source, pos); -buf = (String(buf) + String((isSxTruthy((esc == "n")) ? "\n" : (isSxTruthy((esc == "t")) ? "\t" : (isSxTruthy((esc == "r")) ? "\r" : esc))))); -pos = (pos + 1); -continue; } } else { buf = (String(buf) + String(ch)); -pos = (pos + 1); -continue; } } } } }; - readStrLoop(); - return buf; -})(); }; - var readIdent = function() { return (function() { - var start = pos; - var readIdentLoop = function() { while(true) { if (isSxTruthy((isSxTruthy((pos < lenSrc)) && isIdentChar(nth(source, pos))))) { pos = (pos + 1); -continue; } else { return NIL; } } }; - readIdentLoop(); - return slice(source, start, pos); -})(); }; - var readKeyword = function() { pos = (pos + 1); -return makeKeyword(readIdent()); }; - var readNumber = function() { return (function() { - var start = pos; - if (isSxTruthy((isSxTruthy((pos < lenSrc)) && (nth(source, pos) == "-")))) { - pos = (pos + 1); -} - var readDigits = function() { while(true) { if (isSxTruthy((isSxTruthy((pos < lenSrc)) && (function() { - var c = nth(source, pos); - return (isSxTruthy((c >= "0")) && (c <= "9")); -})()))) { pos = (pos + 1); -continue; } else { return NIL; } } }; - readDigits(); - if (isSxTruthy((isSxTruthy((pos < lenSrc)) && (nth(source, pos) == ".")))) { - pos = (pos + 1); - readDigits(); -} - if (isSxTruthy((isSxTruthy((pos < lenSrc)) && sxOr((nth(source, pos) == "e"), (nth(source, pos) == "E"))))) { - pos = (pos + 1); - if (isSxTruthy((isSxTruthy((pos < lenSrc)) && sxOr((nth(source, pos) == "+"), (nth(source, pos) == "-"))))) { - pos = (pos + 1); -} - readDigits(); -} - return parseNumber(slice(source, start, pos)); -})(); }; - var readSymbol = function() { return (function() { - var name = readIdent(); - return (isSxTruthy((name == "true")) ? true : (isSxTruthy((name == "false")) ? false : (isSxTruthy((name == "nil")) ? NIL : makeSymbol(name)))); -})(); }; - var readList = function(closeCh) { return (function() { - var items = []; - var readListLoop = function() { while(true) { skipWs(); -if (isSxTruthy((pos >= lenSrc))) { return error("Unterminated list"); } else { if (isSxTruthy((nth(source, pos) == closeCh))) { pos = (pos + 1); -return NIL; } else { items.push(readExpr()); -continue; } } } }; - readListLoop(); - return items; -})(); }; - var readMap = function() { return (function() { - var result = {}; - var readMapLoop = function() { while(true) { skipWs(); -if (isSxTruthy((pos >= lenSrc))) { return error("Unterminated map"); } else { if (isSxTruthy((nth(source, pos) == "}"))) { pos = (pos + 1); -return NIL; } else { { var keyExpr = readExpr(); -var keyStr = (isSxTruthy((typeOf(keyExpr) == "keyword")) ? keywordName(keyExpr) : (String(keyExpr))); -var valExpr = readExpr(); -result[keyStr] = valExpr; -continue; } } } } }; - readMapLoop(); - return result; -})(); }; - var readRawString = function() { return (function() { - var buf = ""; - var rawLoop = function() { while(true) { if (isSxTruthy((pos >= lenSrc))) { return error("Unterminated raw string"); } else { { var ch = nth(source, pos); -if (isSxTruthy((ch == "|"))) { pos = (pos + 1); -return NIL; } else { buf = (String(buf) + String(ch)); -pos = (pos + 1); -continue; } } } } }; - rawLoop(); - return buf; -})(); }; - var readExpr = function() { while(true) { skipWs(); -if (isSxTruthy((pos >= lenSrc))) { return error("Unexpected end of input"); } else { { var ch = nth(source, pos); -if (isSxTruthy((ch == "("))) { pos = (pos + 1); -return readList(")"); } else if (isSxTruthy((ch == "["))) { pos = (pos + 1); -return readList("]"); } else if (isSxTruthy((ch == "{"))) { pos = (pos + 1); -return readMap(); } else if (isSxTruthy((ch == "\""))) { return readString(); } else if (isSxTruthy((ch == ":"))) { return readKeyword(); } else if (isSxTruthy((ch == "`"))) { pos = (pos + 1); -return [makeSymbol("quasiquote"), readExpr()]; } else if (isSxTruthy((ch == ","))) { pos = (pos + 1); -if (isSxTruthy((isSxTruthy((pos < lenSrc)) && (nth(source, pos) == "@")))) { pos = (pos + 1); -return [makeSymbol("splice-unquote"), readExpr()]; } else { return [makeSymbol("unquote"), readExpr()]; } } else if (isSxTruthy((ch == "#"))) { pos = (pos + 1); -if (isSxTruthy((pos >= lenSrc))) { return error("Unexpected end of input after #"); } else { { var dispatchCh = nth(source, pos); -if (isSxTruthy((dispatchCh == ";"))) { pos = (pos + 1); -readExpr(); -continue; } else if (isSxTruthy((dispatchCh == "|"))) { pos = (pos + 1); -return readRawString(); } else if (isSxTruthy((dispatchCh == "'"))) { pos = (pos + 1); -return [makeSymbol("quote"), readExpr()]; } else if (isSxTruthy(isIdentStart(dispatchCh))) { { var macroName = readIdent(); -{ var handler = readerMacroGet(macroName); -if (isSxTruthy(handler)) { return handler(readExpr()); } else { return error((String("Unknown reader macro: #") + String(macroName))); } } } } else { return error((String("Unknown reader macro: #") + String(dispatchCh))); } } } } else if (isSxTruthy(sxOr((isSxTruthy((ch >= "0")) && (ch <= "9")), (isSxTruthy((ch == "-")) && isSxTruthy(((pos + 1) < lenSrc)) && (function() { - var nextCh = nth(source, (pos + 1)); - return (isSxTruthy((nextCh >= "0")) && (nextCh <= "9")); -})())))) { return readNumber(); } else if (isSxTruthy((isSxTruthy((ch == ".")) && isSxTruthy(((pos + 2) < lenSrc)) && isSxTruthy((nth(source, (pos + 1)) == ".")) && (nth(source, (pos + 2)) == ".")))) { pos = (pos + 3); -return makeSymbol("..."); } else if (isSxTruthy(isIdentStart(ch))) { return readSymbol(); } else { return error((String("Unexpected character: ") + String(ch))); } } } } }; - return (function() { - var exprs = []; - var parseLoop = function() { while(true) { skipWs(); -if (isSxTruthy((pos < lenSrc))) { exprs.push(readExpr()); -continue; } else { return NIL; } } }; - parseLoop(); - return exprs; -})(); -})(); }; - - // sx-serialize - var sxSerialize = function(val) { return (function() { var _m = typeOf(val); if (_m == "nil") return "nil"; if (_m == "boolean") return (isSxTruthy(val) ? "true" : "false"); if (_m == "number") return (String(val)); if (_m == "string") return (String("\"") + String(escapeString(val)) + String("\"")); if (_m == "symbol") return symbolName(val); if (_m == "keyword") return (String(":") + String(keywordName(val))); if (_m == "list") return (String("(") + String(join(" ", map(sxSerialize, val))) + String(")")); if (_m == "dict") return sxSerializeDict(val); if (_m == "sx-expr") return sxExprSource(val); return (String(val)); })(); }; - - // sx-serialize-dict - var sxSerializeDict = function(d) { return (String("{") + String(join(" ", reduce(function(acc, key) { return concat(acc, [(String(":") + String(key)), sxSerialize(dictGet(d, key))]); }, [], keys(d)))) + String("}")); }; - - // serialize - var serialize = sxSerialize; - - - // === Transpiled from adapter-html === - - // render-to-html - var renderToHtml = function(expr, env) { return (function() { var _m = typeOf(expr); if (_m == "nil") return ""; if (_m == "string") return escapeHtml(expr); if (_m == "number") return (String(expr)); if (_m == "boolean") return (isSxTruthy(expr) ? "true" : "false"); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? "" : renderListToHtml(expr, env)); if (_m == "symbol") return renderValueToHtml(trampoline(evalExpr(expr, env)), env); if (_m == "keyword") return escapeHtml(keywordName(expr)); if (_m == "raw-html") return rawHtmlContent(expr); return renderValueToHtml(trampoline(evalExpr(expr, env)), env); })(); }; - - // render-value-to-html - var renderValueToHtml = function(val, env) { return (function() { var _m = typeOf(val); if (_m == "nil") return ""; if (_m == "string") return escapeHtml(val); if (_m == "number") return (String(val)); if (_m == "boolean") return (isSxTruthy(val) ? "true" : "false"); if (_m == "list") return renderListToHtml(val, env); if (_m == "raw-html") return rawHtmlContent(val); return escapeHtml((String(val))); })(); }; - - // RENDER_HTML_FORMS - var RENDER_HTML_FORMS = ["if", "when", "cond", "case", "let", "let*", "begin", "do", "define", "defcomp", "defisland", "defmacro", "defstyle", "defhandler", "map", "map-indexed", "filter", "for-each"]; - - // render-html-form? - var isRenderHtmlForm = function(name) { return contains(RENDER_HTML_FORMS, name); }; - - // render-list-to-html - var renderListToHtml = function(expr, env) { return (isSxTruthy(isEmpty(expr)) ? "" : (function() { - var head = first(expr); - return (isSxTruthy(!isSxTruthy((typeOf(head) == "symbol"))) ? join("", map(function(x) { return renderValueToHtml(x, env); }, expr)) : (function() { - var name = symbolName(head); - var args = rest(expr); - return (isSxTruthy((name == "<>")) ? join("", map(function(x) { return renderToHtml(x, env); }, args)) : (isSxTruthy((name == "raw!")) ? join("", map(function(x) { return (String(trampoline(evalExpr(x, env)))); }, args)) : (isSxTruthy(contains(HTML_TAGS, name)) ? renderHtmlElement(name, args, env) : (isSxTruthy((isSxTruthy(startsWith(name, "~")) && isSxTruthy(envHas(env, name)) && isIsland(envGet(env, name)))) ? renderHtmlIsland(envGet(env, name), args, env) : (isSxTruthy(startsWith(name, "~")) ? (function() { - var val = envGet(env, name); - return (isSxTruthy(isComponent(val)) ? renderHtmlComponent(val, args, env) : (isSxTruthy(isMacro(val)) ? renderToHtml(expandMacro(val, args, env), env) : error((String("Unknown component: ") + String(name))))); -})() : (isSxTruthy(isRenderHtmlForm(name)) ? dispatchHtmlForm(name, expr, env) : (isSxTruthy((isSxTruthy(envHas(env, name)) && isMacro(envGet(env, name)))) ? renderToHtml(expandMacro(envGet(env, name), args, env), env) : renderValueToHtml(trampoline(evalExpr(expr, env)), env)))))))); -})()); -})()); }; - - // dispatch-html-form - var dispatchHtmlForm = function(name, expr, env) { return (isSxTruthy((name == "if")) ? (function() { - var condVal = trampoline(evalExpr(nth(expr, 1), env)); - return (isSxTruthy(condVal) ? renderToHtml(nth(expr, 2), env) : (isSxTruthy((len(expr) > 3)) ? renderToHtml(nth(expr, 3), env) : "")); -})() : (isSxTruthy((name == "when")) ? (isSxTruthy(!isSxTruthy(trampoline(evalExpr(nth(expr, 1), env)))) ? "" : join("", map(function(i) { return renderToHtml(nth(expr, i), env); }, range(2, len(expr))))) : (isSxTruthy((name == "cond")) ? (function() { - var branch = evalCond(rest(expr), env); - return (isSxTruthy(branch) ? renderToHtml(branch, env) : ""); -})() : (isSxTruthy((name == "case")) ? renderToHtml(trampoline(evalExpr(expr, env)), env) : (isSxTruthy(sxOr((name == "let"), (name == "let*"))) ? (function() { - var local = processBindings(nth(expr, 1), env); - return join("", map(function(i) { return renderToHtml(nth(expr, i), local); }, range(2, len(expr)))); -})() : (isSxTruthy(sxOr((name == "begin"), (name == "do"))) ? join("", map(function(i) { return renderToHtml(nth(expr, i), env); }, range(1, len(expr)))) : (isSxTruthy(isDefinitionForm(name)) ? (trampoline(evalExpr(expr, env)), "") : (isSxTruthy((name == "map")) ? (function() { - var f = trampoline(evalExpr(nth(expr, 1), env)); - var coll = trampoline(evalExpr(nth(expr, 2), env)); - return join("", map(function(item) { return (isSxTruthy(isLambda(f)) ? renderLambdaHtml(f, [item], env) : renderToHtml(apply(f, [item]), env)); }, coll)); -})() : (isSxTruthy((name == "map-indexed")) ? (function() { - var f = trampoline(evalExpr(nth(expr, 1), env)); - var coll = trampoline(evalExpr(nth(expr, 2), env)); - return join("", mapIndexed(function(i, item) { return (isSxTruthy(isLambda(f)) ? renderLambdaHtml(f, [i, item], env) : renderToHtml(apply(f, [i, item]), env)); }, coll)); -})() : (isSxTruthy((name == "filter")) ? renderToHtml(trampoline(evalExpr(expr, env)), env) : (isSxTruthy((name == "for-each")) ? (function() { - var f = trampoline(evalExpr(nth(expr, 1), env)); - var coll = trampoline(evalExpr(nth(expr, 2), env)); - return join("", map(function(item) { return (isSxTruthy(isLambda(f)) ? renderLambdaHtml(f, [item], env) : renderToHtml(apply(f, [item]), env)); }, coll)); -})() : renderValueToHtml(trampoline(evalExpr(expr, env)), env)))))))))))); }; - - // render-lambda-html - var renderLambdaHtml = function(f, args, env) { return (function() { - var local = envMerge(lambdaClosure(f), env); - forEachIndexed(function(i, p) { return envSet(local, p, nth(args, i)); }, lambdaParams(f)); - return renderToHtml(lambdaBody(f), local); -})(); }; - - // render-html-component - var renderHtmlComponent = function(comp, args, env) { return (function() { - var kwargs = {}; - var children = []; - reduce(function(state, arg) { return (function() { - var skip = get(state, "skip"); - return (isSxTruthy(skip) ? assoc(state, "skip", false, "i", (get(state, "i") + 1)) : (isSxTruthy((isSxTruthy((typeOf(arg) == "keyword")) && ((get(state, "i") + 1) < len(args)))) ? (function() { - var val = trampoline(evalExpr(nth(args, (get(state, "i") + 1)), env)); - kwargs[keywordName(arg)] = val; - return assoc(state, "skip", true, "i", (get(state, "i") + 1)); -})() : (append_b(children, arg), assoc(state, "i", (get(state, "i") + 1))))); -})(); }, {["i"]: 0, ["skip"]: false}, args); - return (function() { - var local = envMerge(componentClosure(comp), env); - { var _c = componentParams(comp); for (var _i = 0; _i < _c.length; _i++) { var p = _c[_i]; local[p] = (isSxTruthy(dictHas(kwargs, p)) ? dictGet(kwargs, p) : NIL); } } - if (isSxTruthy(componentHasChildren(comp))) { - local["children"] = makeRawHtml(join("", map(function(c) { return renderToHtml(c, env); }, children))); -} - return renderToHtml(componentBody(comp), local); -})(); -})(); }; - - // render-html-element - var renderHtmlElement = function(tag, args, env) { return (function() { - var parsed = parseElementArgs(args, env); - var attrs = first(parsed); - var children = nth(parsed, 1); - var isVoid = contains(VOID_ELEMENTS, tag); - return (String("<") + String(tag) + String(renderAttrs(attrs)) + String((isSxTruthy(isVoid) ? " />" : (String(">") + String(join("", map(function(c) { return renderToHtml(c, env); }, children))) + String(""))))); -})(); }; - - // render-html-island - var renderHtmlIsland = function(island, args, env) { return (function() { - var kwargs = {}; - var children = []; - reduce(function(state, arg) { return (function() { - var skip = get(state, "skip"); - return (isSxTruthy(skip) ? assoc(state, "skip", false, "i", (get(state, "i") + 1)) : (isSxTruthy((isSxTruthy((typeOf(arg) == "keyword")) && ((get(state, "i") + 1) < len(args)))) ? (function() { - var val = trampoline(evalExpr(nth(args, (get(state, "i") + 1)), env)); - kwargs[keywordName(arg)] = val; - return assoc(state, "skip", true, "i", (get(state, "i") + 1)); -})() : (append_b(children, arg), assoc(state, "i", (get(state, "i") + 1))))); -})(); }, {["i"]: 0, ["skip"]: false}, args); - return (function() { - var local = envMerge(componentClosure(island), env); - var islandName = componentName(island); - { var _c = componentParams(island); for (var _i = 0; _i < _c.length; _i++) { var p = _c[_i]; local[p] = (isSxTruthy(dictHas(kwargs, p)) ? dictGet(kwargs, p) : NIL); } } - if (isSxTruthy(componentHasChildren(island))) { - local["children"] = makeRawHtml(join("", map(function(c) { return renderToHtml(c, env); }, children))); -} - return (function() { - var bodyHtml = renderToHtml(componentBody(island), local); - var stateJson = serializeIslandState(kwargs); - return (String("
") + String(bodyHtml) + String("
")); -})(); -})(); -})(); }; - - // serialize-island-state - var serializeIslandState = function(kwargs) { return (isSxTruthy(isEmptyDict(kwargs)) ? NIL : jsonSerialize(kwargs)); }; - - - // === Transpiled from adapter-sx === - - // render-to-sx - var renderToSx = function(expr, env) { return (function() { - var result = aser(expr, env); - return (isSxTruthy((typeOf(result) == "string")) ? result : serialize(result)); -})(); }; - - // aser - var aser = function(expr, env) { return (function() { var _m = typeOf(expr); if (_m == "number") return expr; if (_m == "string") return expr; if (_m == "boolean") return expr; if (_m == "nil") return NIL; if (_m == "symbol") return (function() { - var name = symbolName(expr); - return (isSxTruthy(envHas(env, name)) ? envGet(env, name) : (isSxTruthy(isPrimitive(name)) ? getPrimitive(name) : (isSxTruthy((name == "true")) ? true : (isSxTruthy((name == "false")) ? false : (isSxTruthy((name == "nil")) ? NIL : error((String("Undefined symbol: ") + String(name)))))))); -})(); if (_m == "keyword") return keywordName(expr); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? [] : aserList(expr, env)); return expr; })(); }; - - // aser-list - var aserList = function(expr, env) { return (function() { - var head = first(expr); - var args = rest(expr); - return (isSxTruthy(!isSxTruthy((typeOf(head) == "symbol"))) ? map(function(x) { return aser(x, env); }, expr) : (function() { - var name = symbolName(head); - return (isSxTruthy((name == "<>")) ? aserFragment(args, env) : (isSxTruthy(startsWith(name, "~")) ? aserCall(name, args, env) : (isSxTruthy(contains(HTML_TAGS, name)) ? aserCall(name, args, env) : (isSxTruthy(sxOr(isSpecialForm(name), isHoForm(name))) ? aserSpecial(name, expr, env) : (isSxTruthy((isSxTruthy(envHas(env, name)) && isMacro(envGet(env, name)))) ? aser(expandMacro(envGet(env, name), args, env), env) : (function() { - var f = trampoline(evalExpr(head, env)); - var evaledArgs = map(function(a) { return trampoline(evalExpr(a, env)); }, args); - return (isSxTruthy((isSxTruthy(isCallable(f)) && isSxTruthy(!isSxTruthy(isLambda(f))) && isSxTruthy(!isSxTruthy(isComponent(f))) && !isSxTruthy(isIsland(f)))) ? apply(f, evaledArgs) : (isSxTruthy(isLambda(f)) ? trampoline(callLambda(f, evaledArgs, env)) : (isSxTruthy(isComponent(f)) ? aserCall((String("~") + String(componentName(f))), args, env) : (isSxTruthy(isIsland(f)) ? aserCall((String("~") + String(componentName(f))), args, env) : error((String("Not callable: ") + String(inspect(f)))))))); -})()))))); -})()); -})(); }; - - // aser-fragment - var aserFragment = function(children, env) { return (function() { - var parts = filter(function(x) { return !isSxTruthy(isNil(x)); }, map(function(c) { return aser(c, env); }, children)); - return (isSxTruthy(isEmpty(parts)) ? "" : (String("(<> ") + String(join(" ", map(serialize, parts))) + String(")"))); -})(); }; - - // aser-call - var aserCall = function(name, args, env) { return (function() { - var parts = [name]; - reduce(function(state, arg) { return (function() { - var skip = get(state, "skip"); - return (isSxTruthy(skip) ? assoc(state, "skip", false, "i", (get(state, "i") + 1)) : (isSxTruthy((isSxTruthy((typeOf(arg) == "keyword")) && ((get(state, "i") + 1) < len(args)))) ? (function() { - var val = aser(nth(args, (get(state, "i") + 1)), env); - if (isSxTruthy(!isSxTruthy(isNil(val)))) { - parts.push((String(":") + String(keywordName(arg)))); - parts.push(serialize(val)); -} - return assoc(state, "skip", true, "i", (get(state, "i") + 1)); -})() : (function() { - var val = aser(arg, env); - if (isSxTruthy(!isSxTruthy(isNil(val)))) { - parts.push(serialize(val)); -} - return assoc(state, "i", (get(state, "i") + 1)); -})())); -})(); }, {["i"]: 0, ["skip"]: false}, args); - return (String("(") + String(join(" ", parts)) + String(")")); -})(); }; - - // SPECIAL_FORM_NAMES - var SPECIAL_FORM_NAMES = ["if", "when", "cond", "case", "and", "or", "let", "let*", "lambda", "fn", "define", "defcomp", "defmacro", "defstyle", "defhandler", "defpage", "defquery", "defaction", "defrelation", "begin", "do", "quote", "quasiquote", "->", "set!", "letrec", "dynamic-wind", "defisland"]; - - // HO_FORM_NAMES - var HO_FORM_NAMES = ["map", "map-indexed", "filter", "reduce", "some", "every?", "for-each"]; - - // special-form? - var isSpecialForm = function(name) { return contains(SPECIAL_FORM_NAMES, name); }; - - // ho-form? - var isHoForm = function(name) { return contains(HO_FORM_NAMES, name); }; - - // aser-special - var aserSpecial = function(name, expr, env) { return (function() { - var args = rest(expr); - return (isSxTruthy((name == "if")) ? (isSxTruthy(trampoline(evalExpr(first(args), env))) ? aser(nth(args, 1), env) : (isSxTruthy((len(args) > 2)) ? aser(nth(args, 2), env) : NIL)) : (isSxTruthy((name == "when")) ? (isSxTruthy(!isSxTruthy(trampoline(evalExpr(first(args), env)))) ? NIL : (function() { - var result = NIL; - { var _c = rest(args); for (var _i = 0; _i < _c.length; _i++) { var body = _c[_i]; result = aser(body, env); } } - return result; -})()) : (isSxTruthy((name == "cond")) ? (function() { - var branch = evalCond(args, env); - return (isSxTruthy(branch) ? aser(branch, env) : NIL); -})() : (isSxTruthy((name == "case")) ? (function() { - var matchVal = trampoline(evalExpr(first(args), env)); - var clauses = rest(args); - return evalCaseAser(matchVal, clauses, env); -})() : (isSxTruthy(sxOr((name == "let"), (name == "let*"))) ? (function() { - var local = processBindings(first(args), env); - var result = NIL; - { var _c = rest(args); for (var _i = 0; _i < _c.length; _i++) { var body = _c[_i]; result = aser(body, local); } } - return result; -})() : (isSxTruthy(sxOr((name == "begin"), (name == "do"))) ? (function() { - var result = NIL; - { var _c = args; for (var _i = 0; _i < _c.length; _i++) { var body = _c[_i]; result = aser(body, env); } } - return result; -})() : (isSxTruthy((name == "and")) ? (function() { - var result = true; - some(function(arg) { result = trampoline(evalExpr(arg, env)); -return !isSxTruthy(result); }, args); - return result; -})() : (isSxTruthy((name == "or")) ? (function() { - var result = false; - some(function(arg) { result = trampoline(evalExpr(arg, env)); -return result; }, args); - return result; -})() : (isSxTruthy((name == "map")) ? (function() { - var f = trampoline(evalExpr(first(args), env)); - var coll = trampoline(evalExpr(nth(args, 1), env)); - return map(function(item) { return (isSxTruthy(isLambda(f)) ? (function() { - var local = envMerge(lambdaClosure(f), env); - local[first(lambdaParams(f))] = item; - return aser(lambdaBody(f), local); -})() : invoke(f, item)); }, coll); -})() : (isSxTruthy((name == "map-indexed")) ? (function() { - var f = trampoline(evalExpr(first(args), env)); - var coll = trampoline(evalExpr(nth(args, 1), env)); - return mapIndexed(function(i, item) { return (isSxTruthy(isLambda(f)) ? (function() { - var local = envMerge(lambdaClosure(f), env); - local[first(lambdaParams(f))] = i; - local[nth(lambdaParams(f), 1)] = item; - return aser(lambdaBody(f), local); -})() : invoke(f, i, item)); }, coll); -})() : (isSxTruthy((name == "for-each")) ? (function() { - var f = trampoline(evalExpr(first(args), env)); - var coll = trampoline(evalExpr(nth(args, 1), env)); - var results = []; - { var _c = coll; for (var _i = 0; _i < _c.length; _i++) { var item = _c[_i]; (isSxTruthy(isLambda(f)) ? (function() { - var local = envMerge(lambdaClosure(f), env); - local[first(lambdaParams(f))] = item; - return append_b(results, aser(lambdaBody(f), local)); -})() : invoke(f, item)); } } - return (isSxTruthy(isEmpty(results)) ? NIL : results); -})() : (isSxTruthy((name == "defisland")) ? (trampoline(evalExpr(expr, env)), serialize(expr)) : (isSxTruthy(sxOr((name == "define"), (name == "defcomp"), (name == "defmacro"), (name == "defstyle"), (name == "defhandler"), (name == "defpage"), (name == "defquery"), (name == "defaction"), (name == "defrelation"))) ? (trampoline(evalExpr(expr, env)), NIL) : trampoline(evalExpr(expr, env))))))))))))))); -})(); }; - - // eval-case-aser - var evalCaseAser = function(matchVal, clauses, env) { return (isSxTruthy((len(clauses) < 2)) ? NIL : (function() { - var test = first(clauses); - var body = nth(clauses, 1); - return (isSxTruthy(sxOr((isSxTruthy((typeOf(test) == "keyword")) && (keywordName(test) == "else")), (isSxTruthy((typeOf(test) == "symbol")) && sxOr((symbolName(test) == ":else"), (symbolName(test) == "else"))))) ? aser(body, env) : (isSxTruthy((matchVal == trampoline(evalExpr(test, env)))) ? aser(body, env) : evalCaseAser(matchVal, slice(clauses, 2), env))); -})()); }; - - - // === Transpiled from adapter-dom === - - // SVG_NS - var SVG_NS = "http://www.w3.org/2000/svg"; - - // MATH_NS - var MATH_NS = "http://www.w3.org/1998/Math/MathML"; - - // render-to-dom - var renderToDom = function(expr, env, ns) { return (function() { var _m = typeOf(expr); if (_m == "nil") return createFragment(); if (_m == "boolean") return createFragment(); if (_m == "raw-html") return domParseHtml(rawHtmlContent(expr)); if (_m == "string") return createTextNode(expr); if (_m == "number") return createTextNode((String(expr))); if (_m == "symbol") return renderToDom(trampoline(evalExpr(expr, env)), env, ns); if (_m == "keyword") return createTextNode(keywordName(expr)); if (_m == "dom-node") return expr; if (_m == "dict") return createFragment(); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? createFragment() : renderDomList(expr, env, ns)); return createTextNode((String(expr))); })(); }; - - // render-dom-list - var renderDomList = function(expr, env, ns) { return (function() { - var head = first(expr); - return (isSxTruthy((typeOf(head) == "symbol")) ? (function() { - var name = symbolName(head); - var args = rest(expr); - return (isSxTruthy((name == "raw!")) ? renderDomRaw(args, env) : (isSxTruthy((name == "<>")) ? renderDomFragment(args, env, ns) : (isSxTruthy(startsWith(name, "html:")) ? renderDomElement(slice(name, 5), args, env, ns) : (isSxTruthy(isRenderDomForm(name)) ? (isSxTruthy((isSxTruthy(contains(HTML_TAGS, name)) && sxOr((isSxTruthy((len(args) > 0)) && (typeOf(first(args)) == "keyword")), ns))) ? renderDomElement(name, args, env, ns) : dispatchRenderForm(name, expr, env, ns)) : (isSxTruthy((isSxTruthy(envHas(env, name)) && isMacro(envGet(env, name)))) ? renderToDom(expandMacro(envGet(env, name), args, env), env, ns) : (isSxTruthy(contains(HTML_TAGS, name)) ? renderDomElement(name, args, env, ns) : (isSxTruthy((isSxTruthy(startsWith(name, "~")) && isSxTruthy(envHas(env, name)) && isIsland(envGet(env, name)))) ? renderDomIsland(envGet(env, name), args, env, ns) : (isSxTruthy(startsWith(name, "~")) ? (function() { - var comp = envGet(env, name); - return (isSxTruthy(isComponent(comp)) ? renderDomComponent(comp, args, env, ns) : renderDomUnknownComponent(name)); -})() : (isSxTruthy((isSxTruthy((indexOf_(name, "-") > 0)) && isSxTruthy((len(args) > 0)) && (typeOf(first(args)) == "keyword"))) ? renderDomElement(name, args, env, ns) : (isSxTruthy(ns) ? renderDomElement(name, args, env, ns) : (isSxTruthy((isSxTruthy((name == "deref")) && _islandScope)) ? (function() { - var sigOrVal = trampoline(evalExpr(first(args), env)); - return (isSxTruthy(isSignal(sigOrVal)) ? reactiveText(sigOrVal) : createTextNode((String(deref(sigOrVal))))); -})() : renderToDom(trampoline(evalExpr(expr, env)), env, ns)))))))))))); -})() : (isSxTruthy(sxOr(isLambda(head), (typeOf(head) == "list"))) ? renderToDom(trampoline(evalExpr(expr, env)), env, ns) : (function() { - var frag = createFragment(); - { var _c = expr; for (var _i = 0; _i < _c.length; _i++) { var x = _c[_i]; domAppend(frag, renderToDom(x, env, ns)); } } - return frag; -})())); -})(); }; - - // render-dom-element - var renderDomElement = function(tag, args, env, ns) { return (function() { - var newNs = (isSxTruthy((tag == "svg")) ? SVG_NS : (isSxTruthy((tag == "math")) ? MATH_NS : ns)); - var el = domCreateElement(tag, newNs); - reduce(function(state, arg) { return (function() { - var skip = get(state, "skip"); - return (isSxTruthy(skip) ? assoc(state, "skip", false, "i", (get(state, "i") + 1)) : (isSxTruthy((isSxTruthy((typeOf(arg) == "keyword")) && ((get(state, "i") + 1) < len(args)))) ? (function() { - var attrName = keywordName(arg); - var attrExpr = nth(args, (get(state, "i") + 1)); - (isSxTruthy(startsWith(attrName, "on-")) ? (function() { - var attrVal = trampoline(evalExpr(attrExpr, env)); - return (isSxTruthy(isCallable(attrVal)) ? domListen(el, slice(attrName, 3), attrVal) : NIL); -})() : (isSxTruthy((attrName == "bind")) ? (function() { - var attrVal = trampoline(evalExpr(attrExpr, env)); - return (isSxTruthy(isSignal(attrVal)) ? bindInput(el, attrVal) : NIL); -})() : (isSxTruthy((attrName == "ref")) ? (function() { - var attrVal = trampoline(evalExpr(attrExpr, env)); - return dictSet(attrVal, "current", el); -})() : (isSxTruthy((attrName == "key")) ? (function() { - var attrVal = trampoline(evalExpr(attrExpr, env)); - return domSetAttr(el, "key", (String(attrVal))); -})() : (isSxTruthy(_islandScope) ? reactiveAttr(el, attrName, function() { return trampoline(evalExpr(attrExpr, env)); }) : (function() { - var attrVal = trampoline(evalExpr(attrExpr, env)); - return (isSxTruthy(sxOr(isNil(attrVal), (attrVal == false))) ? NIL : (isSxTruthy(contains(BOOLEAN_ATTRS, attrName)) ? (isSxTruthy(attrVal) ? domSetAttr(el, attrName, "") : NIL) : (isSxTruthy((attrVal == true)) ? domSetAttr(el, attrName, "") : domSetAttr(el, attrName, (String(attrVal)))))); -})()))))); - return assoc(state, "skip", true, "i", (get(state, "i") + 1)); -})() : ((isSxTruthy(!isSxTruthy(contains(VOID_ELEMENTS, tag))) ? domAppend(el, renderToDom(arg, env, newNs)) : NIL), assoc(state, "i", (get(state, "i") + 1))))); -})(); }, {["i"]: 0, ["skip"]: false}, args); - return el; -})(); }; - - // render-dom-component - var renderDomComponent = function(comp, args, env, ns) { return (function() { - var kwargs = {}; - var children = []; - reduce(function(state, arg) { return (function() { - var skip = get(state, "skip"); - return (isSxTruthy(skip) ? assoc(state, "skip", false, "i", (get(state, "i") + 1)) : (isSxTruthy((isSxTruthy((typeOf(arg) == "keyword")) && ((get(state, "i") + 1) < len(args)))) ? (function() { - var val = trampoline(evalExpr(nth(args, (get(state, "i") + 1)), env)); - kwargs[keywordName(arg)] = val; - return assoc(state, "skip", true, "i", (get(state, "i") + 1)); -})() : (append_b(children, arg), assoc(state, "i", (get(state, "i") + 1))))); -})(); }, {["i"]: 0, ["skip"]: false}, args); - return (function() { - var local = envMerge(componentClosure(comp), env); - { var _c = componentParams(comp); for (var _i = 0; _i < _c.length; _i++) { var p = _c[_i]; local[p] = (isSxTruthy(dictHas(kwargs, p)) ? dictGet(kwargs, p) : NIL); } } - if (isSxTruthy(componentHasChildren(comp))) { - (function() { - var childFrag = createFragment(); - { var _c = children; for (var _i = 0; _i < _c.length; _i++) { var c = _c[_i]; domAppend(childFrag, renderToDom(c, env, ns)); } } - return envSet(local, "children", childFrag); -})(); -} - return renderToDom(componentBody(comp), local, ns); -})(); -})(); }; - - // render-dom-fragment - var renderDomFragment = function(args, env, ns) { return (function() { - var frag = createFragment(); - { var _c = args; for (var _i = 0; _i < _c.length; _i++) { var x = _c[_i]; domAppend(frag, renderToDom(x, env, ns)); } } - return frag; -})(); }; - - // render-dom-raw - var renderDomRaw = function(args, env) { return (function() { - var frag = createFragment(); - { var _c = args; for (var _i = 0; _i < _c.length; _i++) { var arg = _c[_i]; (function() { - var val = trampoline(evalExpr(arg, env)); - return (isSxTruthy((typeOf(val) == "string")) ? domAppend(frag, domParseHtml(val)) : (isSxTruthy((typeOf(val) == "dom-node")) ? domAppend(frag, domClone(val)) : (isSxTruthy(!isSxTruthy(isNil(val))) ? domAppend(frag, createTextNode((String(val)))) : NIL))); -})(); } } - return frag; -})(); }; - - // render-dom-unknown-component - var renderDomUnknownComponent = function(name) { return error((String("Unknown component: ") + String(name))); }; - - // RENDER_DOM_FORMS - var RENDER_DOM_FORMS = ["if", "when", "cond", "case", "let", "let*", "begin", "do", "define", "defcomp", "defisland", "defmacro", "defstyle", "defhandler", "map", "map-indexed", "filter", "for-each", "portal", "error-boundary"]; - - // render-dom-form? - var isRenderDomForm = function(name) { return contains(RENDER_DOM_FORMS, name); }; - - // dispatch-render-form - var dispatchRenderForm = function(name, expr, env, ns) { return (isSxTruthy((name == "if")) ? (isSxTruthy(_islandScope) ? (function() { - var marker = createComment("r-if"); - var currentNodes = []; - var initialResult = NIL; - effect(function() { return (function() { - var result = (function() { - var condVal = trampoline(evalExpr(nth(expr, 1), env)); - return (isSxTruthy(condVal) ? renderToDom(nth(expr, 2), env, ns) : (isSxTruthy((len(expr) > 3)) ? renderToDom(nth(expr, 3), env, ns) : createFragment())); -})(); - return (isSxTruthy(domParent(marker)) ? (forEach(function(n) { return domRemove(n); }, currentNodes), (currentNodes = (isSxTruthy(domIsFragment(result)) ? domChildNodes(result) : [result])), domInsertAfter(marker, result)) : (initialResult = result)); -})(); }); - return (function() { - var frag = createFragment(); - domAppend(frag, marker); - if (isSxTruthy(initialResult)) { - currentNodes = (isSxTruthy(domIsFragment(initialResult)) ? domChildNodes(initialResult) : [initialResult]); - domAppend(frag, initialResult); -} - return frag; -})(); -})() : (function() { - var condVal = trampoline(evalExpr(nth(expr, 1), env)); - return (isSxTruthy(condVal) ? renderToDom(nth(expr, 2), env, ns) : (isSxTruthy((len(expr) > 3)) ? renderToDom(nth(expr, 3), env, ns) : createFragment())); -})()) : (isSxTruthy((name == "when")) ? (isSxTruthy(_islandScope) ? (function() { - var marker = createComment("r-when"); - var currentNodes = []; - var initialResult = NIL; - effect(function() { return (isSxTruthy(domParent(marker)) ? (forEach(function(n) { return domRemove(n); }, currentNodes), (currentNodes = []), (isSxTruthy(trampoline(evalExpr(nth(expr, 1), env))) ? (function() { - var frag = createFragment(); - { var _c = range(2, len(expr)); for (var _i = 0; _i < _c.length; _i++) { var i = _c[_i]; domAppend(frag, renderToDom(nth(expr, i), env, ns)); } } - currentNodes = domChildNodes(frag); - return domInsertAfter(marker, frag); -})() : NIL)) : (isSxTruthy(trampoline(evalExpr(nth(expr, 1), env))) ? (function() { - var frag = createFragment(); - { var _c = range(2, len(expr)); for (var _i = 0; _i < _c.length; _i++) { var i = _c[_i]; domAppend(frag, renderToDom(nth(expr, i), env, ns)); } } - currentNodes = domChildNodes(frag); - return (initialResult = frag); -})() : NIL)); }); - return (function() { - var frag = createFragment(); - domAppend(frag, marker); - if (isSxTruthy(initialResult)) { - domAppend(frag, initialResult); -} - return frag; -})(); -})() : (isSxTruthy(!isSxTruthy(trampoline(evalExpr(nth(expr, 1), env)))) ? createFragment() : (function() { - var frag = createFragment(); - { var _c = range(2, len(expr)); for (var _i = 0; _i < _c.length; _i++) { var i = _c[_i]; domAppend(frag, renderToDom(nth(expr, i), env, ns)); } } - return frag; -})())) : (isSxTruthy((name == "cond")) ? (isSxTruthy(_islandScope) ? (function() { - var marker = createComment("r-cond"); - var currentNodes = []; - var initialResult = NIL; - effect(function() { return (function() { - var branch = evalCond(rest(expr), env); - return (isSxTruthy(domParent(marker)) ? (forEach(function(n) { return domRemove(n); }, currentNodes), (currentNodes = []), (isSxTruthy(branch) ? (function() { - var result = renderToDom(branch, env, ns); - currentNodes = (isSxTruthy(domIsFragment(result)) ? domChildNodes(result) : [result]); - return domInsertAfter(marker, result); -})() : NIL)) : (isSxTruthy(branch) ? (function() { - var result = renderToDom(branch, env, ns); - currentNodes = (isSxTruthy(domIsFragment(result)) ? domChildNodes(result) : [result]); - return (initialResult = result); -})() : NIL)); -})(); }); - return (function() { - var frag = createFragment(); - domAppend(frag, marker); - if (isSxTruthy(initialResult)) { - domAppend(frag, initialResult); -} - return frag; -})(); -})() : (function() { - var branch = evalCond(rest(expr), env); - return (isSxTruthy(branch) ? renderToDom(branch, env, ns) : createFragment()); -})()) : (isSxTruthy((name == "case")) ? renderToDom(trampoline(evalExpr(expr, env)), env, ns) : (isSxTruthy(sxOr((name == "let"), (name == "let*"))) ? (function() { - var local = processBindings(nth(expr, 1), env); - var frag = createFragment(); - { var _c = range(2, len(expr)); for (var _i = 0; _i < _c.length; _i++) { var i = _c[_i]; domAppend(frag, renderToDom(nth(expr, i), local, ns)); } } - return frag; -})() : (isSxTruthy(sxOr((name == "begin"), (name == "do"))) ? (function() { - var frag = createFragment(); - { var _c = range(1, len(expr)); for (var _i = 0; _i < _c.length; _i++) { var i = _c[_i]; domAppend(frag, renderToDom(nth(expr, i), env, ns)); } } - return frag; -})() : (isSxTruthy(isDefinitionForm(name)) ? (trampoline(evalExpr(expr, env)), createFragment()) : (isSxTruthy((name == "map")) ? (function() { - var collExpr = nth(expr, 2); - return (isSxTruthy((isSxTruthy(_islandScope) && isSxTruthy((typeOf(collExpr) == "list")) && isSxTruthy((len(collExpr) > 1)) && (first(collExpr) == "deref"))) ? (function() { - var f = trampoline(evalExpr(nth(expr, 1), env)); - var sig = trampoline(evalExpr(nth(collExpr, 1), env)); - return (isSxTruthy(isSignal(sig)) ? reactiveList(f, sig, env, ns) : (function() { - var coll = deref(sig); - var frag = createFragment(); - { var _c = coll; for (var _i = 0; _i < _c.length; _i++) { var item = _c[_i]; (function() { - var val = (isSxTruthy(isLambda(f)) ? renderLambdaDom(f, [item], env, ns) : renderToDom(apply(f, [item]), env, ns)); - return domAppend(frag, val); -})(); } } - return frag; -})()); -})() : (function() { - var f = trampoline(evalExpr(nth(expr, 1), env)); - var coll = trampoline(evalExpr(nth(expr, 2), env)); - var frag = createFragment(); - { var _c = coll; for (var _i = 0; _i < _c.length; _i++) { var item = _c[_i]; (function() { - var val = (isSxTruthy(isLambda(f)) ? renderLambdaDom(f, [item], env, ns) : renderToDom(apply(f, [item]), env, ns)); - return domAppend(frag, val); -})(); } } - return frag; -})()); -})() : (isSxTruthy((name == "map-indexed")) ? (function() { - var f = trampoline(evalExpr(nth(expr, 1), env)); - var coll = trampoline(evalExpr(nth(expr, 2), env)); - var frag = createFragment(); - forEachIndexed(function(i, item) { return (function() { - var val = (isSxTruthy(isLambda(f)) ? renderLambdaDom(f, [i, item], env, ns) : renderToDom(apply(f, [i, item]), env, ns)); - return domAppend(frag, val); -})(); }, coll); - return frag; -})() : (isSxTruthy((name == "filter")) ? renderToDom(trampoline(evalExpr(expr, env)), env, ns) : (isSxTruthy((name == "portal")) ? renderDomPortal(rest(expr), env, ns) : (isSxTruthy((name == "error-boundary")) ? renderDomErrorBoundary(rest(expr), env, ns) : (isSxTruthy((name == "for-each")) ? (function() { - var f = trampoline(evalExpr(nth(expr, 1), env)); - var coll = trampoline(evalExpr(nth(expr, 2), env)); - var frag = createFragment(); - { var _c = coll; for (var _i = 0; _i < _c.length; _i++) { var item = _c[_i]; (function() { - var val = (isSxTruthy(isLambda(f)) ? renderLambdaDom(f, [item], env, ns) : renderToDom(apply(f, [item]), env, ns)); - return domAppend(frag, val); -})(); } } - return frag; -})() : renderToDom(trampoline(evalExpr(expr, env)), env, ns)))))))))))))); }; - - // render-lambda-dom - var renderLambdaDom = function(f, args, env, ns) { return (function() { - var local = envMerge(lambdaClosure(f), env); - forEachIndexed(function(i, p) { return envSet(local, p, nth(args, i)); }, lambdaParams(f)); - return renderToDom(lambdaBody(f), local, ns); -})(); }; - - // render-dom-island - var renderDomIsland = function(island, args, env, ns) { return (function() { - var kwargs = {}; - var children = []; - reduce(function(state, arg) { return (function() { - var skip = get(state, "skip"); - return (isSxTruthy(skip) ? assoc(state, "skip", false, "i", (get(state, "i") + 1)) : (isSxTruthy((isSxTruthy((typeOf(arg) == "keyword")) && ((get(state, "i") + 1) < len(args)))) ? (function() { - var val = trampoline(evalExpr(nth(args, (get(state, "i") + 1)), env)); - kwargs[keywordName(arg)] = val; - return assoc(state, "skip", true, "i", (get(state, "i") + 1)); -})() : (append_b(children, arg), assoc(state, "i", (get(state, "i") + 1))))); -})(); }, {["i"]: 0, ["skip"]: false}, args); - return (function() { - var local = envMerge(componentClosure(island), env); - var islandName = componentName(island); - { var _c = componentParams(island); for (var _i = 0; _i < _c.length; _i++) { var p = _c[_i]; local[p] = (isSxTruthy(dictHas(kwargs, p)) ? dictGet(kwargs, p) : NIL); } } - if (isSxTruthy(componentHasChildren(island))) { - (function() { - var childFrag = createFragment(); - { var _c = children; for (var _i = 0; _i < _c.length; _i++) { var c = _c[_i]; domAppend(childFrag, renderToDom(c, env, ns)); } } - return envSet(local, "children", childFrag); -})(); -} - return (function() { - var container = domCreateElement("div", NIL); - var disposers = []; - domSetAttr(container, "data-sx-island", islandName); - return (function() { - var bodyDom = withIslandScope(function(disposable) { return append_b(disposers, disposable); }, function() { return renderToDom(componentBody(island), local, ns); }); - domAppend(container, bodyDom); - domSetData(container, "sx-disposers", disposers); - return container; -})(); -})(); -})(); -})(); }; - - // reactive-text - var reactiveText = function(sig) { return (function() { - var node = createTextNode((String(deref(sig)))); - effect(function() { return domSetTextContent(node, (String(deref(sig)))); }); - return node; -})(); }; - - // reactive-attr - var reactiveAttr = function(el, attrName, computeFn) { return effect(function() { return (function() { - var val = computeFn(); - return (isSxTruthy(sxOr(isNil(val), (val == false))) ? domRemoveAttr(el, attrName) : (isSxTruthy((val == true)) ? domSetAttr(el, attrName, "") : domSetAttr(el, attrName, (String(val))))); -})(); }); }; - - // reactive-fragment - var reactiveFragment = function(testFn, renderFn, env, ns) { return (function() { - var marker = createComment("island-fragment"); - var currentNodes = []; - effect(function() { { var _c = currentNodes; for (var _i = 0; _i < _c.length; _i++) { var n = _c[_i]; domRemove(n); } } -currentNodes = []; -return (isSxTruthy(testFn()) ? (function() { - var frag = renderFn(); - currentNodes = domChildNodes(frag); - return domInsertAfter(marker, frag); -})() : NIL); }); - return marker; -})(); }; - - // render-list-item - var renderListItem = function(mapFn, item, env, ns) { return (isSxTruthy(isLambda(mapFn)) ? renderLambdaDom(mapFn, [item], env, ns) : renderToDom(apply(mapFn, [item]), env, ns)); }; - - // extract-key - var extractKey = function(node, index) { return (function() { - var k = domGetAttr(node, "key"); - return (isSxTruthy(k) ? (domRemoveAttr(node, "key"), k) : (function() { - var dk = domGetData(node, "key"); - return (isSxTruthy(dk) ? (String(dk)) : (String("__idx_") + String(index))); -})()); -})(); }; - - // reactive-list - var reactiveList = function(mapFn, itemsSig, env, ns) { return (function() { - var container = createFragment(); - var marker = createComment("island-list"); - var keyMap = {}; - var keyOrder = []; - domAppend(container, marker); - effect(function() { return (function() { - var items = deref(itemsSig); - return (isSxTruthy(domParent(marker)) ? (function() { - var newMap = {}; - var newKeys = []; - var hasKeys = false; - forEachIndexed(function(idx, item) { return (function() { - var rendered = renderListItem(mapFn, item, env, ns); - var key = extractKey(rendered, idx); - if (isSxTruthy((isSxTruthy(!isSxTruthy(hasKeys)) && !isSxTruthy(startsWith(key, "__idx_"))))) { - hasKeys = true; -} - (isSxTruthy(dictHas(keyMap, key)) ? dictSet(newMap, key, dictGet(keyMap, key)) : dictSet(newMap, key, rendered)); - return append_b(newKeys, key); -})(); }, items); - (isSxTruthy(!isSxTruthy(hasKeys)) ? (domRemoveChildrenAfter(marker), (function() { - var frag = createFragment(); - { var _c = newKeys; for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; domAppend(frag, dictGet(newMap, k)); } } - return domInsertAfter(marker, frag); -})()) : (forEach(function(oldKey) { return (isSxTruthy(!isSxTruthy(dictHas(newMap, oldKey))) ? domRemove(dictGet(keyMap, oldKey)) : NIL); }, keyOrder), (function() { - var cursor = marker; - return forEach(function(k) { return (function() { - var node = dictGet(newMap, k); - var next = domNextSibling(cursor); - if (isSxTruthy(!isSxTruthy(isIdentical(node, next)))) { - domInsertAfter(cursor, node); -} - return (cursor = node); -})(); }, newKeys); -})())); - keyMap = newMap; - return (keyOrder = newKeys); -})() : forEachIndexed(function(idx, item) { return (function() { - var rendered = renderListItem(mapFn, item, env, ns); - var key = extractKey(rendered, idx); - keyMap[key] = rendered; - keyOrder.push(key); - return domAppend(container, rendered); -})(); }, items)); -})(); }); - return container; -})(); }; - - // bind-input - var bindInput = function(el, sig) { return (function() { - var inputType = lower(sxOr(domGetAttr(el, "type"), "")); - var isCheckbox = sxOr((inputType == "checkbox"), (inputType == "radio")); - (isSxTruthy(isCheckbox) ? domSetProp(el, "checked", deref(sig)) : domSetProp(el, "value", (String(deref(sig))))); - effect(function() { return (isSxTruthy(isCheckbox) ? domSetProp(el, "checked", deref(sig)) : (function() { - var v = (String(deref(sig))); - return (isSxTruthy((domGetProp(el, "value") != v)) ? domSetProp(el, "value", v) : NIL); -})()); }); - return domListen(el, (isSxTruthy(isCheckbox) ? "change" : "input"), function(e) { return (isSxTruthy(isCheckbox) ? reset_b(sig, domGetProp(el, "checked")) : reset_b(sig, domGetProp(el, "value"))); }); -})(); }; - - // render-dom-portal - var renderDomPortal = function(args, env, ns) { return (function() { - var selector = trampoline(evalExpr(first(args), env)); - var target = sxOr(domQuery(selector), domEnsureElement(selector)); - return (isSxTruthy(!isSxTruthy(target)) ? createComment((String("portal: ") + String(selector) + String(" (not found)"))) : (function() { - var marker = createComment((String("portal: ") + String(selector))); - var frag = createFragment(); - { var _c = rest(args); for (var _i = 0; _i < _c.length; _i++) { var child = _c[_i]; domAppend(frag, renderToDom(child, env, ns)); } } - (function() { - var portalNodes = domChildNodes(frag); - domAppend(target, frag); - return registerInScope(function() { return forEach(function(n) { return domRemove(n); }, portalNodes); }); -})(); - return marker; -})()); -})(); }; - - // render-dom-error-boundary - var renderDomErrorBoundary = function(args, env, ns) { return (function() { - var fallbackExpr = first(args); - var bodyExprs = rest(args); - var container = domCreateElement("div", NIL); - var retryVersion = signal(0); - domSetAttr(container, "data-sx-boundary", "true"); - effect(function() { deref(retryVersion); -domSetProp(container, "innerHTML", ""); -return (function() { - var savedScope = _islandScope; - _islandScope = NIL; - return tryCatch(function() { (function() { - var frag = createFragment(); - { var _c = bodyExprs; for (var _i = 0; _i < _c.length; _i++) { var child = _c[_i]; domAppend(frag, renderToDom(child, env, ns)); } } - return domAppend(container, frag); -})(); -return (_islandScope = savedScope); }, function(err) { _islandScope = savedScope; -return (function() { - var fallbackFn = trampoline(evalExpr(fallbackExpr, env)); - var retryFn = function() { return swap_b(retryVersion, function(n) { return (n + 1); }); }; - return (function() { - var fallbackDom = (isSxTruthy(isLambda(fallbackFn)) ? renderLambdaDom(fallbackFn, [err, retryFn], env, ns) : renderToDom(apply(fallbackFn, [err, retryFn]), env, ns)); - return domAppend(container, fallbackDom); -})(); -})(); }); -})(); }); - return container; -})(); }; - - - // === Transpiled from engine === - - // ENGINE_VERBS - var ENGINE_VERBS = ["get", "post", "put", "delete", "patch"]; - - // DEFAULT_SWAP - var DEFAULT_SWAP = "outerHTML"; - - // parse-time - var parseTime = function(s) { return (isSxTruthy(isNil(s)) ? 0 : (isSxTruthy(endsWith(s, "ms")) ? parseInt_(s, 0) : (isSxTruthy(endsWith(s, "s")) ? (parseInt_(replace_(s, "s", ""), 0) * 1000) : parseInt_(s, 0)))); }; - - // parse-trigger-spec - var parseTriggerSpec = function(spec) { return (isSxTruthy(isNil(spec)) ? NIL : (function() { - var rawParts = split(spec, ","); - return filter(function(x) { return !isSxTruthy(isNil(x)); }, map(function(part) { return (function() { - var tokens = split(trim(part), " "); - return (isSxTruthy(isEmpty(tokens)) ? NIL : (isSxTruthy((isSxTruthy((first(tokens) == "every")) && (len(tokens) >= 2))) ? {["event"]: "every", ["modifiers"]: {["interval"]: parseTime(nth(tokens, 1))}} : (function() { - var mods = {}; - { var _c = rest(tokens); for (var _i = 0; _i < _c.length; _i++) { var tok = _c[_i]; (isSxTruthy((tok == "once")) ? dictSet(mods, "once", true) : (isSxTruthy((tok == "changed")) ? dictSet(mods, "changed", true) : (isSxTruthy(startsWith(tok, "delay:")) ? dictSet(mods, "delay", parseTime(slice(tok, 6))) : (isSxTruthy(startsWith(tok, "from:")) ? dictSet(mods, "from", slice(tok, 5)) : NIL)))); } } - return {["event"]: first(tokens), ["modifiers"]: mods}; -})())); -})(); }, rawParts)); -})()); }; - - // default-trigger - var defaultTrigger = function(tagName) { return (isSxTruthy((tagName == "FORM")) ? [{["event"]: "submit", ["modifiers"]: {}}] : (isSxTruthy(sxOr((tagName == "INPUT"), (tagName == "SELECT"), (tagName == "TEXTAREA"))) ? [{["event"]: "change", ["modifiers"]: {}}] : [{["event"]: "click", ["modifiers"]: {}}])); }; - - // get-verb-info - var getVerbInfo = function(el) { return some(function(verb) { return (function() { - var url = domGetAttr(el, (String("sx-") + String(verb))); - return (isSxTruthy(url) ? {["method"]: upper(verb), ["url"]: url} : NIL); -})(); }, ENGINE_VERBS); }; - - // build-request-headers - var buildRequestHeaders = function(el, loadedComponents, cssHash) { return (function() { - var headers = {["SX-Request"]: "true", ["SX-Current-URL"]: browserLocationHref()}; - (function() { - var targetSel = domGetAttr(el, "sx-target"); - return (isSxTruthy(targetSel) ? dictSet(headers, "SX-Target", targetSel) : NIL); -})(); - if (isSxTruthy(!isSxTruthy(isEmpty(loadedComponents)))) { - headers["SX-Components"] = join(",", loadedComponents); -} - if (isSxTruthy(cssHash)) { - headers["SX-Css"] = cssHash; -} - (function() { - var extraH = domGetAttr(el, "sx-headers"); - return (isSxTruthy(extraH) ? (function() { - var parsed = parseHeaderValue(extraH); - return (isSxTruthy(parsed) ? forEach(function(key) { return dictSet(headers, key, (String(get(parsed, key)))); }, keys(parsed)) : NIL); -})() : NIL); -})(); - return headers; -})(); }; - - // process-response-headers - var processResponseHeaders = function(getHeader) { return {["redirect"]: getHeader("SX-Redirect"), ["refresh"]: getHeader("SX-Refresh"), ["trigger"]: getHeader("SX-Trigger"), ["retarget"]: getHeader("SX-Retarget"), ["reswap"]: getHeader("SX-Reswap"), ["location"]: getHeader("SX-Location"), ["replace-url"]: getHeader("SX-Replace-Url"), ["css-hash"]: getHeader("SX-Css-Hash"), ["trigger-swap"]: getHeader("SX-Trigger-After-Swap"), ["trigger-settle"]: getHeader("SX-Trigger-After-Settle"), ["content-type"]: getHeader("Content-Type"), ["cache-invalidate"]: getHeader("SX-Cache-Invalidate"), ["cache-update"]: getHeader("SX-Cache-Update")}; }; - - // parse-swap-spec - var parseSwapSpec = function(rawSwap, globalTransitions_p) { return (function() { - var parts = split(sxOr(rawSwap, DEFAULT_SWAP), " "); - var style = first(parts); - var useTransition = globalTransitions_p; - { var _c = rest(parts); for (var _i = 0; _i < _c.length; _i++) { var p = _c[_i]; (isSxTruthy((p == "transition:true")) ? (useTransition = true) : (isSxTruthy((p == "transition:false")) ? (useTransition = false) : NIL)); } } - return {["style"]: style, ["transition"]: useTransition}; -})(); }; - - // parse-retry-spec - var parseRetrySpec = function(retryAttr) { return (isSxTruthy(isNil(retryAttr)) ? NIL : (function() { - var parts = split(retryAttr, ":"); - return {["strategy"]: first(parts), ["start-ms"]: parseInt_(nth(parts, 1), 1000), ["cap-ms"]: parseInt_(nth(parts, 2), 30000)}; -})()); }; - - // next-retry-ms - var nextRetryMs = function(currentMs, capMs) { return min((currentMs * 2), capMs); }; - - // filter-params - var filterParams = function(paramsSpec, allParams) { return (isSxTruthy(isNil(paramsSpec)) ? allParams : (isSxTruthy((paramsSpec == "none")) ? [] : (isSxTruthy((paramsSpec == "*")) ? allParams : (isSxTruthy(startsWith(paramsSpec, "not ")) ? (function() { - var excluded = map(trim, split(slice(paramsSpec, 4), ",")); - return filter(function(p) { return !isSxTruthy(contains(excluded, first(p))); }, allParams); -})() : (function() { - var allowed = map(trim, split(paramsSpec, ",")); - return filter(function(p) { return contains(allowed, first(p)); }, allParams); -})())))); }; - - // resolve-target - var resolveTarget = function(el) { return (function() { - var sel = domGetAttr(el, "sx-target"); - return (isSxTruthy(sxOr(isNil(sel), (sel == "this"))) ? el : (isSxTruthy((sel == "closest")) ? domParent(el) : domQuery(sel))); -})(); }; - - // apply-optimistic - var applyOptimistic = function(el) { return (function() { - var directive = domGetAttr(el, "sx-optimistic"); - return (isSxTruthy(isNil(directive)) ? NIL : (function() { - var target = sxOr(resolveTarget(el), el); - var state = {["target"]: target, ["directive"]: directive}; - (isSxTruthy((directive == "remove")) ? (dictSet(state, "opacity", domGetStyle(target, "opacity")), domSetStyle(target, "opacity", "0"), domSetStyle(target, "pointer-events", "none")) : (isSxTruthy((directive == "disable")) ? (dictSet(state, "disabled", domGetProp(target, "disabled")), domSetProp(target, "disabled", true)) : (isSxTruthy(startsWith(directive, "add-class:")) ? (function() { - var cls = slice(directive, 10); - state["add-class"] = cls; - return domAddClass(target, cls); -})() : NIL))); - return state; -})()); -})(); }; - - // revert-optimistic - var revertOptimistic = function(state) { return (isSxTruthy(state) ? (function() { - var target = get(state, "target"); - var directive = get(state, "directive"); - return (isSxTruthy((directive == "remove")) ? (domSetStyle(target, "opacity", sxOr(get(state, "opacity"), "")), domSetStyle(target, "pointer-events", "")) : (isSxTruthy((directive == "disable")) ? domSetProp(target, "disabled", sxOr(get(state, "disabled"), false)) : (isSxTruthy(get(state, "add-class")) ? domRemoveClass(target, get(state, "add-class")) : NIL))); -})() : NIL); }; - - // find-oob-swaps - var findOobSwaps = function(container) { return (function() { - var results = []; - { var _c = ["sx-swap-oob", "hx-swap-oob"]; for (var _i = 0; _i < _c.length; _i++) { var attr = _c[_i]; (function() { - var oobEls = domQueryAll(container, (String("[") + String(attr) + String("]"))); - return forEach(function(oob) { return (function() { - var swapType = sxOr(domGetAttr(oob, attr), "outerHTML"); - var targetId = domId(oob); - domRemoveAttr(oob, attr); - return (isSxTruthy(targetId) ? append_b(results, {["element"]: oob, ["swap-type"]: swapType, ["target-id"]: targetId}) : NIL); -})(); }, oobEls); -})(); } } - return results; -})(); }; - - // morph-node - var morphNode = function(oldNode, newNode) { return (isSxTruthy(sxOr(domHasAttr(oldNode, "sx-preserve"), domHasAttr(oldNode, "sx-ignore"))) ? NIL : (isSxTruthy(sxOr(!isSxTruthy((domNodeType(oldNode) == domNodeType(newNode))), !isSxTruthy((domNodeName(oldNode) == domNodeName(newNode))))) ? domReplaceChild(domParent(oldNode), domClone(newNode), oldNode) : (isSxTruthy(sxOr((domNodeType(oldNode) == 3), (domNodeType(oldNode) == 8))) ? (isSxTruthy(!isSxTruthy((domTextContent(oldNode) == domTextContent(newNode)))) ? domSetTextContent(oldNode, domTextContent(newNode)) : NIL) : (isSxTruthy((domNodeType(oldNode) == 1)) ? (syncAttrs(oldNode, newNode), (isSxTruthy(!isSxTruthy((isSxTruthy(domIsActiveElement(oldNode)) && domIsInputElement(oldNode)))) ? morphChildren(oldNode, newNode) : NIL)) : NIL)))); }; - - // sync-attrs - var syncAttrs = function(oldEl, newEl) { { var _c = domAttrList(newEl); for (var _i = 0; _i < _c.length; _i++) { var attr = _c[_i]; (function() { - var name = first(attr); - var val = nth(attr, 1); - return (isSxTruthy(!isSxTruthy((domGetAttr(oldEl, name) == val))) ? domSetAttr(oldEl, name, val) : NIL); -})(); } } -return forEach(function(attr) { return (isSxTruthy(!isSxTruthy(domHasAttr(newEl, first(attr)))) ? domRemoveAttr(oldEl, first(attr)) : NIL); }, domAttrList(oldEl)); }; - - // morph-children - var morphChildren = function(oldParent, newParent) { return (function() { - var oldKids = domChildList(oldParent); - var newKids = domChildList(newParent); - var oldById = reduce(function(acc, kid) { return (function() { - var id = domId(kid); - return (isSxTruthy(id) ? (dictSet(acc, id, kid), acc) : acc); -})(); }, {}, oldKids); - var oi = 0; - { var _c = newKids; for (var _i = 0; _i < _c.length; _i++) { var newChild = _c[_i]; (function() { - var matchId = domId(newChild); - var matchById = (isSxTruthy(matchId) ? dictGet(oldById, matchId) : NIL); - return (isSxTruthy((isSxTruthy(matchById) && !isSxTruthy(isNil(matchById)))) ? ((isSxTruthy((isSxTruthy((oi < len(oldKids))) && !isSxTruthy((matchById == nth(oldKids, oi))))) ? domInsertBefore(oldParent, matchById, (isSxTruthy((oi < len(oldKids))) ? nth(oldKids, oi) : NIL)) : NIL), morphNode(matchById, newChild), (oi = (oi + 1))) : (isSxTruthy((oi < len(oldKids))) ? (function() { - var oldChild = nth(oldKids, oi); - return (isSxTruthy((isSxTruthy(domId(oldChild)) && !isSxTruthy(matchId))) ? domInsertBefore(oldParent, domClone(newChild), oldChild) : (morphNode(oldChild, newChild), (oi = (oi + 1)))); -})() : domAppend(oldParent, domClone(newChild)))); -})(); } } - return forEach(function(i) { return (isSxTruthy((i >= oi)) ? (function() { - var leftover = nth(oldKids, i); - return (isSxTruthy((isSxTruthy(domIsChildOf(leftover, oldParent)) && isSxTruthy(!isSxTruthy(domHasAttr(leftover, "sx-preserve"))) && !isSxTruthy(domHasAttr(leftover, "sx-ignore")))) ? domRemoveChild(oldParent, leftover) : NIL); -})() : NIL); }, range(oi, len(oldKids))); -})(); }; - - // swap-dom-nodes - var swapDomNodes = function(target, newNodes, strategy) { return (function() { var _m = strategy; if (_m == "innerHTML") return (isSxTruthy(domIsFragment(newNodes)) ? morphChildren(target, newNodes) : (function() { - var wrapper = domCreateElement("div", NIL); - domAppend(wrapper, newNodes); - return morphChildren(target, wrapper); -})()); if (_m == "outerHTML") return (function() { - var parent = domParent(target); - (isSxTruthy(domIsFragment(newNodes)) ? (function() { - var fc = domFirstChild(newNodes); - return (isSxTruthy(fc) ? (morphNode(target, fc), (function() { - var sib = domNextSibling(fc); - return insertRemainingSiblings(parent, target, sib); -})()) : domRemoveChild(parent, target)); -})() : morphNode(target, newNodes)); - return parent; -})(); if (_m == "afterend") return domInsertAfter(target, newNodes); if (_m == "beforeend") return domAppend(target, newNodes); if (_m == "afterbegin") return domPrepend(target, newNodes); if (_m == "beforebegin") return domInsertBefore(domParent(target), newNodes, target); if (_m == "delete") return domRemoveChild(domParent(target), target); if (_m == "none") return NIL; return (isSxTruthy(domIsFragment(newNodes)) ? morphChildren(target, newNodes) : (function() { - var wrapper = domCreateElement("div", NIL); - domAppend(wrapper, newNodes); - return morphChildren(target, wrapper); -})()); })(); }; - - // insert-remaining-siblings - var insertRemainingSiblings = function(parent, refNode, sib) { return (isSxTruthy(sib) ? (function() { - var next = domNextSibling(sib); - domInsertAfter(refNode, sib); - return insertRemainingSiblings(parent, sib, next); -})() : NIL); }; - - // swap-html-string - var swapHtmlString = function(target, html, strategy) { return (function() { var _m = strategy; if (_m == "innerHTML") return domSetInnerHtml(target, html); if (_m == "outerHTML") return (function() { - var parent = domParent(target); - domInsertAdjacentHtml(target, "afterend", html); - domRemoveChild(parent, target); - return parent; -})(); if (_m == "afterend") return domInsertAdjacentHtml(target, "afterend", html); if (_m == "beforeend") return domInsertAdjacentHtml(target, "beforeend", html); if (_m == "afterbegin") return domInsertAdjacentHtml(target, "afterbegin", html); if (_m == "beforebegin") return domInsertAdjacentHtml(target, "beforebegin", html); if (_m == "delete") return domRemoveChild(domParent(target), target); if (_m == "none") return NIL; return domSetInnerHtml(target, html); })(); }; - - // handle-history - var handleHistory = function(el, url, respHeaders) { return (function() { - var pushUrl = domGetAttr(el, "sx-push-url"); - var replaceUrl = domGetAttr(el, "sx-replace-url"); - var hdrReplace = get(respHeaders, "replace-url"); - return (isSxTruthy(hdrReplace) ? browserReplaceState(hdrReplace) : (isSxTruthy((isSxTruthy(pushUrl) && !isSxTruthy((pushUrl == "false")))) ? browserPushState((isSxTruthy((pushUrl == "true")) ? url : pushUrl)) : (isSxTruthy((isSxTruthy(replaceUrl) && !isSxTruthy((replaceUrl == "false")))) ? browserReplaceState((isSxTruthy((replaceUrl == "true")) ? url : replaceUrl)) : NIL))); -})(); }; - - // PRELOAD_TTL - var PRELOAD_TTL = 30000; - - // preload-cache-get - var preloadCacheGet = function(cache, url) { return (function() { - var entry = dictGet(cache, url); - return (isSxTruthy(isNil(entry)) ? NIL : (isSxTruthy(((nowMs() - get(entry, "timestamp")) > PRELOAD_TTL)) ? (dictDelete(cache, url), NIL) : (dictDelete(cache, url), entry))); -})(); }; - - // preload-cache-set - var preloadCacheSet = function(cache, url, text, contentType) { return dictSet(cache, url, {["text"]: text, ["content-type"]: contentType, ["timestamp"]: nowMs()}); }; - - // classify-trigger - var classifyTrigger = function(trigger) { return (function() { - var event = get(trigger, "event"); - return (isSxTruthy((event == "every")) ? "poll" : (isSxTruthy((event == "intersect")) ? "intersect" : (isSxTruthy((event == "load")) ? "load" : (isSxTruthy((event == "revealed")) ? "revealed" : "event")))); -})(); }; - - // should-boost-link? - var shouldBoostLink = function(link) { return (function() { - var href = domGetAttr(link, "href"); - return (isSxTruthy(href) && isSxTruthy(!isSxTruthy(startsWith(href, "#"))) && isSxTruthy(!isSxTruthy(startsWith(href, "javascript:"))) && isSxTruthy(!isSxTruthy(startsWith(href, "mailto:"))) && isSxTruthy(browserSameOrigin(href)) && isSxTruthy(!isSxTruthy(domHasAttr(link, "sx-get"))) && isSxTruthy(!isSxTruthy(domHasAttr(link, "sx-post"))) && !isSxTruthy(domHasAttr(link, "sx-disable"))); -})(); }; - - // should-boost-form? - var shouldBoostForm = function(form) { return (isSxTruthy(!isSxTruthy(domHasAttr(form, "sx-get"))) && isSxTruthy(!isSxTruthy(domHasAttr(form, "sx-post"))) && !isSxTruthy(domHasAttr(form, "sx-disable"))); }; - - // parse-sse-swap - var parseSseSwap = function(el) { return sxOr(domGetAttr(el, "sx-sse-swap"), "message"); }; - - - // === Transpiled from orchestration === - - // _preload-cache - var _preloadCache = {}; - - // _css-hash - var _cssHash = ""; - - // dispatch-trigger-events - var dispatchTriggerEvents = function(el, headerVal) { return (isSxTruthy(headerVal) ? (function() { - var parsed = tryParseJson(headerVal); - return (isSxTruthy(parsed) ? forEach(function(key) { return domDispatch(el, key, get(parsed, key)); }, keys(parsed)) : forEach(function(name) { return (function() { - var trimmed = trim(name); - return (isSxTruthy(!isSxTruthy(isEmpty(trimmed))) ? domDispatch(el, trimmed, {}) : NIL); -})(); }, split(headerVal, ","))); -})() : NIL); }; - - // init-css-tracking - var initCssTracking = function() { return (function() { - var meta = domQuery("meta[name=\"sx-css-classes\"]"); - return (isSxTruthy(meta) ? (function() { - var content = domGetAttr(meta, "content"); - return (isSxTruthy(content) ? (_cssHash = content) : NIL); -})() : NIL); -})(); }; - - // execute-request - var executeRequest = function(el, verbInfo, extraParams) { return (function() { - var info = sxOr(getVerbInfo(el), verbInfo); - return (isSxTruthy(isNil(info)) ? promiseResolve(NIL) : (function() { - var verb = get(info, "method"); - var url = get(info, "url"); - return (isSxTruthy((function() { - var media = domGetAttr(el, "sx-media"); - return (isSxTruthy(media) && !isSxTruthy(browserMediaMatches(media))); -})()) ? promiseResolve(NIL) : (isSxTruthy((function() { - var confirmMsg = domGetAttr(el, "sx-confirm"); - return (isSxTruthy(confirmMsg) && !isSxTruthy(browserConfirm(confirmMsg))); -})()) ? promiseResolve(NIL) : (function() { - var promptMsg = domGetAttr(el, "sx-prompt"); - var promptVal = (isSxTruthy(promptMsg) ? browserPrompt(promptMsg) : NIL); - return (isSxTruthy((isSxTruthy(promptMsg) && isNil(promptVal))) ? promiseResolve(NIL) : (isSxTruthy(!isSxTruthy(validateForRequest(el))) ? promiseResolve(NIL) : doFetch(el, verb, verb, url, (isSxTruthy(promptVal) ? assoc(sxOr(extraParams, {}), "SX-Prompt", promptVal) : extraParams)))); -})())); -})()); -})(); }; - - // do-fetch - var doFetch = function(el, verb, method, url, extraParams) { return (function() { - var sync = domGetAttr(el, "sx-sync"); - if (isSxTruthy((sync == "replace"))) { - abortPrevious(el); -} - (function() { - var targetEl = resolveTarget(el); - return (isSxTruthy(targetEl) ? abortPreviousTarget(targetEl) : NIL); -})(); - return (function() { - var ctrl = newAbortController(); - trackController(el, ctrl); - (function() { - var targetEl = resolveTarget(el); - return (isSxTruthy(targetEl) ? trackControllerTarget(targetEl, ctrl) : NIL); -})(); - return (function() { - var bodyInfo = buildRequestBody(el, method, url); - var finalUrl = get(bodyInfo, "url"); - var body = get(bodyInfo, "body"); - var ct = get(bodyInfo, "content-type"); - var headers = buildRequestHeaders(el, loadedComponentNames(), _cssHash); - var csrf = csrfToken(); - if (isSxTruthy(extraParams)) { - { var _c = keys(extraParams); for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; headers[k] = get(extraParams, k); } } -} - if (isSxTruthy(ct)) { - headers["Content-Type"] = ct; -} - if (isSxTruthy(csrf)) { - headers["X-CSRFToken"] = csrf; -} - return (function() { - var cached = preloadCacheGet(_preloadCache, finalUrl); - var optimisticState = applyOptimistic(el); - var indicator = showIndicator(el); - var disabledElts = disableElements(el); - domAddClass(el, "sx-request"); - domSetAttr(el, "aria-busy", "true"); - domDispatch(el, "sx:beforeRequest", {["url"]: finalUrl, ["method"]: method}); - return fetchRequest({["url"]: finalUrl, ["method"]: method, ["headers"]: headers, ["body"]: body, ["signal"]: controllerSignal(ctrl), ["cross-origin"]: isCrossOrigin(finalUrl), ["preloaded"]: cached}, function(respOk, status, getHeader, text) { return (clearLoadingState(el, indicator, disabledElts), revertOptimistic(optimisticState), (isSxTruthy(!isSxTruthy(respOk)) ? (domDispatch(el, "sx:responseError", {["status"]: status, ["text"]: text}), handleRetry(el, verb, method, finalUrl, extraParams)) : (domDispatch(el, "sx:afterRequest", {["status"]: status}), handleFetchSuccess(el, finalUrl, verb, extraParams, getHeader, text)))); }, function(err) { return (clearLoadingState(el, indicator, disabledElts), revertOptimistic(optimisticState), (isSxTruthy(!isSxTruthy(isAbortError(err))) ? domDispatch(el, "sx:requestError", {["error"]: err}) : NIL)); }); -})(); -})(); -})(); -})(); }; - - // handle-fetch-success - var handleFetchSuccess = function(el, url, verb, extraParams, getHeader, text) { return (function() { - var respHeaders = processResponseHeaders(getHeader); - (function() { - var newHash = get(respHeaders, "css-hash"); - return (isSxTruthy(newHash) ? (_cssHash = newHash) : NIL); -})(); - dispatchTriggerEvents(el, get(respHeaders, "trigger")); - processCacheDirectives(el, respHeaders, text); - return (isSxTruthy(get(respHeaders, "redirect")) ? browserNavigate(get(respHeaders, "redirect")) : (isSxTruthy(get(respHeaders, "refresh")) ? browserReload() : (isSxTruthy(get(respHeaders, "location")) ? fetchLocation(get(respHeaders, "location")) : (function() { - var targetEl = (isSxTruthy(get(respHeaders, "retarget")) ? domQuery(get(respHeaders, "retarget")) : resolveTarget(el)); - var swapSpec = parseSwapSpec(sxOr(get(respHeaders, "reswap"), domGetAttr(el, "sx-swap")), domHasClass(domBody(), "sx-transitions")); - var swapStyle = get(swapSpec, "style"); - var useTransition = get(swapSpec, "transition"); - var ct = sxOr(get(respHeaders, "content-type"), ""); - (isSxTruthy(contains(ct, "text/sx")) ? handleSxResponse(el, targetEl, text, swapStyle, useTransition) : handleHtmlResponse(el, targetEl, text, swapStyle, useTransition)); - dispatchTriggerEvents(el, get(respHeaders, "trigger-swap")); - handleHistory(el, url, respHeaders); - if (isSxTruthy(get(respHeaders, "trigger-settle"))) { - setTimeout_(function() { return dispatchTriggerEvents(el, get(respHeaders, "trigger-settle")); }, 20); -} - return domDispatch(el, "sx:afterSwap", {["target"]: targetEl, ["swap"]: swapStyle}); -})()))); -})(); }; - - // handle-sx-response - var handleSxResponse = function(el, target, text, swapStyle, useTransition) { return (function() { - var cleaned = stripComponentScripts(text); - return (function() { - var final_ = extractResponseCss(cleaned); - return (function() { - var trimmed = trim(final_); - return (isSxTruthy(!isSxTruthy(isEmpty(trimmed))) ? (function() { - var rendered = sxRender(trimmed); - var container = domCreateElement("div", NIL); - domAppend(container, rendered); - processOobSwaps(container, function(t, oob, s) { disposeIslandsIn(t); -swapDomNodes(t, oob, s); -sxHydrate(t); -return processElements(t); }); - return (function() { - var selectSel = domGetAttr(el, "sx-select"); - var content = (isSxTruthy(selectSel) ? selectFromContainer(container, selectSel) : childrenToFragment(container)); - disposeIslandsIn(target); - return withTransition(useTransition, function() { swapDomNodes(target, content, swapStyle); -return postSwap(target); }); -})(); -})() : NIL); -})(); -})(); -})(); }; - - // handle-html-response - var handleHtmlResponse = function(el, target, text, swapStyle, useTransition) { return (function() { - var doc = domParseHtmlDocument(text); - return (isSxTruthy(doc) ? (function() { - var selectSel = domGetAttr(el, "sx-select"); - disposeIslandsIn(target); - return (isSxTruthy(selectSel) ? (function() { - var html = selectHtmlFromDoc(doc, selectSel); - return withTransition(useTransition, function() { swapHtmlString(target, html, swapStyle); -return postSwap(target); }); -})() : (function() { - var container = domCreateElement("div", NIL); - domSetInnerHtml(container, domBodyInnerHtml(doc)); - processOobSwaps(container, function(t, oob, s) { disposeIslandsIn(t); -swapDomNodes(t, oob, s); -return postSwap(t); }); - hoistHeadElements(container); - return withTransition(useTransition, function() { swapDomNodes(target, childrenToFragment(container), swapStyle); -return postSwap(target); }); -})()); -})() : NIL); -})(); }; - - // handle-retry - var handleRetry = function(el, verb, method, url, extraParams) { return (function() { - var retryAttr = domGetAttr(el, "sx-retry"); - var spec = parseRetrySpec(retryAttr); - return (isSxTruthy(spec) ? (function() { - var currentMs = sxOr(domGetAttr(el, "data-sx-retry-ms"), get(spec, "start-ms")); - return (function() { - var ms = parseInt_(currentMs, get(spec, "start-ms")); - domSetAttr(el, "data-sx-retry-ms", (String(nextRetryMs(ms, get(spec, "cap-ms"))))); - return setTimeout_(function() { return doFetch(el, verb, method, url, extraParams); }, ms); -})(); -})() : NIL); -})(); }; - - // bind-triggers - var bindTriggers = function(el, verbInfo) { return (function() { - var triggers = sxOr(parseTriggerSpec(domGetAttr(el, "sx-trigger")), defaultTrigger(domTagName(el))); - return forEach(function(trigger) { return (function() { - var kind = classifyTrigger(trigger); - var mods = get(trigger, "modifiers"); - return (isSxTruthy((kind == "poll")) ? setInterval_(function() { return executeRequest(el, NIL, NIL); }, get(mods, "interval")) : (isSxTruthy((kind == "intersect")) ? observeIntersection(el, function() { return executeRequest(el, NIL, NIL); }, false, get(mods, "delay")) : (isSxTruthy((kind == "load")) ? setTimeout_(function() { return executeRequest(el, NIL, NIL); }, sxOr(get(mods, "delay"), 0)) : (isSxTruthy((kind == "revealed")) ? observeIntersection(el, function() { return executeRequest(el, NIL, NIL); }, true, get(mods, "delay")) : (isSxTruthy((kind == "event")) ? bindEvent(el, get(trigger, "event"), mods, verbInfo) : NIL))))); -})(); }, triggers); -})(); }; - - // bind-event - var bindEvent = function(el, eventName, mods, verbInfo) { return (function() { - var timer = NIL; - var lastVal = NIL; - var listenTarget = (isSxTruthy(get(mods, "from")) ? domQuery(get(mods, "from")) : el); - return (isSxTruthy(listenTarget) ? domAddListener(listenTarget, eventName, function(e) { return (function() { - var shouldFire = true; - if (isSxTruthy(get(mods, "changed"))) { - (function() { - var val = elementValue(el); - return (isSxTruthy((val == lastVal)) ? (shouldFire = false) : (lastVal = val)); -})(); -} - return (isSxTruthy(shouldFire) ? ((isSxTruthy(sxOr((eventName == "submit"), (isSxTruthy((eventName == "click")) && domHasAttr(el, "href")))) ? preventDefault_(e) : NIL), (function() { - var liveInfo = sxOr(getVerbInfo(el), verbInfo); - var isGetLink = (isSxTruthy((eventName == "click")) && isSxTruthy((get(liveInfo, "method") == "GET")) && isSxTruthy(domHasAttr(el, "href")) && !isSxTruthy(get(mods, "delay"))); - var clientRouted = false; - if (isSxTruthy(isGetLink)) { - clientRouted = tryClientRoute(urlPathname(get(liveInfo, "url")), domGetAttr(el, "sx-target")); -} - return (isSxTruthy(clientRouted) ? (browserPushState(get(liveInfo, "url")), browserScrollTo(0, 0)) : ((isSxTruthy(isGetLink) ? logInfo((String("sx:route server fetch ") + String(get(liveInfo, "url")))) : NIL), (isSxTruthy(get(mods, "delay")) ? (clearTimeout_(timer), (timer = setTimeout_(function() { return executeRequest(el, NIL, NIL); }, get(mods, "delay")))) : executeRequest(el, NIL, NIL)))); -})()) : NIL); -})(); }, (isSxTruthy(get(mods, "once")) ? {["once"]: true} : NIL)) : NIL); -})(); }; - - // post-swap - var postSwap = function(root) { activateScripts(root); -sxProcessScripts(root); -sxHydrate(root); -sxHydrateIslands(root); -return processElements(root); }; - - // activate-scripts - var activateScripts = function(root) { return (isSxTruthy(root) ? (function() { - var scripts = domQueryAll(root, "script"); - return forEach(function(dead) { return (isSxTruthy((isSxTruthy(!isSxTruthy(domHasAttr(dead, "data-components"))) && !isSxTruthy(domHasAttr(dead, "data-sx-activated")))) ? (function() { - var live = createScriptClone(dead); - domSetAttr(live, "data-sx-activated", "true"); - return domReplaceChild(domParent(dead), live, dead); -})() : NIL); }, scripts); -})() : NIL); }; - - // process-oob-swaps - var processOobSwaps = function(container, swapFn) { return (function() { - var oobs = findOobSwaps(container); - return forEach(function(oob) { return (function() { - var targetId = get(oob, "target-id"); - var target = domQueryById(targetId); - var oobEl = get(oob, "element"); - var swapType = get(oob, "swap-type"); - if (isSxTruthy(domParent(oobEl))) { - domRemoveChild(domParent(oobEl), oobEl); -} - return (isSxTruthy(target) ? swapFn(target, oobEl, swapType) : NIL); -})(); }, oobs); -})(); }; - - // hoist-head-elements - var hoistHeadElements = function(container) { { var _c = domQueryAll(container, "style[data-sx-css]"); for (var _i = 0; _i < _c.length; _i++) { var style = _c[_i]; if (isSxTruthy(domParent(style))) { - domRemoveChild(domParent(style), style); -} -domAppendToHead(style); } } -return forEach(function(link) { if (isSxTruthy(domParent(link))) { - domRemoveChild(domParent(link), link); -} -return domAppendToHead(link); }, domQueryAll(container, "link[rel=\"stylesheet\"]")); }; - - // process-boosted - var processBoosted = function(root) { return forEach(function(container) { return boostDescendants(container); }, domQueryAll(sxOr(root, domBody()), "[sx-boost]")); }; - - // boost-descendants - var boostDescendants = function(container) { return (function() { - var boostTarget = domGetAttr(container, "sx-boost"); - { var _c = domQueryAll(container, "a[href]"); for (var _i = 0; _i < _c.length; _i++) { var link = _c[_i]; if (isSxTruthy((isSxTruthy(!isSxTruthy(isProcessed(link, "boost"))) && shouldBoostLink(link)))) { - markProcessed(link, "boost"); - if (isSxTruthy((isSxTruthy(!isSxTruthy(domHasAttr(link, "sx-target"))) && isSxTruthy(boostTarget) && !isSxTruthy((boostTarget == "true"))))) { - domSetAttr(link, "sx-target", boostTarget); -} - if (isSxTruthy(!isSxTruthy(domHasAttr(link, "sx-swap")))) { - domSetAttr(link, "sx-swap", "innerHTML"); -} - if (isSxTruthy(!isSxTruthy(domHasAttr(link, "sx-push-url")))) { - domSetAttr(link, "sx-push-url", "true"); -} - bindClientRouteLink(link, domGetAttr(link, "href")); -} } } - return forEach(function(form) { return (isSxTruthy((isSxTruthy(!isSxTruthy(isProcessed(form, "boost"))) && shouldBoostForm(form))) ? (markProcessed(form, "boost"), (function() { - var method = upper(sxOr(domGetAttr(form, "method"), "GET")); - var action = sxOr(domGetAttr(form, "action"), browserLocationHref()); - if (isSxTruthy((isSxTruthy(!isSxTruthy(domHasAttr(form, "sx-target"))) && isSxTruthy(boostTarget) && !isSxTruthy((boostTarget == "true"))))) { - domSetAttr(form, "sx-target", boostTarget); -} - if (isSxTruthy(!isSxTruthy(domHasAttr(form, "sx-swap")))) { - domSetAttr(form, "sx-swap", "innerHTML"); -} - return bindBoostForm(form, method, action); -})()) : NIL); }, domQueryAll(container, "form")); -})(); }; - - // _page-data-cache - var _pageDataCache = {}; - - // _page-data-cache-ttl - var _pageDataCacheTtl = 30000; - - // page-data-cache-key - var pageDataCacheKey = function(pageName, params) { return (function() { - var base = pageName; - return (isSxTruthy(sxOr(isNil(params), isEmpty(keys(params)))) ? base : (function() { - var parts = []; - { var _c = keys(params); for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; parts.push((String(k) + String("=") + String(get(params, k)))); } } - return (String(base) + String(":") + String(join("&", parts))); -})()); -})(); }; - - // page-data-cache-get - var pageDataCacheGet = function(cacheKey) { return (function() { - var entry = get(_pageDataCache, cacheKey); - return (isSxTruthy(isNil(entry)) ? NIL : (isSxTruthy(((nowMs() - get(entry, "ts")) > _pageDataCacheTtl)) ? (dictSet(_pageDataCache, cacheKey, NIL), NIL) : get(entry, "data"))); -})(); }; - - // page-data-cache-set - var pageDataCacheSet = function(cacheKey, data) { return dictSet(_pageDataCache, cacheKey, {"data": data, "ts": nowMs()}); }; - - // invalidate-page-cache - var invalidatePageCache = function(pageName) { { var _c = keys(_pageDataCache); for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; if (isSxTruthy(sxOr((k == pageName), startsWith(k, (String(pageName) + String(":")))))) { - _pageDataCache[k] = NIL; -} } } -swPostMessage({"type": "invalidate", "page": pageName}); -return logInfo((String("sx:cache invalidate ") + String(pageName))); }; - - // invalidate-all-page-cache - var invalidateAllPageCache = function() { _pageDataCache = {}; -swPostMessage({"type": "invalidate", "page": "*"}); -return logInfo("sx:cache invalidate *"); }; - - // update-page-cache - var updatePageCache = function(pageName, data) { return (function() { - var cacheKey = pageDataCacheKey(pageName, {}); - pageDataCacheSet(cacheKey, data); - return logInfo((String("sx:cache update ") + String(pageName))); -})(); }; - - // process-cache-directives - var processCacheDirectives = function(el, respHeaders, responseText) { (function() { - var elInvalidate = domGetAttr(el, "sx-cache-invalidate"); - return (isSxTruthy(elInvalidate) ? (isSxTruthy((elInvalidate == "*")) ? invalidateAllPageCache() : invalidatePageCache(elInvalidate)) : NIL); -})(); -(function() { - var hdrInvalidate = get(respHeaders, "cache-invalidate"); - return (isSxTruthy(hdrInvalidate) ? (isSxTruthy((hdrInvalidate == "*")) ? invalidateAllPageCache() : invalidatePageCache(hdrInvalidate)) : NIL); -})(); -return (function() { - var hdrUpdate = get(respHeaders, "cache-update"); - return (isSxTruthy(hdrUpdate) ? (function() { - var data = parseSxData(responseText); - return (isSxTruthy(data) ? updatePageCache(hdrUpdate, data) : NIL); -})() : NIL); -})(); }; - - // _optimistic-snapshots - var _optimisticSnapshots = {}; - - // optimistic-cache-update - var optimisticCacheUpdate = function(cacheKey, mutator) { return (function() { - var cached = pageDataCacheGet(cacheKey); - return (isSxTruthy(cached) ? (function() { - var predicted = mutator(cached); - _optimisticSnapshots[cacheKey] = cached; - pageDataCacheSet(cacheKey, predicted); - return predicted; -})() : NIL); -})(); }; - - // optimistic-cache-revert - var optimisticCacheRevert = function(cacheKey) { return (function() { - var snapshot = get(_optimisticSnapshots, cacheKey); - return (isSxTruthy(snapshot) ? (pageDataCacheSet(cacheKey, snapshot), dictDelete(_optimisticSnapshots, cacheKey), snapshot) : NIL); -})(); }; - - // optimistic-cache-confirm - var optimisticCacheConfirm = function(cacheKey) { return dictDelete(_optimisticSnapshots, cacheKey); }; - - // submit-mutation - var submitMutation = function(pageName, params, actionName, payload, mutatorFn, onComplete) { return (function() { - var cacheKey = pageDataCacheKey(pageName, params); - var predicted = optimisticCacheUpdate(cacheKey, mutatorFn); - if (isSxTruthy(predicted)) { - tryRerenderPage(pageName, params, predicted); -} - return executeAction(actionName, payload, function(result) { if (isSxTruthy(result)) { - pageDataCacheSet(cacheKey, result); -} -optimisticCacheConfirm(cacheKey); -if (isSxTruthy(result)) { - tryRerenderPage(pageName, params, result); -} -logInfo((String("sx:optimistic confirmed ") + String(pageName))); -return (isSxTruthy(onComplete) ? onComplete("confirmed") : NIL); }, function(error) { return (function() { - var reverted = optimisticCacheRevert(cacheKey); - if (isSxTruthy(reverted)) { - tryRerenderPage(pageName, params, reverted); -} - logWarn((String("sx:optimistic reverted ") + String(pageName) + String(": ") + String(error))); - return (isSxTruthy(onComplete) ? onComplete("reverted") : NIL); -})(); }); -})(); }; - - // _is-online - var _isOnline = true; - - // _offline-queue - var _offlineQueue = []; - - // offline-is-online? - var offlineIsOnline_p = function() { return _isOnline; }; - - // offline-set-online! - var offlineSetOnline_b = function(val) { return (_isOnline = val); }; - - // offline-queue-mutation - var offlineQueueMutation = function(actionName, payload, pageName, params, mutatorFn) { return (function() { - var cacheKey = pageDataCacheKey(pageName, params); - var entry = {["action"]: actionName, ["payload"]: payload, ["page"]: pageName, ["params"]: params, ["timestamp"]: nowMs(), ["status"]: "pending"}; - _offlineQueue.push(entry); - (function() { - var predicted = optimisticCacheUpdate(cacheKey, mutatorFn); - return (isSxTruthy(predicted) ? tryRerenderPage(pageName, params, predicted) : NIL); -})(); - logInfo((String("sx:offline queued ") + String(actionName) + String(" (") + String(len(_offlineQueue)) + String(" pending)"))); - return entry; -})(); }; - - // offline-sync - var offlineSync = function() { return (function() { - var pending = filter(function(e) { return (get(e, "status") == "pending"); }, _offlineQueue); - return (isSxTruthy(!isSxTruthy(isEmpty(pending))) ? (logInfo((String("sx:offline syncing ") + String(len(pending)) + String(" mutations"))), forEach(function(entry) { return executeAction(get(entry, "action"), get(entry, "payload"), function(result) { entry["status"] = "synced"; -return logInfo((String("sx:offline synced ") + String(get(entry, "action")))); }, function(error) { entry["status"] = "failed"; -return logWarn((String("sx:offline sync failed ") + String(get(entry, "action")) + String(": ") + String(error))); }); }, pending)) : NIL); -})(); }; - - // offline-pending-count - var offlinePendingCount = function() { return len(filter(function(e) { return (get(e, "status") == "pending"); }, _offlineQueue)); }; - - // offline-aware-mutation - var offlineAwareMutation = function(pageName, params, actionName, payload, mutatorFn, onComplete) { return (isSxTruthy(_isOnline) ? submitMutation(pageName, params, actionName, payload, mutatorFn, onComplete) : (offlineQueueMutation(actionName, payload, pageName, params, mutatorFn), (isSxTruthy(onComplete) ? onComplete("queued") : NIL))); }; - - // current-page-layout - var currentPageLayout = function() { return (function() { - var pathname = urlPathname(browserLocationHref()); - var match = findMatchingRoute(pathname, _pageRoutes); - return (isSxTruthy(isNil(match)) ? "" : sxOr(get(match, "layout"), "")); -})(); }; - - // swap-rendered-content - var swapRenderedContent = function(target, rendered, pathname) { return (disposeIslandsIn(target), domSetTextContent(target, ""), domAppend(target, rendered), hoistHeadElementsFull(target), processElements(target), sxHydrateElements(target), domDispatch(target, "sx:clientRoute", {["pathname"]: pathname}), logInfo((String("sx:route client ") + String(pathname)))); }; - - // resolve-route-target - var resolveRouteTarget = function(targetSel) { return (isSxTruthy((isSxTruthy(targetSel) && !isSxTruthy((targetSel == "true")))) ? domQuery(targetSel) : NIL); }; - - // deps-satisfied? - var depsSatisfied_p = function(match) { return (function() { - var deps = get(match, "deps"); - var loaded = loadedComponentNames(); - return (isSxTruthy(sxOr(isNil(deps), isEmpty(deps))) ? true : isEvery(function(dep) { return contains(loaded, dep); }, deps)); -})(); }; - - // try-client-route - var tryClientRoute = function(pathname, targetSel) { return (function() { - var match = findMatchingRoute(pathname, _pageRoutes); - return (isSxTruthy(isNil(match)) ? (logInfo((String("sx:route no match (") + String(len(_pageRoutes)) + String(" routes) ") + String(pathname))), false) : (function() { - var targetLayout = sxOr(get(match, "layout"), ""); - var curLayout = currentPageLayout(); - return (isSxTruthy(!isSxTruthy((targetLayout == curLayout))) ? (logInfo((String("sx:route server (layout: ") + String(curLayout) + String(" -> ") + String(targetLayout) + String(") ") + String(pathname))), false) : (function() { - var contentSrc = get(match, "content"); - var closure = sxOr(get(match, "closure"), {}); - var params = get(match, "params"); - var pageName = get(match, "name"); - return (isSxTruthy(sxOr(isNil(contentSrc), isEmpty(contentSrc))) ? (logWarn((String("sx:route no content for ") + String(pathname))), false) : (function() { - var target = resolveRouteTarget(targetSel); - return (isSxTruthy(isNil(target)) ? (logWarn((String("sx:route target not found: ") + String(targetSel))), false) : (isSxTruthy(!isSxTruthy(depsSatisfied_p(match))) ? (logInfo((String("sx:route deps miss for ") + String(pageName))), false) : (function() { - var ioDeps = get(match, "io-deps"); - var hasIo = (isSxTruthy(ioDeps) && !isSxTruthy(isEmpty(ioDeps))); - var renderPlan = get(match, "render-plan"); - if (isSxTruthy(renderPlan)) { - (function() { - var srv = sxOr(get(renderPlan, "server"), []); - var cli = sxOr(get(renderPlan, "client"), []); - return logInfo((String("sx:route plan ") + String(pageName) + String(" — ") + String(len(srv)) + String(" server, ") + String(len(cli)) + String(" client"))); -})(); -} - if (isSxTruthy(hasIo)) { - registerIoDeps(ioDeps); -} - return (isSxTruthy(get(match, "stream")) ? (logInfo((String("sx:route streaming ") + String(pathname))), fetchStreaming(target, pathname, buildRequestHeaders(target, loadedComponentNames(), _cssHash)), true) : (isSxTruthy(get(match, "has-data")) ? (function() { - var cacheKey = pageDataCacheKey(pageName, params); - var cached = pageDataCacheGet(cacheKey); - return (isSxTruthy(cached) ? (function() { - var env = merge(closure, params, cached); - return (isSxTruthy(hasIo) ? (logInfo((String("sx:route client+cache+async ") + String(pathname))), tryAsyncEvalContent(contentSrc, env, function(rendered) { return (isSxTruthy(isNil(rendered)) ? (logWarn((String("sx:route cache+async eval failed for ") + String(pathname) + String(" — server fallback"))), fetchAndRestore(target, pathname, buildRequestHeaders(target, loadedComponentNames(), _cssHash), 0)) : swapRenderedContent(target, rendered, pathname)); }), true) : (function() { - var rendered = tryEvalContent(contentSrc, env); - return (isSxTruthy(isNil(rendered)) ? (logWarn((String("sx:route cached eval failed for ") + String(pathname))), false) : (logInfo((String("sx:route client+cache ") + String(pathname))), swapRenderedContent(target, rendered, pathname), true)); -})()); -})() : (logInfo((String("sx:route client+data ") + String(pathname))), resolvePageData(pageName, params, function(data) { pageDataCacheSet(cacheKey, data); -return (function() { - var env = merge(closure, params, data); - return (isSxTruthy(hasIo) ? tryAsyncEvalContent(contentSrc, env, function(rendered) { return (isSxTruthy(isNil(rendered)) ? (logWarn((String("sx:route data+async eval failed for ") + String(pathname) + String(" — server fallback"))), fetchAndRestore(target, pathname, buildRequestHeaders(target, loadedComponentNames(), _cssHash), 0)) : swapRenderedContent(target, rendered, pathname)); }) : (function() { - var rendered = tryEvalContent(contentSrc, env); - return (isSxTruthy(isNil(rendered)) ? (logWarn((String("sx:route data eval failed for ") + String(pathname) + String(" — server fallback"))), fetchAndRestore(target, pathname, buildRequestHeaders(target, loadedComponentNames(), _cssHash), 0)) : swapRenderedContent(target, rendered, pathname)); -})()); -})(); }), true)); -})() : (isSxTruthy(hasIo) ? (logInfo((String("sx:route client+async ") + String(pathname))), tryAsyncEvalContent(contentSrc, merge(closure, params), function(rendered) { return (isSxTruthy(isNil(rendered)) ? (logWarn((String("sx:route async eval failed for ") + String(pathname) + String(" — server fallback"))), fetchAndRestore(target, pathname, buildRequestHeaders(target, loadedComponentNames(), _cssHash), 0)) : swapRenderedContent(target, rendered, pathname)); }), true) : (function() { - var env = merge(closure, params); - var rendered = tryEvalContent(contentSrc, env); - return (isSxTruthy(isNil(rendered)) ? (logInfo((String("sx:route server (eval failed) ") + String(pathname))), false) : (swapRenderedContent(target, rendered, pathname), true)); -})()))); -})())); -})()); -})()); -})()); -})(); }; - - // bind-client-route-link - var bindClientRouteLink = function(link, href) { return bindClientRouteClick(link, href, function() { return bindBoostLink(link, href); }); }; - - // process-sse - var processSse = function(root) { return forEach(function(el) { return (isSxTruthy(!isSxTruthy(isProcessed(el, "sse"))) ? (markProcessed(el, "sse"), bindSse(el)) : NIL); }, domQueryAll(sxOr(root, domBody()), "[sx-sse]")); }; - - // bind-sse - var bindSse = function(el) { return (function() { - var url = domGetAttr(el, "sx-sse"); - return (isSxTruthy(url) ? (function() { - var source = eventSourceConnect(url, el); - var eventName = parseSseSwap(el); - return eventSourceListen(source, eventName, function(data) { return bindSseSwap(el, data); }); -})() : NIL); -})(); }; - - // bind-sse-swap - var bindSseSwap = function(el, data) { return (function() { - var target = resolveTarget(el); - var swapSpec = parseSwapSpec(domGetAttr(el, "sx-swap"), domHasClass(domBody(), "sx-transitions")); - var swapStyle = get(swapSpec, "style"); - var useTransition = get(swapSpec, "transition"); - var trimmed = trim(data); - return (isSxTruthy(!isSxTruthy(isEmpty(trimmed))) ? (disposeIslandsIn(target), (isSxTruthy(startsWith(trimmed, "(")) ? (function() { - var rendered = sxRender(trimmed); - var container = domCreateElement("div", NIL); - domAppend(container, rendered); - return withTransition(useTransition, function() { swapDomNodes(target, childrenToFragment(container), swapStyle); -return postSwap(target); }); -})() : withTransition(useTransition, function() { swapHtmlString(target, trimmed, swapStyle); -return postSwap(target); }))) : NIL); -})(); }; - - // bind-inline-handlers - var bindInlineHandlers = function(root) { return forEach(function(el) { return forEach(function(attr) { return (function() { - var name = first(attr); - var body = nth(attr, 1); - return (isSxTruthy(startsWith(name, "sx-on:")) ? (function() { - var eventName = slice(name, 6); - return (isSxTruthy(!isSxTruthy(isProcessed(el, (String("on:") + String(eventName))))) ? (markProcessed(el, (String("on:") + String(eventName))), (function() { - var exprs = sxParse(body); - return domListen(el, eventName, function(e) { return (function() { - var handlerEnv = envExtend({}); - handlerEnv["event"] = e; - handlerEnv["this"] = el; - handlerEnv["detail"] = eventDetail(e); - return forEach(function(expr) { return evalExpr(expr, handlerEnv); }, exprs); -})(); }); -})()) : NIL); -})() : NIL); -})(); }, domAttrList(el)); }, domQueryAll(sxOr(root, domBody()), "[sx-on\\:]")); }; - - // bind-preload-for - var bindPreloadFor = function(el) { return (function() { - var preloadAttr = domGetAttr(el, "sx-preload"); - return (isSxTruthy(preloadAttr) ? (function() { - var events = (isSxTruthy((preloadAttr == "mousedown")) ? ["mousedown", "touchstart"] : ["mouseover"]); - var debounceMs = (isSxTruthy((preloadAttr == "mousedown")) ? 0 : 100); - return bindPreload(el, events, debounceMs, function() { return (function() { - var info = getVerbInfo(el); - return (isSxTruthy(info) ? doPreload(get(info, "url"), buildRequestHeaders(el, loadedComponentNames(), _cssHash)) : NIL); -})(); }); -})() : NIL); -})(); }; - - // do-preload - var doPreload = function(url, headers) { return (isSxTruthy(isNil(preloadCacheGet(_preloadCache, url))) ? fetchPreload(url, headers, _preloadCache) : NIL); }; - - // VERB_SELECTOR - var VERB_SELECTOR = (String("[sx-get],[sx-post],[sx-put],[sx-delete],[sx-patch]")); - - // process-elements - var processElements = function(root) { (function() { - var els = domQueryAll(sxOr(root, domBody()), VERB_SELECTOR); - return forEach(function(el) { return (isSxTruthy(!isSxTruthy(isProcessed(el, "verb"))) ? (markProcessed(el, "verb"), processOne(el)) : NIL); }, els); -})(); -processBoosted(root); -processSse(root); -bindInlineHandlers(root); -return processEmitElements(root); }; - - // process-one - var processOne = function(el) { return (function() { - var verbInfo = getVerbInfo(el); - return (isSxTruthy(verbInfo) ? (isSxTruthy(!isSxTruthy(domHasAttr(el, "sx-disable"))) ? (bindTriggers(el, verbInfo), bindPreloadFor(el)) : NIL) : NIL); -})(); }; - - // process-emit-elements - var processEmitElements = function(root) { return (function() { - var els = domQueryAll(sxOr(root, domBody()), "[data-sx-emit]"); - return forEach(function(el) { return (isSxTruthy(!isSxTruthy(isProcessed(el, "emit"))) ? (markProcessed(el, "emit"), (function() { - var eventName = domGetAttr(el, "data-sx-emit"); - return (isSxTruthy(eventName) ? domListen(el, "click", function(e) { return (function() { - var detailJson = domGetAttr(el, "data-sx-emit-detail"); - var detail = (isSxTruthy(detailJson) ? jsonParse(detailJson) : {}); - return domDispatch(el, eventName, detail); -})(); }) : NIL); -})()) : NIL); }, els); -})(); }; - - // handle-popstate - var handlePopstate = function(scrollY) { return (function() { - var url = browserLocationHref(); - var boostEl = domQuery("[sx-boost]"); - var targetSel = (isSxTruthy(boostEl) ? (function() { - var attr = domGetAttr(boostEl, "sx-boost"); - return (isSxTruthy((isSxTruthy(attr) && !isSxTruthy((attr == "true")))) ? attr : NIL); -})() : NIL); - var targetSel = sxOr(targetSel, "#main-panel"); - var target = domQuery(targetSel); - var pathname = urlPathname(url); - return (isSxTruthy(target) ? (isSxTruthy(tryClientRoute(pathname, targetSel)) ? browserScrollTo(0, scrollY) : (function() { - var headers = buildRequestHeaders(target, loadedComponentNames(), _cssHash); - return fetchAndRestore(target, url, headers, scrollY); -})()) : NIL); -})(); }; - - // engine-init - var engineInit = function() { return (initCssTracking(), sxProcessScripts(NIL), sxHydrate(NIL), processElements(NIL)); }; - - - // === Transpiled from boot === - - // HEAD_HOIST_SELECTOR - var HEAD_HOIST_SELECTOR = "meta, title, link[rel='canonical'], script[type='application/ld+json']"; - - // hoist-head-elements-full - var hoistHeadElementsFull = function(root) { return (function() { - var els = domQueryAll(root, HEAD_HOIST_SELECTOR); - return forEach(function(el) { return (function() { - var tag = lower(domTagName(el)); - return (isSxTruthy((tag == "title")) ? (setDocumentTitle(domTextContent(el)), domRemoveChild(domParent(el), el)) : (isSxTruthy((tag == "meta")) ? ((function() { - var name = domGetAttr(el, "name"); - var prop = domGetAttr(el, "property"); - if (isSxTruthy(name)) { - removeHeadElement((String("meta[name=\"") + String(name) + String("\"]"))); -} - return (isSxTruthy(prop) ? removeHeadElement((String("meta[property=\"") + String(prop) + String("\"]"))) : NIL); -})(), domRemoveChild(domParent(el), el), domAppendToHead(el)) : (isSxTruthy((isSxTruthy((tag == "link")) && (domGetAttr(el, "rel") == "canonical"))) ? (removeHeadElement("link[rel=\"canonical\"]"), domRemoveChild(domParent(el), el), domAppendToHead(el)) : (domRemoveChild(domParent(el), el), domAppendToHead(el))))); -})(); }, els); -})(); }; - - // sx-mount - var sxMount = function(target, source, extraEnv) { return (function() { - var el = resolveMountTarget(target); - return (isSxTruthy(el) ? (function() { - var node = sxRenderWithEnv(source, extraEnv); - domSetTextContent(el, ""); - domAppend(el, node); - hoistHeadElementsFull(el); - processElements(el); - sxHydrateElements(el); - return sxHydrateIslands(el); -})() : NIL); -})(); }; - - // resolve-suspense - var resolveSuspense = function(id, sx) { processSxScripts(NIL); -return (function() { - var el = domQuery((String("[data-suspense=\"") + String(id) + String("\"]"))); - return (isSxTruthy(el) ? (function() { - var exprs = parse(sx); - var env = getRenderEnv(NIL); - domSetTextContent(el, ""); - { var _c = exprs; for (var _i = 0; _i < _c.length; _i++) { var expr = _c[_i]; domAppend(el, renderToDom(expr, env, NIL)); } } - processElements(el); - sxHydrateElements(el); - sxHydrateIslands(el); - return domDispatch(el, "sx:resolved", {"id": id}); -})() : logWarn((String("resolveSuspense: no element for id=") + String(id)))); -})(); }; - - // sx-hydrate-elements - var sxHydrateElements = function(root) { return (function() { - var els = domQueryAll(sxOr(root, domBody()), "[data-sx]"); - return forEach(function(el) { return (isSxTruthy(!isSxTruthy(isProcessed(el, "hydrated"))) ? (markProcessed(el, "hydrated"), sxUpdateElement(el, NIL)) : NIL); }, els); -})(); }; - - // sx-update-element - var sxUpdateElement = function(el, newEnv) { return (function() { - var target = resolveMountTarget(el); - return (isSxTruthy(target) ? (function() { - var source = domGetAttr(target, "data-sx"); - return (isSxTruthy(source) ? (function() { - var baseEnv = parseEnvAttr(target); - var env = mergeEnvs(baseEnv, newEnv); - return (function() { - var node = sxRenderWithEnv(source, env); - domSetTextContent(target, ""); - domAppend(target, node); - return (isSxTruthy(newEnv) ? storeEnvAttr(target, baseEnv, newEnv) : NIL); -})(); -})() : NIL); -})() : NIL); -})(); }; - - // sx-render-component - var sxRenderComponent = function(name, kwargs, extraEnv) { return (function() { - var fullName = (isSxTruthy(startsWith(name, "~")) ? name : (String("~") + String(name))); - return (function() { - var env = getRenderEnv(extraEnv); - var comp = envGet(env, fullName); - return (isSxTruthy(!isSxTruthy(isComponent(comp))) ? error((String("Unknown component: ") + String(fullName))) : (function() { - var callExpr = [makeSymbol(fullName)]; - { var _c = keys(kwargs); for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; callExpr.push(makeKeyword(toKebab(k))); -callExpr.push(dictGet(kwargs, k)); } } - return renderToDom(callExpr, env, NIL); -})()); -})(); -})(); }; - - // process-sx-scripts - var processSxScripts = function(root) { return (function() { - var scripts = querySxScripts(root); - return forEach(function(s) { return (isSxTruthy(!isSxTruthy(isProcessed(s, "script"))) ? (markProcessed(s, "script"), (function() { - var text = domTextContent(s); - return (isSxTruthy(domHasAttr(s, "data-components")) ? processComponentScript(s, text) : (isSxTruthy(sxOr(isNil(text), isEmpty(trim(text)))) ? NIL : (isSxTruthy(domHasAttr(s, "data-init")) ? (function() { - var exprs = sxParse(text); - return forEach(function(expr) { return evalExpr(expr, envExtend({})); }, exprs); -})() : (isSxTruthy(domHasAttr(s, "data-mount")) ? (function() { - var mountSel = domGetAttr(s, "data-mount"); - var target = domQuery(mountSel); - return (isSxTruthy(target) ? sxMount(target, text, NIL) : NIL); -})() : sxLoadComponents(text))))); -})()) : NIL); }, scripts); -})(); }; - - // process-component-script - var processComponentScript = function(script, text) { return (function() { - var hash = domGetAttr(script, "data-hash"); - return (isSxTruthy(isNil(hash)) ? (isSxTruthy((isSxTruthy(text) && !isSxTruthy(isEmpty(trim(text))))) ? sxLoadComponents(text) : NIL) : (function() { - var hasInline = (isSxTruthy(text) && !isSxTruthy(isEmpty(trim(text)))); - (function() { - var cachedHash = localStorageGet("sx-components-hash"); - return (isSxTruthy((cachedHash == hash)) ? (isSxTruthy(hasInline) ? (localStorageSet("sx-components-hash", hash), localStorageSet("sx-components-src", text), sxLoadComponents(text), logInfo("components: downloaded (cookie stale)")) : (function() { - var cached = localStorageGet("sx-components-src"); - return (isSxTruthy(cached) ? (sxLoadComponents(cached), logInfo((String("components: cached (") + String(hash) + String(")")))) : (clearSxCompCookie(), browserReload())); -})()) : (isSxTruthy(hasInline) ? (localStorageSet("sx-components-hash", hash), localStorageSet("sx-components-src", text), sxLoadComponents(text), logInfo((String("components: downloaded (") + String(hash) + String(")")))) : (localStorageRemove("sx-components-hash"), localStorageRemove("sx-components-src"), clearSxCompCookie(), browserReload()))); -})(); - return setSxCompCookie(hash); -})()); -})(); }; - - // _page-routes - var _pageRoutes = []; - - // process-page-scripts - var processPageScripts = function() { return (function() { - var scripts = queryPageScripts(); - logInfo((String("pages: found ") + String(len(scripts)) + String(" script tags"))); - { var _c = scripts; for (var _i = 0; _i < _c.length; _i++) { var s = _c[_i]; if (isSxTruthy(!isSxTruthy(isProcessed(s, "pages")))) { - markProcessed(s, "pages"); - (function() { - var text = domTextContent(s); - logInfo((String("pages: script text length=") + String((isSxTruthy(text) ? len(text) : 0)))); - return (isSxTruthy((isSxTruthy(text) && !isSxTruthy(isEmpty(trim(text))))) ? (function() { - var pages = parse(text); - logInfo((String("pages: parsed ") + String(len(pages)) + String(" entries"))); - return forEach(function(page) { return append_b(_pageRoutes, merge(page, {"parsed": parseRoutePattern(get(page, "path"))})); }, pages); -})() : logWarn("pages: script tag is empty")); -})(); -} } } - return logInfo((String("pages: ") + String(len(_pageRoutes)) + String(" routes loaded"))); -})(); }; - - // sx-hydrate-islands - var sxHydrateIslands = function(root) { return (function() { - var els = domQueryAll(sxOr(root, domBody()), "[data-sx-island]"); - return forEach(function(el) { return (isSxTruthy(!isSxTruthy(isProcessed(el, "island-hydrated"))) ? (markProcessed(el, "island-hydrated"), hydrateIsland(el)) : NIL); }, els); -})(); }; - - // hydrate-island - var hydrateIsland = function(el) { return (function() { - var name = domGetAttr(el, "data-sx-island"); - var stateJson = sxOr(domGetAttr(el, "data-sx-state"), "{}"); - return (function() { - var compName = (String("~") + String(name)); - var env = getRenderEnv(NIL); - return (function() { - var comp = envGet(env, compName); - return (isSxTruthy(!isSxTruthy(sxOr(isComponent(comp), isIsland(comp)))) ? logWarn((String("hydrate-island: unknown island ") + String(compName))) : (function() { - var kwargs = jsonParse(stateJson); - var disposers = []; - var local = envMerge(componentClosure(comp), env); - { var _c = componentParams(comp); for (var _i = 0; _i < _c.length; _i++) { var p = _c[_i]; local[p] = (isSxTruthy(dictHas(kwargs, p)) ? dictGet(kwargs, p) : NIL); } } - return (function() { - var bodyDom = withIslandScope(function(disposable) { return append_b(disposers, disposable); }, function() { return renderToDom(componentBody(comp), local, NIL); }); - domSetTextContent(el, ""); - domAppend(el, bodyDom); - domSetData(el, "sx-disposers", disposers); - processElements(el); - return logInfo((String("hydrated island: ") + String(compName) + String(" (") + String(len(disposers)) + String(" disposers)"))); -})(); -})()); -})(); -})(); -})(); }; - - // dispose-island - var disposeIsland = function(el) { return (function() { - var disposers = domGetData(el, "sx-disposers"); - return (isSxTruthy(disposers) ? (forEach(function(d) { return (isSxTruthy(isCallable(d)) ? d() : NIL); }, disposers), domSetData(el, "sx-disposers", NIL)) : NIL); -})(); }; - - // dispose-islands-in - var disposeIslandsIn = function(root) { return (isSxTruthy(root) ? (function() { - var islands = domQueryAll(root, "[data-sx-island]"); - return (isSxTruthy((isSxTruthy(islands) && !isSxTruthy(isEmpty(islands)))) ? (logInfo((String("disposing ") + String(len(islands)) + String(" island(s)"))), forEach(disposeIsland, islands)) : NIL); -})() : NIL); }; - - // boot-init - var bootInit = function() { return (logInfo((String("sx-browser ") + String(SX_VERSION))), initCssTracking(), processPageScripts(), processSxScripts(NIL), sxHydrateElements(NIL), sxHydrateIslands(NIL), processElements(NIL)); }; - - - // === Transpiled from router (client-side route matching) === - - // split-path-segments - var splitPathSegments = function(path) { return (function() { - var trimmed = (isSxTruthy(startsWith(path, "/")) ? slice(path, 1) : path); - return (function() { - var trimmed2 = (isSxTruthy((isSxTruthy(!isSxTruthy(isEmpty(trimmed))) && endsWith(trimmed, "/"))) ? slice(trimmed, 0, (len(trimmed) - 1)) : trimmed); - return (isSxTruthy(isEmpty(trimmed2)) ? [] : split(trimmed2, "/")); -})(); -})(); }; - - // make-route-segment - var makeRouteSegment = function(seg) { return (isSxTruthy((isSxTruthy(startsWith(seg, "<")) && endsWith(seg, ">"))) ? (function() { - var paramName = slice(seg, 1, (len(seg) - 1)); - return (function() { - var d = {}; - d["type"] = "param"; - d["value"] = paramName; - return d; -})(); -})() : (function() { - var d = {}; - d["type"] = "literal"; - d["value"] = seg; - return d; -})()); }; - - // parse-route-pattern - var parseRoutePattern = function(pattern) { return (function() { - var segments = splitPathSegments(pattern); - return map(makeRouteSegment, segments); -})(); }; - - // match-route-segments - var matchRouteSegments = function(pathSegs, parsedSegs) { return (isSxTruthy(!isSxTruthy((len(pathSegs) == len(parsedSegs)))) ? NIL : (function() { - var params = {}; - var matched = true; - forEachIndexed(function(i, parsedSeg) { return (isSxTruthy(matched) ? (function() { - var pathSeg = nth(pathSegs, i); - var segType = get(parsedSeg, "type"); - return (isSxTruthy((segType == "literal")) ? (isSxTruthy(!isSxTruthy((pathSeg == get(parsedSeg, "value")))) ? (matched = false) : NIL) : (isSxTruthy((segType == "param")) ? dictSet(params, get(parsedSeg, "value"), pathSeg) : (matched = false))); -})() : NIL); }, parsedSegs); - return (isSxTruthy(matched) ? params : NIL); -})()); }; - - // match-route - var matchRoute = function(path, pattern) { return (function() { - var pathSegs = splitPathSegments(path); - var parsedSegs = parseRoutePattern(pattern); - return matchRouteSegments(pathSegs, parsedSegs); -})(); }; - - // find-matching-route - var findMatchingRoute = function(path, routes) { return (function() { - var pathSegs = splitPathSegments(path); - var result = NIL; - { var _c = routes; for (var _i = 0; _i < _c.length; _i++) { var route = _c[_i]; if (isSxTruthy(isNil(result))) { - (function() { - var params = matchRouteSegments(pathSegs, get(route, "parsed")); - return (isSxTruthy(!isSxTruthy(isNil(params))) ? (function() { - var matched = merge(route, {}); - matched["params"] = params; - return (result = matched); -})() : NIL); -})(); -} } } - return result; -})(); }; - - - // === Transpiled from signals (reactive signal runtime) === - - // signal - var signal = function(initialValue) { return makeSignal(initialValue); }; - - // deref - var deref = function(s) { return (isSxTruthy(!isSxTruthy(isSignal(s))) ? s : (function() { - var ctx = getTrackingContext(); - if (isSxTruthy(ctx)) { - trackingContextAddDep(ctx, s); - signalAddSub(s, trackingContextNotifyFn(ctx)); -} - return signalValue(s); -})()); }; - - // reset! - var reset_b = function(s, value) { return (isSxTruthy(isSignal(s)) ? (function() { - var old = signalValue(s); - return (isSxTruthy(!isSxTruthy(isIdentical(old, value))) ? (signalSetValue(s, value), notifySubscribers(s)) : NIL); -})() : NIL); }; - - // swap! - var swap_b = function(s, f) { var args = Array.prototype.slice.call(arguments, 2); return (isSxTruthy(isSignal(s)) ? (function() { - var old = signalValue(s); - var newVal = apply(f, cons(old, args)); - return (isSxTruthy(!isSxTruthy(isIdentical(old, newVal))) ? (signalSetValue(s, newVal), notifySubscribers(s)) : NIL); -})() : NIL); }; - - // computed - var computed = function(computeFn) { return (function() { - var s = makeSignal(NIL); - var deps = []; - var computeCtx = NIL; - return (function() { - var recompute = function() { { var _c = signalDeps(s); for (var _i = 0; _i < _c.length; _i++) { var dep = _c[_i]; signalRemoveSub(dep, recompute); } } -signalSetDeps(s, []); -return (function() { - var ctx = makeTrackingContext(recompute); - return (function() { - var prev = getTrackingContext(); - setTrackingContext(ctx); - return (function() { - var newVal = invoke(computeFn); - setTrackingContext(prev); - signalSetDeps(s, trackingContextDeps(ctx)); - return (function() { - var old = signalValue(s); - signalSetValue(s, newVal); - return (isSxTruthy(!isSxTruthy(isIdentical(old, newVal))) ? notifySubscribers(s) : NIL); -})(); -})(); -})(); -})(); }; - recompute(); - registerInScope(function() { return disposeComputed(s); }); - return s; -})(); -})(); }; - - // effect - var effect = function(effectFn) { return (function() { - var deps = []; - var disposed = false; - var cleanupFn = NIL; - return (function() { - var runEffect = function() { return (isSxTruthy(!isSxTruthy(disposed)) ? ((isSxTruthy(cleanupFn) ? invoke(cleanupFn) : NIL), forEach(function(dep) { return signalRemoveSub(dep, runEffect); }, deps), (deps = []), (function() { - var ctx = makeTrackingContext(runEffect); - return (function() { - var prev = getTrackingContext(); - setTrackingContext(ctx); - return (function() { - var result = invoke(effectFn); - setTrackingContext(prev); - deps = trackingContextDeps(ctx); - return (isSxTruthy(isCallable(result)) ? (cleanupFn = result) : NIL); -})(); -})(); -})()) : NIL); }; - runEffect(); - return (function() { - var disposeFn = function() { disposed = true; -if (isSxTruthy(cleanupFn)) { - invoke(cleanupFn); -} -{ var _c = deps; for (var _i = 0; _i < _c.length; _i++) { var dep = _c[_i]; signalRemoveSub(dep, runEffect); } } -return (deps = []); }; - registerInScope(disposeFn); - return disposeFn; -})(); -})(); -})(); }; - - // *batch-depth* - var _batchDepth = 0; - - // *batch-queue* - var _batchQueue = []; - - // batch - var batch = function(thunk) { _batchDepth = (_batchDepth + 1); -invoke(thunk); -_batchDepth = (_batchDepth - 1); -return (isSxTruthy((_batchDepth == 0)) ? (function() { - var queue = _batchQueue; - _batchQueue = []; - return (function() { - var seen = []; - var pending = []; - { var _c = queue; for (var _i = 0; _i < _c.length; _i++) { var s = _c[_i]; { var _c = signalSubscribers(s); for (var _i = 0; _i < _c.length; _i++) { var sub = _c[_i]; if (isSxTruthy(!isSxTruthy(contains(seen, sub)))) { - seen.push(sub); - pending.push(sub); -} } } } } - return forEach(function(sub) { return sub(); }, pending); -})(); -})() : NIL); }; - - // notify-subscribers - var notifySubscribers = function(s) { return (isSxTruthy((_batchDepth > 0)) ? (isSxTruthy(!isSxTruthy(contains(_batchQueue, s))) ? append_b(_batchQueue, s) : NIL) : flushSubscribers(s)); }; - - // flush-subscribers - var flushSubscribers = function(s) { return forEach(function(sub) { return sub(); }, signalSubscribers(s)); }; - - // dispose-computed - var disposeComputed = function(s) { return (isSxTruthy(isSignal(s)) ? (forEach(function(dep) { return signalRemoveSub(dep, NIL); }, signalDeps(s)), signalSetDeps(s, [])) : NIL); }; - - // *island-scope* - var _islandScope = NIL; - - // with-island-scope - var withIslandScope = function(scopeFn, bodyFn) { return (function() { - var prev = _islandScope; - _islandScope = scopeFn; - return (function() { - var result = bodyFn(); - _islandScope = prev; - return result; -})(); -})(); }; - - // register-in-scope - var registerInScope = function(disposable) { return (isSxTruthy(_islandScope) ? _islandScope(disposable) : NIL); }; - - // *store-registry* - var _storeRegistry = {}; - - // def-store - var defStore = function(name, initFn) { return (function() { - var registry = _storeRegistry; - if (isSxTruthy(!isSxTruthy(dictHas(registry, name)))) { - _storeRegistry = assoc(registry, name, invoke(initFn)); -} - return get(_storeRegistry, name); -})(); }; - - // use-store - var useStore = function(name) { return (isSxTruthy(dictHas(_storeRegistry, name)) ? get(_storeRegistry, name) : error((String("Store not found: ") + String(name) + String(". Call (def-store ...) before (use-store ...).")))); }; - - // clear-stores - var clearStores = function() { return (_storeRegistry = {}); }; - - // emit-event - var emitEvent = function(el, eventName, detail) { return domDispatch(el, eventName, detail); }; - - // on-event - var onEvent = function(el, eventName, handler) { return domListen(el, eventName, handler); }; - - // bridge-event - var bridgeEvent = function(el, eventName, targetSignal, transformFn) { return effect(function() { return (function() { - var remove = domListen(el, eventName, function(e) { return (function() { - var detail = eventDetail(e); - var newVal = (isSxTruthy(transformFn) ? invoke(transformFn, detail) : detail); - return reset_b(targetSignal, newVal); -})(); }); - return remove; -})(); }); }; - - // resource - var resource = function(fetchFn) { return (function() { - var state = signal({["loading"]: true, ["data"]: NIL, ["error"]: NIL}); - promiseThen(invoke(fetchFn), function(data) { return reset_b(state, {["loading"]: false, ["data"]: data, ["error"]: NIL}); }, function(err) { return reset_b(state, {["loading"]: false, ["data"]: NIL, ["error"]: err}); }); - return state; -})(); }; - - - // ========================================================================= - // Platform interface — DOM adapter (browser-only) - // ========================================================================= - - var _hasDom = typeof document !== "undefined"; - - // Register DOM adapter as the render dispatch target for the evaluator. - _renderExprFn = function(expr, env) { return renderToDom(expr, env, null); }; - - var SVG_NS = "http://www.w3.org/2000/svg"; - var MATH_NS = "http://www.w3.org/1998/Math/MathML"; - - function domCreateElement(tag, ns) { - if (!_hasDom) return null; - if (ns && ns !== NIL) return document.createElementNS(ns, tag); - return document.createElement(tag); - } - - function createTextNode(s) { - return _hasDom ? document.createTextNode(s) : null; - } - - function createComment(s) { - return _hasDom ? document.createComment(s || "") : null; - } - - function createFragment() { - return _hasDom ? document.createDocumentFragment() : null; - } - - function domAppend(parent, child) { - if (parent && child) parent.appendChild(child); - } - - function domPrepend(parent, child) { - if (parent && child) parent.insertBefore(child, parent.firstChild); - } - - function domSetAttr(el, name, val) { - if (el && el.setAttribute) el.setAttribute(name, val); - } - - function domGetAttr(el, name) { - if (!el || !el.getAttribute) return NIL; - var v = el.getAttribute(name); - return v === null ? NIL : v; - } - - function domRemoveAttr(el, name) { - if (el && el.removeAttribute) el.removeAttribute(name); - } - - function domHasAttr(el, name) { - return !!(el && el.hasAttribute && el.hasAttribute(name)); - } - - function domParseHtml(html) { - if (!_hasDom) return null; - var tpl = document.createElement("template"); - tpl.innerHTML = html; - return tpl.content; - } - - function domClone(node) { - return node && node.cloneNode ? node.cloneNode(true) : node; - } - - function domParent(el) { return el ? el.parentNode : null; } - function domId(el) { return el && el.id ? el.id : NIL; } - function domNodeType(el) { return el ? el.nodeType : 0; } - function domNodeName(el) { return el ? el.nodeName : ""; } - function domTextContent(el) { return el ? el.textContent || el.nodeValue || "" : ""; } - function domSetTextContent(el, s) { if (el) { if (el.nodeType === 3 || el.nodeType === 8) el.nodeValue = s; else el.textContent = s; } } - function domIsFragment(el) { return el ? el.nodeType === 11 : false; } - function domIsChildOf(child, parent) { return !!(parent && child && child.parentNode === parent); } - function domIsActiveElement(el) { return _hasDom && el === document.activeElement; } - function domIsInputElement(el) { - if (!el || !el.tagName) return false; - var t = el.tagName; - return t === "INPUT" || t === "TEXTAREA" || t === "SELECT"; - } - function domFirstChild(el) { return el ? el.firstChild : null; } - function domNextSibling(el) { return el ? el.nextSibling : null; } - - function domChildList(el) { - if (!el || !el.childNodes) return []; - return Array.prototype.slice.call(el.childNodes); - } - - function domAttrList(el) { - if (!el || !el.attributes) return []; - var r = []; - for (var i = 0; i < el.attributes.length; i++) { - r.push([el.attributes[i].name, el.attributes[i].value]); - } - return r; - } - - function domInsertBefore(parent, node, ref) { - if (parent && node) parent.insertBefore(node, ref || null); - } - - function domInsertAfter(ref, node) { - if (ref && ref.parentNode && node) { - ref.parentNode.insertBefore(node, ref.nextSibling); - } - } - - function domRemoveChild(parent, child) { - if (parent && child && child.parentNode === parent) parent.removeChild(child); - } - - function domReplaceChild(parent, newChild, oldChild) { - if (parent && newChild && oldChild) parent.replaceChild(newChild, oldChild); - } - - function domSetInnerHtml(el, html) { - if (el) el.innerHTML = html; - } - - function domInsertAdjacentHtml(el, pos, html) { - if (el && el.insertAdjacentHTML) el.insertAdjacentHTML(pos, html); - } - - function domGetStyle(el, prop) { - return el && el.style ? el.style[prop] || "" : ""; - } - - function domSetStyle(el, prop, val) { - if (el && el.style) el.style[prop] = val; - } - - function domGetProp(el, name) { return el ? el[name] : NIL; } - function domSetProp(el, name, val) { if (el) el[name] = val; } - - function domAddClass(el, cls) { - if (el && el.classList) el.classList.add(cls); - } - - function domRemoveClass(el, cls) { - if (el && el.classList) el.classList.remove(cls); - } - - function domDispatch(el, name, detail) { - if (!_hasDom || !el) return false; - var evt = new CustomEvent(name, { bubbles: true, cancelable: true, detail: detail || {} }); - return el.dispatchEvent(evt); - } - - function domListen(el, name, handler) { - if (!_hasDom || !el) return function() {}; - // Wrap SX lambdas from runtime-evaluated island code into native fns - var wrapped = isLambda(handler) - ? function(e) { invoke(handler, e); } - : handler; - el.addEventListener(name, wrapped); - return function() { el.removeEventListener(name, wrapped); }; - } - - function eventDetail(e) { - return (e && e.detail != null) ? e.detail : nil; - } - - function domQuery(sel) { - return _hasDom ? document.querySelector(sel) : null; - } - - function domEnsureElement(sel) { - if (!_hasDom) return null; - var el = document.querySelector(sel); - if (el) return el; - // Parse #id selector → create div with that id, append to body - if (sel.charAt(0) === '#') { - el = document.createElement('div'); - el.id = sel.slice(1); - document.body.appendChild(el); - return el; - } - return null; - } - - function domQueryAll(root, sel) { - if (!root || !root.querySelectorAll) return []; - return Array.prototype.slice.call(root.querySelectorAll(sel)); - } - - function domTagName(el) { return el && el.tagName ? el.tagName : ""; } - - // Island DOM helpers - function domRemove(node) { - if (node && node.parentNode) node.parentNode.removeChild(node); - } - function domChildNodes(el) { - if (!el || !el.childNodes) return []; - return Array.prototype.slice.call(el.childNodes); - } - function domRemoveChildrenAfter(marker) { - if (!marker || !marker.parentNode) return; - var parent = marker.parentNode; - while (marker.nextSibling) parent.removeChild(marker.nextSibling); - } - function domSetData(el, key, val) { - if (el) { if (!el._sxData) el._sxData = {}; el._sxData[key] = val; } - } - function domGetData(el, key) { - return (el && el._sxData) ? (el._sxData[key] != null ? el._sxData[key] : nil) : nil; - } - function jsonParse(s) { - try { return JSON.parse(s); } catch(e) { return {}; } - } - - // renderDomComponent and renderDomElement are transpiled from - // adapter-dom.sx — no imperative overrides needed. - - - // ========================================================================= - // Platform interface — Engine pure logic (browser + node compatible) - // ========================================================================= - - function browserLocationHref() { - return typeof location !== "undefined" ? location.href : ""; - } - - function browserSameOrigin(url) { - try { return new URL(url, location.href).origin === location.origin; } - catch (e) { return true; } - } - - function browserPushState(url) { - if (typeof history !== "undefined") { - try { history.pushState({ sxUrl: url, scrollY: typeof window !== "undefined" ? window.scrollY : 0 }, "", url); } - catch (e) {} - } - } - - function browserReplaceState(url) { - if (typeof history !== "undefined") { - try { history.replaceState({ sxUrl: url, scrollY: typeof window !== "undefined" ? window.scrollY : 0 }, "", url); } - catch (e) {} - } - } - - function nowMs() { return Date.now(); } - - function parseHeaderValue(s) { - if (!s) return null; - try { - if (s.charAt(0) === "{" && s.charAt(1) === ":") return parse(s); - return JSON.parse(s); - } catch (e) { return null; } - } - - - // ========================================================================= - // Platform interface — Orchestration (browser-only) - // ========================================================================= - - // --- Browser/Network --- - - function browserNavigate(url) { - if (typeof location !== "undefined") location.assign(url); - } - - function browserReload() { - if (typeof location !== "undefined") location.reload(); - } - - function browserScrollTo(x, y) { - if (typeof window !== "undefined") window.scrollTo(x, y); - } - - function browserMediaMatches(query) { - if (typeof window === "undefined") return false; - return window.matchMedia(query).matches; - } - - function browserConfirm(msg) { - if (typeof window === "undefined") return false; - return window.confirm(msg); - } - - function browserPrompt(msg) { - if (typeof window === "undefined") return NIL; - var r = window.prompt(msg); - return r === null ? NIL : r; - } - - function csrfToken() { - if (!_hasDom) return NIL; - var m = document.querySelector('meta[name="csrf-token"]'); - return m ? m.getAttribute("content") : NIL; - } - - function isCrossOrigin(url) { - try { - var h = new URL(url, location.href).hostname; - return h !== location.hostname && - (h.indexOf(".rose-ash.com") >= 0 || h.indexOf(".localhost") >= 0); - } catch (e) { return false; } - } - - // --- Promises --- - - function promiseResolve(val) { return Promise.resolve(val); } - - function promiseThen(p, onResolve, onReject) { - if (!p || !p.then) return p; - return onReject ? p.then(onResolve, onReject) : p.then(onResolve); - } - - function promiseCatch(p, fn) { return p && p.catch ? p.catch(fn) : p; } - - function promiseDelayed(ms, value) { - return new Promise(function(resolve) { - setTimeout(function() { resolve(value); }, ms); - }); - } - - // --- Abort controllers --- - - var _controllers = typeof WeakMap !== "undefined" ? new WeakMap() : null; - - function abortPrevious(el) { - if (_controllers) { - var prev = _controllers.get(el); - if (prev) prev.abort(); - } - } - - function trackController(el, ctrl) { - if (_controllers) _controllers.set(el, ctrl); - } - - var _targetControllers = typeof WeakMap !== "undefined" ? new WeakMap() : null; - - function abortPreviousTarget(el) { - if (_targetControllers) { - var prev = _targetControllers.get(el); - if (prev) prev.abort(); - } - } - - function trackControllerTarget(el, ctrl) { - if (_targetControllers) _targetControllers.set(el, ctrl); - } - - function newAbortController() { - return typeof AbortController !== "undefined" ? new AbortController() : { signal: null, abort: function() {} }; - } - - function controllerSignal(ctrl) { return ctrl ? ctrl.signal : null; } - - function isAbortError(err) { return err && err.name === "AbortError"; } - - // --- Timers --- - - function _wrapSxFn(fn) { - if (fn && fn._lambda) { - return function() { return trampoline(callLambda(fn, [], lambdaClosure(fn))); }; - } - return fn; - } - function setTimeout_(fn, ms) { return setTimeout(_wrapSxFn(fn), ms || 0); } - function setInterval_(fn, ms) { return setInterval(_wrapSxFn(fn), ms || 1000); } - function clearTimeout_(id) { clearTimeout(id); } - function clearInterval_(id) { clearInterval(id); } - function requestAnimationFrame_(fn) { - var cb = _wrapSxFn(fn); - if (typeof requestAnimationFrame !== "undefined") requestAnimationFrame(cb); - else setTimeout(cb, 16); - } - - // --- Fetch --- - - function fetchRequest(config, successFn, errorFn) { - var opts = { method: config.method, headers: config.headers }; - if (config.signal) opts.signal = config.signal; - if (config.body && config.method !== "GET") opts.body = config.body; - if (config["cross-origin"]) opts.credentials = "include"; - - var p = (config.preloaded && config.preloaded !== NIL) - ? Promise.resolve({ - ok: true, status: 200, - headers: new Headers({ "Content-Type": config.preloaded["content-type"] || "" }), - text: function() { return Promise.resolve(config.preloaded.text); } - }) - : fetch(config.url, opts); - - return p.then(function(resp) { - return resp.text().then(function(text) { - var getHeader = function(name) { - var v = resp.headers.get(name); - return v === null ? NIL : v; - }; - return successFn(resp.ok, resp.status, getHeader, text); - }); - }).catch(function(err) { - return errorFn(err); - }); - } - - function fetchLocation(headerVal) { - if (!_hasDom) return; - var locUrl = headerVal; - try { var obj = JSON.parse(headerVal); locUrl = obj.path || obj; } catch (e) {} - fetch(locUrl, { headers: { "SX-Request": "true" } }).then(function(r) { - return r.text().then(function(t) { - var main = document.getElementById("main-panel"); - if (main) { - main.innerHTML = t; - postSwap(main); - try { history.pushState({ sxUrl: locUrl }, "", locUrl); } catch (e) {} - } - }); - }); - } - - function fetchAndRestore(main, url, headers, scrollY) { - var opts = { headers: headers }; - try { - var h = new URL(url, location.href).hostname; - if (h !== location.hostname && - (h.indexOf(".rose-ash.com") >= 0 || h.indexOf(".localhost") >= 0)) { - opts.credentials = "include"; - } - } catch (e) {} - - fetch(url, opts).then(function(resp) { - return resp.text().then(function(text) { - text = stripComponentScripts(text); - text = extractResponseCss(text); - text = text.trim(); - if (text.charAt(0) === "(") { - try { - var dom = sxRender(text); - var container = document.createElement("div"); - container.appendChild(dom); - processOobSwaps(container, function(t, oob, s) { - swapDomNodes(t, oob, s); - sxHydrate(t); - processElements(t); - }); - var newMain = container.querySelector("#main-panel"); - morphChildren(main, newMain || container); - postSwap(main); - if (typeof window !== "undefined") window.scrollTo(0, scrollY || 0); - } catch (err) { - console.error("sx-ref popstate error:", err); - location.reload(); - } - } else { - var parser = new DOMParser(); - var doc = parser.parseFromString(text, "text/html"); - var newMain = doc.getElementById("main-panel"); - if (newMain) { - morphChildren(main, newMain); - postSwap(main); - if (typeof window !== "undefined") window.scrollTo(0, scrollY || 0); - } else { - location.reload(); - } - } - }); - }).catch(function() { location.reload(); }); - } - - function fetchStreaming(target, url, headers) { - // Streaming fetch for multi-stream pages. - // First chunk = OOB SX swap (shell with skeletons). - // Subsequent chunks = __sxResolve script tags filling suspense slots. - var opts = { headers: headers }; - try { - var h = new URL(url, location.href).hostname; - if (h !== location.hostname && - (h.indexOf(".rose-ash.com") >= 0 || h.indexOf(".localhost") >= 0)) { - opts.credentials = "include"; - } - } catch (e) {} - - fetch(url, opts).then(function(resp) { - if (!resp.ok || !resp.body) { - // Fallback: non-streaming - return resp.text().then(function(text) { - text = stripComponentScripts(text); - text = extractResponseCss(text); - text = text.trim(); - if (text.charAt(0) === "(") { - var dom = sxRender(text); - var container = document.createElement("div"); - container.appendChild(dom); - processOobSwaps(container, function(t, oob, s) { - swapDomNodes(t, oob, s); - sxHydrate(t); - processElements(t); - }); - var newMain = container.querySelector("#main-panel"); - morphChildren(target, newMain || container); - postSwap(target); - } - }); - } - - var reader = resp.body.getReader(); - var decoder = new TextDecoder(); - var buffer = ""; - var initialSwapDone = false; - // Regex to match __sxResolve script tags - var RESOLVE_START = ""; - - function processResolveScripts() { - // Strip and load any extra component defs before resolve scripts - buffer = stripSxScripts(buffer); - var idx; - while ((idx = buffer.indexOf(RESOLVE_START)) >= 0) { - var endIdx = buffer.indexOf(RESOLVE_END, idx); - if (endIdx < 0) break; // incomplete, wait for more data - var argsStr = buffer.substring(idx + RESOLVE_START.length, endIdx); - buffer = buffer.substring(endIdx + RESOLVE_END.length); - // argsStr is: "stream-id","sx source" - var commaIdx = argsStr.indexOf(","); - if (commaIdx >= 0) { - try { - var id = JSON.parse(argsStr.substring(0, commaIdx)); - var sx = JSON.parse(argsStr.substring(commaIdx + 1)); - if (typeof Sx !== "undefined" && Sx.resolveSuspense) { - Sx.resolveSuspense(id, sx); - } - } catch (e) { - console.error("[sx-ref] resolve parse error:", e); - } - } - } - } - - function pump() { - return reader.read().then(function(result) { - buffer += decoder.decode(result.value || new Uint8Array(), { stream: !result.done }); - - if (!initialSwapDone) { - // Look for the first resolve script — everything before it is OOB content - var scriptIdx = buffer.indexOf(" (without data-components). - // These contain extra component defs from streaming resolve chunks. - var SxObj = typeof Sx !== "undefined" ? Sx : null; - return text.replace(/]*type="text\/sx"[^>]*>([\s\S]*?)<\/script>/gi, - function(_, defs) { if (SxObj && SxObj.loadComponents) SxObj.loadComponents(defs); return ""; }); - } - - function extractResponseCss(text) { - if (!_hasDom) return text; - var target = document.getElementById("sx-css"); - if (!target) return text; - return text.replace(/]*data-sx-css[^>]*>([\s\S]*?)<\/style>/gi, - function(_, css) { target.textContent += css; return ""; }); - } - - function selectFromContainer(container, sel) { - var frag = document.createDocumentFragment(); - sel.split(",").forEach(function(s) { - container.querySelectorAll(s.trim()).forEach(function(m) { frag.appendChild(m); }); - }); - return frag; - } - - function childrenToFragment(container) { - var frag = document.createDocumentFragment(); - while (container.firstChild) frag.appendChild(container.firstChild); - return frag; - } - - function selectHtmlFromDoc(doc, sel) { - var parts = sel.split(",").map(function(s) { return s.trim(); }); - var frags = []; - parts.forEach(function(s) { - doc.querySelectorAll(s).forEach(function(m) { frags.push(m.outerHTML); }); - }); - return frags.join(""); - } - - // --- Parsing --- - - function tryParseJson(s) { - if (!s) return NIL; - try { return JSON.parse(s); } catch (e) { return NIL; } - } - - - // ========================================================================= - // Platform interface — Boot (mount, hydrate, scripts, cookies) - // ========================================================================= - - function resolveMountTarget(target) { - if (typeof target === "string") return _hasDom ? document.querySelector(target) : null; - return target; - } - - function sxRenderWithEnv(source, extraEnv) { - var env = extraEnv ? merge(componentEnv, extraEnv) : componentEnv; - var exprs = parse(source); - if (!_hasDom) return null; - var frag = document.createDocumentFragment(); - for (var i = 0; i < exprs.length; i++) { - var node = renderToDom(exprs[i], env, null); - if (node) frag.appendChild(node); - } - return frag; - } - - function getRenderEnv(extraEnv) { - return extraEnv ? merge(componentEnv, extraEnv) : componentEnv; - } - - function mergeEnvs(base, newEnv) { - return newEnv ? merge(componentEnv, base, newEnv) : merge(componentEnv, base); - } - - function sxLoadComponents(text) { - try { - var exprs = parse(text); - for (var i = 0; i < exprs.length; i++) trampoline(evalExpr(exprs[i], componentEnv)); - } catch (err) { - logParseError("loadComponents", text, err); - throw err; - } - } - - function setDocumentTitle(s) { - if (_hasDom) document.title = s || ""; - } - - function removeHeadElement(sel) { - if (!_hasDom) return; - var old = document.head.querySelector(sel); - if (old) old.parentNode.removeChild(old); - } - - function querySxScripts(root) { - if (!_hasDom) return []; - var r = (root && root !== NIL) ? root : document; - return Array.prototype.slice.call( - r.querySelectorAll('script[type="text/sx"]')); - } - - function queryPageScripts() { - if (!_hasDom) return []; - return Array.prototype.slice.call( - document.querySelectorAll('script[type="text/sx-pages"]')); - } - - // --- localStorage --- - - function localStorageGet(key) { - try { var v = localStorage.getItem(key); return v === null ? NIL : v; } - catch (e) { return NIL; } - } - - function localStorageSet(key, val) { - try { localStorage.setItem(key, val); } catch (e) {} - } - - function localStorageRemove(key) { - try { localStorage.removeItem(key); } catch (e) {} - } - - // --- Cookies --- - - function setSxCompCookie(hash) { - if (_hasDom) document.cookie = "sx-comp-hash=" + hash + ";path=/;max-age=31536000;SameSite=Lax"; - } - - function clearSxCompCookie() { - if (_hasDom) document.cookie = "sx-comp-hash=;path=/;max-age=0;SameSite=Lax"; - } - - // --- Env helpers --- - - function parseEnvAttr(el) { - var attr = el && el.getAttribute ? el.getAttribute("data-sx-env") : null; - if (!attr) return {}; - try { return JSON.parse(attr); } catch (e) { return {}; } - } - - function storeEnvAttr(el, base, newEnv) { - var merged = merge(base, newEnv); - if (el && el.setAttribute) el.setAttribute("data-sx-env", JSON.stringify(merged)); - } - - function toKebab(s) { return s.replace(/_/g, "-"); } - - // --- Logging --- - - function logInfo(msg) { - if (typeof console !== "undefined") console.log("[sx-ref] " + msg); - } - - function logWarn(msg) { - if (typeof console !== "undefined") console.warn("[sx-ref] " + msg); - } - - function logParseError(label, text, err) { - if (typeof console === "undefined") return; - var msg = err && err.message ? err.message : String(err); - var colMatch = msg.match(/col (\d+)/); - var lineMatch = msg.match(/line (\d+)/); - if (colMatch && text) { - var errLine = lineMatch ? parseInt(lineMatch[1]) : 1; - var errCol = parseInt(colMatch[1]); - var lines = text.split("\n"); - var pos = 0; - for (var i = 0; i < errLine - 1 && i < lines.length; i++) pos += lines[i].length + 1; - pos += errCol; - var ws = 80; - var start = Math.max(0, pos - ws); - var end = Math.min(text.length, pos + ws); - console.error("[sx-ref] " + label + ":", msg, - "\n around error (pos ~" + pos + "):", - "\n \u00ab" + text.substring(start, pos) + "\u26d4" + text.substring(pos, end) + "\u00bb"); - } else { - console.error("[sx-ref] " + label + ":", msg); - } - } - - - - // ========================================================================= - // Post-transpilation fixups - // ========================================================================= - // The reference spec's call-lambda only handles Lambda objects, but HO forms - // (map, reduce, etc.) may receive native primitives. Wrap to handle both. - var _rawCallLambda = callLambda; - callLambda = function(f, args, callerEnv) { - if (typeof f === "function") return f.apply(null, args); - return _rawCallLambda(f, args, callerEnv); - }; - - // Expose render functions as primitives so SX code can call them - if (typeof renderToHtml === "function") PRIMITIVES["render-to-html"] = renderToHtml; - if (typeof renderToSx === "function") PRIMITIVES["render-to-sx"] = renderToSx; - if (typeof aser === "function") PRIMITIVES["aser"] = aser; - if (typeof renderToDom === "function") PRIMITIVES["render-to-dom"] = renderToDom; - - // Expose signal functions as primitives so runtime-evaluated SX code - // (e.g. island bodies from .sx files) can call them - PRIMITIVES["signal"] = signal; - PRIMITIVES["signal?"] = isSignal; - PRIMITIVES["deref"] = deref; - PRIMITIVES["reset!"] = reset_b; - PRIMITIVES["swap!"] = swap_b; - PRIMITIVES["computed"] = computed; - PRIMITIVES["effect"] = effect; - PRIMITIVES["batch"] = batch; - // Timer primitives for island code - PRIMITIVES["set-interval"] = setInterval_; - PRIMITIVES["clear-interval"] = clearInterval_; - // Reactive DOM helpers for island code - PRIMITIVES["reactive-text"] = reactiveText; - PRIMITIVES["create-text-node"] = createTextNode; - PRIMITIVES["dom-set-text-content"] = domSetTextContent; - PRIMITIVES["dom-listen"] = domListen; - PRIMITIVES["dom-dispatch"] = domDispatch; - PRIMITIVES["event-detail"] = eventDetail; - PRIMITIVES["resource"] = resource; - PRIMITIVES["promise-delayed"] = promiseDelayed; - PRIMITIVES["promise-then"] = promiseThen; - PRIMITIVES["def-store"] = defStore; - PRIMITIVES["use-store"] = useStore; - PRIMITIVES["emit-event"] = emitEvent; - PRIMITIVES["on-event"] = onEvent; - PRIMITIVES["bridge-event"] = bridgeEvent; - // DOM primitives for island code - PRIMITIVES["dom-focus"] = domFocus; - PRIMITIVES["dom-tag-name"] = domTagName; - PRIMITIVES["dom-get-prop"] = domGetProp; - PRIMITIVES["stop-propagation"] = stopPropagation_; - PRIMITIVES["error-message"] = errorMessage; - PRIMITIVES["schedule-idle"] = scheduleIdle; - PRIMITIVES["invoke"] = invoke; - PRIMITIVES["error"] = function(msg) { throw new Error(msg); }; - PRIMITIVES["filter"] = filter; - // DOM primitives for sx-on:* handlers and data-init scripts - if (typeof domBody === "function") PRIMITIVES["dom-body"] = domBody; - if (typeof domQuery === "function") PRIMITIVES["dom-query"] = domQuery; - if (typeof domQueryAll === "function") PRIMITIVES["dom-query-all"] = domQueryAll; - if (typeof domQueryById === "function") PRIMITIVES["dom-query-by-id"] = domQueryById; - if (typeof domSetAttr === "function") PRIMITIVES["dom-set-attr"] = domSetAttr; - if (typeof domGetAttr === "function") PRIMITIVES["dom-get-attr"] = domGetAttr; - if (typeof domRemoveAttr === "function") PRIMITIVES["dom-remove-attr"] = domRemoveAttr; - if (typeof domHasAttr === "function") PRIMITIVES["dom-has-attr?"] = domHasAttr; - if (typeof domAddClass === "function") PRIMITIVES["dom-add-class"] = domAddClass; - if (typeof domRemoveClass === "function") PRIMITIVES["dom-remove-class"] = domRemoveClass; - if (typeof domHasClass === "function") PRIMITIVES["dom-has-class?"] = domHasClass; - if (typeof domClosest === "function") PRIMITIVES["dom-closest"] = domClosest; - if (typeof domMatches === "function") PRIMITIVES["dom-matches?"] = domMatches; - if (typeof preventDefault_ === "function") PRIMITIVES["prevent-default"] = preventDefault_; - if (typeof elementValue === "function") PRIMITIVES["element-value"] = elementValue; - if (typeof domOuterHtml === "function") PRIMITIVES["dom-outer-html"] = domOuterHtml; - - // ========================================================================= - // Async IO: Promise-aware rendering for client-side IO primitives - // ========================================================================= - // - // IO primitives (query, current-user, etc.) return Promises on the client. - // asyncRenderToDom walks the component tree; when it encounters an IO - // primitive, it awaits the Promise and continues rendering. - // - // The sync evaluator/renderer is untouched. This is a separate async path - // used only when a page's component tree contains IO references. - - var IO_PRIMITIVES = {}; - - function registerIoPrimitive(name, fn) { - IO_PRIMITIVES[name] = fn; - } - - function isPromise(x) { - return x != null && typeof x === "object" && typeof x.then === "function"; - } - - // Async trampoline: resolves thunks, awaits Promises - function asyncTrampoline(val) { - if (isPromise(val)) return val.then(asyncTrampoline); - if (isThunk(val)) return asyncTrampoline(evalExpr(thunkExpr(val), thunkEnv(val))); - return val; - } - - // Async eval: like trampoline(evalExpr(...)) but handles IO primitives - function asyncEval(expr, env) { - // Intercept IO primitive calls at the AST level - if (Array.isArray(expr) && expr.length > 0) { - var head = expr[0]; - if (head && head._sym) { - var name = head.name; - if (IO_PRIMITIVES[name]) { - // Evaluate args, then call the IO primitive - return asyncEvalIoCall(name, expr.slice(1), env); - } - } - } - // Non-IO: use sync eval, but result might be a thunk - var result = evalExpr(expr, env); - return asyncTrampoline(result); - } - - function asyncEvalIoCall(name, rawArgs, env) { - // Parse keyword args and positional args, evaluating each (may be async) - var kwargs = {}; - var args = []; - var promises = []; - var i = 0; - while (i < rawArgs.length) { - var arg = rawArgs[i]; - if (arg && arg._kw && (i + 1) < rawArgs.length) { - var kName = arg.name; - var kVal = asyncEval(rawArgs[i + 1], env); - if (isPromise(kVal)) { - (function(k) { promises.push(kVal.then(function(v) { kwargs[k] = v; })); })(kName); - } else { - kwargs[kName] = kVal; - } - i += 2; - } else { - var aVal = asyncEval(arg, env); - if (isPromise(aVal)) { - (function(idx) { promises.push(aVal.then(function(v) { args[idx] = v; })); })(args.length); - args.push(null); // placeholder - } else { - args.push(aVal); - } - i++; - } - } - var ioFn = IO_PRIMITIVES[name]; - if (promises.length > 0) { - return Promise.all(promises).then(function() { return ioFn(args, kwargs); }); - } - return ioFn(args, kwargs); - } - - // Async render-to-dom: returns Promise or Node - function asyncRenderToDom(expr, env, ns) { - // Literals - if (expr === NIL || expr === null || expr === undefined) return null; - if (expr === true || expr === false) return null; - if (typeof expr === "string") return document.createTextNode(expr); - if (typeof expr === "number") return document.createTextNode(String(expr)); - - // Symbol -> async eval then render - if (expr && expr._sym) { - var val = asyncEval(expr, env); - if (isPromise(val)) return val.then(function(v) { return asyncRenderToDom(v, env, ns); }); - return asyncRenderToDom(val, env, ns); - } - - // Keyword - if (expr && expr._kw) return document.createTextNode(expr.name); - - // DocumentFragment / DOM nodes pass through - if (expr instanceof DocumentFragment || (expr && expr.nodeType)) return expr; - - // Dict -> skip - if (expr && typeof expr === "object" && !Array.isArray(expr)) return null; - - // List - if (!Array.isArray(expr) || expr.length === 0) return null; - - var head = expr[0]; - if (!head) return null; - - // Symbol head - if (head._sym) { - var hname = head.name; - - // IO primitive - if (IO_PRIMITIVES[hname]) { - var ioResult = asyncEval(expr, env); - if (isPromise(ioResult)) return ioResult.then(function(v) { return asyncRenderToDom(v, env, ns); }); - return asyncRenderToDom(ioResult, env, ns); - } - - // Fragment - if (hname === "<>") return asyncRenderChildren(expr.slice(1), env, ns); - - // raw! - if (hname === "raw!") { - return asyncEvalRaw(expr.slice(1), env); - } - - // Special forms that need async handling - if (hname === "if") return asyncRenderIf(expr, env, ns); - if (hname === "when") return asyncRenderWhen(expr, env, ns); - if (hname === "cond") return asyncRenderCond(expr, env, ns); - if (hname === "case") return asyncRenderCase(expr, env, ns); - if (hname === "let" || hname === "let*") return asyncRenderLet(expr, env, ns); - if (hname === "begin" || hname === "do") return asyncRenderChildren(expr.slice(1), env, ns); - if (hname === "map") return asyncRenderMap(expr, env, ns); - if (hname === "map-indexed") return asyncRenderMapIndexed(expr, env, ns); - if (hname === "for-each") return asyncRenderMap(expr, env, ns); - - // define/defcomp/defmacro — eval for side effects - if (hname === "define" || hname === "defcomp" || hname === "defmacro" || - hname === "defstyle" || hname === "defhandler") { - trampoline(evalExpr(expr, env)); - return null; - } - - // quote - if (hname === "quote") return null; - - // lambda/fn - if (hname === "lambda" || hname === "fn") { - trampoline(evalExpr(expr, env)); - return null; - } - - // and/or — eval and render result - if (hname === "and" || hname === "or" || hname === "->") { - var aoResult = asyncEval(expr, env); - if (isPromise(aoResult)) return aoResult.then(function(v) { return asyncRenderToDom(v, env, ns); }); - return asyncRenderToDom(aoResult, env, ns); - } - - // set! - if (hname === "set!") { - asyncEval(expr, env); - return null; - } - - // Component or Island - if (hname.charAt(0) === "~") { - var comp = env[hname]; - if (comp && comp._island) return renderDomIsland(comp, expr.slice(1), env, ns); - if (comp && comp._component) return asyncRenderComponent(comp, expr.slice(1), env, ns); - if (comp && comp._macro) { - var expanded = trampoline(expandMacro(comp, expr.slice(1), env)); - return asyncRenderToDom(expanded, env, ns); - } - } - - // Macro - if (env[hname] && env[hname]._macro) { - var mac = env[hname]; - var expanded = trampoline(expandMacro(mac, expr.slice(1), env)); - return asyncRenderToDom(expanded, env, ns); - } - - // HTML tag - if (typeof renderDomElement === "function" && contains(HTML_TAGS, hname)) { - return asyncRenderElement(hname, expr.slice(1), env, ns); - } - - // html: prefix - if (hname.indexOf("html:") === 0) { - return asyncRenderElement(hname.slice(5), expr.slice(1), env, ns); - } - - // Custom element - if (hname.indexOf("-") >= 0 && expr.length > 1 && expr[1] && expr[1]._kw) { - return asyncRenderElement(hname, expr.slice(1), env, ns); - } - - // SVG context - if (ns) return asyncRenderElement(hname, expr.slice(1), env, ns); - - // Fallback: eval and render - var fResult = asyncEval(expr, env); - if (isPromise(fResult)) return fResult.then(function(v) { return asyncRenderToDom(v, env, ns); }); - return asyncRenderToDom(fResult, env, ns); - } - - // Non-symbol head: eval call - var cResult = asyncEval(expr, env); - if (isPromise(cResult)) return cResult.then(function(v) { return asyncRenderToDom(v, env, ns); }); - return asyncRenderToDom(cResult, env, ns); - } - - function asyncRenderChildren(exprs, env, ns) { - var frag = document.createDocumentFragment(); - var pending = []; - for (var i = 0; i < exprs.length; i++) { - var result = asyncRenderToDom(exprs[i], env, ns); - if (isPromise(result)) { - // Insert placeholder, replace when resolved - var placeholder = document.createComment("async"); - frag.appendChild(placeholder); - (function(ph) { - pending.push(result.then(function(node) { - if (node) ph.parentNode.replaceChild(node, ph); - else ph.parentNode.removeChild(ph); - })); - })(placeholder); - } else if (result) { - frag.appendChild(result); - } - } - if (pending.length > 0) { - return Promise.all(pending).then(function() { return frag; }); - } - return frag; - } - - function asyncRenderElement(tag, args, env, ns) { - var newNs = tag === "svg" ? SVG_NS : tag === "math" ? MATH_NS : ns; - var el = domCreateElement(tag, newNs); - var pending = []; - var isVoid = contains(VOID_ELEMENTS, tag); - for (var i = 0; i < args.length; i++) { - var arg = args[i]; - if (arg && arg._kw && (i + 1) < args.length) { - var attrName = arg.name; - var attrVal = asyncEval(args[i + 1], env); - i++; - if (isPromise(attrVal)) { - (function(an, av) { - pending.push(av.then(function(v) { - if (!isNil(v) && v !== false) { - if (contains(BOOLEAN_ATTRS, an)) { if (isSxTruthy(v)) el.setAttribute(an, ""); } - else if (v === true) el.setAttribute(an, ""); - else el.setAttribute(an, String(v)); - } - })); - })(attrName, attrVal); - } else { - if (!isNil(attrVal) && attrVal !== false) { - if (contains(BOOLEAN_ATTRS, attrName)) { - if (isSxTruthy(attrVal)) el.setAttribute(attrName, ""); - } else if (attrVal === true) { - el.setAttribute(attrName, ""); - } else { - el.setAttribute(attrName, String(attrVal)); - } - } - } - } else if (!isVoid) { - var child = asyncRenderToDom(arg, env, newNs); - if (isPromise(child)) { - var placeholder = document.createComment("async"); - el.appendChild(placeholder); - (function(ph) { - pending.push(child.then(function(node) { - if (node) ph.parentNode.replaceChild(node, ph); - else ph.parentNode.removeChild(ph); - })); - })(placeholder); - } else if (child) { - el.appendChild(child); - } - } - } - if (pending.length > 0) return Promise.all(pending).then(function() { return el; }); - return el; - } - - function asyncRenderComponent(comp, args, env, ns) { - var kwargs = {}; - var children = []; - var pending = []; - for (var i = 0; i < args.length; i++) { - var arg = args[i]; - if (arg && arg._kw && (i + 1) < args.length) { - var kName = arg.name; - var kVal = asyncEval(args[i + 1], env); - if (isPromise(kVal)) { - (function(k) { pending.push(kVal.then(function(v) { kwargs[k] = v; })); })(kName); - } else { - kwargs[kName] = kVal; - } - i++; - } else { - children.push(arg); - } - } - - function doRender() { - var local = Object.create(componentClosure(comp)); - for (var k in env) if (env.hasOwnProperty(k)) local[k] = env[k]; - var params = componentParams(comp); - for (var j = 0; j < params.length; j++) { - local[params[j]] = params[j] in kwargs ? kwargs[params[j]] : NIL; - } - if (componentHasChildren(comp)) { - var childResult = asyncRenderChildren(children, env, ns); - if (isPromise(childResult)) { - return childResult.then(function(childFrag) { - local["children"] = childFrag; - return asyncRenderToDom(componentBody(comp), local, ns); - }); - } - local["children"] = childResult; - } - return asyncRenderToDom(componentBody(comp), local, ns); - } - - if (pending.length > 0) return Promise.all(pending).then(doRender); - return doRender(); - } - - function asyncRenderIf(expr, env, ns) { - var cond = asyncEval(expr[1], env); - if (isPromise(cond)) { - return cond.then(function(v) { - return isSxTruthy(v) - ? asyncRenderToDom(expr[2], env, ns) - : (expr.length > 3 ? asyncRenderToDom(expr[3], env, ns) : null); - }); - } - return isSxTruthy(cond) - ? asyncRenderToDom(expr[2], env, ns) - : (expr.length > 3 ? asyncRenderToDom(expr[3], env, ns) : null); - } - - function asyncRenderWhen(expr, env, ns) { - var cond = asyncEval(expr[1], env); - if (isPromise(cond)) { - return cond.then(function(v) { - return isSxTruthy(v) ? asyncRenderChildren(expr.slice(2), env, ns) : null; - }); - } - return isSxTruthy(cond) ? asyncRenderChildren(expr.slice(2), env, ns) : null; - } - - function asyncRenderCond(expr, env, ns) { - var clauses = expr.slice(1); - function step(idx) { - if (idx >= clauses.length) return null; - var clause = clauses[idx]; - if (!Array.isArray(clause) || clause.length < 2) return step(idx + 1); - var test = clause[0]; - if ((test && test._sym && (test.name === "else" || test.name === ":else")) || - (test && test._kw && test.name === "else")) { - return asyncRenderToDom(clause[1], env, ns); - } - var v = asyncEval(test, env); - if (isPromise(v)) return v.then(function(r) { return isSxTruthy(r) ? asyncRenderToDom(clause[1], env, ns) : step(idx + 1); }); - return isSxTruthy(v) ? asyncRenderToDom(clause[1], env, ns) : step(idx + 1); - } - return step(0); - } - - function asyncRenderCase(expr, env, ns) { - var matchVal = asyncEval(expr[1], env); - function doCase(mv) { - var clauses = expr.slice(2); - for (var i = 0; i < clauses.length - 1; i += 2) { - var test = clauses[i]; - if ((test && test._kw && test.name === "else") || - (test && test._sym && (test.name === "else" || test.name === ":else"))) { - return asyncRenderToDom(clauses[i + 1], env, ns); - } - var tv = trampoline(evalExpr(test, env)); - if (mv === tv || (typeof mv === "string" && typeof tv === "string" && mv === tv)) { - return asyncRenderToDom(clauses[i + 1], env, ns); - } - } - return null; - } - if (isPromise(matchVal)) return matchVal.then(doCase); - return doCase(matchVal); - } - - function asyncRenderLet(expr, env, ns) { - var bindings = expr[1]; - var local = Object.create(env); - for (var k in env) if (env.hasOwnProperty(k)) local[k] = env[k]; - function bindStep(idx) { - if (!Array.isArray(bindings)) return asyncRenderChildren(expr.slice(2), local, ns); - // Nested pairs: ((a 1) (b 2)) - if (bindings.length > 0 && Array.isArray(bindings[0])) { - if (idx >= bindings.length) return asyncRenderChildren(expr.slice(2), local, ns); - var b = bindings[idx]; - var vname = b[0]._sym ? b[0].name : String(b[0]); - var val = asyncEval(b[1], local); - if (isPromise(val)) return val.then(function(v) { local[vname] = v; return bindStep(idx + 1); }); - local[vname] = val; - return bindStep(idx + 1); - } - // Flat pairs: (a 1 b 2) - if (idx >= bindings.length) return asyncRenderChildren(expr.slice(2), local, ns); - var vn = bindings[idx]._sym ? bindings[idx].name : String(bindings[idx]); - var vv = asyncEval(bindings[idx + 1], local); - if (isPromise(vv)) return vv.then(function(v) { local[vn] = v; return bindStep(idx + 2); }); - local[vn] = vv; - return bindStep(idx + 2); - } - return bindStep(0); - } - - function asyncRenderMap(expr, env, ns) { - var fn = asyncEval(expr[1], env); - var coll = asyncEval(expr[2], env); - function doMap(f, c) { - if (!Array.isArray(c)) return null; - var frag = document.createDocumentFragment(); - var pending = []; - for (var i = 0; i < c.length; i++) { - var item = c[i]; - var result; - if (f && f._lambda) { - var lenv = Object.create(f.closure || env); - for (var k in env) if (env.hasOwnProperty(k)) lenv[k] = env[k]; - lenv[f.params[0]] = item; - result = asyncRenderToDom(f.body, lenv, null); - } else if (typeof f === "function") { - var r = f(item); - result = isPromise(r) ? r.then(function(v) { return asyncRenderToDom(v, env, null); }) : asyncRenderToDom(r, env, null); - } else { - result = asyncRenderToDom(item, env, null); - } - if (isPromise(result)) { - var ph = document.createComment("async"); - frag.appendChild(ph); - (function(p) { pending.push(result.then(function(n) { if (n) p.parentNode.replaceChild(n, p); else p.parentNode.removeChild(p); })); })(ph); - } else if (result) { - frag.appendChild(result); - } - } - if (pending.length) return Promise.all(pending).then(function() { return frag; }); - return frag; - } - if (isPromise(fn) || isPromise(coll)) { - return Promise.all([isPromise(fn) ? fn : Promise.resolve(fn), isPromise(coll) ? coll : Promise.resolve(coll)]) - .then(function(r) { return doMap(r[0], r[1]); }); - } - return doMap(fn, coll); - } - - function asyncRenderMapIndexed(expr, env, ns) { - var fn = asyncEval(expr[1], env); - var coll = asyncEval(expr[2], env); - function doMap(f, c) { - if (!Array.isArray(c)) return null; - var frag = document.createDocumentFragment(); - var pending = []; - for (var i = 0; i < c.length; i++) { - var item = c[i]; - var result; - if (f && f._lambda) { - var lenv = Object.create(f.closure || env); - for (var k in env) if (env.hasOwnProperty(k)) lenv[k] = env[k]; - lenv[f.params[0]] = i; - lenv[f.params[1]] = item; - result = asyncRenderToDom(f.body, lenv, null); - } else if (typeof f === "function") { - var r = f(i, item); - result = isPromise(r) ? r.then(function(v) { return asyncRenderToDom(v, env, null); }) : asyncRenderToDom(r, env, null); - } else { - result = asyncRenderToDom(item, env, null); - } - if (isPromise(result)) { - var ph = document.createComment("async"); - frag.appendChild(ph); - (function(p) { pending.push(result.then(function(n) { if (n) p.parentNode.replaceChild(n, p); else p.parentNode.removeChild(p); })); })(ph); - } else if (result) { - frag.appendChild(result); - } - } - if (pending.length) return Promise.all(pending).then(function() { return frag; }); - return frag; - } - if (isPromise(fn) || isPromise(coll)) { - return Promise.all([isPromise(fn) ? fn : Promise.resolve(fn), isPromise(coll) ? coll : Promise.resolve(coll)]) - .then(function(r) { return doMap(r[0], r[1]); }); - } - return doMap(fn, coll); - } - - function asyncEvalRaw(args, env) { - var parts = []; - var pending = []; - for (var i = 0; i < args.length; i++) { - var val = asyncEval(args[i], env); - if (isPromise(val)) { - (function(idx) { - pending.push(val.then(function(v) { parts[idx] = v; })); - })(parts.length); - parts.push(null); - } else { - parts.push(val); - } - } - function assemble() { - var html = ""; - for (var j = 0; j < parts.length; j++) { - var p = parts[j]; - if (p && p._rawHtml) html += p.html; - else if (typeof p === "string") html += p; - else if (p != null && !isNil(p)) html += String(p); - } - var el = document.createElement("span"); - el.innerHTML = html; - var frag = document.createDocumentFragment(); - while (el.firstChild) frag.appendChild(el.firstChild); - return frag; - } - if (pending.length) return Promise.all(pending).then(assemble); - return assemble(); - } - - // Async version of sxRenderWithEnv — returns Promise - function asyncSxRenderWithEnv(source, extraEnv) { - var env = extraEnv ? merge(componentEnv, extraEnv) : componentEnv; - var exprs = parse(source); - if (!_hasDom) return Promise.resolve(null); - return asyncRenderChildren(exprs, env, null); - } - - // IO proxy cache: key → { value, expires } - var _ioCache = {}; - var IO_CACHE_TTL = 300000; // 5 minutes - - // Register a server-proxied IO primitive: fetches from /sx/io/ - // Uses GET for short args, POST for long payloads (URL length safety). - // Results are cached client-side by (name + args) with a TTL. - function registerProxiedIo(name) { - registerIoPrimitive(name, function(args, kwargs) { - // Cache key: name + serialized args - var cacheKey = name; - for (var ci = 0; ci < args.length; ci++) cacheKey += "" + String(args[ci]); - for (var ck in kwargs) { - if (kwargs.hasOwnProperty(ck)) cacheKey += "" + ck + "=" + String(kwargs[ck]); - } - var cached = _ioCache[cacheKey]; - if (cached && cached.expires > Date.now()) return cached.value; - - var url = "/sx/io/" + encodeURIComponent(name); - var qs = []; - for (var i = 0; i < args.length; i++) { - qs.push("_arg" + i + "=" + encodeURIComponent(String(args[i]))); - } - for (var k in kwargs) { - if (kwargs.hasOwnProperty(k)) { - qs.push(encodeURIComponent(k) + "=" + encodeURIComponent(String(kwargs[k]))); - } - } - var queryStr = qs.join("&"); - var fetchOpts; - if (queryStr.length > 1500) { - // POST with JSON body for long payloads - var sArgs = []; - for (var j = 0; j < args.length; j++) sArgs.push(String(args[j])); - var sKwargs = {}; - for (var kk in kwargs) { - if (kwargs.hasOwnProperty(kk)) sKwargs[kk] = String(kwargs[kk]); - } - var postHeaders = { "SX-Request": "true", "Content-Type": "application/json" }; - var csrf = csrfToken(); - if (csrf && csrf !== NIL) postHeaders["X-CSRFToken"] = csrf; - fetchOpts = { - method: "POST", - headers: postHeaders, - body: JSON.stringify({ args: sArgs, kwargs: sKwargs }) - }; - } else { - if (queryStr) url += "?" + queryStr; - fetchOpts = { headers: { "SX-Request": "true" } }; - } - var result = fetch(url, fetchOpts) - .then(function(resp) { - if (!resp.ok) { - logWarn("sx:io " + name + " failed " + resp.status); - return NIL; - } - return resp.text(); - }) - .then(function(text) { - if (!text || text === "nil") return NIL; - try { - var exprs = parse(text); - var val = exprs.length === 1 ? exprs[0] : exprs; - _ioCache[cacheKey] = { value: val, expires: Date.now() + IO_CACHE_TTL }; - return val; - } catch (e) { - logWarn("sx:io " + name + " parse error: " + (e && e.message ? e.message : e)); - return NIL; - } - }) - .catch(function(e) { - logWarn("sx:io " + name + " network error: " + (e && e.message ? e.message : e)); - return NIL; - }); - // Cache the in-flight promise too (dedup concurrent calls for same args) - _ioCache[cacheKey] = { value: result, expires: Date.now() + IO_CACHE_TTL }; - return result; - }); - } - - // Register IO deps as proxied primitives (idempotent, called per-page) - function registerIoDeps(names) { - if (!names || !names.length) return; - var registered = 0; - for (var i = 0; i < names.length; i++) { - var name = names[i]; - if (!IO_PRIMITIVES[name]) { - registerProxiedIo(name); - registered++; - } - } - if (registered > 0) { - logInfo("sx:io registered " + registered + " proxied primitives: " + names.join(", ")); - } - } - - - // Parser — compiled from parser.sx (see PLATFORM_PARSER_JS for ident char classes) - var parse = sxParse; - - // ========================================================================= - // Public API - // ========================================================================= - - var componentEnv = {}; - - function loadComponents(source) { - var exprs = parse(source); - for (var i = 0; i < exprs.length; i++) { - trampoline(evalExpr(exprs[i], componentEnv)); - } - } - - function render(source) { - if (!_hasDom) { - var exprs = parse(source); - var parts = []; - for (var i = 0; i < exprs.length; i++) parts.push(renderToHtml(exprs[i], merge(componentEnv))); - return parts.join(""); - } - var exprs = parse(source); - var frag = document.createDocumentFragment(); - for (var i = 0; i < exprs.length; i++) frag.appendChild(renderToDom(exprs[i], merge(componentEnv), null)); - return frag; - } - - function renderToString(source) { - var exprs = parse(source); - var parts = []; - for (var i = 0; i < exprs.length; i++) parts.push(renderToHtml(exprs[i], merge(componentEnv))); - return parts.join(""); - } - - var Sx = { - VERSION: "ref-2.0", - parse: parse, - parseAll: parse, - eval: function(expr, env) { return trampoline(evalExpr(expr, env || merge(componentEnv))); }, - loadComponents: loadComponents, - render: render, - renderToString: renderToString, - serialize: serialize, - NIL: NIL, - Symbol: Symbol, - Keyword: Keyword, - isTruthy: isSxTruthy, - isNil: isNil, - componentEnv: componentEnv, - renderToHtml: function(expr, env) { return renderToHtml(expr, env || merge(componentEnv)); }, - renderToSx: function(expr, env) { return renderToSx(expr, env || merge(componentEnv)); }, - renderToDom: _hasDom ? function(expr, env, ns) { return renderToDom(expr, env || merge(componentEnv), ns || null); } : null, - parseTriggerSpec: typeof parseTriggerSpec === "function" ? parseTriggerSpec : null, - parseTime: typeof parseTime === "function" ? parseTime : null, - defaultTrigger: typeof defaultTrigger === "function" ? defaultTrigger : null, - parseSwapSpec: typeof parseSwapSpec === "function" ? parseSwapSpec : null, - parseRetrySpec: typeof parseRetrySpec === "function" ? parseRetrySpec : null, - nextRetryMs: typeof nextRetryMs === "function" ? nextRetryMs : null, - filterParams: typeof filterParams === "function" ? filterParams : null, - morphNode: typeof morphNode === "function" ? morphNode : null, - morphChildren: typeof morphChildren === "function" ? morphChildren : null, - swapDomNodes: typeof swapDomNodes === "function" ? swapDomNodes : null, - process: typeof processElements === "function" ? processElements : null, - executeRequest: typeof executeRequest === "function" ? executeRequest : null, - postSwap: typeof postSwap === "function" ? postSwap : null, - processScripts: typeof processSxScripts === "function" ? processSxScripts : null, - mount: typeof sxMount === "function" ? sxMount : null, - hydrate: typeof sxHydrateElements === "function" ? sxHydrateElements : null, - update: typeof sxUpdateElement === "function" ? sxUpdateElement : null, - renderComponent: typeof sxRenderComponent === "function" ? sxRenderComponent : null, - getEnv: function() { return componentEnv; }, - resolveSuspense: typeof resolveSuspense === "function" ? resolveSuspense : null, - hydrateIslands: typeof sxHydrateIslands === "function" ? sxHydrateIslands : null, - disposeIsland: typeof disposeIsland === "function" ? disposeIsland : null, - init: typeof bootInit === "function" ? bootInit : null, - splitPathSegments: splitPathSegments, - parseRoutePattern: parseRoutePattern, - matchRoute: matchRoute, - findMatchingRoute: findMatchingRoute, - registerIo: typeof registerIoPrimitive === "function" ? registerIoPrimitive : null, - registerIoDeps: typeof registerIoDeps === "function" ? registerIoDeps : null, - asyncRender: typeof asyncSxRenderWithEnv === "function" ? asyncSxRenderWithEnv : null, - asyncRenderToDom: typeof asyncRenderToDom === "function" ? asyncRenderToDom : null, - signal: signal, - deref: deref, - reset: reset_b, - swap: swap_b, - computed: computed, - effect: effect, - batch: batch, - isSignal: isSignal, - makeSignal: makeSignal, - defStore: defStore, - useStore: useStore, - clearStores: clearStores, - emitEvent: emitEvent, - onEvent: onEvent, - bridgeEvent: bridgeEvent, - _version: "ref-2.0 (boot+dom+engine+html+orchestration+parser+sx, bootstrap-compiled)" - }; - - - // --- Popstate listener --- - if (typeof window !== "undefined") { - window.addEventListener("popstate", function(e) { - handlePopstate(e && e.state ? e.state.scrollY || 0 : 0); - }); - } - - // --- Auto-init --- - if (typeof document !== "undefined") { - var _sxInit = function() { - bootInit(); - // Process any suspense resolutions that arrived before init - if (global.__sxPending) { - for (var pi = 0; pi < global.__sxPending.length; pi++) { - resolveSuspense(global.__sxPending[pi].id, global.__sxPending[pi].sx); - } - global.__sxPending = null; - } - // Set up direct resolution for future chunks - global.__sxResolve = function(id, sx) { resolveSuspense(id, sx); }; - // Register service worker for offline data caching - if ("serviceWorker" in navigator) { - navigator.serviceWorker.register("/sx-sw.js", { scope: "/" }).then(function(reg) { - logInfo("sx:sw registered (scope: " + reg.scope + ")"); - }).catch(function(err) { - logWarn("sx:sw registration failed: " + (err && err.message ? err.message : err)); - }); - } - }; - if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", _sxInit); - } else { - _sxInit(); - } - } - if (typeof module !== "undefined" && module.exports) module.exports = Sx; - else global.Sx = Sx; - -})(typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : this); \ No newline at end of file diff --git a/shared/sx/templates/cssx.sx b/shared/sx/templates/cssx.sx new file mode 100644 index 0000000..11c5714 --- /dev/null +++ b/shared/sx/templates/cssx.sx @@ -0,0 +1,219 @@ +;; @client — send all define forms to browser for client-side use. +;; CSSX — computed CSS from s-expressions. +;; +;; Generic mechanism: cssx is a macro that groups CSS property declarations. +;; The vocabulary (property mappings, value functions) is pluggable — the +;; Tailwind-inspired defaults below are just one possible style system. +;; +;; Usage: +;; (cssx (:text (colour "violet" 699) (size "4xl") (weight "bold") (family "mono")) +;; (:bg (colour "stone" 50))) +;; +;; Each group is (:keyword value ...modifiers): +;; - keyword maps to a CSS property via cssx-properties dict +;; - value is the CSS value for that property +;; - modifiers are extra CSS declaration strings, concatenated in +;; +;; Single group: +;; (cssx (:text (colour "violet" 699))) +;; +;; Modifiers without a colour: +;; (cssx (:text nil (size "4xl") (weight "bold"))) +;; +;; Unknown keywords pass through as raw CSS property names: +;; (cssx (:outline (colour "red" 500))) → "outline:hsl(0,72%,53%);" +;; +;; Standalone modifiers work outside cssx too: +;; :style (size "4xl") +;; :style (str (weight "bold") (family "mono")) + +;; ========================================================================= +;; Layer 1: Generic mechanism — cssx macro + cssxgroup function +;; ========================================================================= + +;; Property keyword → CSS property name. Extend this dict for new mappings. +(define cssx-properties + {"text" "color" + "bg" "background-color" + "border" "border-color"}) + +;; Evaluate one property group: (:text value modifier1 modifier2 ...) +;; If value is nil, only modifiers are emitted (no property declaration). +;; NOTE: name must NOT contain hyphens — the evaluator's isRenderExpr check +;; treats (hyphenated-name :keyword ...) as a custom HTML element. +(define cssxgroup + (fn (prop value b c d e) + (let ((css-prop (or (get cssx-properties prop) prop))) + (str (if (nil? value) "" (str css-prop ":" value ";")) + (or b "") (or c "") (or d "") (or e ""))))) + +;; cssx macro — takes one or more property groups, expands to (str ...). +;; (cssx (:text val ...) (:bg val ...)) +;; → (str (cssxgroup :text val ...) (cssxgroup :bg val ...)) +(defmacro cssx (&rest groups) + `(str ,@(map (fn (g) (cons 'cssxgroup g)) groups))) + +;; ========================================================================= +;; Layer 2: Value vocabulary — colour, size, weight, family +;; These are independent functions. Use inside cssx groups or standalone. +;; Replace or extend with any style system. +;; ========================================================================= + +;; --------------------------------------------------------------------------- +;; Colour — compute CSS colour value from name + shade +;; --------------------------------------------------------------------------- + +(define colour-bases + {"violet" {"h" 263 "s" 70} + "purple" {"h" 271 "s" 81} + "indigo" {"h" 239 "s" 84} + "blue" {"h" 217 "s" 91} + "sky" {"h" 199 "s" 89} + "cyan" {"h" 188 "s" 94} + "teal" {"h" 173 "s" 80} + "emerald" {"h" 160 "s" 84} + "green" {"h" 142 "s" 71} + "lime" {"h" 84 "s" 78} + "yellow" {"h" 48 "s" 96} + "amber" {"h" 38 "s" 92} + "orange" {"h" 25 "s" 95} + "red" {"h" 0 "s" 72} + "rose" {"h" 350 "s" 89} + "pink" {"h" 330 "s" 81} + "stone" {"h" 25 "s" 6} + "slate" {"h" 215 "s" 16} + "gray" {"h" 220 "s" 9} + "zinc" {"h" 240 "s" 5} + "neutral" {"h" 0 "s" 0}}) + +(define lerp (fn (a b t) (+ a (* t (- b a))))) + +(define shade-to-lightness + (fn (shade) + (cond + (<= shade 50) (lerp 100 97 (/ shade 50)) + (<= shade 100) (lerp 97 93 (/ (- shade 50) 50)) + (<= shade 200) (lerp 93 87 (/ (- shade 100) 100)) + (<= shade 300) (lerp 87 77 (/ (- shade 200) 100)) + (<= shade 400) (lerp 77 64 (/ (- shade 300) 100)) + (<= shade 500) (lerp 64 53 (/ (- shade 400) 100)) + (<= shade 600) (lerp 53 45 (/ (- shade 500) 100)) + (<= shade 700) (lerp 45 38 (/ (- shade 600) 100)) + (<= shade 800) (lerp 38 30 (/ (- shade 700) 100)) + (<= shade 900) (lerp 30 21 (/ (- shade 800) 100)) + (<= shade 950) (lerp 21 13 (/ (- shade 900) 50)) + true 13))) + +(define colour + (fn (name shade) + (let ((base (get colour-bases name))) + (if (nil? base) + name + (let ((h (get base "h")) + (s (get base "s")) + (l (shade-to-lightness shade))) + (str "hsl(" h "," s "%," (round l) "%)")))))) + +;; --------------------------------------------------------------------------- +;; Font sizes — named size → font-size + line-height (Tailwind v3 scale) +;; --------------------------------------------------------------------------- + +(define cssx-sizes + {"xs" "font-size:0.75rem;line-height:1rem;" + "sm" "font-size:0.875rem;line-height:1.25rem;" + "base" "font-size:1rem;line-height:1.5rem;" + "lg" "font-size:1.125rem;line-height:1.75rem;" + "xl" "font-size:1.25rem;line-height:1.75rem;" + "2xl" "font-size:1.5rem;line-height:2rem;" + "3xl" "font-size:1.875rem;line-height:2.25rem;" + "4xl" "font-size:2.25rem;line-height:2.5rem;" + "5xl" "font-size:3rem;line-height:1;" + "6xl" "font-size:3.75rem;line-height:1;" + "7xl" "font-size:4.5rem;line-height:1;" + "8xl" "font-size:6rem;line-height:1;" + "9xl" "font-size:8rem;line-height:1;"}) + +;; --------------------------------------------------------------------------- +;; Font weights — named weight → numeric value +;; --------------------------------------------------------------------------- + +(define cssx-weights + {"thin" "100" + "extralight" "200" + "light" "300" + "normal" "400" + "medium" "500" + "semibold" "600" + "bold" "700" + "extrabold" "800" + "black" "900"}) + +;; --------------------------------------------------------------------------- +;; Font families — named family → CSS font stack +;; --------------------------------------------------------------------------- + +(define cssx-families + {"sans" "ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,\"Noto Sans\",sans-serif" + "serif" "ui-serif,Georgia,Cambria,\"Times New Roman\",Times,serif" + "mono" "ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace"}) + +;; --------------------------------------------------------------------------- +;; Standalone modifier functions — return CSS declaration strings +;; Each returns a complete CSS declaration string. Use inside cssx groups +;; or standalone on :style with str. +;; --------------------------------------------------------------------------- + +;; -- Typography -- + +(define size + (fn (s) (or (get cssx-sizes s) (str "font-size:" s ";")))) + +(define weight + (fn (w) + (let ((v (get cssx-weights w))) + (str "font-weight:" (or v w) ";")))) + +(define family + (fn (f) + (let ((v (get cssx-families f))) + (str "font-family:" (or v f) ";")))) + +(define align + (fn (a) (str "text-align:" a ";"))) + +(define decoration + (fn (d) (str "text-decoration:" d ";"))) + +;; -- Spacing (Tailwind scale: 1 unit = 0.25rem) -- + +(define spacing (fn (n) (str (* n 0.25) "rem"))) + +(define p (fn (n) (str "padding:" (spacing n) ";"))) +(define px (fn (n) (str "padding-left:" (spacing n) ";padding-right:" (spacing n) ";"))) +(define py (fn (n) (str "padding-top:" (spacing n) ";padding-bottom:" (spacing n) ";"))) +(define pt (fn (n) (str "padding-top:" (spacing n) ";"))) +(define pb (fn (n) (str "padding-bottom:" (spacing n) ";"))) +(define pl (fn (n) (str "padding-left:" (spacing n) ";"))) +(define pr (fn (n) (str "padding-right:" (spacing n) ";"))) + +(define m (fn (n) (str "margin:" (spacing n) ";"))) +(define mx (fn (n) (str "margin-left:" (spacing n) ";margin-right:" (spacing n) ";"))) +(define my (fn (n) (str "margin-top:" (spacing n) ";margin-bottom:" (spacing n) ";"))) +(define mt (fn (n) (str "margin-top:" (spacing n) ";"))) +(define mb (fn (n) (str "margin-bottom:" (spacing n) ";"))) +(define ml (fn (n) (str "margin-left:" (spacing n) ";"))) +(define mr (fn (n) (str "margin-right:" (spacing n) ";"))) +(define mx-auto (fn () "margin-left:auto;margin-right:auto;")) + +;; -- Display & layout -- + +(define display (fn (d) (str "display:" d ";"))) +(define max-w (fn (w) (str "max-width:" w ";"))) + +;; Named max-widths (Tailwind scale) +(define cssx-max-widths + {"xs" "20rem" "sm" "24rem" "md" "28rem" + "lg" "32rem" "xl" "36rem" "2xl" "42rem" + "3xl" "48rem" "4xl" "56rem" "5xl" "64rem" + "6xl" "72rem" "7xl" "80rem" + "full" "100%" "none" "none"}) diff --git a/sx/sx/essays/hegelian-synthesis.sx b/sx/sx/essays/hegelian-synthesis.sx new file mode 100644 index 0000000..3658ee7 --- /dev/null +++ b/sx/sx/essays/hegelian-synthesis.sx @@ -0,0 +1,79 @@ +;; --------------------------------------------------------------------------- +;; The Hegelian Synthesis of Hypertext and Reactivity +;; --------------------------------------------------------------------------- + +(defcomp ~essay-hegelian-synthesis () + (~doc-page :title "The Hegelian Synthesis" + (p :class "text-stone-500 text-sm italic mb-8" + "On the dialectical resolution of the hypertext/reactive contradiction.") + (~doc-section :title "I. Thesis: The server renders" :id "thesis" + (p :class "text-stone-600" + "In the beginning was the hyperlink. The web was born as a system of documents connected by references. A page was a " (em "representation") " — complete, self-contained, delivered whole by the server. The browser was a thin client. It received, it rendered, it followed links. The server was the sole author of state.") + (p :class "text-stone-600" + "This is the thesis of the web: " (strong "the server knows") ". It knows what to show. It knows what state you're in. It knows what actions are available. It encodes all of this in the document it sends you. When you click a link, the server sends a new document. When you submit a form, the server processes it and sends another document. The cycle is: request, represent, repeat.") + (p :class "text-stone-600" + "Hegel would recognise this as a form of " (em "Substance") " — the server as the unmoved mover, the ground of all content. The client has no interiority. It does not " (em "decide") " anything. It displays what it is told to display. The web page is Spinozistic: a mode of the server, one of its infinite expressions, determined entirely by its cause.") + (p :class "text-stone-600" + "htmx is the most faithful modern expression of this thesis. It extends the hyperlink and the form — the two primordial hypermedia controls — to every element and every HTTP verb, but it does not break the paradigm. The server still renders. The server still knows. The server still authors state. htmx simply makes the authoring more fine-grained: instead of replacing the entire page, the server replaces a fragment. The thesis is refined, not rejected.") + (p :class "text-stone-600" + "The beauty of the thesis is its simplicity. One source of truth. One rendering pipeline. No synchronisation problems. No stale state. No " (code "useEffect") " cleanup. Every request produces a fresh representation, and the representation " (em "is") " the application. There is nothing hidden behind it, no shadow state, no ghost in the machine.") + (p :class "text-stone-600" + "But the thesis has a limit. The server cannot know everything the client experiences. It cannot know that the user's mouse is hovering over a button. It cannot know that a drag is in progress. It cannot know that a counter should increment " (em "now") ", this millisecond, without a round trip. The thesis renders the world from the server's perspective — and the client's perspective, its " (em "Erlebnis") ", its lived experience, is absent.")) + (~doc-section :title "II. Antithesis: The client reacts" :id "antithesis" + (p :class "text-stone-600" + "React arrived as the negation of the server-rendered web. Where the thesis said " (em "the server knows") ", React said " (em "the client knows better") ". Where the thesis treated the browser as a display surface, React treated it as an application runtime. Where the thesis sent documents, React sent " (em "programs") ".") + (p :class "text-stone-600" + "The Hegelian antithesis is not mere opposition — it is " (em "determinate negation") ". React does not simply reject server rendering. It rejects the specific limitation that the thesis cannot overcome: the absence of immediate, client-local state. React gives the client " (em "interiority") ". The component has state. It has a lifecycle. It makes decisions without consulting the server. It " (em "reacts") ".") + (p :class "text-stone-600" + "This is the birth of " (em "Subject") " in the Hegelian sense. The client is no longer pure receptivity. It has " (em "Selbstbewusstsein") " — self-consciousness. It knows its own state. A " (code "useState") " hook is an act of self-positing: the component declares " (em "I have an interior") ". A " (code "useEffect") " is self-reflection: the component observes its own changes and responds. The client becomes, in miniature, a knowing subject.") + (p :class "text-stone-600" + "But the antithesis inherits its own contradiction. By giving the client interiority, React creates " (em "two sources of truth") ". The server has its state. The client has its state. They must be synchronised. And synchronisation is the eternal problem of distributed systems — it cannot be solved, only managed. Hence the endless parade of state management libraries, cache invalidation strategies, optimistic updates, revalidation hooks. Each is an attempt to paper over the fundamental contradiction: the client and server both claim to know, and they do not always agree.") + (p :class "text-stone-600" + "Worse, the antithesis destroys what the thesis had achieved. The representation is no longer self-contained. A React SPA sends a JavaScript bundle — a program, not a document. The server sends an empty " (code "
") " and a prayer. The browser must compile, execute, fetch data, and construct the interface from scratch. The document — the web's primordial unit of meaning — is hollowed out. What arrives is not a representation but an " (em "instruction to construct one") ".") + (p :class "text-stone-600" + "Hegel would diagnose this as the antithesis's characteristic failure: it achieves freedom (client autonomy) at the cost of substance (server authority). The SPA is the " (em "beautiful soul") " of web development — pure subjectivity that has cut itself off from the objective world and wonders why everything is so complicated.")) + (~doc-section :title "III. The contradiction in practice" :id "contradiction" + (p :class "text-stone-600" + "The practical manifestation of the dialectic is visible in every web team's daily life. The server-rendered camp says: " (em "just use HTML and htmx, it's simpler") ". The React camp says: " (em "you can't build a real app without client state") ". Both are correct. Both are incomplete.") + (p :class "text-stone-600" + "The server camp cannot build a colour picker. Cannot build a drag-and-drop interface. Cannot build a spreadsheet. Cannot build anything that requires the client to know its own state between HTTP requests. Every interaction that needs immediacy — every tooltip, every animation, every character-by-character validation — forces the server camp to smuggle in JavaScript through the back door, breaking their own thesis.") + (p :class "text-stone-600" + "The React camp cannot deliver a page without JavaScript. Cannot render on first load without a server-rendering framework bolted on top. Cannot cache a representation because there is no stable representation to cache. Cannot inspect a page without devtools because the document is an empty shell. Every improvement — server components, streaming SSR, partial hydration — is an attempt to recover what the thesis already had: server-authored, self-contained documents.") + (p :class "text-stone-600" + "The two camps are not in disagreement about different things. They are in disagreement about " (em "the same thing") ": where should state live? The thesis says: on the server. The antithesis says: on the client. Neither can accommodate the obvious truth that " (strong "some state belongs on the server and some belongs on the client") ", and that a coherent architecture must handle both without privileging either.")) + (~doc-section :title "IV. Synthesis: The island in the lake" :id "synthesis" + (p :class "text-stone-600" + "Hegel's dialectic does not end in compromise. The synthesis is not half-thesis, half-antithesis. It is a new category that " (em "sublates") " — " (em "aufhebt") " — both: preserving what is true in each while resolving the contradiction between them. The synthesis contains the thesis and antithesis as " (em "moments") " within a higher unity.") + (p :class "text-stone-600" + "The island architecture is this synthesis. The " (em "lake") " is the thesis preserved: server-rendered HTML, delivered as complete representations, navigated by hypermedia controls. The " (em "island") " is the antithesis preserved: client-local state, reactive signals, immediate interaction. Neither is primary. Neither is a concession to the other. Both are " (em "moments") " of the same page.") + (p :class "text-stone-600" + "But the crucial move — the one that makes this a genuine Hegelian synthesis rather than a mere juxtaposition — is " (strong "the morph") ". When the server sends new content and the client merges it into the existing DOM, hydrated islands are " (em "preserved") ". The server updates the lake. The islands keep their state. The server's new representation flows around the islands like water around rocks. The client's interiority survives the server's authority.") + (p :class "text-stone-600" + "This is " (em "Aufhebung") " in its precise meaning: cancellation, preservation, and elevation. The thesis (server authority) is " (em "cancelled") " — the server no longer has total control over the page. It is " (em "preserved") " — the server still renders the document, still determines structure, still delivers representations. It is " (em "elevated") " — the server now renders " (em "around") " reactive islands, acknowledging their autonomy. Simultaneously, the antithesis (client autonomy) is cancelled (the client no longer controls the whole page), preserved (islands keep their state), and elevated (client state now coexists with server-driven updates).") + (~doc-code :code (highlight ";; The server sends this. The island already exists\n;; in the DOM with reactive state. The morph preserves it.\n\n(defisland ~sx-header ()\n (let ((families (list \"violet\" \"rose\" \"blue\" \"emerald\"))\n (idx (signal 0))\n (current (computed (fn ()\n (nth families (mod (deref idx) (len families)))))))\n (a :href \"/\" :sx-get \"/\" :sx-target \"#main-panel\"\n (span :style (cssx (:text (colour (deref current) 500)))\n :on-click (fn (e) (swap! idx inc))\n \"reactive\"))))\n\n;; Click: colour changes (client state)\n;; Click also triggers sx-get (server fetch)\n;; Server response morphs the page\n;; Island keeps its colour — state survives the swap" "lisp")) + (p :class "text-stone-600" + "In this example, the word " (em "reactive") " cycles through colours on click. The same click triggers a server navigation. The server responds with a fresh page. The morph algorithm encounters the island, recognises it as already hydrated, and " (em "skips it") ". The signal — the " (code "idx") " that tracks which colour family we're on — survives. The client's inner life persists through the server's outer renewal.")) + (~doc-section :title "V. Spirit: The self-knowing page" :id "spirit" + (p :class "text-stone-600" + "Hegel's system does not end with synthesis. Synthesis becomes a new thesis, which generates its own antithesis, and the dialectic continues. The island architecture is not a final resting place. It is a " (em "moment") " in the self-development of the web.") + (p :class "text-stone-600" + "Consider what the synthesis makes possible. The page now has two modes of knowledge: the server's knowledge (what the page should contain) and the client's knowledge (what the user is doing right now). Neither is reducible to the other. Neither is privileged over the other. The page is, in Hegel's terminology, " (em "Spirit") " — " (em "Geist") " — the unity of substance and subject, the place where objective content and subjective experience meet.") + (p :class "text-stone-600" + "The self-hosting nature of SX deepens this. The evaluator that renders islands is specified in the same language as the islands themselves (" (code "eval.sx") "). The renderer is specified in the same language (" (code "render.sx") "). The parser is specified in the same language (" (code "parser.sx") "). The system knows itself. It is a specification that specifies its own interpretation. This is Hegel's " (em "absolute knowing") " — not omniscience, but self-transparency. The system is what it knows, and it knows what it is.") + (p :class "text-stone-600" + "The morph algorithm is the phenomenological crux. It is the mechanism by which the page achieves continuity of experience through discontinuity of content. The server sends a completely new representation — new HTML, new structure, new text. The morph walks the old and new DOM trees, reconciling them. Where it finds an island — a locus of client subjectivity — it preserves it. Where it finds static content — server substance — it updates it. The result is a page that is simultaneously the same (the island's state persists) and different (the surrounding content has changed).") + (p :class "text-stone-600" + "This is Hegel's " (em "identity of identity and difference") ". The page after the morph is the same page (same islands, same signals, same DOM nodes) and a different page (new server content, new navigation state, new URL). The dialectic is not resolved by eliminating one side. It is resolved by maintaining both simultaneously — and the morph is the concrete mechanism that achieves this.")) + (~doc-section :title "VI. The speculative proposition" :id "speculative" + (p :class "text-stone-600" + "Hegel distinguished " (em "ordinary") " propositions from " (em "speculative") " ones. An ordinary proposition has a fixed subject and a predicate attached to it from outside: " (em "the rose is red") ". A speculative proposition is one where the predicate reflects back on the subject and transforms it: " (em "the actual is the rational") ".") + (p :class "text-stone-600" + "The proposition " (em "React is hypermedia") " is speculative in this sense. It does not mean that React, as it exists, is hypermedia. It means that what React " (em "was trying to be") " — a way to specify interactive UI — is what hypermedia " (em "always already was") " — a way to specify interactive documents. The predicate (hypermedia) transforms the subject (React): once you see that reactive islands are hypermedia controls, you can no longer see React as merely a JavaScript library. It was always an attempt to extend the expressiveness of hypermedia. It just didn't know it.") + (p :class "text-stone-600" + "And the converse: " (em "hypermedia is reactive") ". The hyperlink is already a reactive control — it responds to user input (click) by producing a state change (navigation). The form is reactive — it responds to submission by producing a state change (server-side processing + new representation). htmx makes this explicit: any element can react to any event by triggering any HTTP verb. The only thing htmx lacks is " (em "local") " reactivity — the ability to change without consulting the server.") + (p :class "text-stone-600" + "Islands supply exactly this. And by doing so, they do not add something foreign to hypermedia. They complete it. They give hypermedia the last degree of freedom it was missing: the ability for a control to react to itself, to maintain its own state, to compute derived values from its own inputs — without breaking the hypermedia contract that the server is the author of the document.") + (p :class "text-stone-600" + "The Hegelian synthesis is not a compromise between server rendering and client reactivity. It is the recognition that they were always the same thing seen from different sides. The server renders the document. The document contains controls. Some controls maintain local state. The document flows around them. The state persists through the flow. The server and the client are not two systems bridged together. They are one system that knows itself from two perspectives.") + (p :class "text-stone-600" + "Click the word " (em "reactive") " in the header above. The colour changes. The page navigates. The colour survives. That is the Hegelian synthesis of the hypertext/reactive dialectic — not in theory, but in the DOM.")))) diff --git a/sx/sx/layouts.sx b/sx/sx/layouts.sx index 6603cc7..b204f7f 100644 --- a/sx/sx/layouts.sx +++ b/sx/sx/layouts.sx @@ -5,21 +5,40 @@ ;; Nav components — logo header, sibling arrows, children links ;; --------------------------------------------------------------------------- -;; @css text-violet-700 text-violet-600 text-violet-500 text-stone-400 text-stone-500 text-stone-600 -;; @css hover:text-violet-600 hover:text-violet-700 hover:bg-violet-50 -;; @css bg-violet-50 border-violet-200 border +;; CSSX replaces Tailwind text-*/bg-*/font-* classes — computed via cssx.sx ;; Logo + tagline + copyright — always shown at top of page area. -(defcomp ~sx-header () - (a :href "/" - :sx-get "/" :sx-target "#main-panel" :sx-select "#main-panel" - :sx-swap "outerHTML" :sx-push-url "true" - :class "block max-w-3xl mx-auto px-4 pt-8 pb-4 text-center no-underline" - (span :class "text-4xl font-bold font-mono text-violet-700 block mb-2" "()") - (p :class "text-lg text-stone-500 mb-1" - "Framework free reactive hypermedia") - (p :class "text-xs text-stone-400" - "© Giles Bradshaw 2026"))) +;; The header itself is an island so the "reactive" word can cycle colours +;; on click — demonstrates inline signals without a separate component. +(defisland ~sx-header () + (let ((families (list "violet" "rose" "blue" "emerald" "amber" "cyan" "red" "teal" "pink" "indigo")) + (idx (signal 0)) + (shade (signal 500)) + (current-family (computed (fn () + (nth families (mod (deref idx) (len families))))))) + (a :href "/" + :sx-get "/" :sx-target "#main-panel" :sx-select "#main-panel" + :sx-swap "outerHTML" :sx-push-url "true" + :style (str (display "block") (max-w (get cssx-max-widths "3xl")) + (mx-auto) (px 4) (pt 8) (pb 4) (align "center") + (decoration "none")) + (span :style (str (display "block") (mb 2) + (cssx (:text (colour "violet" 699) (size "4xl") (weight "bold") (family "mono")))) + "()") + (p :style (str (mb 1) (cssx (:text (colour "stone" 500) (size "lg")))) + "Framework free " + (span + :style (str (cssx (:text (colour (deref current-family) (deref shade)) + (weight "bold"))) + "cursor:pointer;transition:color 0.3s,font-weight 0.3s;") + :on-click (fn (e) + (batch (fn () + (swap! idx inc) + (reset! shade (+ 400 (* (mod (* (deref idx) 137) 5) 50)))))) + "reactive") + " hypermedia") + (p :style (cssx (:text (colour "stone" 400) (size "xs"))) + "© Giles Bradshaw 2026")))) ;; @css grid grid-cols-3 @@ -40,21 +59,24 @@ :sx-get (get prev-node "href") :sx-target "#main-panel" :sx-select "#main-panel" :sx-swap "outerHTML" :sx-push-url "true" - :class "text-sm text-stone-500 hover:text-violet-600 text-right" + :class "text-right" + :style (cssx (:text (colour "stone" 500) (size "sm"))) (str "← " (get prev-node "label"))) (a :href (get node "href") :sx-get (get node "href") :sx-target "#main-panel" :sx-select "#main-panel" :sx-swap "outerHTML" :sx-push-url "true" - :class (if is-leaf - "text-2xl font-bold text-violet-700 text-center px-4" - "text-lg font-semibold text-violet-700 text-center px-4") + :class "text-center px-4" + :style (if is-leaf + (cssx (:text (colour "violet" 700) (size "2xl") (weight "bold"))) + (cssx (:text (colour "violet" 700) (size "lg") (weight "semibold")))) (get node "label")) (a :href (get next-node "href") :sx-get (get next-node "href") :sx-target "#main-panel" :sx-select "#main-panel" :sx-swap "outerHTML" :sx-push-url "true" - :class "text-sm text-stone-500 hover:text-violet-600 text-left" + :class "text-left" + :style (cssx (:text (colour "stone" 500) (size "sm"))) (str (get next-node "label") " →"))))))) ;; Children links — shown as clearly clickable buttons. @@ -66,7 +88,9 @@ :sx-get (get item "href") :sx-target "#main-panel" :sx-select "#main-panel" :sx-swap "outerHTML" :sx-push-url "true" - :class "px-3 py-1.5 text-sm rounded border border-violet-200 text-violet-700 hover:bg-violet-50 transition-colors" + :class "px-3 py-1.5 rounded border transition-colors" + :style (cssx (:text (colour "violet" 700) (size "sm")) + (:border (colour "violet" 200))) (get item "label"))) items)))) diff --git a/sx/sx/nav-data.sx b/sx/sx/nav-data.sx index 4b3006f..4b6f542 100644 --- a/sx/sx/nav-data.sx +++ b/sx/sx/nav-data.sx @@ -93,7 +93,9 @@ (dict :label "Tools for Fools" :href "/essays/zero-tooling" :summary "SX was built without a code editor. No IDE, no build tools, no linters, no bundlers. What zero-tooling web development looks like.") (dict :label "React is Hypermedia" :href "/essays/react-is-hypermedia" - :summary "A React Island is a hypermedia control. Its behavior is specified in SX."))) + :summary "A React Island is a hypermedia control. Its behavior is specified in SX.") + (dict :label "The Hegelian Synthesis" :href "/essays/hegelian-synthesis" + :summary "On the dialectical resolution of the hypertext/reactive contradiction. Thesis: the server renders. Antithesis: the client reacts. Synthesis: the island in the lake."))) (define philosophy-nav-items (list (dict :label "The SX Manifesto" :href "/philosophy/sx-manifesto" @@ -194,7 +196,11 @@ (dict :label "sx-swarm" :href "/plans/sx-swarm" :summary "Container orchestration in SX — service definitions, environment macros, deploy pipelines. Replace YAML with a real language.") (dict :label "sx-proxy" :href "/plans/sx-proxy" - :summary "Reverse proxy in SX — routes, TLS, middleware chains, load balancing. Macros generate config from the same service definitions as the orchestrator."))) + :summary "Reverse proxy in SX — routes, TLS, middleware chains, load balancing. Macros generate config from the same service definitions as the orchestrator.") + (dict :label "Async Eval Convergence" :href "/plans/async-eval-convergence" + :summary "Eliminate hand-written evaluators — bootstrap async_eval.py from the spec via an async adapter layer. One spec, one truth, zero divergence.") + (dict :label "WASM Bytecode VM" :href "/plans/wasm-bytecode-vm" + :summary "Compile SX to bytecode, run in a Rust/WASM VM. Compact wire format, no parse overhead, near-native speed, DOM via JS bindings."))) (define reactive-islands-nav-items (list (dict :label "Overview" :href "/reactive-islands/" diff --git a/sx/sxc/pages/docs.sx b/sx/sxc/pages/docs.sx index 4070a49..ac8dd3d 100644 --- a/sx/sxc/pages/docs.sx +++ b/sx/sxc/pages/docs.sx @@ -221,6 +221,7 @@ "no-alternative" (~essay-no-alternative) "zero-tooling" (~essay-zero-tooling) "react-is-hypermedia" (~essay-react-is-hypermedia) + "hegelian-synthesis" (~essay-hegelian-synthesis) :else (~essays-index-content)))) ;; --------------------------------------------------------------------------- @@ -516,6 +517,8 @@ "sx-forge" (~plan-sx-forge-content) "sx-swarm" (~plan-sx-swarm-content) "sx-proxy" (~plan-sx-proxy-content) + "async-eval-convergence" (~plan-async-eval-convergence-content) + "wasm-bytecode-vm" (~plan-wasm-bytecode-vm-content) :else (~plans-index-content)))) ;; --------------------------------------------------------------------------- diff --git a/sx/sxc/pages/helpers.py b/sx/sxc/pages/helpers.py index 1ad02ca..558e5a5 100644 --- a/sx/sxc/pages/helpers.py +++ b/sx/sxc/pages/helpers.py @@ -13,6 +13,7 @@ def _register_sx_helpers() -> None: register_page_helpers("sx", { "highlight": _highlight, + "component-source": _component_source, "primitives-data": _primitives_data, "special-forms-data": _special_forms_data, "reference-data": _reference_data, @@ -35,6 +36,33 @@ def _register_sx_helpers() -> None: }) +def _component_source(name: str) -> str: + """Return the pretty-printed defcomp/defisland source for a named component.""" + from shared.sx.jinja_bridge import get_component_env + from shared.sx.parser import serialize + from shared.sx.types import Component, Island + + comp = get_component_env().get(name) + if isinstance(comp, Island): + param_strs = list(comp.params) + if comp.has_children: + param_strs.extend(["&rest", "children"]) + params_sx = "(" + " ".join(param_strs) + ")" + body_sx = serialize(comp.body, pretty=True) + return f"(defisland {name} {params_sx}\n {body_sx})" + if not isinstance(comp, Component): + return f";; component {name} not found" + param_strs = ["&key"] + list(comp.params) + if comp.has_children: + param_strs.extend(["&rest", "children"]) + params_sx = "(" + " ".join(param_strs) + ")" + body_sx = serialize(comp.body, pretty=True) + affinity = "" + if comp.render_target == "server": + affinity = " :affinity :server" + return f"(defcomp {name} {params_sx}{affinity}\n {body_sx})" + + def _primitives_data() -> dict: """Return the PRIMITIVES dict for the primitives docs page.""" from content.pages import PRIMITIVES