Previously these mutating operations were internal helpers in the JS bootstrapper but not declared in primitives.sx or registered in the Python evaluator. Now properly specced and available in both hosts. Removes mock injections from cache tests — they use real primitives. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3690 lines
169 KiB
JavaScript
3690 lines
169 KiB
JavaScript
/**
|
|
* 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-07T00:20:00Z";
|
|
|
|
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) {
|
|
this.name = name;
|
|
this.params = params;
|
|
this.hasChildren = hasChildren;
|
|
this.body = body;
|
|
this.closure = closure || {};
|
|
}
|
|
Component.prototype._component = true;
|
|
|
|
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 StyleValue(className, declarations, mediaRules, pseudoRules, keyframes) {
|
|
this.className = className;
|
|
this.declarations = declarations || "";
|
|
this.mediaRules = mediaRules || [];
|
|
this.pseudoRules = pseudoRules || [];
|
|
this.keyframes = keyframes || [];
|
|
}
|
|
StyleValue.prototype._styleValue = 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._macro) return "macro";
|
|
if (x._raw) return "raw-html";
|
|
if (x._styleValue) return "style-value";
|
|
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) {
|
|
return new Component(name, params, hasChildren, body, merge(env));
|
|
}
|
|
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 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 isMacro(x) { return x != null && x._macro === true; }
|
|
|
|
function isStyleValue(x) { return x != null && x._styleValue === true; }
|
|
function styleValueClass(x) { return x.className; }
|
|
function styleValue_p(x) { return x != null && x._styleValue === true; }
|
|
|
|
function buildKeyframes(kfName, steps, env) {
|
|
// Platform implementation of defkeyframes
|
|
var parts = [];
|
|
for (var i = 0; i < steps.length; i++) {
|
|
var step = steps[i];
|
|
var selector = isSym(step[0]) ? step[0].name : String(step[0]);
|
|
var body = trampoline(evalExpr(step[1], env));
|
|
var decls = isStyleValue(body) ? body.declarations : String(body);
|
|
parts.push(selector + "{" + decls + "}");
|
|
}
|
|
var kfRule = "@keyframes " + kfName + "{" + parts.join("") + "}";
|
|
var cn = "sx-ref-kf-" + kfName;
|
|
var sv = new StyleValue(cn, "animation-name:" + kfName, [], [], [[kfName, kfRule]]);
|
|
env[kfName] = sv;
|
|
return sv;
|
|
}
|
|
|
|
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 merge(env); }
|
|
function envMerge(base, overlay) { return merge(base, overlay); }
|
|
|
|
function dictSet(d, k, v) { d[k] = v; return v; }
|
|
PRIMITIVES["dict-set!"] = dictSet;
|
|
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.
|
|
function isRenderExpr(expr) {
|
|
if (!Array.isArray(expr) || !expr.length) return false;
|
|
var h = expr[0];
|
|
if (!h || !h._sym) return false;
|
|
var n = h.name;
|
|
return !!(n === "<>" || n === "raw!" ||
|
|
n.charAt(0) === "~" || n.indexOf("html:") === 0 ||
|
|
(typeof HTML_TAGS !== "undefined" && HTML_TAGS.indexOf(n) >= 0) ||
|
|
(typeof SVG_TAGS !== "undefined" && SVG_TAGS.indexOf(n) >= 0) ||
|
|
(n.indexOf("-") > 0 && expr.length > 1 && expr[1] && expr[1]._kw));
|
|
}
|
|
|
|
// 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; };
|
|
|
|
|
|
// 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["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["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;
|
|
};
|
|
|
|
|
|
// 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["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,""").replace(/'/g,"'");
|
|
};
|
|
PRIMITIVES["strip-tags"] = function(s) { return String(s).replace(/<[^>]+>/g, ""); };
|
|
|
|
|
|
// stdlib.style
|
|
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 new StyleValue("sx-" + atoms.join("-"), atoms.join(";"), [], [], []);
|
|
};
|
|
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];
|
|
var allDecls = valid.map(function(v) { return v.declarations; }).join(";");
|
|
return new StyleValue("sx-merged", allDecls, [], [], []);
|
|
};
|
|
|
|
|
|
// 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; }
|
|
PRIMITIVES["append!"] = append_b;
|
|
var apply = function(f, args) { 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,">").replace(/"/g,""");
|
|
}
|
|
function escapeAttr(s) { return escapeHtml(s); }
|
|
function rawHtmlContent(r) { return r.html; }
|
|
function makeRawHtml(s) { return { _raw: true, html: s }; }
|
|
|
|
// Serializer
|
|
function serialize(val) {
|
|
if (isNil(val)) return "nil";
|
|
if (typeof val === "boolean") return val ? "true" : "false";
|
|
if (typeof val === "number") return String(val);
|
|
if (typeof val === "string") return '"' + val.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"';
|
|
if (isSym(val)) return val.name;
|
|
if (isKw(val)) return ":" + val.name;
|
|
if (Array.isArray(val)) return "(" + val.map(serialize).join(" ") + ")";
|
|
return String(val);
|
|
}
|
|
|
|
function isSpecialForm(n) { return n in {
|
|
"if":1,"when":1,"cond":1,"case":1,"and":1,"or":1,"let":1,"let*":1,
|
|
"lambda":1,"fn":1,"define":1,"defcomp":1,"defmacro":1,"defstyle":1,
|
|
"defkeyframes":1,"defhandler":1,"begin":1,"do":1,
|
|
"quote":1,"quasiquote":1,"->":1,"set!":1
|
|
}; }
|
|
function isHoForm(n) { return n in {
|
|
"map":1,"map-indexed":1,"filter":1,"reduce":1,"some":1,"every?":1,"for-each":1
|
|
}; }
|
|
|
|
// processBindings and evalCond — now specced in render.sx, bootstrapped above
|
|
|
|
function isDefinitionForm(name) {
|
|
return name === "define" || name === "defcomp" || name === "defmacro" ||
|
|
name === "defstyle" || name === "defkeyframes" || 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;
|
|
}
|
|
|
|
// =========================================================================
|
|
// 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 == "defmacro")) ? sfDefmacro(args, env) : (isSxTruthy((name == "defstyle")) ? sfDefstyle(args, env) : (isSxTruthy((name == "defkeyframes")) ? sfDefkeyframes(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(isComponent(f)))) ? apply(f, evaluatedArgs) : (isSxTruthy(isLambda(f)) ? callLambda(f, evaluatedArgs, env) : (isSxTruthy(isComponent(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 body = nth(args, 1);
|
|
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 = nth(args, 2);
|
|
var compName = stripPrefix(symbolName(nameSym), "~");
|
|
var parsed = parseCompParams(paramsRaw);
|
|
var params = first(parsed);
|
|
var hasChildren = nth(parsed, 1);
|
|
return (function() {
|
|
var comp = makeComponent(compName, params, hasChildren, body, env);
|
|
env[symbolName(nameSym)] = comp;
|
|
return comp;
|
|
})();
|
|
})(); };
|
|
|
|
// 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-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-defkeyframes
|
|
var sfDefkeyframes = function(args, env) { return (function() {
|
|
var kfName = symbolName(first(args));
|
|
var steps = rest(args);
|
|
return buildKeyframes(kfName, steps, env);
|
|
})(); };
|
|
|
|
// 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 == "defmacro"), (name == "defstyle"), (name == "defkeyframes"), (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)) ? "" : (isSxTruthy((isSxTruthy((key == "style")) && isStyleValue(val))) ? (String(" class=\"") + String(styleValueClass(val)) + String("\"")) : (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;
|
|
})(); };
|
|
|
|
|
|
// === 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 readExpr = function() { skipWs();
|
|
return (isSxTruthy((pos >= lenSrc)) ? error("Unexpected end of input") : (function() {
|
|
var ch = nth(source, pos);
|
|
return (isSxTruthy((ch == "(")) ? ((pos = (pos + 1)), readList(")")) : (isSxTruthy((ch == "[")) ? ((pos = (pos + 1)), readList("]")) : (isSxTruthy((ch == "{")) ? ((pos = (pos + 1)), readMap()) : (isSxTruthy((ch == "\"")) ? readString() : (isSxTruthy((ch == ":")) ? readKeyword() : (isSxTruthy((ch == "`")) ? ((pos = (pos + 1)), [makeSymbol("quasiquote"), readExpr()]) : (isSxTruthy((ch == ",")) ? ((pos = (pos + 1)), (isSxTruthy((isSxTruthy((pos < lenSrc)) && (nth(source, pos) == "@"))) ? ((pos = (pos + 1)), [makeSymbol("splice-unquote"), readExpr()]) : [makeSymbol("unquote"), readExpr()])) : (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"));
|
|
})()))) ? readNumber() : (isSxTruthy((isSxTruthy((ch == ".")) && isSxTruthy(((pos + 2) < lenSrc)) && isSxTruthy((nth(source, (pos + 1)) == ".")) && (nth(source, (pos + 2)) == "."))) ? ((pos = (pos + 3)), makeSymbol("...")) : (isSxTruthy(isIdentStart(ch)) ? readSymbol() : 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("}")); };
|
|
|
|
|
|
// === 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); if (_m == "style-value") return styleValueClass(val); return escapeHtml((String(val))); })(); };
|
|
|
|
// RENDER_HTML_FORMS
|
|
var RENDER_HTML_FORMS = ["if", "when", "cond", "case", "let", "let*", "begin", "do", "define", "defcomp", "defmacro", "defstyle", "defkeyframes", "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(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("</") + String(tag) + String(">")))));
|
|
})(); };
|
|
|
|
|
|
// === 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(isComponent(f)))) ? apply(f, evaledArgs) : (isSxTruthy(isLambda(f)) ? trampoline(callLambda(f, evaledArgs, env)) : (isSxTruthy(isComponent(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(")"));
|
|
})(); };
|
|
|
|
|
|
// === 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)); if (_m == "style-value") return createTextNode(styleValueClass(expr)); 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(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) : 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);
|
|
var extraClass = NIL;
|
|
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 attrVal = trampoline(evalExpr(nth(args, (get(state, "i") + 1)), env));
|
|
(isSxTruthy(sxOr(isNil(attrVal), (attrVal == false))) ? NIL : (isSxTruthy((isSxTruthy((attrName == "style")) && isStyleValue(attrVal))) ? (extraClass = styleValueClass(attrVal)) : (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);
|
|
if (isSxTruthy(extraClass)) {
|
|
(function() {
|
|
var existing = domGetAttr(el, "class");
|
|
return domSetAttr(el, "class", (isSxTruthy(existing) ? (String(existing) + String(" ") + String(extraClass)) : extraClass));
|
|
})();
|
|
}
|
|
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", "defmacro", "defstyle", "defkeyframes", "defhandler", "map", "map-indexed", "filter", "for-each"];
|
|
|
|
// 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")) ? (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(!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")) ? (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 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 == "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);
|
|
})(); };
|
|
|
|
|
|
// === 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")}; };
|
|
|
|
// 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 = NIL;
|
|
|
|
// 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);
|
|
}
|
|
return (function() {
|
|
var ctrl = newAbortController();
|
|
trackController(el, ctrl);
|
|
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"));
|
|
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) { 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));
|
|
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");
|
|
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) { 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 isGetLink = (isSxTruthy((eventName == "click")) && isSxTruthy((get(verbInfo, "method") == "GET")) && isSxTruthy(domHasAttr(el, "href")) && !isSxTruthy(get(mods, "delay")));
|
|
var clientRouted = false;
|
|
if (isSxTruthy(isGetLink)) {
|
|
logInfo((String("sx:route trying ") + String(get(verbInfo, "url"))));
|
|
clientRouted = tryClientRoute(urlPathname(get(verbInfo, "url")), domGetAttr(el, "sx-target"));
|
|
}
|
|
return (isSxTruthy(clientRouted) ? (browserPushState(get(verbInfo, "url")), browserScrollTo(0, 0)) : ((isSxTruthy(isGetLink) ? logInfo((String("sx:route server fetch ") + String(get(verbInfo, "url")))) : 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) { activateScripts(root);
|
|
sxProcessScripts(root);
|
|
sxHydrate(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()}); };
|
|
|
|
// swap-rendered-content
|
|
var swapRenderedContent = function(target, rendered, pathname) { return (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); };
|
|
|
|
// 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 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(get(match, "has-data")) ? (function() {
|
|
var cacheKey = pageDataCacheKey(pageName, params);
|
|
var cached = pageDataCacheGet(cacheKey);
|
|
return (isSxTruthy(cached) ? (function() {
|
|
var env = merge(closure, params, cached);
|
|
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);
|
|
var rendered = tryEvalContent(contentSrc, env);
|
|
return (isSxTruthy(isNil(rendered)) ? logWarn((String("sx:route data eval failed for ") + String(pathname))) : 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))) ? (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))), 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 (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);
|
|
})(); };
|
|
|
|
// 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);
|
|
return bindInlineHandlers(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);
|
|
})(); };
|
|
|
|
// handle-popstate
|
|
var handlePopstate = function(scrollY) { return (function() {
|
|
var boostEl = domQuery("[sx-boost]");
|
|
var url = browserLocationHref();
|
|
return (isSxTruthy(boostEl) ? (function() {
|
|
var targetSel = domGetAttr(boostEl, "sx-boost");
|
|
var target = (isSxTruthy((isSxTruthy(targetSel) && !isSxTruthy((targetSel == "true")))) ? domQuery(targetSel) : NIL);
|
|
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);
|
|
})() : 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) { _styleAtoms = sxOr(get(data, "a"), {});
|
|
_pseudoVariants = sxOr(get(data, "v"), {});
|
|
_responsiveBreakpoints = sxOr(get(data, "b"), {});
|
|
_styleKeyframes = sxOr(get(data, "k"), {});
|
|
_childSelectorPrefixes = sxOr(get(data, "c"), []);
|
|
_arbitraryPatterns = map(function(pair) { return {["re"]: compileRegex((String("^") + String(first(pair)) + String("$"))), ["tmpl"]: nth(pair, 1)}; }, sxOr(get(data, "p"), []));
|
|
return (_styleCache = {}); };
|
|
|
|
// 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(!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(!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)) ? (isSxTruthy(isChildSelectorAtom(base)) ? append_b(pseudoRules, [">:not(:first-child)", decls]) : 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 = mediaRules; 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 = pseudoRules; 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 = kfNeeded; 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), mediaRules, pseudoRules, kfNeeded);
|
|
_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));
|
|
}
|
|
allMedia = concat(allMedia, styleValueMediaRules(sv));
|
|
allPseudo = concat(allPseudo, styleValuePseudoRules(sv));
|
|
allKf = concat(allKf, styleValueKeyframes_(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(!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-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);
|
|
})());
|
|
})(); };
|
|
|
|
// init-style-dict
|
|
var initStyleDict = function() { return (function() {
|
|
var scripts = queryStyleScripts();
|
|
return forEach(function(s) { return (isSxTruthy(!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) && !isSxTruthy(isEmpty(trim(text))))) ? parseAndLoadStyleDict(text) : NIL) : (function() {
|
|
var hasInline = (isSxTruthy(text) && !isSxTruthy(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);
|
|
})(); };
|
|
|
|
// _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")));
|
|
})(); };
|
|
|
|
// boot-init
|
|
var bootInit = function() { return (logInfo((String("sx-browser ") + String(SX_VERSION))), initCssTracking(), initStyleDict(), processPageScripts(), processSxScripts(NIL), sxHydrateElements(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;
|
|
})(); };
|
|
|
|
|
|
// =========================================================================
|
|
// 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) return document.createElementNS(ns, tag);
|
|
return document.createElement(tag);
|
|
}
|
|
|
|
function createTextNode(s) {
|
|
return _hasDom ? document.createTextNode(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 domQuery(sel) {
|
|
return _hasDom ? document.querySelector(sel) : 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 : ""; }
|
|
|
|
|
|
// =========================================================================
|
|
// 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 promiseCatch(p, fn) { return p && p.catch ? p.catch(fn) : p; }
|
|
|
|
// --- 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);
|
|
}
|
|
|
|
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 setTimeout_(fn, ms) { return setTimeout(fn, ms || 0); }
|
|
function setInterval_(fn, ms) { return setInterval(fn, ms || 1000); }
|
|
function clearTimeout_(id) { clearTimeout(id); }
|
|
function requestAnimationFrame_(fn) {
|
|
if (typeof requestAnimationFrame !== "undefined") requestAnimationFrame(fn);
|
|
else setTimeout(fn, 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 fetchPreload(url, headers, cache) {
|
|
fetch(url, { headers: headers }).then(function(resp) {
|
|
if (!resp.ok) return;
|
|
var ct = resp.headers.get("Content-Type") || "";
|
|
return resp.text().then(function(text) {
|
|
preloadCacheSet(cache, url, text, ct);
|
|
});
|
|
}).catch(function() { /* ignore */ });
|
|
}
|
|
|
|
// --- Request body building ---
|
|
|
|
function buildRequestBody(el, method, url) {
|
|
if (!_hasDom) return { body: null, url: url, "content-type": NIL };
|
|
var body = null;
|
|
var ct = NIL;
|
|
var finalUrl = url;
|
|
var isJson = el.getAttribute("sx-encoding") === "json";
|
|
|
|
if (method !== "GET") {
|
|
var form = el.closest("form") || (el.tagName === "FORM" ? el : null);
|
|
if (form) {
|
|
if (isJson) {
|
|
var fd = new FormData(form);
|
|
var obj = {};
|
|
fd.forEach(function(v, k) {
|
|
if (obj[k] !== undefined) {
|
|
if (!Array.isArray(obj[k])) obj[k] = [obj[k]];
|
|
obj[k].push(v);
|
|
} else { obj[k] = v; }
|
|
});
|
|
body = JSON.stringify(obj);
|
|
ct = "application/json";
|
|
} else {
|
|
body = new URLSearchParams(new FormData(form));
|
|
ct = "application/x-www-form-urlencoded";
|
|
}
|
|
}
|
|
}
|
|
|
|
// sx-params
|
|
var paramsSpec = el.getAttribute("sx-params");
|
|
if (paramsSpec && body instanceof URLSearchParams) {
|
|
if (paramsSpec === "none") {
|
|
body = new URLSearchParams();
|
|
} else if (paramsSpec.indexOf("not ") === 0) {
|
|
paramsSpec.substring(4).split(",").forEach(function(k) { body.delete(k.trim()); });
|
|
} else if (paramsSpec !== "*") {
|
|
var allowed = paramsSpec.split(",").map(function(s) { return s.trim(); });
|
|
var filtered = new URLSearchParams();
|
|
allowed.forEach(function(k) {
|
|
body.getAll(k).forEach(function(v) { filtered.append(k, v); });
|
|
});
|
|
body = filtered;
|
|
}
|
|
}
|
|
|
|
// sx-include
|
|
var includeSel = el.getAttribute("sx-include");
|
|
if (includeSel && method !== "GET") {
|
|
if (!body) body = new URLSearchParams();
|
|
document.querySelectorAll(includeSel).forEach(function(inp) {
|
|
if (inp.name) body.append(inp.name, inp.value);
|
|
});
|
|
}
|
|
|
|
// sx-vals
|
|
var valsAttr = el.getAttribute("sx-vals");
|
|
if (valsAttr) {
|
|
try {
|
|
var vals = valsAttr.charAt(0) === "{" && valsAttr.charAt(1) === ":" ? parse(valsAttr) : JSON.parse(valsAttr);
|
|
if (method === "GET") {
|
|
for (var vk in vals) finalUrl += (finalUrl.indexOf("?") >= 0 ? "&" : "?") + encodeURIComponent(vk) + "=" + encodeURIComponent(vals[vk]);
|
|
} else if (body instanceof URLSearchParams) {
|
|
for (var vk2 in vals) body.append(vk2, vals[vk2]);
|
|
} else if (!body) {
|
|
body = new URLSearchParams();
|
|
for (var vk3 in vals) body.append(vk3, vals[vk3]);
|
|
ct = "application/x-www-form-urlencoded";
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
|
|
// GET form data → URL
|
|
if (method === "GET") {
|
|
var form2 = el.closest("form") || (el.tagName === "FORM" ? el : null);
|
|
if (form2) {
|
|
var qs = new URLSearchParams(new FormData(form2)).toString();
|
|
if (qs) finalUrl += (finalUrl.indexOf("?") >= 0 ? "&" : "?") + qs;
|
|
}
|
|
if ((el.tagName === "INPUT" || el.tagName === "SELECT" || el.tagName === "TEXTAREA") && el.name) {
|
|
finalUrl += (finalUrl.indexOf("?") >= 0 ? "&" : "?") + encodeURIComponent(el.name) + "=" + encodeURIComponent(el.value);
|
|
}
|
|
}
|
|
|
|
return { body: body, url: finalUrl, "content-type": ct };
|
|
}
|
|
|
|
// --- Loading state ---
|
|
|
|
function showIndicator(el) {
|
|
if (!_hasDom) return NIL;
|
|
var sel = el.getAttribute("sx-indicator");
|
|
var ind = sel ? (document.querySelector(sel) || el.closest(sel)) : null;
|
|
if (ind) { ind.classList.add("sx-request"); ind.style.display = ""; }
|
|
return ind || NIL;
|
|
}
|
|
|
|
function disableElements(el) {
|
|
if (!_hasDom) return [];
|
|
var sel = el.getAttribute("sx-disabled-elt");
|
|
if (!sel) return [];
|
|
var elts = Array.prototype.slice.call(document.querySelectorAll(sel));
|
|
elts.forEach(function(e) { e.disabled = true; });
|
|
return elts;
|
|
}
|
|
|
|
function clearLoadingState(el, indicator, disabledElts) {
|
|
el.classList.remove("sx-request");
|
|
el.removeAttribute("aria-busy");
|
|
if (indicator && !isNil(indicator)) {
|
|
indicator.classList.remove("sx-request");
|
|
indicator.style.display = "none";
|
|
}
|
|
if (disabledElts) {
|
|
for (var i = 0; i < disabledElts.length; i++) disabledElts[i].disabled = false;
|
|
}
|
|
}
|
|
|
|
// --- DOM extras ---
|
|
|
|
function domQueryById(id) {
|
|
return _hasDom ? document.getElementById(id) : null;
|
|
}
|
|
|
|
function domMatches(el, sel) {
|
|
return el && el.matches ? el.matches(sel) : false;
|
|
}
|
|
|
|
function domClosest(el, sel) {
|
|
return el && el.closest ? el.closest(sel) : null;
|
|
}
|
|
|
|
function domBody() {
|
|
return _hasDom ? document.body : null;
|
|
}
|
|
|
|
function domHasClass(el, cls) {
|
|
return el && el.classList ? el.classList.contains(cls) : false;
|
|
}
|
|
|
|
function domAppendToHead(el) {
|
|
if (_hasDom && document.head) document.head.appendChild(el);
|
|
}
|
|
|
|
function domParseHtmlDocument(text) {
|
|
if (!_hasDom) return null;
|
|
return new DOMParser().parseFromString(text, "text/html");
|
|
}
|
|
|
|
function domOuterHtml(el) {
|
|
return el ? el.outerHTML : "";
|
|
}
|
|
|
|
function domBodyInnerHtml(doc) {
|
|
return doc && doc.body ? doc.body.innerHTML : "";
|
|
}
|
|
|
|
// --- Events ---
|
|
|
|
function preventDefault_(e) { if (e && e.preventDefault) e.preventDefault(); }
|
|
function elementValue(el) { return el && el.value !== undefined ? el.value : NIL; }
|
|
|
|
function domAddListener(el, event, fn, opts) {
|
|
if (!el || !el.addEventListener) return;
|
|
var o = {};
|
|
if (opts && !isNil(opts)) {
|
|
if (opts.once || opts["once"]) o.once = true;
|
|
}
|
|
el.addEventListener(event, function(e) {
|
|
try { fn(e); } catch (err) { logInfo("EVENT ERROR: " + event + " " + (err && err.message ? err.message : err)); console.error("[sx-ref] event handler error:", event, err); }
|
|
}, o);
|
|
}
|
|
|
|
// --- Validation ---
|
|
|
|
function validateForRequest(el) {
|
|
if (!_hasDom) return true;
|
|
var attr = el.getAttribute("sx-validate");
|
|
if (attr === null) {
|
|
var vForm = el.closest("[sx-validate]");
|
|
if (vForm) attr = vForm.getAttribute("sx-validate");
|
|
}
|
|
if (attr === null) return true; // no validation configured
|
|
var form = el.tagName === "FORM" ? el : el.closest("form");
|
|
if (form && !form.reportValidity()) return false;
|
|
if (attr && attr !== "true" && attr !== "") {
|
|
var fn = window[attr];
|
|
if (typeof fn === "function" && !fn(el)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// --- View Transitions ---
|
|
|
|
function withTransition(enabled, fn) {
|
|
if (enabled && _hasDom && document.startViewTransition) {
|
|
document.startViewTransition(fn);
|
|
} else {
|
|
fn();
|
|
}
|
|
}
|
|
|
|
// --- IntersectionObserver ---
|
|
|
|
function observeIntersection(el, fn, once, delay) {
|
|
if (!_hasDom || !("IntersectionObserver" in window)) { fn(); return; }
|
|
var fired = false;
|
|
var d = isNil(delay) ? 0 : delay;
|
|
var obs = new IntersectionObserver(function(entries) {
|
|
entries.forEach(function(entry) {
|
|
if (!entry.isIntersecting) return;
|
|
if (once && fired) return;
|
|
fired = true;
|
|
if (once) obs.unobserve(el);
|
|
if (d) setTimeout(fn, d); else fn();
|
|
});
|
|
});
|
|
obs.observe(el);
|
|
}
|
|
|
|
// --- EventSource ---
|
|
|
|
function eventSourceConnect(url, el) {
|
|
var source = new EventSource(url);
|
|
source.addEventListener("error", function() { domDispatch(el, "sx:sseError", {}); });
|
|
source.addEventListener("open", function() { domDispatch(el, "sx:sseOpen", {}); });
|
|
if (typeof MutationObserver !== "undefined") {
|
|
var obs = new MutationObserver(function() {
|
|
if (!document.body.contains(el)) { source.close(); obs.disconnect(); }
|
|
});
|
|
obs.observe(document.body, { childList: true, subtree: true });
|
|
}
|
|
return source;
|
|
}
|
|
|
|
function eventSourceListen(source, event, fn) {
|
|
source.addEventListener(event, function(e) { fn(e.data); });
|
|
}
|
|
|
|
// --- Boost bindings ---
|
|
|
|
function bindBoostLink(el, href) {
|
|
el.addEventListener("click", function(e) {
|
|
e.preventDefault();
|
|
executeRequest(el, { method: "GET", url: href }).then(function() {
|
|
try { history.pushState({ sxUrl: href, scrollY: window.scrollY }, "", href); } catch (err) {}
|
|
});
|
|
});
|
|
}
|
|
|
|
function bindBoostForm(form, method, action) {
|
|
form.addEventListener("submit", function(e) {
|
|
e.preventDefault();
|
|
executeRequest(form, { method: method, url: action }).then(function() {
|
|
try { history.pushState({ sxUrl: action, scrollY: window.scrollY }, "", action); } catch (err) {}
|
|
});
|
|
});
|
|
}
|
|
|
|
// --- Client-side route bindings ---
|
|
|
|
function bindClientRouteClick(link, href, fallbackFn) {
|
|
link.addEventListener("click", function(e) {
|
|
e.preventDefault();
|
|
var pathname = urlPathname(href);
|
|
if (tryClientRoute(pathname)) {
|
|
try { history.pushState({ sxUrl: href, scrollY: window.scrollY }, "", href); } catch (err) {}
|
|
if (typeof window !== "undefined") window.scrollTo(0, 0);
|
|
} else {
|
|
logInfo("sx:route server " + pathname);
|
|
executeRequest(link, { method: "GET", url: href }).then(function() {
|
|
try { history.pushState({ sxUrl: href, scrollY: window.scrollY }, "", href); } catch (err) {}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function tryEvalContent(source, env) {
|
|
try {
|
|
var merged = merge(componentEnv);
|
|
if (env && !isNil(env)) {
|
|
var ks = Object.keys(env);
|
|
for (var i = 0; i < ks.length; i++) merged[ks[i]] = env[ks[i]];
|
|
}
|
|
return sxRenderWithEnv(source, merged);
|
|
} catch (e) {
|
|
logInfo("sx:route eval miss: " + (e && e.message ? e.message : e));
|
|
return NIL;
|
|
}
|
|
}
|
|
|
|
function resolvePageData(pageName, params, callback) {
|
|
// Platform implementation: fetch page data via HTTP from /sx/data/ endpoint.
|
|
// The spec only knows about resolve-page-data(name, params, callback) —
|
|
// this function provides the concrete transport.
|
|
var url = "/sx/data/" + encodeURIComponent(pageName);
|
|
if (params && !isNil(params)) {
|
|
var qs = [];
|
|
var ks = Object.keys(params);
|
|
for (var i = 0; i < ks.length; i++) {
|
|
var v = params[ks[i]];
|
|
if (v !== null && v !== undefined && v !== NIL) {
|
|
qs.push(encodeURIComponent(ks[i]) + "=" + encodeURIComponent(v));
|
|
}
|
|
}
|
|
if (qs.length) url += "?" + qs.join("&");
|
|
}
|
|
var headers = { "SX-Request": "true" };
|
|
fetch(url, { headers: headers }).then(function(resp) {
|
|
if (!resp.ok) {
|
|
logWarn("sx:data resolve failed " + resp.status + " for " + pageName);
|
|
return;
|
|
}
|
|
return resp.text().then(function(text) {
|
|
try {
|
|
var exprs = parse(text);
|
|
var data = exprs.length === 1 ? exprs[0] : {};
|
|
callback(data || {});
|
|
} catch (e) {
|
|
logWarn("sx:data parse error for " + pageName + ": " + (e && e.message ? e.message : e));
|
|
}
|
|
});
|
|
}).catch(function(err) {
|
|
logWarn("sx:data resolve error for " + pageName + ": " + (err && err.message ? err.message : err));
|
|
});
|
|
}
|
|
|
|
function urlPathname(href) {
|
|
try {
|
|
return new URL(href, location.href).pathname;
|
|
} catch (e) {
|
|
// Fallback: strip query/hash
|
|
var idx = href.indexOf("?");
|
|
if (idx >= 0) href = href.substring(0, idx);
|
|
idx = href.indexOf("#");
|
|
if (idx >= 0) href = href.substring(0, idx);
|
|
return href;
|
|
}
|
|
}
|
|
|
|
// --- Inline handlers ---
|
|
|
|
function bindInlineHandler(el, eventName, body) {
|
|
el.addEventListener(eventName, new Function("event", body));
|
|
}
|
|
|
|
// --- Preload binding ---
|
|
|
|
function bindPreload(el, events, debounceMs, fn) {
|
|
var timer = null;
|
|
events.forEach(function(evt) {
|
|
el.addEventListener(evt, function() {
|
|
if (debounceMs) {
|
|
clearTimeout(timer);
|
|
timer = setTimeout(fn, debounceMs);
|
|
} else {
|
|
fn();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// --- Processing markers ---
|
|
|
|
var PROCESSED = "_sxBound";
|
|
|
|
function markProcessed(el, key) { el[PROCESSED + key] = true; }
|
|
function isProcessed(el, key) { return !!el[PROCESSED + key]; }
|
|
|
|
// --- Script cloning ---
|
|
|
|
function createScriptClone(dead) {
|
|
var live = document.createElement("script");
|
|
for (var i = 0; i < dead.attributes.length; i++)
|
|
live.setAttribute(dead.attributes[i].name, dead.attributes[i].value);
|
|
live.textContent = dead.textContent;
|
|
return live;
|
|
}
|
|
|
|
// --- SX API references ---
|
|
|
|
function sxRender(source) {
|
|
var SxObj = typeof Sx !== "undefined" ? Sx : null;
|
|
if (SxObj && SxObj.render) return SxObj.render(source);
|
|
throw new Error("No SX renderer available");
|
|
}
|
|
|
|
function sxProcessScripts(root) {
|
|
var SxObj = typeof Sx !== "undefined" ? Sx : null;
|
|
var r = (root && root !== NIL) ? root : undefined;
|
|
if (SxObj && SxObj.processScripts) SxObj.processScripts(r);
|
|
}
|
|
|
|
function sxHydrate(root) {
|
|
var SxObj = typeof Sx !== "undefined" ? Sx : null;
|
|
var r = (root && root !== NIL) ? root : undefined;
|
|
if (SxObj && SxObj.hydrate) SxObj.hydrate(r);
|
|
}
|
|
|
|
function loadedComponentNames() {
|
|
var SxObj = typeof Sx !== "undefined" ? Sx : null;
|
|
if (!SxObj) return [];
|
|
var env = SxObj.componentEnv || (SxObj.getEnv ? SxObj.getEnv() : {});
|
|
return Object.keys(env).filter(function(k) { return k.charAt(0) === "~"; });
|
|
}
|
|
|
|
// --- Response processing ---
|
|
|
|
function stripComponentScripts(text) {
|
|
var SxObj = typeof Sx !== "undefined" ? Sx : null;
|
|
return text.replace(/<script[^>]*type="text\/sx"[^>]*data-components[^>]*>([\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(/<style[^>]*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 — 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 = [];
|
|
// Child-selector atoms are now routed to pseudoRules by the resolver
|
|
// with selector ">:not(:first-child)", so base declarations are always
|
|
// applied directly to the class.
|
|
if (sv.declarations) {
|
|
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 [];
|
|
var r = (root && root !== NIL) ? root : document;
|
|
return Array.prototype.slice.call(
|
|
r.querySelectorAll('script[type="text/sx"]'));
|
|
}
|
|
|
|
function queryStyleScripts() {
|
|
if (!_hasDom) return [];
|
|
return Array.prototype.slice.call(
|
|
document.querySelectorAll('script[type="text/sx-styles"]'));
|
|
}
|
|
|
|
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";
|
|
}
|
|
|
|
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 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);
|
|
}
|
|
}
|
|
|
|
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
|
|
// =========================================================================
|
|
// 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;
|
|
|
|
// 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,
|
|
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; },
|
|
init: typeof bootInit === "function" ? bootInit : null,
|
|
splitPathSegments: splitPathSegments,
|
|
parseRoutePattern: parseRoutePattern,
|
|
matchRoute: matchRoute,
|
|
findMatchingRoute: findMatchingRoute,
|
|
_version: "ref-2.0 (boot+cssx+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(); };
|
|
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); |