;; _hyperscript runtime shims ;; ;; Thin wrappers over web/lib/dom.sx and web/lib/browser.sx primitives ;; that implement hyperscript-specific semantics (async transparency, ;; class toggling, event waiting, iteration, type coercion). ;; ;; These are the functions that hs-to-sx (compiler.sx) emits calls to. ;; Each is a pure define — no platform dependency beyond the DOM/browser ;; primitives already available in the SX web platform. ;; ── Event handling ────────────────────────────────────────────── ;; Register an event listener. Returns unlisten function. ;; (hs-on target event-name handler) → unlisten-fn (define hs-on (fn (target event-name handler) (dom-listen target event-name handler))) ;; Register for every occurrence (no queuing — each fires independently). ;; Stock hyperscript queues by default; "every" disables queuing. (define hs-on-every (fn (target event-name handler) (dom-listen target event-name handler))) ;; Run an initializer function immediately. ;; (hs-init thunk) — called at element boot time (define hs-init (fn (thunk) (thunk))) ;; ── Async / timing ────────────────────────────────────────────── ;; Wait for a duration in milliseconds. ;; In hyperscript, wait is async-transparent — execution pauses. ;; Here we use perform/IO suspension for true pause semantics. (define hs-wait (fn (ms) (perform (list (quote io-sleep) ms)))) ;; Wait for a DOM event on a target. ;; (hs-wait-for target event-name) — suspends until event fires (define hs-wait-for (fn (target event-name) (perform (list (quote io-wait-event) target event-name)))) ;; Wait for CSS transitions/animations to settle on an element. (define hs-settle (fn (target) (perform (list (quote io-settle) target)))) ;; ── Class manipulation ────────────────────────────────────────── ;; Toggle a single class on an element. (define hs-toggle-class! (fn (target cls) (if (dom-has-class? target cls) (dom-remove-class target cls) (dom-add-class target cls)))) ;; Toggle between two classes — exactly one is active at a time. (define hs-toggle-between! (fn (target cls1 cls2) (if (dom-has-class? target cls1) (do (dom-remove-class target cls1) (dom-add-class target cls2)) (do (dom-remove-class target cls2) (dom-add-class target cls1))))) ;; Take a class from siblings — add to target, remove from others. ;; (hs-take! target cls) — like radio button class behavior (define hs-take! (fn (target cls) (let ((parent (dom-parent target))) (when parent (for-each (fn (child) (dom-remove-class child cls)) (dom-child-list parent))) (dom-add-class target cls)))) ;; ── DOM insertion ─────────────────────────────────────────────── ;; Put content at a position relative to a target. ;; pos: "into" | "before" | "after" (define hs-put! (fn (value pos target) (cond ((= pos "into") (dom-set-inner-html target value)) ((= pos "before") (dom-insert-adjacent-html target "beforebegin" value)) ((= pos "after") (dom-insert-adjacent-html target "afterend" value))))) ;; ── Navigation / traversal ────────────────────────────────────── ;; Navigate to a URL. (define hs-navigate! (fn (url) (perform (list (quote io-navigate) url)))) ;; Find next sibling matching a selector (or any sibling). (define hs-next (fn (target sel) (if (= sel "*") (dom-next-sibling target) (let ((sibling (dom-next-sibling target))) (define find-next (fn (el) (cond ((nil? el) nil) ((dom-matches? el sel) el) (true (find-next (dom-next-sibling el)))))) (find-next sibling))))) ;; Find previous sibling matching a selector. (define hs-previous (fn (target sel) (if (= sel "*") (dom-get-prop target "previousElementSibling") (let ((sibling (dom-get-prop target "previousElementSibling"))) (define find-prev (fn (el) (cond ((nil? el) nil) ((dom-matches? el sel) el) (true (find-prev (dom-get-prop el "previousElementSibling")))))) (find-prev sibling))))) ;; First element matching selector within a scope. (define hs-query-first (fn (sel) (dom-query sel))) ;; Last element matching selector. (define hs-query-last (fn (sel) (let ((all (dom-query-all (dom-body) sel))) (if (> (len all) 0) (nth all (- (len all) 1)) nil)))) ;; First/last within a specific scope. (define hs-first (fn (scope sel) (dom-query-all scope sel))) (define hs-last (fn (scope sel) (let ((all (dom-query-all scope sel))) (if (> (len all) 0) (nth all (- (len all) 1)) nil)))) ;; ── Iteration ─────────────────────────────────────────────────── ;; Repeat a thunk N times. (define hs-repeat-times (fn (n thunk) (define do-repeat (fn (i) (when (< i n) (thunk) (do-repeat (+ i 1))))) (do-repeat 0))) ;; Repeat forever (until break — relies on exception/continuation). (define hs-repeat-forever (fn (thunk) (define do-forever (fn () (thunk) (do-forever))) (do-forever))) ;; ── Fetch ─────────────────────────────────────────────────────── ;; Fetch a URL, parse response according to format. ;; (hs-fetch url format) — format is "json" | "text" | "html" (define hs-fetch (fn (url format) (let ((response (perform (list (quote io-fetch) url)))) (cond ((= format "json") (perform (list (quote io-parse-json) response))) ((= format "text") (perform (list (quote io-parse-text) response))) ((= format "html") (perform (list (quote io-parse-html) response))) (true response))))) ;; ── Type coercion ─────────────────────────────────────────────── ;; Coerce a value to a type by name. ;; (hs-coerce value type-name) — type-name is "Int", "Float", "String", etc. (define hs-coerce (fn (value type-name) (cond ((= type-name "Int") (+ value 0)) ((= type-name "Integer") (+ value 0)) ((= type-name "Float") (+ value 0)) ((= type-name "Number") (+ value 0)) ((= type-name "String") (str value)) ((= type-name "Boolean") (if value true false)) ((= type-name "Array") (if (list? value) value (list value))) (true value)))) ;; ── Object creation ───────────────────────────────────────────── ;; Make a new object of a given type. ;; (hs-make type-name) — creates empty object/collection (define hs-make (fn (type-name) (cond ((= type-name "Object") (dict)) ((= type-name "Array") (list)) ((= type-name "Set") (list)) ((= type-name "Map") (dict)) (true (dict))))) ;; ── Behavior installation ─────────────────────────────────────── ;; Install a behavior on an element. ;; A behavior is a function that takes (me ...params) and sets up features. ;; (hs-install behavior-fn me ...args) (define hs-install (fn (behavior-fn) (behavior-fn me))) ;; ── Measurement ───────────────────────────────────────────────── ;; Measure an element's bounding rect, store as local variables. ;; Returns a dict with x, y, width, height, top, left, right, bottom. (define hs-measure (fn (target) (perform (list (quote io-measure) target)))) ;; ── Transition ────────────────────────────────────────────────── ;; Transition a CSS property to a value, optionally with duration. ;; (hs-transition target prop value duration) (define hs-transition (fn (target prop value duration) (when duration (dom-set-style target "transition" (str prop " " (/ duration 1000) "s"))) (dom-set-style target prop value) (when duration (hs-settle target))))