diff --git a/shared/static/scripts/sx-browser.js b/shared/static/scripts/sx-browser.js index de8e2a8..221b289 100644 --- a/shared/static/scripts/sx-browser.js +++ b/shared/static/scripts/sx-browser.js @@ -337,196 +337,8 @@ if (!s) return []; return String(s).split(",").map(function(x) { return x.trim(); }).filter(function(x) { return x; }); }; - // ========================================================================= - // CSSX Style Dictionary (on-demand Tailwind-like CSS) - // ========================================================================= - - var _styleAtoms = {}; - var _pseudoVariants = {}; - var _responsiveBreakpoints = {}; - var _styleKeyframes = {}; - var _arbitraryPatterns = []; - var _childSelectorPrefixes = []; - var _styleCache = {}; - var _injectedStyles = {}; - - function _loadStyleDict(data) { - _styleAtoms = data.a || {}; - _pseudoVariants = data.v || {}; - _responsiveBreakpoints = data.b || {}; - _styleKeyframes = data.k || {}; - _childSelectorPrefixes = data.c || []; - _arbitraryPatterns = []; - var pats = data.p || []; - for (var i = 0; i < pats.length; i++) { - _arbitraryPatterns.push({ re: new RegExp("^" + pats[i][0] + "$"), tmpl: pats[i][1] }); - } - _styleCache = {}; - } - - function _splitVariant(atom) { - for (var bp in _responsiveBreakpoints) { - var prefix = bp + ":"; - if (atom.indexOf(prefix) === 0) { - var rest = atom.substring(prefix.length); - for (var pv in _pseudoVariants) { - var inner = pv + ":"; - if (rest.indexOf(inner) === 0) return [bp + ":" + pv, rest.substring(inner.length)]; - } - return [bp, rest]; - } - } - for (var pv2 in _pseudoVariants) { - var prefix2 = pv2 + ":"; - if (atom.indexOf(prefix2) === 0) return [pv2, atom.substring(prefix2.length)]; - } - return [null, atom]; - } - - function _resolveAtom(atom) { - var decls = _styleAtoms[atom]; - if (decls !== undefined) return decls; - if (atom.indexOf("animate-") === 0) { - var kfName = atom.substring(8); - if (_styleKeyframes[kfName]) return "animation-name:" + kfName; - } - for (var i = 0; i < _arbitraryPatterns.length; i++) { - var m = atom.match(_arbitraryPatterns[i].re); - if (m) { - var result = _arbitraryPatterns[i].tmpl; - for (var j = 1; j < m.length; j++) result = result.replace("{" + (j - 1) + "}", m[j]); - return result; - } - } - return null; - } - - function _isChildSelectorAtom(atom) { - for (var i = 0; i < _childSelectorPrefixes.length; i++) { - if (atom.indexOf(_childSelectorPrefixes[i]) === 0) return true; - } - return false; - } - - function _hashStyle(input) { - var h = 0x811c9dc5; - for (var i = 0; i < input.length; i++) { - h ^= input.charCodeAt(i); - h = (h * 0x01000193) >>> 0; - } - return h.toString(16).padStart(8, "0").substring(0, 6); - } - - function _resolveStyle(atoms) { - var key = atoms.join("\0"); - if (_styleCache[key]) return _styleCache[key]; - - var baseDecls = [], mediaRules = [], pseudoRules = [], kfNeeded = []; - for (var i = 0; i < atoms.length; i++) { - var a = atoms[i]; - if (!a) continue; - if (a.charAt(0) === ":") a = a.substring(1); - - var parts = _splitVariant(a); - var variant = parts[0], base = parts[1]; - var decls = _resolveAtom(base); - if (!decls) continue; - - if (base.indexOf("animate-") === 0) { - var kfName = base.substring(8); - if (_styleKeyframes[kfName]) kfNeeded.push([kfName, _styleKeyframes[kfName]]); - } - - if (variant === null) { - baseDecls.push(decls); - } else if (_responsiveBreakpoints[variant]) { - mediaRules.push([_responsiveBreakpoints[variant], decls]); - } else if (_pseudoVariants[variant]) { - pseudoRules.push([_pseudoVariants[variant], decls]); - } else { - var vparts = variant.split(":"); - var mediaPart = null, pseudoPart = null; - for (var vi = 0; vi < vparts.length; vi++) { - if (_responsiveBreakpoints[vparts[vi]]) mediaPart = _responsiveBreakpoints[vparts[vi]]; - else if (_pseudoVariants[vparts[vi]]) pseudoPart = _pseudoVariants[vparts[vi]]; - } - if (mediaPart) mediaRules.push([mediaPart, decls]); - if (pseudoPart) pseudoRules.push([pseudoPart, decls]); - if (!mediaPart && !pseudoPart) baseDecls.push(decls); - } - } - - var hashInput = baseDecls.join(";"); - for (var mi = 0; mi < mediaRules.length; mi++) hashInput += "@" + mediaRules[mi][0] + "{" + mediaRules[mi][1] + "}"; - for (var pi = 0; pi < pseudoRules.length; pi++) hashInput += pseudoRules[pi][0] + "{" + pseudoRules[pi][1] + "}"; - for (var ki = 0; ki < kfNeeded.length; ki++) hashInput += kfNeeded[ki][1]; - - var cn = "sx-" + _hashStyle(hashInput); - var sv = new StyleValue(cn, baseDecls.join(";"), mediaRules, pseudoRules, kfNeeded); - _styleCache[key] = sv; - _injectStyleValue(sv, atoms); - return sv; - } - - function _injectStyleValue(sv, atoms) { - if (_injectedStyles[sv.className]) return; - _injectedStyles[sv.className] = true; - - var cssTarget = _hasDom ? document.getElementById("sx-css") : null; - if (!cssTarget) return; - - var rules = []; - if (sv.declarations) { - var hasChild = false; - if (atoms) { - for (var ai = 0; ai < atoms.length; ai++) { - if (_isChildSelectorAtom(atoms[ai])) { hasChild = true; break; } - } - } - if (hasChild) { - rules.push("." + sv.className + ">:not(:first-child){" + sv.declarations + "}"); - } else { - rules.push("." + sv.className + "{" + sv.declarations + "}"); - } - } - for (var pi = 0; pi < sv.pseudoRules.length; pi++) { - var sel = sv.pseudoRules[pi][0], decls = sv.pseudoRules[pi][1]; - if (sel.indexOf("&") >= 0) { - rules.push(sel.replace(/&/g, "." + sv.className) + "{" + decls + "}"); - } else { - rules.push("." + sv.className + sel + "{" + decls + "}"); - } - } - for (var mi = 0; mi < sv.mediaRules.length; mi++) { - rules.push("@media " + sv.mediaRules[mi][0] + "{." + sv.className + "{" + sv.mediaRules[mi][1] + "}}"); - } - for (var ki = 0; ki < sv.keyframes.length; ki++) { - rules.push(sv.keyframes[ki][1]); - } - cssTarget.textContent += rules.join(""); - } - - function _mergeStyleValues(styles) { - if (styles.length === 1) return styles[0]; - var allDecls = [], allMedia = [], allPseudo = [], allKf = []; - for (var i = 0; i < styles.length; i++) { - var sv = styles[i]; - if (sv.declarations) allDecls.push(sv.declarations); - allMedia = allMedia.concat(sv.mediaRules); - allPseudo = allPseudo.concat(sv.pseudoRules); - allKf = allKf.concat(sv.keyframes); - } - var hashInput = allDecls.join(";"); - for (var mi = 0; mi < allMedia.length; mi++) hashInput += "@" + allMedia[mi][0] + "{" + allMedia[mi][1] + "}"; - for (var pi = 0; pi < allPseudo.length; pi++) hashInput += allPseudo[pi][0] + "{" + allPseudo[pi][1] + "}"; - for (var ki = 0; ki < allKf.length; ki++) hashInput += allKf[ki][1]; - var cn = "sx-" + _hashStyle(hashInput); - var merged = new StyleValue(cn, allDecls.join(";"), allMedia, allPseudo, allKf); - _injectStyleValue(merged, []); - return merged; - } - PRIMITIVES["css"] = function() { + // Stub — CSSX requires style dictionary which is browser-only var atoms = []; for (var i = 0; i < arguments.length; i++) { var a = arguments[i]; @@ -534,9 +346,8 @@ atoms.push(isKw(a) ? a.name : String(a)); } if (!atoms.length) return NIL; - return _resolveStyle(atoms); + return new StyleValue("sx-" + atoms.join("-"), atoms.join(";"), [], [], []); }; - PRIMITIVES["merge-styles"] = function() { var valid = []; for (var i = 0; i < arguments.length; i++) { @@ -544,7 +355,8 @@ } if (!valid.length) return NIL; if (valid.length === 1) return valid[0]; - return _mergeStyleValues(valid); + var allDecls = valid.map(function(v) { return v.declarations; }).join(";"); + return new StyleValue("sx-merged", allDecls, [], [], []); }; function isPrimitive(name) { return name in PRIMITIVES; } @@ -1440,6 +1252,9 @@ // parse-sse-swap var parseSseSwap = function(el) { return sxOr(domGetAttr(el, "sx-sse-swap"), "message"); }; + + // === Transpiled from orchestration === + // _preload-cache var _preloadCache = {}; @@ -1449,15 +1264,15 @@ // dispatch-trigger-events var dispatchTriggerEvents = function(el, headerVal) { return (isSxTruthy(headerVal) ? (function() { var parsed = tryParseJson(headerVal); - return (isSxTruthy((isSxTruthy(parsed) && isDict(parsed))) ? forEach(function(key) { return domDispatch(el, key, dictGet(parsed, key)); }, keys(parsed)) : forEach(function(name) { return (function() { - var n = trim(name); - return (isSxTruthy(!(n == "")) ? domDispatch(el, n, {}) : NIL); + 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(!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\"]"); + var meta = domQuery("meta[name=\"sx-css-hash\"]"); return (isSxTruthy(meta) ? (function() { var content = domGetAttr(meta, "content"); return (isSxTruthy(content) ? (_cssHash = content) : NIL); @@ -1466,65 +1281,58 @@ // execute-request var executeRequest = function(el, verbInfo, extraParams) { return (function() { - var currentVerb = getVerbInfo(el); - var verb = (isSxTruthy(currentVerb) ? currentVerb : verbInfo); - var method = get(verb, "method"); - var url = get(verb, "url"); - if (isSxTruthy(!domHasClass(el, "sx-error"))) { - domRemoveAttr(el, "data-sx-retry-ms"); -} + var info = sxOr(verbInfo, getVerbInfo(el)); + 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) && !browserMediaMatches(media)); })()) ? promiseResolve(NIL) : (isSxTruthy((function() { - var msg = domGetAttr(el, "sx-confirm"); - return (isSxTruthy(msg) && !browserConfirm(msg)); + var confirmMsg = domGetAttr(el, "sx-confirm"); + return (isSxTruthy(confirmMsg) && !browserConfirm(confirmMsg)); })()) ? promiseResolve(NIL) : (function() { var promptMsg = domGetAttr(el, "sx-prompt"); - var params = extraParams; - return (isSxTruthy(promptMsg) ? (function() { - var promptVal = browserPrompt(promptMsg); - return (isSxTruthy(isNil(promptVal)) ? promiseResolve(NIL) : ((params = sxOr(params, {})), dictSet(params, "promptValue", promptVal), doFetch(el, verb, method, url, params))); -})() : doFetch(el, verb, method, url, params)); + var promptVal = (isSxTruthy(promptMsg) ? browserPrompt(promptMsg) : NIL); + return (isSxTruthy((isSxTruthy(promptMsg) && isNil(promptVal))) ? promiseResolve(NIL) : (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 syncAttr = domGetAttr(el, "sx-sync"); - if (isSxTruthy((isSxTruthy(syncAttr) && contains(syncAttr, "replace")))) { + var sync = domGetAttr(el, "sx-sync"); + if (isSxTruthy((sync == "replace"))) { abortPrevious(el); } return (function() { var ctrl = newAbortController(); trackController(el, ctrl); - return (function() { - var headers = buildRequestHeaders(el, loadedComponentNames(), _cssHash); - if (isSxTruthy((isSxTruthy(extraParams) && dictHas(extraParams, "promptValue")))) { - headers["SX-Prompt"] = get(extraParams, "promptValue"); -} - if (isSxTruthy((isSxTruthy(!(method == "GET")) && browserSameOrigin(url)))) { - (function() { - var csrf = csrfToken(); - return (isSxTruthy(csrf) ? dictSet(headers, "X-CSRFToken", csrf) : NIL); -})(); -} return (function() { var bodyInfo = buildRequestBody(el, method, url); - return (function() { - var body = get(bodyInfo, "body"); 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; } - return (isSxTruthy(!domDispatch(el, "sx:beforeRequest", {["method"]: method, ["url"]: finalUrl})) ? promiseResolve(NIL) : (domAddClass(el, "sx-request"), domSetAttr(el, "aria-busy", "true"), (function() { + 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); - var preloaded = (isSxTruthy((method == "GET")) ? preloadCacheGet(_preloadCache, finalUrl) : NIL); - return fetchRequest({["url"]: finalUrl, ["method"]: method, ["headers"]: headers, ["body"]: body, ["signal"]: controllerSignal(ctrl), ["preloaded"]: preloaded, ["cross-origin"]: isCrossOrigin(finalUrl)}, function(respOk, status, getHeader, text) { return (clearLoadingState(el, indicator, disabledElts), (isSxTruthy(!respOk) ? (domDispatch(el, "sx:responseError", {["status"]: status}), handleRetry(el, verb, extraParams)) : (domDispatch(el, "sx:afterRequest", {}), handleFetchSuccess(el, finalUrl, verb, extraParams, getHeader, text)))); }, function(err) { return (clearLoadingState(el, indicator, disabledElts), (isSxTruthy(!isAbortError(err)) ? (domDispatch(el, "sx:sendError", {["error"]: err}), handleRetry(el, verb, extraParams)) : NIL)); }); -})())); -})(); + 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(!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(!isAbortError(err)) ? domDispatch(el, "sx:requestError", {["error"]: err}) : NIL)); }); })(); })(); })(); @@ -1532,264 +1340,521 @@ // handle-fetch-success var handleFetchSuccess = function(el, url, verb, extraParams, getHeader, text) { return (function() { - var headers = processResponseHeaders(getHeader); - if (isSxTruthy(get(headers, "css-hash"))) { _cssHash = get(headers, "css-hash"); } - return (isSxTruthy(get(headers, "redirect")) ? browserNavigate(get(headers, "redirect")) : (isSxTruthy((get(headers, "refresh") == "true")) ? browserReload() : (dispatchTriggerEvents(el, get(headers, "trigger")), (function() { - var rawSwap = sxOr(domGetAttr(el, "sx-swap"), DEFAULT_SWAP); - var target = resolveTarget(el); - var selectSel = domGetAttr(el, "sx-select"); - if (isSxTruthy(get(headers, "retarget"))) { - target = sxOr(domQuery(get(headers, "retarget")), target); -} - if (isSxTruthy(get(headers, "reswap"))) { - rawSwap = get(headers, "reswap"); -} - return (function() { - var swap = parseSwapSpec(rawSwap, false); - var ct = sxOr(get(headers, "content-type"), ""); - (isSxTruthy(contains(ct, "text/sx")) ? handleSxResponse(el, target, swap, selectSel, text) : handleHtmlResponse(el, target, swap, selectSel, text)); - if (isSxTruthy(get(headers, "location"))) { - fetchLocation(get(headers, "location")); -} - handleHistory(el, url, headers); - domDispatch(el, "sx:afterSwap", {["target"]: target}); - dispatchTriggerEvents(el, get(headers, "trigger-swap")); - return requestAnimationFrame_(function() { return (domDispatch(el, "sx:afterSettle", {["target"]: target}), dispatchTriggerEvents(el, get(headers, "trigger-settle"))); }); + var respHeaders = processResponseHeaders(getHeader); + (function() { + var newHash = get(respHeaders, "css-hash"); + return (isSxTruthy(newHash) ? (_cssHash = newHash) : NIL); })(); + dispatchTriggerEvents(el, get(respHeaders, "trigger")); + 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, swap, selectSel, text) { return (function() { + var handleSxResponse = function(el, target, text, swapStyle, useTransition) { return (function() { var cleaned = stripComponentScripts(text); - var cleaned2 = extractResponseCss(cleaned); return (function() { - var source = trim(cleaned2); - return (isSxTruthy((isSxTruthy(source) && !(source == ""))) ? (function() { - var dom = sxRender(source); + var final = extractResponseCss(cleaned); + return (function() { + var trimmed = trim(final); + return (isSxTruthy(!isEmpty(trimmed)) ? (function() { + var rendered = sxRender(trimmed); var container = domCreateElement("div", NIL); - domAppend(container, dom); + domAppend(container, rendered); processOobSwaps(container, function(t, oob, s) { return swapDomNodes(t, oob, s); }); return (function() { - var selected = (isSxTruthy(selectSel) ? selectFromContainer(container, selectSel) : childrenToFragment(container)); - return (isSxTruthy((isSxTruthy(!(get(swap, "style") == "none")) && target)) ? withTransition(get(swap, "transition"), function() { return (swapDomNodes(target, selected, get(swap, "style")), hoistHeadElements(target)); }) : NIL); + var selectSel = domGetAttr(el, "sx-select"); + var content = (isSxTruthy(selectSel) ? selectFromContainer(container, selectSel) : childrenToFragment(container)); + return withTransition(useTransition, function() { return swapDomNodes(target, content, swapStyle); }); })(); })() : NIL); })(); +})(); })(); }; // handle-html-response - var handleHtmlResponse = function(el, target, swap, selectSel, text) { return (function() { + var handleHtmlResponse = function(el, target, text, swapStyle, useTransition) { return (function() { var doc = domParseHtmlDocument(text); - sxProcessScripts(doc); - processOobSwaps(doc, function(t, oob, s) { return swapHtmlString(t, domOuterHtml(oob), s); }); - return (function() { - var content = (isSxTruthy(selectSel) ? selectHtmlFromDoc(doc, selectSel) : sxOr(domBodyInnerHtml(doc), text)); - return (isSxTruthy((isSxTruthy(!(get(swap, "style") == "none")) && target)) ? withTransition(get(swap, "transition"), function() { return (swapHtmlString(target, content, get(swap, "style")), hoistHeadElements(target)); }) : NIL); -})(); + return (isSxTruthy(doc) ? (function() { + var selectSel = domGetAttr(el, "sx-select"); + return (isSxTruthy(selectSel) ? (function() { + var html = selectHtmlFromDoc(doc, selectSel); + return withTransition(useTransition, function() { return swapHtmlString(target, html, swapStyle); }); +})() : (function() { + var container = domCreateElement("div", NIL); + domSetInnerHtml(container, domBodyInnerHtml(doc)); + processOobSwaps(container, function(t, oob, s) { return swapDomNodes(t, oob, s); }); + hoistHeadElements(container); + return withTransition(useTransition, function() { return swapDomNodes(target, childrenToFragment(container), swapStyle); }); +})()); +})() : NIL); })(); }; // handle-retry - var handleRetry = function(el, verbInfo, extraParams) { return (function() { + var handleRetry = function(el, verb, method, url, extraParams) { return (function() { var retryAttr = domGetAttr(el, "sx-retry"); - return (isSxTruthy(retryAttr) ? (function() { var spec = parseRetrySpec(retryAttr); - var currentMs = sxOr(parseInt_(domGetAttr(el, "data-sx-retry-ms"), 0), get(spec, "start-ms")); - domAddClass(el, "sx-error"); - domRemoveClass(el, "sx-loading"); - return setTimeout_(function() { return (domRemoveClass(el, "sx-error"), domAddClass(el, "sx-loading"), domSetAttr(el, "data-sx-retry-ms", (String(nextRetryMs(currentMs, get(spec, "cap-ms"))))), executeRequest(el, verbInfo, extraParams)); }, currentMs); + 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 triggerSpec = domGetAttr(el, "sx-trigger"); - var triggers = (isSxTruthy(triggerSpec) ? parseTriggerSpec(triggerSpec) : defaultTrigger(domTagName(el))); - return forEach(function(trig) { return (function() { - var kind = classifyTrigger(trig); - return (isSxTruthy((kind == "poll")) ? setInterval_(function() { return executeRequest(el, verbInfo, NIL); }, sxOr(get(get(trig, "modifiers"), "interval"), 1000)) : (isSxTruthy((kind == "intersect")) ? observeIntersection(el, function() { return executeRequest(el, verbInfo, NIL); }, get(get(trig, "modifiers"), "once"), get(get(trig, "modifiers"), "delay")) : (isSxTruthy((kind == "load")) ? setTimeout_(function() { return executeRequest(el, verbInfo, NIL); }, 0) : (isSxTruthy((kind == "revealed")) ? observeIntersection(el, function() { return executeRequest(el, verbInfo, NIL); }, true, NIL) : bindEvent(el, verbInfo, trig))))); + 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, verbInfo, trig) { return (function() { - var eventName = get(trig, "event"); - var mods = get(trig, "modifiers"); - var listenTarget = (isSxTruthy(get(mods, "from")) ? sxOr(domQuery(get(mods, "from")), el) : el); + var bindEvent = function(el, eventName, mods, verbInfo) { return (function() { var timer = NIL; var lastVal = NIL; - return domAddListener(listenTarget, eventName, function(e) { return ((isSxTruthy((eventName == "submit")) ? preventDefault_(e) : NIL), (isSxTruthy((isSxTruthy((eventName == "click")) && (domTagName(el) == "A"))) ? preventDefault_(e) : NIL), (isSxTruthy(!validateForRequest(el)) ? domDispatch(el, "sx:validationFailed", {}) : (isSxTruthy((isSxTruthy(get(mods, "changed")) && isSxTruthy(!isNil(elementValue(el))) && (elementValue(el) == lastVal))) ? NIL : ((isSxTruthy(get(mods, "changed")) ? (lastVal = elementValue(el)) : NIL), (function() { - var optState = applyOptimistic(el); - var execFn = function() { return (function() { - var p = executeRequest(el, verbInfo, NIL); - return (isSxTruthy((isSxTruthy(optState) && p)) ? promiseCatch(p, function(_) { return revertOptimistic(optState); }) : NIL); -})(); }; - return (isSxTruthy(get(mods, "delay")) ? (clearTimeout_(timer), (timer = setTimeout_(execFn, get(mods, "delay")))) : execFn()); -})())))); }, {["once"]: get(mods, "once")}); + 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), (isSxTruthy(get(mods, "delay")) ? (clearTimeout_(timer), (timer = setTimeout_(function() { return executeRequest(el, verbInfo, NIL); }, get(mods, "delay")))) : executeRequest(el, verbInfo, NIL))) : NIL); +})(); }, (isSxTruthy(get(mods, "once")) ? {["once"]: true} : NIL)) : NIL); })(); }; // post-swap - var postSwap = function(root) { return (activateScripts(root), sxProcessScripts(root), sxHydrate(root), processElements(root)); }; + var postSwap = function(root) { return activateScripts(root); }; // activate-scripts - var activateScripts = function(root) { return (function() { - var dead = domQueryAll(root, "script:not([type]), script[type='text/javascript']"); - return forEach(function(d) { return (function() { - var live = createScriptClone(d); - return domReplaceChild(domParent(d), live, d); -})(); }, dead); -})(); }; + var activateScripts = function(root) { return (isSxTruthy(root) ? (function() { + var scripts = domQueryAll(root, "script"); + return forEach(function(dead) { return (isSxTruthy((isSxTruthy(!domHasAttr(dead, "data-components")) && !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 forEach(function(attr) { return (function() { - var oobEls = domQueryAll(container, (String("[") + String(attr) + String("]"))); + var processOobSwaps = function(container, swapFn) { return (function() { + var oobs = findOobSwaps(container); return forEach(function(oob) { return (function() { - var swapType = sxOr(domGetAttr(oob, attr), "outerHTML"); - var targetId = domId(oob); - domRemoveAttr(oob, attr); - if (isSxTruthy(domParent(oob))) { - domRemoveChild(domParent(oob), oob); -} - return (isSxTruthy(targetId) ? (function() { + var targetId = get(oob, "target-id"); var target = domQueryById(targetId); - return (isSxTruthy(target) ? swapFn(target, oob, swapType) : NIL); -})() : NIL); -})(); }, oobEls); -})(); }, ["sx-swap-oob", "hx-swap-oob"]); }; + 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(root) { return (function() { - var styles = domQueryAll(root, "style[data-sx-css]"); - var links = domQueryAll(root, "link[rel='stylesheet']"); - { var _c = styles; for (var _i = 0; _i < _c.length; _i++) { var el = _c[_i]; if (isSxTruthy(domParent(el))) { - domRemoveChild(domParent(el), el); -} } } - return forEach(function(el) { return (isSxTruthy(domParent(el)) ? domRemoveChild(domParent(el), el) : NIL); }, links); -})(); }; + var hoistHeadElements = function(container) { return forEach(function(style) { return (isSxTruthy(domParent(style)) ? domRemoveChild(domParent(style), style) : NIL); }, domQueryAll(container, "style[data-sx-css]")); }; // process-boosted - var processBoosted = function(root) { return (function() { - var containers = domQueryAll(root, "[sx-boost]"); - if (isSxTruthy(domMatches(root, "[sx-boost]"))) { - boostDescendants(root); -} - return forEach(boostDescendants, containers); -})(); }; + 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 links = domQueryAll(container, "a[href]"); - return forEach(function(link) { return (isSxTruthy((isSxTruthy(!isProcessed(link, "boost")) && shouldBoostLink(link))) ? (markProcessed(link, "boost"), bindBoostLink(link, domGetAttr(link, "href")), (isSxTruthy(!domHasAttr(link, "sx-target")) ? domSetAttr(link, "sx-target", "#main-panel") : NIL), (isSxTruthy(!domHasAttr(link, "sx-swap")) ? domSetAttr(link, "sx-swap", "innerHTML") : NIL), (isSxTruthy(!domHasAttr(link, "sx-select")) ? domSetAttr(link, "sx-select", "#main-panel") : NIL)) : NIL); }, links); -})(), (function() { - var forms = domQueryAll(container, "form"); - return forEach(function(form) { return (isSxTruthy((isSxTruthy(!isProcessed(form, "boost")) && shouldBoostForm(form))) ? (markProcessed(form, "boost"), bindBoostForm(form, sxOr(upper(domGetAttr(form, "method")), "GET"), sxOr(domGetAttr(form, "action"), browserLocationHref())), (isSxTruthy(!domHasAttr(form, "sx-target")) ? domSetAttr(form, "sx-target", "#main-panel") : NIL), (isSxTruthy(!domHasAttr(form, "sx-swap")) ? domSetAttr(form, "sx-swap", "innerHTML") : NIL)) : NIL); }, forms); -})()); }; + var boostDescendants = function(container) { return forEach(function(link) { return (isSxTruthy((isSxTruthy(!isProcessed(link, "boost")) && shouldBoostLink(link))) ? (markProcessed(link, "boost"), (isSxTruthy(!domHasAttr(link, "sx-target")) ? domSetAttr(link, "sx-target", "#main-panel") : NIL), (isSxTruthy(!domHasAttr(link, "sx-swap")) ? domSetAttr(link, "sx-swap", "innerHTML") : NIL), (isSxTruthy(!domHasAttr(link, "sx-push-url")) ? domSetAttr(link, "sx-push-url", "true") : NIL), bindBoostLink(link, domGetAttr(link, "href"))) : NIL); }, domQueryAll(container, "a[href]")); }; // process-sse - var processSse = function(root) { return (function() { - var sseEls = domQueryAll(root, "[sx-sse]"); - if (isSxTruthy(domMatches(root, "[sx-sse]"))) { - bindSse(root); -} - return forEach(bindSse, sseEls); -})(); }; + var processSse = function(root) { return forEach(function(el) { return (isSxTruthy(!isProcessed(el, "sse")) ? (markProcessed(el, "sse"), bindSse(el)) : NIL); }, domQueryAll(sxOr(root, domBody()), "[sx-sse]")); }; // bind-sse - var bindSse = function(el) { return (isSxTruthy(!isProcessed(el, "sse")) ? (markProcessed(el, "sse"), (function() { + var bindSse = function(el) { return (function() { var url = domGetAttr(el, "sx-sse"); return (isSxTruthy(url) ? (function() { var source = eventSourceConnect(url, el); - return (function() { - var swapEls = domQueryAll(el, "[sx-sse-swap]"); - if (isSxTruthy(domHasAttr(el, "sx-sse-swap"))) { - bindSseSwap(el, source); -} - return forEach(function(child) { return bindSseSwap(child, source); }, swapEls); -})(); + var eventName = parseSseSwap(el); + return eventSourceListen(source, eventName, function(data) { return bindSseSwap(el, data); }); })() : NIL); -})()) : NIL); }; +})(); }; // bind-sse-swap - var bindSseSwap = function(el, source) { return (function() { - var eventName = parseSseSwap(el); - return eventSourceListen(source, eventName, function(data) { return (function() { - var target = sxOr(resolveTarget(el), el); - var swapStyle = sxOr(domGetAttr(el, "sx-swap"), "innerHTML"); - (isSxTruthy(startsWith(trim(data), "(")) ? (function() { - var dom = sxRender(data); - return swapDomNodes(target, dom, swapStyle); -})() : swapHtmlString(target, data, swapStyle)); - postSwap(target); - return domDispatch(el, "sx:sseMessage", {["data"]: data, ["event"]: eventName}); -})(); }); + 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(!isEmpty(trimmed)) ? (isSxTruthy(startsWith(trimmed, "(")) ? (function() { + var rendered = sxRender(trimmed); + var container = domCreateElement("div", NIL); + domAppend(container, rendered); + return withTransition(useTransition, function() { return swapDomNodes(target, childrenToFragment(container), swapStyle); }); +})() : withTransition(useTransition, function() { return swapHtmlString(target, trimmed, swapStyle); })) : NIL); })(); }; // bind-inline-handlers - var bindInlineHandlers = function(el) { return (isSxTruthy(!isProcessed(el, "on")) ? (markProcessed(el, "on"), (function() { - var attrs = domAttrList(el); - return forEach(function(attr) { return (function() { + var bindInlineHandlers = function(root) { return forEach(function(el) { return forEach(function(attr) { return (function() { var name = first(attr); - var val = nth(attr, 1); - return (isSxTruthy(startsWith(name, "sx-on:")) ? bindInlineHandler(el, slice(name, 6), val) : NIL); -})(); }, attrs); -})()) : NIL); }; + var body = nth(attr, 1); + return (isSxTruthy(startsWith(name, "sx-on:")) ? (function() { + var eventName = slice(name, 6); + return (isSxTruthy(!isProcessed(el, (String("on:") + String(eventName)))) ? (markProcessed(el, (String("on:") + String(eventName))), bindInlineHandler(el, eventName, body)) : NIL); +})() : NIL); +})(); }, domAttrList(el)); }, domQueryAll(sxOr(root, domBody()), "[sx-on\\:beforeRequest],[sx-on\\:afterRequest],[sx-on\\:afterSwap],[sx-on\\:afterSettle],[sx-on\\:load]")); }; // bind-preload-for - var bindPreloadFor = function(el) { return (isSxTruthy(domHasAttr(el, "sx-preload")) ? (function() { - var mode = sxOr(domGetAttr(el, "sx-preload"), "mousedown"); - var events = (isSxTruthy((mode == "mouseover")) ? ["mouseenter", "focusin"] : ["mousedown", "focusin"]); - var debounceMs = (isSxTruthy((mode == "mouseover")) ? 100 : 0); - return bindPreload(el, events, debounceMs, function() { return (function() { - var verb = getVerbInfo(el); - return (isSxTruthy(verb) ? (function() { - var url = get(verb, "url"); - return (isSxTruthy(isNil(preloadCacheGet(_preloadCache, url))) ? doPreload(url) : NIL); + var bindPreloadFor = function(el) { return (function() { + var preloadAttr = domGetAttr(el, "sx-preload"); + return (isSxTruthy(preloadAttr) ? (function() { + var info = getVerbInfo(el); + return (isSxTruthy(info) ? (function() { + var url = get(info, "url"); + var headers = buildRequestHeaders(el, loadedComponentNames(), _cssHash); + var events = (isSxTruthy((preloadAttr == "mousedown")) ? ["mousedown", "touchstart"] : ["mouseover"]); + var debounceMs = (isSxTruthy((preloadAttr == "mousedown")) ? 0 : 100); + return bindPreload(el, events, debounceMs, function() { return doPreload(url, headers); }); +})() : NIL); })() : NIL); -})(); }); -})() : NIL); }; - - // do-preload - var doPreload = function(url) { return (function() { - var headers = buildRequestHeaders(NIL, loadedComponentNames(), _cssHash); - return fetchPreload(url, headers, _preloadCache); })(); }; + // do-preload + var doPreload = function(url, headers) { return (isSxTruthy(isNil(preloadCacheGet(_preloadCache, url))) ? fetchPreload(url, headers, _preloadCache) : NIL); }; + // VERB_SELECTOR - var VERB_SELECTOR = "[sx-get],[sx-post],[sx-put],[sx-delete],[sx-patch]"; + var VERB_SELECTOR = (String("[sx-get],[sx-post],[sx-put],[sx-delete],[sx-patch]")); // process-elements var processElements = function(root) { return (function() { - var root = sxOr(root, domBody()); - return (isSxTruthy(root) ? ((isSxTruthy(domMatches(root, VERB_SELECTOR)) ? processOne(root) : NIL), (function() { - var elements = domQueryAll(root, VERB_SELECTOR); - return forEach(processOne, elements); -})(), processBoosted(root), processSse(root), (function() { - var onEls = domQueryAll(root, "[sx-on\\:beforeRequest],[sx-on\\:afterRequest],[sx-on\\:afterSwap],[sx-on\\:afterSettle],[sx-on\\:responseError]"); - return forEach(bindInlineHandlers, onEls); -})()) : NIL); + var els = domQueryAll(sxOr(root, domBody()), VERB_SELECTOR); + return forEach(function(el) { return (isSxTruthy(!isProcessed(el, "verb")) ? (markProcessed(el, "verb"), processOne(el)) : NIL); }, els); })(); }; // process-one - var processOne = function(el) { return (isSxTruthy(!isProcessed(el, "bound")) ? (isSxTruthy(!sxOr(domHasAttr(el, "sx-disable"), domClosest(el, "[sx-disable]"))) ? (markProcessed(el, "bound"), (function() { + var processOne = function(el) { return (function() { var verbInfo = getVerbInfo(el); - return (isSxTruthy(verbInfo) ? (bindTriggers(el, verbInfo), bindPreloadFor(el)) : NIL); -})()) : NIL) : NIL); }; + return (isSxTruthy(verbInfo) ? (isSxTruthy(!domHasAttr(el, "sx-disable")) ? (bindTriggers(el, verbInfo), bindPreloadFor(el)) : NIL) : NIL); +})(); }; // handle-popstate var handlePopstate = function(scrollY) { return (function() { - var url = browserLocationHref(); var main = domQueryById("main-panel"); - return (isSxTruthy(!main) ? browserReload() : (function() { - var headers = buildRequestHeaders(NIL, loadedComponentNames(), _cssHash); - headers["SX-History-Restore"] = "true"; + var url = browserLocationHref(); + return (isSxTruthy(main) ? (function() { + var headers = buildRequestHeaders(main, loadedComponentNames(), _cssHash); return fetchAndRestore(main, url, headers, scrollY); -})()); +})() : NIL); })(); }; // engine-init var engineInit = function() { return (initCssTracking(), sxProcessScripts(NIL), sxHydrate(NIL), processElements(NIL)); }; + // === Transpiled from cssx === + + // _style-atoms + var _styleAtoms = {}; + + // _pseudo-variants + var _pseudoVariants = {}; + + // _responsive-breakpoints + var _responsiveBreakpoints = {}; + + // _style-keyframes + var _styleKeyframes = {}; + + // _arbitrary-patterns + var _arbitraryPatterns = []; + + // _child-selector-prefixes + var _childSelectorPrefixes = []; + + // _style-cache + var _styleCache = {}; + + // _injected-styles + var _injectedStyles = {}; + + // load-style-dict + var loadStyleDict = function(data) { return (_styleAtoms = sxOr(get(data, "a"), {})); }; + + // split-variant + var splitVariant = function(atom) { return (function() { + var result = NIL; + { var _c = keys(_responsiveBreakpoints); for (var _i = 0; _i < _c.length; _i++) { var bp = _c[_i]; if (isSxTruthy(isNil(result))) { + (function() { + var prefix = (String(bp) + String(":")); + return (isSxTruthy(startsWith(atom, prefix)) ? (function() { + var restAtom = slice(atom, len(prefix)); + return (function() { + var innerMatch = NIL; + { var _c = keys(_pseudoVariants); for (var _i = 0; _i < _c.length; _i++) { var pv = _c[_i]; if (isSxTruthy(isNil(innerMatch))) { + (function() { + var innerPrefix = (String(pv) + String(":")); + return (isSxTruthy(startsWith(restAtom, innerPrefix)) ? (innerMatch = [(String(bp) + String(":") + String(pv)), slice(restAtom, len(innerPrefix))]) : NIL); +})(); +} } } + return (result = sxOr(innerMatch, [bp, restAtom])); +})(); +})() : NIL); +})(); +} } } + if (isSxTruthy(isNil(result))) { + { var _c = keys(_pseudoVariants); for (var _i = 0; _i < _c.length; _i++) { var pv = _c[_i]; if (isSxTruthy(isNil(result))) { + (function() { + var prefix = (String(pv) + String(":")); + return (isSxTruthy(startsWith(atom, prefix)) ? (result = [pv, slice(atom, len(prefix))]) : NIL); +})(); +} } } +} + return sxOr(result, [NIL, atom]); +})(); }; + + // resolve-atom + var resolveAtom = function(atom) { return (function() { + var decls = dictGet(_styleAtoms, atom); + return (isSxTruthy(!isNil(decls)) ? decls : (isSxTruthy(startsWith(atom, "animate-")) ? (function() { + var kfName = slice(atom, 8); + return (isSxTruthy(dictHas(_styleKeyframes, kfName)) ? (String("animation-name:") + String(kfName)) : NIL); +})() : (function() { + var matchResult = NIL; + { var _c = _arbitraryPatterns; for (var _i = 0; _i < _c.length; _i++) { var pat = _c[_i]; if (isSxTruthy(isNil(matchResult))) { + (function() { + var m = regexMatch(get(pat, "re"), atom); + return (isSxTruthy(m) ? (matchResult = regexReplaceGroups(get(pat, "tmpl"), m)) : NIL); +})(); +} } } + return matchResult; +})())); +})(); }; + + // is-child-selector-atom? + var isChildSelectorAtom = function(atom) { return some(function(prefix) { return startsWith(atom, prefix); }, _childSelectorPrefixes); }; + + // hash-style + var hashStyle = function(input) { return fnv1aHash(input); }; + + // resolve-style + var resolveStyle = function(atoms) { return (function() { + var key = join("\\0", atoms); + return (function() { + var cached = dictGet(_styleCache, key); + return (isSxTruthy(!isNil(cached)) ? cached : (function() { + var baseDecls = []; + var mediaRules = []; + var pseudoRules = []; + var kfNeeded = []; + { var _c = atoms; for (var _i = 0; _i < _c.length; _i++) { var a = _c[_i]; if (isSxTruthy(a)) { + (function() { + var clean = (isSxTruthy(startsWith(a, ":")) ? slice(a, 1) : a); + return (function() { + var parts = splitVariant(clean); + return (function() { + var variant = first(parts); + var base = nth(parts, 1); + var decls = resolveAtom(base); + return (isSxTruthy(decls) ? ((isSxTruthy(startsWith(base, "animate-")) ? (function() { + var kfName = slice(base, 8); + return (isSxTruthy(dictHas(_styleKeyframes, kfName)) ? append_b(kfNeeded, [kfName, dictGet(_styleKeyframes, kfName)]) : NIL); +})() : NIL), (isSxTruthy(isNil(variant)) ? append_b(baseDecls, decls) : (isSxTruthy(dictHas(_responsiveBreakpoints, variant)) ? append_b(mediaRules, [dictGet(_responsiveBreakpoints, variant), decls]) : (isSxTruthy(dictHas(_pseudoVariants, variant)) ? append_b(pseudoRules, [dictGet(_pseudoVariants, variant), decls]) : (function() { + var vparts = split(variant, ":"); + var mediaPart = NIL; + var pseudoPart = NIL; + { var _c = vparts; for (var _i = 0; _i < _c.length; _i++) { var vp = _c[_i]; (isSxTruthy(dictHas(_responsiveBreakpoints, vp)) ? (mediaPart = dictGet(_responsiveBreakpoints, vp)) : (isSxTruthy(dictHas(_pseudoVariants, vp)) ? (pseudoPart = dictGet(_pseudoVariants, vp)) : NIL)); } } + if (isSxTruthy(mediaPart)) { + mediaRules.push([mediaPart, decls]); +} + if (isSxTruthy(pseudoPart)) { + pseudoRules.push([pseudoPart, decls]); +} + return (isSxTruthy((isSxTruthy(isNil(mediaPart)) && isNil(pseudoPart))) ? append_b(baseDecls, decls) : NIL); +})())))) : NIL); +})(); +})(); +})(); +} } } + return (function() { + var hashInput = join(";", baseDecls); + { var _c = chunkEvery(mediaRules, 2); for (var _i = 0; _i < _c.length; _i++) { var mr = _c[_i]; hashInput = (String(hashInput) + String("@") + String(first(mr)) + String("{") + String(nth(mr, 1)) + String("}")); } } + { var _c = chunkEvery(pseudoRules, 2); for (var _i = 0; _i < _c.length; _i++) { var pr = _c[_i]; hashInput = (String(hashInput) + String(first(pr)) + String("{") + String(nth(pr, 1)) + String("}")); } } + { var _c = chunkEvery(kfNeeded, 2); for (var _i = 0; _i < _c.length; _i++) { var kf = _c[_i]; hashInput = (String(hashInput) + String(nth(kf, 1))); } } + return (function() { + var cn = (String("sx-") + String(hashStyle(hashInput))); + var sv = makeStyleValue_(cn, join(";", baseDecls), chunkEvery(mediaRules, 2), chunkEvery(pseudoRules, 2), chunkEvery(kfNeeded, 2)); + _styleCache[key] = sv; + injectStyleValue(sv, atoms); + return sv; +})(); +})(); +})()); +})(); +})(); }; + + // merge-style-values + var mergeStyleValues = function(styles) { return (isSxTruthy((len(styles) == 1)) ? first(styles) : (function() { + var allDecls = []; + var allMedia = []; + var allPseudo = []; + var allKf = []; + { var _c = styles; for (var _i = 0; _i < _c.length; _i++) { var sv = _c[_i]; if (isSxTruthy(styleValueDeclarations(sv))) { + allDecls.push(styleValueDeclarations(sv)); +} } } + return (function() { + var hashInput = join(";", allDecls); + { var _c = allMedia; for (var _i = 0; _i < _c.length; _i++) { var mr = _c[_i]; hashInput = (String(hashInput) + String("@") + String(first(mr)) + String("{") + String(nth(mr, 1)) + String("}")); } } + { var _c = allPseudo; for (var _i = 0; _i < _c.length; _i++) { var pr = _c[_i]; hashInput = (String(hashInput) + String(first(pr)) + String("{") + String(nth(pr, 1)) + String("}")); } } + { var _c = allKf; for (var _i = 0; _i < _c.length; _i++) { var kf = _c[_i]; hashInput = (String(hashInput) + String(nth(kf, 1))); } } + return (function() { + var cn = (String("sx-") + String(hashStyle(hashInput))); + var merged = makeStyleValue_(cn, join(";", allDecls), allMedia, allPseudo, allKf); + injectStyleValue(merged, []); + return merged; +})(); +})(); +})()); }; + + + // === 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); + return sxHydrateElements(el); +})() : NIL); +})(); }; + + // sx-hydrate-elements + var sxHydrateElements = function(root) { return (function() { + var els = domQueryAll(sxOr(root, domBody()), "[data-sx]"); + return forEach(function(el) { return (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(!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))); } } + return renderToDom(callExpr, env, NIL); +})()); +})(); +})(); }; + + // process-sx-scripts + var processSxScripts = function(root) { return (function() { + var scripts = querySxScripts(root); + return forEach(function(s) { return (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-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) && !isEmpty(trim(text)))) ? sxLoadComponents(text) : NIL) : (function() { + var hasInline = (isSxTruthy(text) && !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); +})()); +})(); }; + + // init-style-dict + var initStyleDict = function() { return (function() { + var scripts = queryStyleScripts(); + return forEach(function(s) { return (isSxTruthy(!isProcessed(s, "styles")) ? (markProcessed(s, "styles"), (function() { + var text = domTextContent(s); + var hash = domGetAttr(s, "data-hash"); + return (isSxTruthy(isNil(hash)) ? (isSxTruthy((isSxTruthy(text) && !isEmpty(trim(text)))) ? parseAndLoadStyleDict(text) : NIL) : (function() { + var hasInline = (isSxTruthy(text) && !isEmpty(trim(text))); + (function() { + var cachedHash = localStorageGet("sx-styles-hash"); + return (isSxTruthy((cachedHash == hash)) ? (isSxTruthy(hasInline) ? (localStorageSet("sx-styles-src", text), parseAndLoadStyleDict(text), logInfo("styles: downloaded (cookie stale)")) : (function() { + var cached = localStorageGet("sx-styles-src"); + return (isSxTruthy(cached) ? (parseAndLoadStyleDict(cached), logInfo((String("styles: cached (") + String(hash) + String(")")))) : (clearSxStylesCookie(), browserReload())); +})()) : (isSxTruthy(hasInline) ? (localStorageSet("sx-styles-hash", hash), localStorageSet("sx-styles-src", text), parseAndLoadStyleDict(text), logInfo((String("styles: downloaded (") + String(hash) + String(")")))) : (localStorageRemove("sx-styles-hash"), localStorageRemove("sx-styles-src"), clearSxStylesCookie(), browserReload()))); +})(); + return setSxStylesCookie(hash); +})()); +})()) : NIL); }, scripts); +})(); }; + + // boot-init + var bootInit = function() { return (initCssTracking(), initStyleDict(), processSxScripts(NIL), sxHydrateElements(NIL), processElements(NIL)); }; + + // ========================================================================= // Platform interface — DOM adapter (browser-only) // ========================================================================= @@ -1945,11 +2010,9 @@ // ========================================================================= - // Platform interface — Engine (browser-only) + // Platform interface — Engine pure logic (browser + node compatible) // ========================================================================= - // --- Browser/Network --- - function browserLocationHref() { return typeof location !== "undefined" ? location.href : ""; } @@ -1973,6 +2036,23 @@ } } + 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); } @@ -2001,16 +2081,6 @@ return r === null ? NIL : r; } - 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; } - } - function csrfToken() { if (!_hasDom) return NIL; var m = document.querySelector('meta[name="csrf-token"]'); @@ -2537,6 +2607,252 @@ } + // ========================================================================= + // Platform interface — CSSX (style dictionary) + // ========================================================================= + + function fnv1aHash(input) { + var h = 0x811c9dc5; + for (var i = 0; i < input.length; i++) { + h ^= input.charCodeAt(i); + h = (h * 0x01000193) >>> 0; + } + return h.toString(16).padStart(8, "0").substring(0, 6); + } + + function compileRegex(pattern) { + try { return new RegExp(pattern); } catch (e) { return null; } + } + + function regexMatch(re, s) { + if (!re) return NIL; + var m = s.match(re); + return m ? Array.prototype.slice.call(m) : NIL; + } + + function regexReplaceGroups(tmpl, match) { + var result = tmpl; + for (var j = 1; j < match.length; j++) { + result = result.split("{" + (j - 1) + "}").join(match[j]); + } + return result; + } + + function makeStyleValue_(cn, decls, media, pseudo, kf) { + return new StyleValue(cn, decls || "", media || [], pseudo || [], kf || []); + } + + function styleValueDeclarations(sv) { return sv.declarations; } + function styleValueMediaRules(sv) { return sv.mediaRules; } + function styleValuePseudoRules(sv) { return sv.pseudoRules; } + function styleValueKeyframes_(sv) { return sv.keyframes; } + + function injectStyleValue(sv, atoms) { + if (_injectedStyles[sv.className]) return; + _injectedStyles[sv.className] = true; + + if (!_hasDom) return; + var cssTarget = document.getElementById("sx-css"); + if (!cssTarget) return; + + var rules = []; + if (sv.declarations) { + var hasChild = false; + if (atoms) { + for (var ai = 0; ai < atoms.length; ai++) { + if (isChildSelectorAtom(atoms[ai])) { hasChild = true; break; } + } + } + if (hasChild) { + rules.push("." + sv.className + ">:not(:first-child){" + sv.declarations + "}"); + } else { + rules.push("." + sv.className + "{" + sv.declarations + "}"); + } + } + for (var pi = 0; pi < sv.pseudoRules.length; pi++) { + var sel = sv.pseudoRules[pi][0], decls = sv.pseudoRules[pi][1]; + if (sel.indexOf("&") >= 0) { + rules.push(sel.replace(/&/g, "." + sv.className) + "{" + decls + "}"); + } else { + rules.push("." + sv.className + sel + "{" + decls + "}"); + } + } + for (var mi = 0; mi < sv.mediaRules.length; mi++) { + rules.push("@media " + sv.mediaRules[mi][0] + "{." + sv.className + "{" + sv.mediaRules[mi][1] + "}}"); + } + for (var ki = 0; ki < sv.keyframes.length; ki++) { + rules.push(sv.keyframes[ki][1]); + } + cssTarget.textContent += rules.join(""); + } + + // Replace stub css primitive with real CSSX implementation + PRIMITIVES["css"] = function() { + var atoms = []; + for (var i = 0; i < arguments.length; i++) { + var a = arguments[i]; + if (isNil(a) || a === false) continue; + atoms.push(isKw(a) ? a.name : String(a)); + } + if (!atoms.length) return NIL; + return resolveStyle(atoms); + }; + + PRIMITIVES["merge-styles"] = function() { + var valid = []; + for (var i = 0; i < arguments.length; i++) { + if (isStyleValue(arguments[i])) valid.push(arguments[i]); + } + if (!valid.length) return NIL; + if (valid.length === 1) return valid[0]; + return mergeStyleValues(valid); + }; + + + // ========================================================================= + // 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 []; + return Array.prototype.slice.call( + (root || document).querySelectorAll('script[type="text/sx"]')); + } + + function queryStyleScripts() { + if (!_hasDom) return []; + return Array.prototype.slice.call( + document.querySelectorAll('script[type="text/sx-styles"]')); + } + + // --- 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"; + } + + function setSxStylesCookie(hash) { + if (_hasDom) document.cookie = "sx-styles-hash=" + hash + ";path=/;max-age=31536000;SameSite=Lax"; + } + + function clearSxStylesCookie() { + if (_hasDom) document.cookie = "sx-styles-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 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); + } + } + + function parseAndLoadStyleDict(text) { + try { loadStyleDict(JSON.parse(text)); } + catch (e) { if (typeof console !== "undefined") console.warn("[sx-ref] style dict parse error", e); } + } + + // ========================================================================= // Post-transpilation fixups // ========================================================================= @@ -2692,8 +3008,14 @@ process: typeof processElements === "function" ? processElements : null, executeRequest: typeof executeRequest === "function" ? executeRequest : null, postSwap: typeof postSwap === "function" ? postSwap : null, - init: typeof engineInit === "function" ? engineInit : null, - _version: "ref-2.0 (dom+engine, bootstrap-compiled)" + 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; }, + init: typeof bootInit === "function" ? bootInit : null, + _version: "ref-2.0 (boot+cssx+dom+engine+orchestration, bootstrap-compiled)" }; @@ -2706,7 +3028,7 @@ // --- Auto-init --- if (typeof document !== "undefined") { - var _sxRefInit = function() { engineInit(); }; + var _sxRefInit = function() { bootInit(); }; if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", _sxRefInit); } else { diff --git a/shared/static/scripts/sx-ref.js b/shared/static/scripts/sx-ref.js index e7dc6ea..47c31a3 100644 --- a/shared/static/scripts/sx-ref.js +++ b/shared/static/scripts/sx-ref.js @@ -1713,6 +1713,296 @@ var engineInit = function() { return (initCssTracking(), sxProcessScripts(NIL), sxHydrate(NIL), processElements(NIL)); }; + // === Transpiled from cssx === + + // _style-atoms + var _styleAtoms = {}; + + // _pseudo-variants + var _pseudoVariants = {}; + + // _responsive-breakpoints + var _responsiveBreakpoints = {}; + + // _style-keyframes + var _styleKeyframes = {}; + + // _arbitrary-patterns + var _arbitraryPatterns = []; + + // _child-selector-prefixes + var _childSelectorPrefixes = []; + + // _style-cache + var _styleCache = {}; + + // _injected-styles + var _injectedStyles = {}; + + // load-style-dict + var loadStyleDict = function(data) { return (_styleAtoms = sxOr(get(data, "a"), {})); }; + + // split-variant + var splitVariant = function(atom) { return (function() { + var result = NIL; + { var _c = keys(_responsiveBreakpoints); for (var _i = 0; _i < _c.length; _i++) { var bp = _c[_i]; if (isSxTruthy(isNil(result))) { + (function() { + var prefix = (String(bp) + String(":")); + return (isSxTruthy(startsWith(atom, prefix)) ? (function() { + var restAtom = slice(atom, len(prefix)); + return (function() { + var innerMatch = NIL; + { var _c = keys(_pseudoVariants); for (var _i = 0; _i < _c.length; _i++) { var pv = _c[_i]; if (isSxTruthy(isNil(innerMatch))) { + (function() { + var innerPrefix = (String(pv) + String(":")); + return (isSxTruthy(startsWith(restAtom, innerPrefix)) ? (innerMatch = [(String(bp) + String(":") + String(pv)), slice(restAtom, len(innerPrefix))]) : NIL); +})(); +} } } + return (result = sxOr(innerMatch, [bp, restAtom])); +})(); +})() : NIL); +})(); +} } } + if (isSxTruthy(isNil(result))) { + { var _c = keys(_pseudoVariants); for (var _i = 0; _i < _c.length; _i++) { var pv = _c[_i]; if (isSxTruthy(isNil(result))) { + (function() { + var prefix = (String(pv) + String(":")); + return (isSxTruthy(startsWith(atom, prefix)) ? (result = [pv, slice(atom, len(prefix))]) : NIL); +})(); +} } } +} + return sxOr(result, [NIL, atom]); +})(); }; + + // resolve-atom + var resolveAtom = function(atom) { return (function() { + var decls = dictGet(_styleAtoms, atom); + return (isSxTruthy(!isNil(decls)) ? decls : (isSxTruthy(startsWith(atom, "animate-")) ? (function() { + var kfName = slice(atom, 8); + return (isSxTruthy(dictHas(_styleKeyframes, kfName)) ? (String("animation-name:") + String(kfName)) : NIL); +})() : (function() { + var matchResult = NIL; + { var _c = _arbitraryPatterns; for (var _i = 0; _i < _c.length; _i++) { var pat = _c[_i]; if (isSxTruthy(isNil(matchResult))) { + (function() { + var m = regexMatch(get(pat, "re"), atom); + return (isSxTruthy(m) ? (matchResult = regexReplaceGroups(get(pat, "tmpl"), m)) : NIL); +})(); +} } } + return matchResult; +})())); +})(); }; + + // is-child-selector-atom? + var isChildSelectorAtom = function(atom) { return some(function(prefix) { return startsWith(atom, prefix); }, _childSelectorPrefixes); }; + + // hash-style + var hashStyle = function(input) { return fnv1aHash(input); }; + + // resolve-style + var resolveStyle = function(atoms) { return (function() { + var key = join("\\0", atoms); + return (function() { + var cached = dictGet(_styleCache, key); + return (isSxTruthy(!isNil(cached)) ? cached : (function() { + var baseDecls = []; + var mediaRules = []; + var pseudoRules = []; + var kfNeeded = []; + { var _c = atoms; for (var _i = 0; _i < _c.length; _i++) { var a = _c[_i]; if (isSxTruthy(a)) { + (function() { + var clean = (isSxTruthy(startsWith(a, ":")) ? slice(a, 1) : a); + return (function() { + var parts = splitVariant(clean); + return (function() { + var variant = first(parts); + var base = nth(parts, 1); + var decls = resolveAtom(base); + return (isSxTruthy(decls) ? ((isSxTruthy(startsWith(base, "animate-")) ? (function() { + var kfName = slice(base, 8); + return (isSxTruthy(dictHas(_styleKeyframes, kfName)) ? append_b(kfNeeded, [kfName, dictGet(_styleKeyframes, kfName)]) : NIL); +})() : NIL), (isSxTruthy(isNil(variant)) ? append_b(baseDecls, decls) : (isSxTruthy(dictHas(_responsiveBreakpoints, variant)) ? append_b(mediaRules, [dictGet(_responsiveBreakpoints, variant), decls]) : (isSxTruthy(dictHas(_pseudoVariants, variant)) ? append_b(pseudoRules, [dictGet(_pseudoVariants, variant), decls]) : (function() { + var vparts = split(variant, ":"); + var mediaPart = NIL; + var pseudoPart = NIL; + { var _c = vparts; for (var _i = 0; _i < _c.length; _i++) { var vp = _c[_i]; (isSxTruthy(dictHas(_responsiveBreakpoints, vp)) ? (mediaPart = dictGet(_responsiveBreakpoints, vp)) : (isSxTruthy(dictHas(_pseudoVariants, vp)) ? (pseudoPart = dictGet(_pseudoVariants, vp)) : NIL)); } } + if (isSxTruthy(mediaPart)) { + mediaRules.push([mediaPart, decls]); +} + if (isSxTruthy(pseudoPart)) { + pseudoRules.push([pseudoPart, decls]); +} + return (isSxTruthy((isSxTruthy(isNil(mediaPart)) && isNil(pseudoPart))) ? append_b(baseDecls, decls) : NIL); +})())))) : NIL); +})(); +})(); +})(); +} } } + return (function() { + var hashInput = join(";", baseDecls); + { var _c = chunkEvery(mediaRules, 2); for (var _i = 0; _i < _c.length; _i++) { var mr = _c[_i]; hashInput = (String(hashInput) + String("@") + String(first(mr)) + String("{") + String(nth(mr, 1)) + String("}")); } } + { var _c = chunkEvery(pseudoRules, 2); for (var _i = 0; _i < _c.length; _i++) { var pr = _c[_i]; hashInput = (String(hashInput) + String(first(pr)) + String("{") + String(nth(pr, 1)) + String("}")); } } + { var _c = chunkEvery(kfNeeded, 2); for (var _i = 0; _i < _c.length; _i++) { var kf = _c[_i]; hashInput = (String(hashInput) + String(nth(kf, 1))); } } + return (function() { + var cn = (String("sx-") + String(hashStyle(hashInput))); + var sv = makeStyleValue_(cn, join(";", baseDecls), chunkEvery(mediaRules, 2), chunkEvery(pseudoRules, 2), chunkEvery(kfNeeded, 2)); + _styleCache[key] = sv; + injectStyleValue(sv, atoms); + return sv; +})(); +})(); +})()); +})(); +})(); }; + + // merge-style-values + var mergeStyleValues = function(styles) { return (isSxTruthy((len(styles) == 1)) ? first(styles) : (function() { + var allDecls = []; + var allMedia = []; + var allPseudo = []; + var allKf = []; + { var _c = styles; for (var _i = 0; _i < _c.length; _i++) { var sv = _c[_i]; if (isSxTruthy(styleValueDeclarations(sv))) { + allDecls.push(styleValueDeclarations(sv)); +} } } + return (function() { + var hashInput = join(";", allDecls); + { var _c = allMedia; for (var _i = 0; _i < _c.length; _i++) { var mr = _c[_i]; hashInput = (String(hashInput) + String("@") + String(first(mr)) + String("{") + String(nth(mr, 1)) + String("}")); } } + { var _c = allPseudo; for (var _i = 0; _i < _c.length; _i++) { var pr = _c[_i]; hashInput = (String(hashInput) + String(first(pr)) + String("{") + String(nth(pr, 1)) + String("}")); } } + { var _c = allKf; for (var _i = 0; _i < _c.length; _i++) { var kf = _c[_i]; hashInput = (String(hashInput) + String(nth(kf, 1))); } } + return (function() { + var cn = (String("sx-") + String(hashStyle(hashInput))); + var merged = makeStyleValue_(cn, join(";", allDecls), allMedia, allPseudo, allKf); + injectStyleValue(merged, []); + return merged; +})(); +})(); +})()); }; + + + // === 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); + return sxHydrateElements(el); +})() : NIL); +})(); }; + + // sx-hydrate-elements + var sxHydrateElements = function(root) { return (function() { + var els = domQueryAll(sxOr(root, domBody()), "[data-sx]"); + return forEach(function(el) { return (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(!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))); } } + return renderToDom(callExpr, env, NIL); +})()); +})(); +})(); }; + + // process-sx-scripts + var processSxScripts = function(root) { return (function() { + var scripts = querySxScripts(root); + return forEach(function(s) { return (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-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) && !isEmpty(trim(text)))) ? sxLoadComponents(text) : NIL) : (function() { + var hasInline = (isSxTruthy(text) && !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); +})()); +})(); }; + + // init-style-dict + var initStyleDict = function() { return (function() { + var scripts = queryStyleScripts(); + return forEach(function(s) { return (isSxTruthy(!isProcessed(s, "styles")) ? (markProcessed(s, "styles"), (function() { + var text = domTextContent(s); + var hash = domGetAttr(s, "data-hash"); + return (isSxTruthy(isNil(hash)) ? (isSxTruthy((isSxTruthy(text) && !isEmpty(trim(text)))) ? parseAndLoadStyleDict(text) : NIL) : (function() { + var hasInline = (isSxTruthy(text) && !isEmpty(trim(text))); + (function() { + var cachedHash = localStorageGet("sx-styles-hash"); + return (isSxTruthy((cachedHash == hash)) ? (isSxTruthy(hasInline) ? (localStorageSet("sx-styles-src", text), parseAndLoadStyleDict(text), logInfo("styles: downloaded (cookie stale)")) : (function() { + var cached = localStorageGet("sx-styles-src"); + return (isSxTruthy(cached) ? (parseAndLoadStyleDict(cached), logInfo((String("styles: cached (") + String(hash) + String(")")))) : (clearSxStylesCookie(), browserReload())); +})()) : (isSxTruthy(hasInline) ? (localStorageSet("sx-styles-hash", hash), localStorageSet("sx-styles-src", text), parseAndLoadStyleDict(text), logInfo((String("styles: downloaded (") + String(hash) + String(")")))) : (localStorageRemove("sx-styles-hash"), localStorageRemove("sx-styles-src"), clearSxStylesCookie(), browserReload()))); +})(); + return setSxStylesCookie(hash); +})()); +})()) : NIL); }, scripts); +})(); }; + + // boot-init + var bootInit = function() { return (initCssTracking(), initStyleDict(), processSxScripts(NIL), sxHydrateElements(NIL), processElements(NIL)); }; + + // ========================================================================= // Platform interface — DOM adapter (browser-only) // ========================================================================= @@ -2465,6 +2755,252 @@ } + // ========================================================================= + // Platform interface — CSSX (style dictionary) + // ========================================================================= + + function fnv1aHash(input) { + var h = 0x811c9dc5; + for (var i = 0; i < input.length; i++) { + h ^= input.charCodeAt(i); + h = (h * 0x01000193) >>> 0; + } + return h.toString(16).padStart(8, "0").substring(0, 6); + } + + function compileRegex(pattern) { + try { return new RegExp(pattern); } catch (e) { return null; } + } + + function regexMatch(re, s) { + if (!re) return NIL; + var m = s.match(re); + return m ? Array.prototype.slice.call(m) : NIL; + } + + function regexReplaceGroups(tmpl, match) { + var result = tmpl; + for (var j = 1; j < match.length; j++) { + result = result.split("{" + (j - 1) + "}").join(match[j]); + } + return result; + } + + function makeStyleValue_(cn, decls, media, pseudo, kf) { + return new StyleValue(cn, decls || "", media || [], pseudo || [], kf || []); + } + + function styleValueDeclarations(sv) { return sv.declarations; } + function styleValueMediaRules(sv) { return sv.mediaRules; } + function styleValuePseudoRules(sv) { return sv.pseudoRules; } + function styleValueKeyframes_(sv) { return sv.keyframes; } + + function injectStyleValue(sv, atoms) { + if (_injectedStyles[sv.className]) return; + _injectedStyles[sv.className] = true; + + if (!_hasDom) return; + var cssTarget = document.getElementById("sx-css"); + if (!cssTarget) return; + + var rules = []; + if (sv.declarations) { + var hasChild = false; + if (atoms) { + for (var ai = 0; ai < atoms.length; ai++) { + if (isChildSelectorAtom(atoms[ai])) { hasChild = true; break; } + } + } + if (hasChild) { + rules.push("." + sv.className + ">:not(:first-child){" + sv.declarations + "}"); + } else { + rules.push("." + sv.className + "{" + sv.declarations + "}"); + } + } + for (var pi = 0; pi < sv.pseudoRules.length; pi++) { + var sel = sv.pseudoRules[pi][0], decls = sv.pseudoRules[pi][1]; + if (sel.indexOf("&") >= 0) { + rules.push(sel.replace(/&/g, "." + sv.className) + "{" + decls + "}"); + } else { + rules.push("." + sv.className + sel + "{" + decls + "}"); + } + } + for (var mi = 0; mi < sv.mediaRules.length; mi++) { + rules.push("@media " + sv.mediaRules[mi][0] + "{." + sv.className + "{" + sv.mediaRules[mi][1] + "}}"); + } + for (var ki = 0; ki < sv.keyframes.length; ki++) { + rules.push(sv.keyframes[ki][1]); + } + cssTarget.textContent += rules.join(""); + } + + // Replace stub css primitive with real CSSX implementation + PRIMITIVES["css"] = function() { + var atoms = []; + for (var i = 0; i < arguments.length; i++) { + var a = arguments[i]; + if (isNil(a) || a === false) continue; + atoms.push(isKw(a) ? a.name : String(a)); + } + if (!atoms.length) return NIL; + return resolveStyle(atoms); + }; + + PRIMITIVES["merge-styles"] = function() { + var valid = []; + for (var i = 0; i < arguments.length; i++) { + if (isStyleValue(arguments[i])) valid.push(arguments[i]); + } + if (!valid.length) return NIL; + if (valid.length === 1) return valid[0]; + return mergeStyleValues(valid); + }; + + + // ========================================================================= + // 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 []; + return Array.prototype.slice.call( + (root || document).querySelectorAll('script[type="text/sx"]')); + } + + function queryStyleScripts() { + if (!_hasDom) return []; + return Array.prototype.slice.call( + document.querySelectorAll('script[type="text/sx-styles"]')); + } + + // --- 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"; + } + + function setSxStylesCookie(hash) { + if (_hasDom) document.cookie = "sx-styles-hash=" + hash + ";path=/;max-age=31536000;SameSite=Lax"; + } + + function clearSxStylesCookie() { + if (_hasDom) document.cookie = "sx-styles-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 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); + } + } + + function parseAndLoadStyleDict(text) { + try { loadStyleDict(JSON.parse(text)); } + catch (e) { if (typeof console !== "undefined") console.warn("[sx-ref] style dict parse error", e); } + } + + // ========================================================================= // Post-transpilation fixups // ========================================================================= @@ -2638,8 +3174,14 @@ process: typeof processElements === "function" ? processElements : null, executeRequest: typeof executeRequest === "function" ? executeRequest : null, postSwap: typeof postSwap === "function" ? postSwap : null, - init: typeof engineInit === "function" ? engineInit : null, - _version: "ref-2.0 (dom+engine+html+orchestration+sx, bootstrap-compiled)" + 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; }, + init: typeof bootInit === "function" ? bootInit : null, + _version: "ref-2.0 (boot+cssx+dom+engine+html+orchestration+sx, bootstrap-compiled)" }; @@ -2652,7 +3194,7 @@ // --- Auto-init --- if (typeof document !== "undefined") { - var _sxRefInit = function() { engineInit(); }; + var _sxRefInit = function() { bootInit(); }; if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", _sxRefInit); } else { diff --git a/shared/sx/ref/boot.sx b/shared/sx/ref/boot.sx new file mode 100644 index 0000000..94dc7dc --- /dev/null +++ b/shared/sx/ref/boot.sx @@ -0,0 +1,384 @@ +;; ========================================================================== +;; boot.sx — Browser boot, mount, hydrate, script processing +;; +;; Handles the browser startup lifecycle: +;; 1. CSS tracking init +;; 2. Style dictionary loading (from