diff --git a/hosts/javascript/platform.py b/hosts/javascript/platform.py index 92fb08d..2605f52 100644 --- a/hosts/javascript/platform.py +++ b/hosts/javascript/platform.py @@ -1175,17 +1175,6 @@ PRIMITIVES_JS_MODULES: dict[str, str] = { lst[idx] = val; return NIL; }; - // Cookie access — isomorphic state persistence - PRIMITIVES["set-cookie"] = function(name, value, days) { - var d = days || 365; - var expires = new Date(Date.now() + d * 864e5).toUTCString(); - document.cookie = name + "=" + encodeURIComponent(value) + ";expires=" + expires + ";path=/;SameSite=Lax"; - return NIL; - }; - PRIMITIVES["get-cookie"] = function(name) { - var m = document.cookie.match(new RegExp("(?:^|;\\\\s*)" + name + "=([^;]*)")); - return m ? decodeURIComponent(m[1]) : NIL; - }; PRIMITIVES["env-parent"] = function(env) { if (env && Object.getPrototypeOf(env) !== Object.prototype && @@ -1734,20 +1723,6 @@ CEK_FIXUPS_JS = ''' PRIMITIVES["is-html-tag?"] = function(n) { return HTML_TAGS.indexOf(n) >= 0; }; function makeEnv() { return merge(componentEnv, PRIMITIVES); } PRIMITIVES["make-env"] = makeEnv; - - // localStorage — defined here (before boot) so islands can use at hydration - PRIMITIVES["local-storage-get"] = function(key) { - try { var v = localStorage.getItem(key); return v === null ? NIL : v; } - catch (e) { return NIL; } - }; - PRIMITIVES["local-storage-set"] = function(key, val) { - try { localStorage.setItem(key, val); } catch (e) {} - return NIL; - }; - PRIMITIVES["local-storage-remove"] = function(key) { - try { localStorage.removeItem(key); } catch (e) {} - return NIL; - }; ''' @@ -3223,127 +3198,18 @@ def fixups_js(has_html, has_sx, has_dom, has_signals=False, has_deps=False, has_ return _rawCallLambda(f, args, callerEnv); }; - // Expose render functions as primitives so SX code can call them'''] - if has_html: - lines.append(' if (typeof renderToHtml === "function") PRIMITIVES["render-to-html"] = renderToHtml;') - if has_sx: - lines.append(' if (typeof renderToSx === "function") PRIMITIVES["render-to-sx"] = renderToSx;') - lines.append(' if (typeof aser === "function") PRIMITIVES["aser"] = aser;') - if has_dom: - lines.append(' if (typeof renderToDom === "function") PRIMITIVES["render-to-dom"] = renderToDom;') - if has_signals: - lines.append(''' - // Expose signal functions as primitives so runtime-evaluated SX code - // (e.g. island bodies from .sx files) can call them - PRIMITIVES["signal"] = signal; - PRIMITIVES["signal?"] = isSignal; - PRIMITIVES["deref"] = deref; - PRIMITIVES["reset!"] = reset_b; - PRIMITIVES["swap!"] = swap_b; - PRIMITIVES["computed"] = computed; - PRIMITIVES["effect"] = effect; - PRIMITIVES["batch"] = batch; - // Timer primitives for island code - PRIMITIVES["set-interval"] = setInterval_; - PRIMITIVES["clear-interval"] = clearInterval_; - // Reactive DOM helpers for island code - PRIMITIVES["reactive-text"] = reactiveText; - PRIMITIVES["create-text-node"] = createTextNode; - PRIMITIVES["dom-set-text-content"] = domSetTextContent; - PRIMITIVES["dom-listen"] = domListen; - PRIMITIVES["dom-dispatch"] = domDispatch; - PRIMITIVES["event-detail"] = eventDetail; - PRIMITIVES["resource"] = resource; - PRIMITIVES["promise-delayed"] = promiseDelayed; - PRIMITIVES["promise-then"] = promiseThen; - PRIMITIVES["def-store"] = defStore; - PRIMITIVES["use-store"] = useStore; - PRIMITIVES["emit-event"] = emitEvent; - PRIMITIVES["on-event"] = onEvent; - PRIMITIVES["bridge-event"] = bridgeEvent; - // DOM primitives for island code - PRIMITIVES["dom-focus"] = domFocus; - PRIMITIVES["dom-tag-name"] = domTagName; - PRIMITIVES["dom-get-prop"] = domGetProp; - PRIMITIVES["dom-set-prop"] = domSetProp; - PRIMITIVES["dom-call-method"] = domCallMethod; - PRIMITIVES["dom-post-message"] = domPostMessage; - PRIMITIVES["stop-propagation"] = stopPropagation_; - PRIMITIVES["error-message"] = errorMessage; - PRIMITIVES["schedule-idle"] = scheduleIdle; - PRIMITIVES["error"] = function(msg) { throw new Error(msg); }; - PRIMITIVES["filter"] = filter; - // DOM primitives for sx-on:* handlers and data-init scripts - if (typeof domBody === "function") PRIMITIVES["dom-body"] = domBody; - if (typeof domQuery === "function") PRIMITIVES["dom-query"] = domQuery; - if (typeof domQueryAll === "function") PRIMITIVES["dom-query-all"] = domQueryAll; - if (typeof domQueryById === "function") PRIMITIVES["dom-query-by-id"] = domQueryById; - if (typeof domSetAttr === "function") PRIMITIVES["dom-set-attr"] = domSetAttr; - if (typeof domGetAttr === "function") PRIMITIVES["dom-get-attr"] = domGetAttr; - if (typeof domRemoveAttr === "function") PRIMITIVES["dom-remove-attr"] = domRemoveAttr; - if (typeof domHasAttr === "function") PRIMITIVES["dom-has-attr?"] = domHasAttr; - if (typeof domAddClass === "function") PRIMITIVES["dom-add-class"] = domAddClass; - if (typeof domRemoveClass === "function") PRIMITIVES["dom-remove-class"] = domRemoveClass; - if (typeof domHasClass === "function") PRIMITIVES["dom-has-class?"] = domHasClass; - if (typeof domClosest === "function") PRIMITIVES["dom-closest"] = domClosest; - if (typeof domMatches === "function") PRIMITIVES["dom-matches?"] = domMatches; - if (typeof preventDefault_ === "function") PRIMITIVES["prevent-default"] = preventDefault_; - if (typeof eventModifierKey_p === "function") PRIMITIVES["event-modifier-key?"] = eventModifierKey_p; - if (typeof elementValue === "function") PRIMITIVES["element-value"] = elementValue; - if (typeof domOuterHtml === "function") PRIMITIVES["dom-outer-html"] = domOuterHtml; - if (typeof domInnerHtml === "function") PRIMITIVES["dom-inner-html"] = domInnerHtml; - if (typeof domTextContent === "function") PRIMITIVES["dom-text-content"] = domTextContent; - if (typeof domCreateElement === "function") PRIMITIVES["dom-create-element"] = domCreateElement; - if (typeof domAppend === "function") PRIMITIVES["dom-append"] = domAppend; - if (typeof domAppendToHead === "function") PRIMITIVES["dom-append-to-head"] = domAppendToHead; - if (typeof jsonParse === "function") PRIMITIVES["json-parse"] = jsonParse; - if (typeof nowMs === "function") PRIMITIVES["now-ms"] = nowMs; - PRIMITIVES["sx-parse"] = sxParse; - PRIMITIVES["console-log"] = function() { console.log.apply(console, ["[sx]"].concat(Array.prototype.slice.call(arguments))); return arguments.length > 0 ? arguments[0] : NIL; };''') + // ----------------------------------------------------------------------- + // Core primitives that require native JS (cannot be expressed via FFI) + // ----------------------------------------------------------------------- + PRIMITIVES["error"] = function(msg) { throw new Error(msg); };'''] if has_deps: lines.append(''' - // Expose deps module functions as primitives so runtime-evaluated SX code - // (e.g. test-deps.sx in browser) can call them - // Platform functions (from PLATFORM_DEPS_JS) + // Platform deps functions (native JS, not transpiled — need explicit registration) PRIMITIVES["component-deps"] = componentDeps; PRIMITIVES["component-set-deps!"] = componentSetDeps; PRIMITIVES["component-css-classes"] = componentCssClasses; - PRIMITIVES["env-components"] = envComponents; PRIMITIVES["regex-find-all"] = regexFindAll; - PRIMITIVES["scan-css-classes"] = scanCssClasses; - // Transpiled functions (from deps.sx) - PRIMITIVES["scan-refs"] = scanRefs; - PRIMITIVES["scan-refs-walk"] = scanRefsWalk; - PRIMITIVES["transitive-deps"] = transitiveDeps; - PRIMITIVES["transitive-deps-walk"] = transitiveDepsWalk; - PRIMITIVES["compute-all-deps"] = computeAllDeps; - PRIMITIVES["scan-components-from-source"] = scanComponentsFromSource; - PRIMITIVES["components-needed"] = componentsNeeded; - PRIMITIVES["page-component-bundle"] = pageComponentBundle; - PRIMITIVES["page-css-classes"] = pageCssClasses; - PRIMITIVES["scan-io-refs-walk"] = scanIoRefsWalk; - PRIMITIVES["scan-io-refs"] = scanIoRefs; - PRIMITIVES["transitive-io-refs-walk"] = transitiveIoRefsWalk; - PRIMITIVES["transitive-io-refs"] = transitiveIoRefs; - PRIMITIVES["compute-all-io-refs"] = computeAllIoRefs; - PRIMITIVES["component-io-refs-cached"] = componentIoRefsCached; - PRIMITIVES["component-pure?"] = componentPure_p; - PRIMITIVES["render-target"] = renderTarget; - PRIMITIVES["page-render-plan"] = pageRenderPlan;''') - if has_page_helpers: - lines.append(''' - // Expose page-helper functions as primitives - PRIMITIVES["categorize-special-forms"] = categorizeSpecialForms; - PRIMITIVES["extract-define-kwargs"] = extractDefineKwargs; - PRIMITIVES["build-reference-data"] = buildReferenceData; - PRIMITIVES["build-ref-items-with-href"] = buildRefItemsWithHref; - PRIMITIVES["build-attr-detail"] = buildAttrDetail; - PRIMITIVES["build-header-detail"] = buildHeaderDetail; - PRIMITIVES["build-event-detail"] = buildEventDetail; - PRIMITIVES["build-component-source"] = buildComponentSource; - PRIMITIVES["build-bundle-analysis"] = buildBundleAnalysis; - PRIMITIVES["build-routing-analysis"] = buildRoutingAnalysis; - PRIMITIVES["build-affinity-analysis"] = buildAffinityAnalysis;''') + PRIMITIVES["scan-css-classes"] = scanCssClasses;''') return "\n".join(lines) diff --git a/hosts/javascript/transpiler.sx b/hosts/javascript/transpiler.sx index 0fc612e..d426810 100644 --- a/hosts/javascript/transpiler.sx +++ b/hosts/javascript/transpiler.sx @@ -624,6 +624,7 @@ "scope-push!" "scopePush" "scope-pop!" "scopePop" "scope-emit!" "scopeEmit" + "scope-emitted" "sxEmitted" "scope-peek" "scopePeek" "provide-push!" "providePush" "provide-pop!" "providePop" diff --git a/shared/static/scripts/sx-browser.js b/shared/static/scripts/sx-browser.js index 1f6aeb0..c7b0c2b 100644 --- a/shared/static/scripts/sx-browser.js +++ b/shared/static/scripts/sx-browser.js @@ -14,7 +14,7 @@ // ========================================================================= var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } }); - var SX_VERSION = "2026-03-24T10:16:21Z"; + var SX_VERSION = "2026-03-24T11:14:33Z"; function isNil(x) { return x === NIL || x === null || x === undefined; } function isSxTruthy(x) { return x !== false && !isNil(x); } @@ -555,17 +555,6 @@ lst[idx] = val; return NIL; }; - // Cookie access — isomorphic state persistence - PRIMITIVES["set-cookie"] = function(name, value, days) { - var d = days || 365; - var expires = new Date(Date.now() + d * 864e5).toUTCString(); - document.cookie = name + "=" + encodeURIComponent(value) + ";expires=" + expires + ";path=/;SameSite=Lax"; - return NIL; - }; - PRIMITIVES["get-cookie"] = function(name) { - var m = document.cookie.match(new RegExp("(?:^|;\\s*)" + name + "=([^;]*)")); - return m ? decodeURIComponent(m[1]) : NIL; - }; PRIMITIVES["env-parent"] = function(env) { if (env && Object.getPrototypeOf(env) !== Object.prototype && @@ -2648,7 +2637,7 @@ PRIMITIVES["render-html-component"] = renderHtmlComponent; var isVoid = contains(VOID_ELEMENTS, tag); return (isSxTruthy(isVoid) ? (String("<") + String(tag) + String(renderAttrs(attrs)) + String(" />")) : (scopePush("element-attrs", NIL), (function() { var content = join("", map(function(c) { return renderToHtml(c, env); }, children)); - { var _c = scopeEmitted("element-attrs"); for (var _i = 0; _i < _c.length; _i++) { var spreadDict = _c[_i]; mergeSpreadAttrs(attrs, spreadDict); } } + { var _c = sxEmitted("element-attrs"); for (var _i = 0; _i < _c.length; _i++) { var spreadDict = _c[_i]; mergeSpreadAttrs(attrs, spreadDict); } } scopePop("element-attrs"); return (String("<") + String(tag) + String(renderAttrs(attrs)) + String(">") + String(content) + String("")); })())); @@ -2674,7 +2663,7 @@ PRIMITIVES["render-html-element"] = renderHtmlElement; scopePush("element-attrs", NIL); return (function() { var content = join("", map(function(c) { return renderToHtml(c, env); }, children)); - { var _c = scopeEmitted("element-attrs"); for (var _i = 0; _i < _c.length; _i++) { var spreadDict = _c[_i]; mergeSpreadAttrs(lakeAttrs, spreadDict); } } + { var _c = sxEmitted("element-attrs"); for (var _i = 0; _i < _c.length; _i++) { var spreadDict = _c[_i]; mergeSpreadAttrs(lakeAttrs, spreadDict); } } scopePop("element-attrs"); return (String("<") + String(lakeTag) + String(renderAttrs(lakeAttrs)) + String(">") + String(content) + String("")); })(); @@ -2701,7 +2690,7 @@ PRIMITIVES["render-html-lake"] = renderHtmlLake; scopePush("element-attrs", NIL); return (function() { var content = join("", map(function(c) { return renderToHtml(c, env); }, children)); - { var _c = scopeEmitted("element-attrs"); for (var _i = 0; _i < _c.length; _i++) { var spreadDict = _c[_i]; mergeSpreadAttrs(marshAttrs, spreadDict); } } + { var _c = sxEmitted("element-attrs"); for (var _i = 0; _i < _c.length; _i++) { var spreadDict = _c[_i]; mergeSpreadAttrs(marshAttrs, spreadDict); } } scopePop("element-attrs"); return (String("<") + String(marshTag) + String(renderAttrs(marshAttrs)) + String(">") + String(content) + String("")); })(); @@ -5823,121 +5812,17 @@ PRIMITIVES["resource"] = resource; return _rawCallLambda(f, args, callerEnv); }; - // Expose render functions as primitives so SX code can call them - if (typeof renderToHtml === "function") PRIMITIVES["render-to-html"] = renderToHtml; - if (typeof renderToSx === "function") PRIMITIVES["render-to-sx"] = renderToSx; - if (typeof aser === "function") PRIMITIVES["aser"] = aser; - if (typeof renderToDom === "function") PRIMITIVES["render-to-dom"] = renderToDom; - - // Expose signal functions as primitives so runtime-evaluated SX code - // (e.g. island bodies from .sx files) can call them - PRIMITIVES["signal"] = signal; - PRIMITIVES["signal?"] = isSignal; - PRIMITIVES["deref"] = deref; - PRIMITIVES["reset!"] = reset_b; - PRIMITIVES["swap!"] = swap_b; - PRIMITIVES["computed"] = computed; - PRIMITIVES["effect"] = effect; - PRIMITIVES["batch"] = batch; - // Timer primitives for island code - PRIMITIVES["set-interval"] = setInterval_; - PRIMITIVES["clear-interval"] = clearInterval_; - // Reactive DOM helpers for island code - PRIMITIVES["reactive-text"] = reactiveText; - PRIMITIVES["create-text-node"] = createTextNode; - PRIMITIVES["dom-set-text-content"] = domSetTextContent; - PRIMITIVES["dom-listen"] = domListen; - PRIMITIVES["dom-dispatch"] = domDispatch; - PRIMITIVES["event-detail"] = eventDetail; - PRIMITIVES["resource"] = resource; - PRIMITIVES["promise-delayed"] = promiseDelayed; - PRIMITIVES["promise-then"] = promiseThen; - PRIMITIVES["def-store"] = defStore; - PRIMITIVES["use-store"] = useStore; - PRIMITIVES["emit-event"] = emitEvent; - PRIMITIVES["on-event"] = onEvent; - PRIMITIVES["bridge-event"] = bridgeEvent; - // DOM primitives for island code - PRIMITIVES["dom-focus"] = domFocus; - PRIMITIVES["dom-tag-name"] = domTagName; - PRIMITIVES["dom-get-prop"] = domGetProp; - PRIMITIVES["dom-set-prop"] = domSetProp; - PRIMITIVES["dom-call-method"] = domCallMethod; - PRIMITIVES["dom-post-message"] = domPostMessage; - PRIMITIVES["stop-propagation"] = stopPropagation_; - PRIMITIVES["error-message"] = errorMessage; - PRIMITIVES["schedule-idle"] = scheduleIdle; + // ----------------------------------------------------------------------- + // Core primitives that require native JS (cannot be expressed via FFI) + // ----------------------------------------------------------------------- PRIMITIVES["error"] = function(msg) { throw new Error(msg); }; - PRIMITIVES["filter"] = filter; - // DOM primitives for sx-on:* handlers and data-init scripts - if (typeof domBody === "function") PRIMITIVES["dom-body"] = domBody; - if (typeof domQuery === "function") PRIMITIVES["dom-query"] = domQuery; - if (typeof domQueryAll === "function") PRIMITIVES["dom-query-all"] = domQueryAll; - if (typeof domQueryById === "function") PRIMITIVES["dom-query-by-id"] = domQueryById; - if (typeof domSetAttr === "function") PRIMITIVES["dom-set-attr"] = domSetAttr; - if (typeof domGetAttr === "function") PRIMITIVES["dom-get-attr"] = domGetAttr; - if (typeof domRemoveAttr === "function") PRIMITIVES["dom-remove-attr"] = domRemoveAttr; - if (typeof domHasAttr === "function") PRIMITIVES["dom-has-attr?"] = domHasAttr; - if (typeof domAddClass === "function") PRIMITIVES["dom-add-class"] = domAddClass; - if (typeof domRemoveClass === "function") PRIMITIVES["dom-remove-class"] = domRemoveClass; - if (typeof domHasClass === "function") PRIMITIVES["dom-has-class?"] = domHasClass; - if (typeof domClosest === "function") PRIMITIVES["dom-closest"] = domClosest; - if (typeof domMatches === "function") PRIMITIVES["dom-matches?"] = domMatches; - if (typeof preventDefault_ === "function") PRIMITIVES["prevent-default"] = preventDefault_; - if (typeof eventModifierKey_p === "function") PRIMITIVES["event-modifier-key?"] = eventModifierKey_p; - if (typeof elementValue === "function") PRIMITIVES["element-value"] = elementValue; - if (typeof domOuterHtml === "function") PRIMITIVES["dom-outer-html"] = domOuterHtml; - if (typeof domInnerHtml === "function") PRIMITIVES["dom-inner-html"] = domInnerHtml; - if (typeof domTextContent === "function") PRIMITIVES["dom-text-content"] = domTextContent; - if (typeof domCreateElement === "function") PRIMITIVES["dom-create-element"] = domCreateElement; - if (typeof domAppend === "function") PRIMITIVES["dom-append"] = domAppend; - if (typeof domAppendToHead === "function") PRIMITIVES["dom-append-to-head"] = domAppendToHead; - if (typeof jsonParse === "function") PRIMITIVES["json-parse"] = jsonParse; - if (typeof nowMs === "function") PRIMITIVES["now-ms"] = nowMs; - PRIMITIVES["sx-parse"] = sxParse; - PRIMITIVES["console-log"] = function() { console.log.apply(console, ["[sx]"].concat(Array.prototype.slice.call(arguments))); return arguments.length > 0 ? arguments[0] : NIL; }; - // Expose deps module functions as primitives so runtime-evaluated SX code - // (e.g. test-deps.sx in browser) can call them - // Platform functions (from PLATFORM_DEPS_JS) + // Platform deps functions (native JS, not transpiled — need explicit registration) PRIMITIVES["component-deps"] = componentDeps; PRIMITIVES["component-set-deps!"] = componentSetDeps; PRIMITIVES["component-css-classes"] = componentCssClasses; - PRIMITIVES["env-components"] = envComponents; PRIMITIVES["regex-find-all"] = regexFindAll; PRIMITIVES["scan-css-classes"] = scanCssClasses; - // Transpiled functions (from deps.sx) - PRIMITIVES["scan-refs"] = scanRefs; - PRIMITIVES["scan-refs-walk"] = scanRefsWalk; - PRIMITIVES["transitive-deps"] = transitiveDeps; - PRIMITIVES["transitive-deps-walk"] = transitiveDepsWalk; - PRIMITIVES["compute-all-deps"] = computeAllDeps; - PRIMITIVES["scan-components-from-source"] = scanComponentsFromSource; - PRIMITIVES["components-needed"] = componentsNeeded; - PRIMITIVES["page-component-bundle"] = pageComponentBundle; - PRIMITIVES["page-css-classes"] = pageCssClasses; - PRIMITIVES["scan-io-refs-walk"] = scanIoRefsWalk; - PRIMITIVES["scan-io-refs"] = scanIoRefs; - PRIMITIVES["transitive-io-refs-walk"] = transitiveIoRefsWalk; - PRIMITIVES["transitive-io-refs"] = transitiveIoRefs; - PRIMITIVES["compute-all-io-refs"] = computeAllIoRefs; - PRIMITIVES["component-io-refs-cached"] = componentIoRefsCached; - PRIMITIVES["component-pure?"] = componentPure_p; - PRIMITIVES["render-target"] = renderTarget; - PRIMITIVES["page-render-plan"] = pageRenderPlan; - - // Expose page-helper functions as primitives - PRIMITIVES["categorize-special-forms"] = categorizeSpecialForms; - PRIMITIVES["extract-define-kwargs"] = extractDefineKwargs; - PRIMITIVES["build-reference-data"] = buildReferenceData; - PRIMITIVES["build-ref-items-with-href"] = buildRefItemsWithHref; - PRIMITIVES["build-attr-detail"] = buildAttrDetail; - PRIMITIVES["build-header-detail"] = buildHeaderDetail; - PRIMITIVES["build-event-detail"] = buildEventDetail; - PRIMITIVES["build-component-source"] = buildComponentSource; - PRIMITIVES["build-bundle-analysis"] = buildBundleAnalysis; - PRIMITIVES["build-routing-analysis"] = buildRoutingAnalysis; - PRIMITIVES["build-affinity-analysis"] = buildAffinityAnalysis; // Override recursive cekRun with iterative loop (avoids stack overflow) cekRun = function(state) { @@ -5974,20 +5859,6 @@ PRIMITIVES["resource"] = resource; function makeEnv() { return merge(componentEnv, PRIMITIVES); } PRIMITIVES["make-env"] = makeEnv; - // localStorage — defined here (before boot) so islands can use at hydration - PRIMITIVES["local-storage-get"] = function(key) { - try { var v = localStorage.getItem(key); return v === null ? NIL : v; } - catch (e) { return NIL; } - }; - PRIMITIVES["local-storage-set"] = function(key, val) { - try { localStorage.setItem(key, val); } catch (e) {} - return NIL; - }; - PRIMITIVES["local-storage-remove"] = function(key) { - try { localStorage.removeItem(key); } catch (e) {} - return NIL; - }; - // === stdlib.sx (eval'd at runtime, not transpiled) === (function() { diff --git a/web/lib/browser.sx b/web/lib/browser.sx index f2ee4fb..94d17ab 100644 --- a/web/lib/browser.sx +++ b/web/lib/browser.sx @@ -169,3 +169,43 @@ (define now-ms (fn () (host-call (host-global "Date") "now"))) + + +;; -------------------------------------------------------------------------- +;; Scheduling +;; -------------------------------------------------------------------------- + +(define schedule-idle + (fn (f) + (let ((cb (host-callback f))) + (if (host-get (dom-window) "requestIdleCallback") + (host-call (dom-window) "requestIdleCallback" cb) + (set-timeout cb 0))))) + + +;; -------------------------------------------------------------------------- +;; Cookies +;; -------------------------------------------------------------------------- + +(define set-cookie + (fn (name value days) + (let ((d (or days 365)) + (expires (host-call + (host-new "Date" + (+ (host-call (host-global "Date") "now") + (* d 864e5))) + "toUTCString"))) + (host-set! (dom-document) "cookie" + (str name "=" + (host-call nil "encodeURIComponent" value) + ";expires=" expires ";path=/;SameSite=Lax"))))) + +(define get-cookie + (fn (name) + (let ((cookies (host-get (dom-document) "cookie")) + (match (host-call cookies "match" + (host-new "RegExp" + (str "(?:^|;\\s*)" name "=([^;]*)"))))) + (if match + (host-call nil "decodeURIComponent" (host-get match 1)) + nil)))) diff --git a/web/lib/dom.sx b/web/lib/dom.sx index 427a2f7..10a7e65 100644 --- a/web/lib/dom.sx +++ b/web/lib/dom.sx @@ -263,6 +263,29 @@ (define event-detail (fn (evt) (host-get evt "detail"))) +(define prevent-default + (fn (e) (when e (host-call e "preventDefault")))) + +(define stop-propagation + (fn (e) (when e (host-call e "stopPropagation")))) + +(define event-modifier-key? + (fn (e) + (and e (or (host-get e "ctrlKey") (host-get e "metaKey") + (host-get e "shiftKey") (host-get e "altKey"))))) + +(define element-value + (fn (el) + (if (and el (not (nil? (host-get el "value")))) + (host-get el "value") + nil))) + +(define error-message + (fn (e) + (if (and e (host-get e "message")) + (host-get e "message") + (str e)))) + ;; -------------------------------------------------------------------------- ;; DOM data storage