diff --git a/dist/_hyperscript-max.js b/dist/_hyperscript-max.js index f79cb8071..5f3000b32 100644 --- a/dist/_hyperscript-max.js +++ b/dist/_hyperscript-max.js @@ -11530,14 +11530,14 @@ if (document.querySelector("style[data-hs-debugger-font]")) return; var style = document.createElement("style"); style.setAttribute("data-hs-debugger-font", ""); - style.textContent = '@font-face { font-family: "ChicagoFLF"; src: url("https://hyperscript.org/fonts/ChicagoFLF.woff") format("woff"); font-display: swap; }.hs-dbg-bp-glyph{background:#c03030;border-radius:50%;width:10px!important;height:10px!important;margin-top:4px;margin-left:4px}.hs-dbg-bp-line{background:rgba(192,48,48,.1)}.hs-dbg-current-line{background:rgba(255,210,60,.6)!important}.hs-dbg-current-glyph{background:#ffc850;border-radius:2px;width:10px!important;height:10px!important;margin-top:4px;margin-left:4px}'; + style.textContent = ".hs-dbg-bp-glyph{background:#c03030;border-radius:50%;width:10px!important;height:10px!important;margin-top:4px;margin-left:4px}.hs-dbg-bp-line{background:rgba(192,48,48,.1)}.hs-dbg-current-line{background:rgba(255,210,60,.6)!important}.hs-dbg-current-glyph{background:#ffc850;border-radius:2px;width:10px!important;height:10px!important;margin-top:4px;margin-left:4px}"; document.head.appendChild(style); } function injectStyles() { if (document.getElementById("hs-debugger-styles")) return; var style = document.createElement("style"); style.id = "hs-debugger-styles"; - style.textContent = '#hs-debugger{all:initial;display:block;position:fixed;z-index:2147483647;font-family:"IBM Plex Sans",-apple-system,"Segoe UI",system-ui,sans-serif;font-size:13px;color:#222;line-height:1.4}#hs-debugger.hs-bottom{left:0;right:0;bottom:0;height:320px}#hs-debugger.hs-right{top:0;right:0;bottom:0;width:420px}#hs-debugger.hs-hidden{display:none!important}#hs-debugger .d-root{display:flex;flex-direction:column;height:100%;background:#fff;border-top:2px solid #b0b0b0;box-shadow:0 -2px 8px rgba(0,0,0,.12)}#hs-debugger.hs-right .d-root{border-top:none;border-left:2px solid #b0b0b0;box-shadow:-2px 0 8px rgba(0,0,0,.12)}#hs-debugger .d-resize{position:absolute;z-index:1}#hs-debugger.hs-bottom .d-resize{top:-4px;left:0;right:0;height:8px;cursor:ns-resize}#hs-debugger.hs-right .d-resize{top:0;left:-4px;bottom:0;width:8px;cursor:ew-resize}#hs-debugger .d-resize:hover{background:#4a84c4;opacity:.3}#hs-debugger .d-toolbar{display:flex;align-items:center;gap:4px;padding:3px 10px;background:#ebebeb;border-bottom:1px solid #b0b0b0;user-select:none;flex-shrink:0}#hs-debugger .d-logo{height:32px;width:auto;margin-right:8px}#hs-debugger .d-title{font-family:"ChicagoFLF","Chicago","Geneva",system-ui,sans-serif;font-size:20px;font-weight:bold;margin-right:auto}#hs-debugger .d-title em{color:#4a84c4;font-style:normal}#hs-debugger .d-btn{background:none;border:1px solid #d4d4d4;color:#666;cursor:pointer;padding:2px 8px;border-radius:3px;font:inherit;font-size:12px}#hs-debugger .d-btn:hover{color:#222;background:#e0e8f4;border-color:#b0b0b0}#hs-debugger .d-btn.active{color:#4a84c4;background:#e8f0fb;border-color:#4a84c4}#hs-debugger .d-btn-close{font-size:16px;padding:0 4px;margin-left:4px;border:none}#hs-debugger .d-body{display:grid;grid-template-columns:1fr 6px 40%;flex:1;overflow:hidden;min-height:0}#hs-debugger.hs-right .d-body{grid-template-columns:1fr;grid-template-rows:1fr 6px 40%}#hs-debugger .d-elements{display:grid;grid-template-columns:auto 4px 1fr;min-width:0;min-height:0;overflow:hidden}#hs-debugger.hs-right .d-elements{grid-template-columns:1fr;grid-template-rows:auto 4px 1fr}#hs-debugger .d-el-list{width:200px;min-width:100px;border-right:1px solid #d4d4d4;display:flex;flex-direction:column;min-height:0}#hs-debugger.hs-right .d-el-list{width:auto;height:150px;border-right:none;border-bottom:1px solid #d4d4d4;max-height:none}#hs-debugger .d-el-search{display:block;box-sizing:border-box;padding:4px 8px;margin:6px;width:calc(100% - 12px);border:1px solid #8a8a8a;border-radius:3px;background:#fff;color:#222;font-family:"IBM Plex Mono","SF Mono","Consolas",monospace;font-size:11px;outline:none;flex-shrink:0;box-shadow:inset 0 2px 4px rgba(0,0,0,.28)}#hs-debugger .d-el-search::placeholder{color:#999}#hs-debugger .d-el-search:focus{border-color:#4a84c4;box-shadow:inset 0 2px 4px rgba(0,0,0,.28),0 0 0 2px rgba(74,132,196,.25)}#hs-debugger .d-el-items{flex:1;min-height:0;overflow-y:auto}#hs-debugger .d-el-split{cursor:col-resize;background:#d4d4d4}#hs-debugger.hs-right .d-el-split{cursor:row-resize}#hs-debugger .d-el-split:hover{background:#4a84c4}#hs-debugger .d-el-item{padding:4px 10px;cursor:pointer;border-bottom:1px solid #d4d4d4;font-family:"IBM Plex Mono","SF Mono","Consolas",monospace;font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#hs-debugger .d-el-item:hover{background:#e0e8f4}#hs-debugger .d-el-item.selected{background:#4a84c4;color:#fff}#hs-debugger .d-el-item.selected .d-tag,#hs-debugger .d-el-item.selected .d-id,#hs-debugger .d-el-item.selected .d-cls{color:inherit}#hs-debugger .d-tag{color:#4a84c4}#hs-debugger .d-id{color:#2b6b1f}#hs-debugger .d-cls{color:#8a6d00}#hs-debugger .d-detail{overflow-y:auto;padding:10px;display:flex;flex-direction:column;min-width:0}#hs-debugger .d-dbg-toolbar{display:flex;align-items:center;gap:10px;min-height:26px;margin:12px 0 6px}#hs-debugger .d-dbg-toolbar .d-label{margin:0}#hs-debugger .d-dbg-btns{display:flex;gap:2px;visibility:hidden}#hs-debugger.hs-paused .d-dbg-btns{visibility:visible}#hs-debugger.hs-right .d-detail{min-width:auto}#hs-debugger .d-code{white-space:pre-wrap;word-break:break-word;font-family:"IBM Plex Mono","SF Mono","Consolas",monospace;font-size:12px;line-height:1.5;padding:8px;background:#f6f6f6;border-radius:4px;border:1px solid #d4d4d4}#hs-debugger .d-code-area{display:flex;flex:1;min-height:80px;gap:8px}#hs-debugger .d-editor{flex:1;border:1px solid #d4d4d4;border-radius:4px;overflow:hidden}@keyframes hs-dbg-flash{0%{box-shadow:0 0 0 0 rgba(255,200,80,0)}25%{box-shadow:0 0 0 4px rgba(255,200,80,.85)}100%{box-shadow:0 0 0 0 rgba(255,200,80,0)}}#hs-debugger .d-editor.hs-dbg-flash{animation:hs-dbg-flash .5s ease-out}#hs-debugger.hs-paused .d-editor{border-color:#4a84c4;box-shadow:0 0 0 2px rgba(74,132,196,.5)}#hs-debugger .d-label{font-family:"ChicagoFLF","Chicago","Geneva",system-ui,sans-serif;font-size:11px;color:#666;text-transform:uppercase;letter-spacing:.05em;margin:0 0 4px}#hs-debugger .d-label+.d-label,#hs-debugger .d-code+.d-label{margin-top:12px}#hs-debugger .d-console{display:flex;flex-direction:column;border-left:2px solid #b0b0b0;background:#1a0e00;min-width:0;min-height:0;overflow:hidden;cursor:text}#hs-debugger.hs-right .d-console{border-left:none;border-top:2px solid #b0b0b0}#hs-debugger .d-con-hdr{padding:4px 10px;font-family:"ChicagoFLF","Chicago","Geneva",system-ui,sans-serif;font-size:11px;color:#ffdd60;border-bottom:1px solid #3a2800;text-transform:uppercase;letter-spacing:.08em;user-select:none;flex-shrink:0}#hs-debugger .d-con-scroll{flex:1;min-height:0;overflow-y:auto}#hs-debugger .d-con-out{padding:6px 10px;font-family:"IBM Plex Mono","SF Mono","Consolas",monospace;font-size:13px;font-weight:700;color:#ffe060;background-image:repeating-linear-gradient(0deg,transparent,transparent 10px,rgba(255,160,30,.06) 10px,rgba(255,160,30,.06) 20px);background-attachment:local}#hs-debugger .d-con-entry{padding:3px 0}#hs-debugger .d-con-in{display:flex;align-items:center;padding:2px 10px 6px}#hs-debugger .d-prompt{padding:0 6px 0 0;color:#ffdd60;font-family:"ChicagoFLF","Chicago","Geneva",system-ui,sans-serif;user-select:none;font-size:13px;font-weight:700;line-height:1.5;white-space:nowrap}#hs-debugger .d-input{flex:1;background:transparent;border:none;color:#ffdd60;font-family:"IBM Plex Mono","SF Mono","Consolas",monospace;font-size:13px;font-weight:700;padding:0;outline:none;caret-color:#ffdd60}#hs-debugger .d-log-input{color:#ffdd60;font-weight:700}#hs-debugger .d-log-result{color:#ffdd60;font-weight:700}#hs-debugger .d-log-error{color:#ff6040;font-weight:700}#hs-debugger .d-log-coll{color:#ffdd60;font-weight:700}#hs-debugger .d-log-coll-item{padding:1px 0 1px 12px;cursor:pointer;color:#ffdd60;font-weight:700}#hs-debugger .d-log-coll-item:hover{text-decoration:underline}#hs-debugger .d-split{cursor:col-resize;background:#b0b0b0;position:relative}#hs-debugger .d-split:hover{background:#4a84c4}#hs-debugger.hs-right .d-split{cursor:row-resize}#hs-debugger .d-con-toggle{position:absolute;top:50%;transform:translateY(-50%);right:-1px;background:#b0b0b0;color:#fff;border:none;cursor:pointer;font-size:10px;padding:8px 2px;border-radius:0 3px 3px 0;line-height:1}#hs-debugger.hs-right .d-con-toggle{top:50%;left:50%;right:auto;transform:translate(-50%,-50%);padding:1px 8px;border-radius:3px}#hs-debugger .d-con-collapsed .d-con-hdr,#hs-debugger .d-con-collapsed .d-con-scroll{display:none}#hs-debugger .d-con-collapsed{min-width:0;min-height:0}#hs-debugger.hs-bottom .d-con-collapsed{width:24px}#hs-debugger.hs-right .d-con-collapsed{height:24px;width:auto}#hs-debugger .d-con-vlabel{display:none;writing-mode:vertical-rl;text-orientation:mixed;font-family:"ChicagoFLF","Chicago","Geneva",system-ui,sans-serif;font-size:11px;color:#ffb030;letter-spacing:.08em;text-transform:uppercase;padding:10px 4px;user-select:none;cursor:pointer}#hs-debugger.hs-right .d-con-vlabel{writing-mode:horizontal-tb;padding:4px 10px;text-align:center}#hs-debugger .d-con-collapsed .d-con-vlabel{display:block}#hs-debugger .d-empty{color:#666;text-align:center;padding:20px;font-family:"ChicagoFLF","Chicago","Geneva",system-ui,sans-serif;font-size:12px}#hs-debugger .d-dbg-btn{background:#fff;border:1px solid #c0c0c0;box-shadow:0 1px 2px rgba(0,0,0,.1);cursor:pointer;padding:3px 8px;border-radius:3px;font-size:13px;line-height:1;color:#888}#hs-debugger .d-dbg-btn:hover{background:#f4f4f4;box-shadow:0 1px 3px rgba(0,0,0,.15)}#hs-debugger .d-dbg-btn:active{box-shadow:inset 0 1px 2px rgba(0,0,0,.12)}#hs-debugger .d-step,#hs-debugger .d-continue,#hs-debugger .d-step-back,#hs-debugger .d-step-over{color:#2b8a3e}#hs-debugger .d-stop{color:#c03030}#hs-debugger .d-vars{display:none;width:180px;flex-shrink:0;overflow-y:auto;border:1px solid #d4d4d4;border-radius:4px;padding:6px 8px;background:#f8f8f8}#hs-debugger.hs-paused .d-vars{display:block}#hs-debugger .d-vars table{width:100%;border-collapse:collapse}#hs-debugger .d-vars td{padding:2px 4px;border-bottom:1px solid #eee;font-family:"IBM Plex Mono","SF Mono","Consolas",monospace;font-size:11px}#hs-debugger .d-vars td:first-child{color:#c05020;white-space:nowrap}#hs-debugger .d-var-scope{color:#888!important;font-family:"ChicagoFLF","Chicago","Geneva",system-ui,sans-serif;font-size:10px;text-transform:uppercase;letter-spacing:.05em;padding-top:6px!important;border-bottom:none!important}'; + style.textContent = '#hs-debugger{all:initial;display:block;position:fixed;z-index:2147483647;overflow:auto;overscroll-behavior:none;font-family:"IBM Plex Sans",-apple-system,"Segoe UI",system-ui,sans-serif;font-size:13px;color:#222;line-height:1.4}#hs-debugger.hs-bottom{left:0;right:0;bottom:0;height:320px}#hs-debugger.hs-right{top:0;right:0;bottom:0;width:420px}#hs-debugger.hs-hidden{display:none!important}#hs-debugger .d-root{display:flex;flex-direction:column;height:100%;background:#fff;border-top:2px solid #b0b0b0;box-shadow:0 -2px 8px rgba(0,0,0,.12)}#hs-debugger.hs-right .d-root{border-top:none;border-left:2px solid #b0b0b0;box-shadow:-2px 0 8px rgba(0,0,0,.12)}#hs-debugger .d-resize{position:absolute;z-index:1}#hs-debugger.hs-bottom .d-resize{top:-4px;left:0;right:0;height:8px;cursor:ns-resize}#hs-debugger.hs-right .d-resize{top:0;left:-4px;bottom:0;width:8px;cursor:ew-resize}#hs-debugger .d-resize:hover{background:#4a84c4;opacity:.3}#hs-debugger .d-toolbar{display:flex;align-items:center;gap:4px;padding:3px 10px;background:#ebebeb;border-bottom:1px solid #b0b0b0;user-select:none;flex-shrink:0}#hs-debugger .d-logo{height:32px;width:auto;margin-right:8px}#hs-debugger .d-title{font-family:system-ui,sans-serif;font-size:20px;font-weight:bold;margin-right:auto}#hs-debugger .d-title em{color:#4a84c4;font-style:normal}#hs-debugger .d-btn{background:none;border:1px solid #d4d4d4;color:#666;cursor:pointer;padding:2px 8px;border-radius:3px;font:inherit;font-size:12px}#hs-debugger .d-btn:hover{color:#222;background:#e0e8f4;border-color:#b0b0b0}#hs-debugger .d-btn.active{color:#4a84c4;background:#e8f0fb;border-color:#4a84c4}#hs-debugger .d-btn-close{font-size:16px;padding:0 4px;margin-left:4px;border:none}#hs-debugger .d-body{display:grid;grid-template-columns:1fr 6px 40%;flex:1;overflow:hidden;min-height:0}#hs-debugger.hs-right .d-body{grid-template-columns:1fr;grid-template-rows:1fr 6px 40%}#hs-debugger .d-elements{display:grid;grid-template-columns:auto 4px 1fr;min-width:0;min-height:0;overflow:hidden}#hs-debugger.hs-right .d-elements{grid-template-columns:1fr;grid-template-rows:auto 4px 1fr}#hs-debugger .d-el-list{width:200px;min-width:100px;border-right:1px solid #d4d4d4;display:flex;flex-direction:column;min-height:0}#hs-debugger.hs-right .d-el-list{width:auto;height:150px;border-right:none;border-bottom:1px solid #d4d4d4;max-height:none}#hs-debugger .d-el-search{display:block;box-sizing:border-box;padding:4px 8px;margin:6px;width:calc(100% - 12px);border:1px solid #8a8a8a;border-radius:3px;background:#fff;color:#222;font-family:"IBM Plex Mono","SF Mono","Consolas",monospace;font-size:11px;outline:none;flex-shrink:0;box-shadow:inset 0 2px 4px rgba(0,0,0,.28)}#hs-debugger .d-el-search::placeholder{color:#999}#hs-debugger .d-el-search:focus{border-color:#4a84c4;box-shadow:inset 0 2px 4px rgba(0,0,0,.28),0 0 0 2px rgba(74,132,196,.25)}#hs-debugger .d-el-items{flex:1;min-height:0;overflow-y:auto}#hs-debugger .d-el-split{cursor:col-resize;background:#d4d4d4}#hs-debugger.hs-right .d-el-split{cursor:row-resize}#hs-debugger .d-el-split:hover{background:#4a84c4}#hs-debugger .d-el-item{padding:4px 10px;cursor:pointer;border-bottom:1px solid #d4d4d4;font-family:"IBM Plex Mono","SF Mono","Consolas",monospace;font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#hs-debugger .d-el-item:hover{background:#e0e8f4}#hs-debugger .d-el-item.selected{background:#4a84c4;color:#fff}#hs-debugger .d-el-item.selected .d-tag,#hs-debugger .d-el-item.selected .d-id,#hs-debugger .d-el-item.selected .d-cls{color:inherit}#hs-debugger .d-tag{color:#4a84c4}#hs-debugger .d-id{color:#2b6b1f}#hs-debugger .d-cls{color:#8a6d00}#hs-debugger .d-detail{overflow-y:auto;padding:10px;display:flex;flex-direction:column;min-width:0}#hs-debugger .d-dbg-toolbar{display:flex;align-items:center;gap:10px;min-height:26px;margin:12px 0 6px}#hs-debugger .d-dbg-toolbar .d-label{margin:0}#hs-debugger .d-dbg-btns{display:flex;gap:2px;visibility:hidden}#hs-debugger.hs-paused .d-dbg-btns{visibility:visible}#hs-debugger.hs-right .d-detail{min-width:auto}#hs-debugger .d-code{white-space:pre-wrap;word-break:break-word;font-family:"IBM Plex Mono","SF Mono","Consolas",monospace;font-size:12px;line-height:1.5;padding:8px;background:#f6f6f6;border-radius:4px;border:1px solid #d4d4d4}#hs-debugger .d-code-area{display:flex;flex:1;min-height:80px;gap:8px}#hs-debugger .d-editor{flex:1;border:1px solid #d4d4d4;border-radius:4px;overflow:hidden}@keyframes hs-dbg-flash{0%{box-shadow:0 0 0 0 rgba(255,200,80,0)}25%{box-shadow:0 0 0 4px rgba(255,200,80,.85)}100%{box-shadow:0 0 0 0 rgba(255,200,80,0)}}#hs-debugger .d-editor.hs-dbg-flash{animation:hs-dbg-flash .5s ease-out}#hs-debugger.hs-paused .d-editor{border-color:#4a84c4;box-shadow:0 0 0 2px rgba(74,132,196,.5)}#hs-debugger .d-label{font-family:system-ui,sans-serif;font-size:11px;color:#666;text-transform:uppercase;letter-spacing:.05em;margin:0 0 4px}#hs-debugger .d-label+.d-label,#hs-debugger .d-code+.d-label{margin-top:12px}#hs-debugger .d-console{display:flex;flex-direction:column;border-left:2px solid #b0b0b0;background:#1a0e00;min-width:0;min-height:0;overflow:hidden;cursor:text}#hs-debugger.hs-right .d-console{border-left:none;border-top:2px solid #b0b0b0}#hs-debugger .d-con-hdr{padding:4px 10px;font-family:system-ui,sans-serif;font-size:11px;color:#ffdd60;border-bottom:1px solid #3a2800;text-transform:uppercase;letter-spacing:.08em;user-select:none;flex-shrink:0}#hs-debugger .d-con-scroll{flex:1;min-height:0;overflow-y:auto}#hs-debugger .d-con-out{padding:6px 10px;font-family:"IBM Plex Mono","SF Mono","Consolas",monospace;font-size:13px;font-weight:700;color:#ffe060;background-image:repeating-linear-gradient(0deg,transparent,transparent 10px,rgba(255,160,30,.06) 10px,rgba(255,160,30,.06) 20px);background-attachment:local}#hs-debugger .d-con-entry{padding:3px 0}#hs-debugger .d-con-in{display:flex;align-items:center;padding:2px 10px 6px}#hs-debugger .d-prompt{padding:0 6px 0 0;color:#ffdd60;font-family:system-ui,sans-serif;user-select:none;font-size:13px;font-weight:700;line-height:1.5;white-space:nowrap}#hs-debugger .d-input{flex:1;background:transparent;border:none;color:#ffdd60;font-family:"IBM Plex Mono","SF Mono","Consolas",monospace;font-size:13px;font-weight:700;padding:0;outline:none;caret-color:#ffdd60}#hs-debugger .d-log-input{color:#ffdd60;font-weight:700}#hs-debugger .d-log-result{color:#ffdd60;font-weight:700}#hs-debugger .d-log-error{color:#ff6040;font-weight:700}#hs-debugger .d-log-coll{color:#ffdd60;font-weight:700}#hs-debugger .d-log-coll-item{padding:1px 0 1px 12px;cursor:pointer;color:#ffdd60;font-weight:700}#hs-debugger .d-log-coll-item:hover{text-decoration:underline}#hs-debugger .d-split{cursor:col-resize;background:#b0b0b0;position:relative}#hs-debugger .d-split:hover{background:#4a84c4}#hs-debugger.hs-right .d-split{cursor:row-resize}#hs-debugger .d-con-toggle{position:absolute;top:50%;transform:translateY(-50%);right:-1px;background:#b0b0b0;color:#fff;border:none;cursor:pointer;font-size:10px;padding:8px 2px;border-radius:0 3px 3px 0;line-height:1}#hs-debugger.hs-right .d-con-toggle{top:50%;left:50%;right:auto;transform:translate(-50%,-50%);padding:1px 8px;border-radius:3px}#hs-debugger .d-con-collapsed .d-con-hdr,#hs-debugger .d-con-collapsed .d-con-scroll{display:none}#hs-debugger .d-con-collapsed{min-width:0;min-height:0}#hs-debugger.hs-bottom .d-con-collapsed{width:24px}#hs-debugger.hs-right .d-con-collapsed{height:24px;width:auto}#hs-debugger .d-con-vlabel{display:none;writing-mode:vertical-rl;text-orientation:mixed;font-family:system-ui,sans-serif;font-size:11px;color:#ffb030;letter-spacing:.08em;text-transform:uppercase;padding:10px 4px;user-select:none;cursor:pointer}#hs-debugger.hs-right .d-con-vlabel{writing-mode:horizontal-tb;padding:4px 10px;text-align:center}#hs-debugger .d-con-collapsed .d-con-vlabel{display:block}#hs-debugger .d-empty{color:#666;text-align:center;padding:20px;font-family:system-ui,sans-serif;font-size:12px}#hs-debugger .d-dbg-btn{background:#fff;border:1px solid #c0c0c0;box-shadow:0 1px 2px rgba(0,0,0,.1);cursor:pointer;padding:3px 8px;border-radius:3px;font-size:13px;line-height:1;color:#888}#hs-debugger .d-dbg-btn:hover{background:#f4f4f4;box-shadow:0 1px 3px rgba(0,0,0,.15)}#hs-debugger .d-dbg-btn:active{box-shadow:inset 0 1px 2px rgba(0,0,0,.12)}#hs-debugger .d-step,#hs-debugger .d-continue,#hs-debugger .d-step-back,#hs-debugger .d-step-over{color:#2b8a3e}#hs-debugger .d-stop{color:#c03030}#hs-debugger .d-vars{display:none;width:180px;flex-shrink:0;overflow-y:auto;border:1px solid #d4d4d4;border-radius:4px;padding:6px 8px;background:#f8f8f8}#hs-debugger.hs-paused .d-vars{display:block}#hs-debugger .d-vars table{width:100%;border-collapse:collapse}#hs-debugger .d-vars td{padding:2px 4px;border-bottom:1px solid #eee;font-family:"IBM Plex Mono","SF Mono","Consolas",monospace;font-size:11px}#hs-debugger .d-vars td:first-child{color:#c05020;white-space:nowrap}#hs-debugger .d-var-scope{color:#888!important;font-family:system-ui,sans-serif;font-size:10px;text-transform:uppercase;letter-spacing:.05em;padding-top:6px!important;border-bottom:none!important}'; document.head.appendChild(style); } function createPanel(_hyperscript2, ttd, timeline, domRestorer, recorder) { diff --git a/dist/_hyperscript-max.js.map b/dist/_hyperscript-max.js.map index e4397e77c..64a25fcf0 100644 --- a/dist/_hyperscript-max.js.map +++ b/dist/_hyperscript-max.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../src/core/tokenizer.js", "../src/parsetree/base.js", "../src/parsetree/internals.js", "../src/parsetree/expressions/literals.js", "../src/core/parser.js", "../src/core/kernel.js", "../src/core/config.js", "../src/core/runtime/conversions.js", "../src/core/runtime/cookies.js", "../src/core/runtime/collections.js", "../src/core/runtime/runtime.js", "../src/core/runtime/reactivity.js", "../src/core/runtime/morph.js", "../src/core/runtime/htmx-compat.js", "../src/parsetree/expressions/expressions.js", "../src/parsetree/expressions/webliterals.js", "../src/parsetree/expressions/postfix.js", "../src/parsetree/expressions/positional.js", "../src/parsetree/expressions/existentials.js", "../src/parsetree/expressions/targets.js", "../src/parsetree/commands/basic.js", "../src/parsetree/commands/setters.js", "../src/parsetree/commands/events.js", "../src/parsetree/commands/controlflow.js", "../src/parsetree/commands/execution.js", "../src/parsetree/commands/pseudoCommand.js", "../src/parsetree/commands/dom.js", "../src/parsetree/commands/animations.js", "../src/parsetree/commands/debug.js", "../src/parsetree/features/on.js", "../src/parsetree/features/def.js", "../src/parsetree/features/set.js", "../src/parsetree/features/init.js", "../src/parsetree/features/worker.js", "../src/parsetree/features/behavior.js", "../src/parsetree/features/install.js", "../src/parsetree/features/js.js", "../src/parsetree/features/when.js", "../src/parsetree/features/bind.js", "../src/parsetree/features/live.js", "../src/parsetree/commands/template.js", "../src/_hyperscript.js", "../src/ext/debugger.js", "../src/ext/component.js", "../src/ext/socket.js", "../src/ext/worker.js", "../src/ext/eventsource.js", "../src/ext/tailwind.js"], - "sourcesContent": ["// Tokenizer - Lexical analysis for _hyperscript\n\n// ============================================================\n// Tokens - Token stream with matching/consuming API\n// ============================================================\n\nexport class Tokens {\n #tokens;\n #consumed = [];\n #lastConsumed = null;\n #follows = [];\n source;\n\n constructor(tokens, source) {\n this.#tokens = tokens;\n this.source = source;\n this.consumeWhitespace();\n }\n\n get list() {\n return this.#tokens;\n }\n\n get consumed() {\n return this.#consumed;\n }\n\n // ----- Debug -----\n\n toString() {\n var cur = this.currentToken();\n var lines = this.source.split(\"\\n\");\n var lineIdx = cur?.line ? cur.line - 1 : lines.length - 1;\n var col = cur?.line ? cur.column : 0;\n var contextLine = lines[lineIdx] || \"\";\n var tokenLen = Math.max(1, cur?.value?.length || 1);\n var gutter = String(lineIdx + 1).length;\n\n var out = \"Tokens(\";\n out += this.#consumed.filter(t => t.type !== \"WHITESPACE\").length + \" consumed, \";\n out += this.#tokens.filter(t => t.type !== \"WHITESPACE\").length + \" remaining\";\n out += \", line \" + (lineIdx + 1) + \")\\n\";\n out += \" \" + String(lineIdx + 1).padStart(gutter) + \" | \" + contextLine + \"\\n\";\n out += \" \".repeat(gutter + 5) + \" \".repeat(col) + \"^\".repeat(tokenLen);\n if (cur) out += \" \" + cur.type + \" '\" + cur.value + \"'\";\n return out;\n }\n\n // ----- Token access -----\n\n currentToken() {\n return this.token(0);\n }\n\n token(n, includeWhitespace) {\n var token;\n var i = 0;\n do {\n if (!includeWhitespace) {\n while (this.#tokens[i] && this.#tokens[i].type === \"WHITESPACE\") {\n i++;\n }\n }\n token = this.#tokens[i];\n n--;\n i++;\n } while (n > -1);\n return token || { type: \"EOF\", value: \"<<>>\" };\n }\n\n hasMore() {\n return this.#tokens.length > 0;\n }\n\n lastMatch() {\n return this.#lastConsumed;\n }\n\n // ----- Token matching -----\n\n matchToken(value, type) {\n if (this.#follows.includes(value)) return;\n type = type || \"IDENTIFIER\";\n if (this.currentToken() && this.currentToken().value === value && this.currentToken().type === type) {\n return this.consumeToken();\n }\n }\n\n matchOpToken(value) {\n if (this.currentToken() && this.currentToken().op && this.currentToken().value === value) {\n return this.consumeToken();\n }\n }\n\n matchTokenType(...types) {\n if (this.currentToken() && this.currentToken().type && types.includes(this.currentToken().type)) {\n return this.consumeToken();\n }\n }\n\n matchAnyToken(...tokens) {\n for (var i = 0; i < tokens.length; i++) {\n var match = this.matchToken(tokens[i]);\n if (match) return match;\n }\n }\n\n matchAnyOpToken(...ops) {\n for (var i = 0; i < ops.length; i++) {\n var match = this.matchOpToken(ops[i]);\n if (match) return match;\n }\n }\n\n // ----- Token consuming -----\n\n consumeToken() {\n var match = this.#tokens.shift();\n this.#consumed.push(match);\n this.#lastConsumed = match;\n this.consumeWhitespace();\n return match;\n }\n\n consumeWhitespace() {\n while (this.token(0, true).type === \"WHITESPACE\") {\n this.#consumed.push(this.#tokens.shift());\n }\n }\n\n consumeUntil(value, type) {\n var tokenList = [];\n var currentToken = this.token(0, true);\n while (\n (type == null || currentToken.type !== type) &&\n (value == null || currentToken.value !== value) &&\n currentToken.type !== \"EOF\"\n ) {\n var match = this.#tokens.shift();\n this.#consumed.push(match);\n tokenList.push(currentToken);\n currentToken = this.token(0, true);\n }\n this.consumeWhitespace();\n return tokenList;\n }\n\n consumeUntilWhitespace() {\n return this.consumeUntil(null, \"WHITESPACE\");\n }\n\n // ----- Lookahead -----\n\n peekToken(value, peek, type) {\n peek = peek || 0;\n type = type || \"IDENTIFIER\";\n let peekNoWhitespace = 0;\n while (peek > 0) {\n peekNoWhitespace++;\n if (this.#tokens[peekNoWhitespace]?.type !== \"WHITESPACE\") {\n peek--;\n }\n }\n if (this.#tokens[peekNoWhitespace] &&\n this.#tokens[peekNoWhitespace].value === value &&\n this.#tokens[peekNoWhitespace].type === type) {\n return this.#tokens[peekNoWhitespace];\n }\n }\n\n // ----- Whitespace -----\n\n lastWhitespace() {\n var last = this.#consumed.at(-1);\n return (last && last.type === \"WHITESPACE\") ? last.value : \"\";\n }\n\n // ----- Follow set management -----\n\n pushFollow(str) {\n this.#follows.push(str);\n }\n\n popFollow() {\n this.#follows.pop();\n }\n\n pushFollows(...strs) {\n for (var i = 0; i < strs.length; i++) this.#follows.push(strs[i]);\n return strs.length;\n }\n\n popFollows(count) {\n for (var i = 0; i < count; i++) this.#follows.pop();\n }\n\n clearFollows() {\n var tmp = this.#follows;\n this.#follows = [];\n return tmp;\n }\n\n restoreFollows(f) {\n this.#follows = f;\n }\n\n}\n\n// ============================================================\n// Tokenizer - Lexical analysis engine\n// ============================================================\n\nconst OP_TABLE = {\n \"+\": \"PLUS\",\n \"-\": \"MINUS\",\n \"*\": \"MULTIPLY\",\n \"/\": \"DIVIDE\",\n \".\": \"PERIOD\",\n \"..\": \"ELLIPSIS\",\n \"\\\\\": \"BACKSLASH\",\n \":\": \"COLON\",\n \"%\": \"PERCENT\",\n \"|\": \"PIPE\",\n \"!\": \"EXCLAMATION\",\n \"?\": \"QUESTION\",\n \"#\": \"POUND\",\n \"&\": \"AMPERSAND\",\n \"$\": \"DOLLAR\",\n \";\": \"SEMI\",\n \",\": \"COMMA\",\n \"(\": \"L_PAREN\",\n \")\": \"R_PAREN\",\n \"<\": \"L_ANG\",\n \">\": \"R_ANG\",\n \"<=\": \"LTE_ANG\",\n \">=\": \"GTE_ANG\",\n \"==\": \"EQ\",\n \"===\": \"EQQ\",\n \"!=\": \"NEQ\",\n \"!==\": \"NEQQ\",\n \"{\": \"L_BRACE\",\n \"}\": \"R_BRACE\",\n \"[\": \"L_BRACKET\",\n \"]\": \"R_BRACKET\",\n \"=\": \"EQUALS\",\n \"~\": \"TILDE\",\n \"^\": \"CARET\",\n};\n\nexport class Tokenizer {\n\n // ----- Instance state -----\n #source = \"\";\n #position = 0;\n #column = 0;\n #line = 1;\n #lastToken = \"\";\n #templateBraceCount = 0;\n #tokens = [];\n #template = false;\n #templateMode;\n\n // ----- Character classification -----\n\n #isAlpha(c) {\n return (c >= \"a\" && c <= \"z\") || (c >= \"A\" && c <= \"Z\");\n }\n\n #isNumeric(c) {\n return c >= \"0\" && c <= \"9\";\n }\n\n #isWhitespace(c) {\n return c === \" \" || c === \"\\t\" || c === \"\\r\" || c === \"\\n\";\n }\n\n #isNewline(c) {\n return c === \"\\r\" || c === \"\\n\";\n }\n\n #isValidCSSChar(c) {\n return this.#isAlpha(c) || this.#isNumeric(c) || c === \"-\" || c === \"_\" || c === \":\";\n }\n\n #isIdentifierChar(c) {\n return c === \"_\" || c === \"$\";\n }\n\n #isReservedChar(c) {\n return c === \"`\";\n }\n\n static tokenize(string, template) {\n return new Tokenizer().tokenize(string, template);\n }\n\n tokenize(string, template) {\n this.#source = string;\n this.#position = 0;\n this.#column = 0;\n this.#line = 1;\n this.#lastToken = \"\";\n this.#templateBraceCount = 0;\n this.#tokens = [];\n this.#template = template || false;\n this.#templateMode = \"indeterminant\";\n return this.#tokenize();\n }\n\n // ----- Character access -----\n\n #currentChar() {\n return this.#source.charAt(this.#position);\n }\n\n #nextChar() {\n return this.#source.charAt(this.#position + 1);\n }\n\n #charAt(offset = 1) {\n return this.#source.charAt(this.#position + offset);\n }\n\n #consumeChar() {\n this.#lastToken = this.#currentChar();\n this.#position++;\n if (this.#lastToken === \"\\n\") {\n this.#line++;\n this.#column = 0;\n } else {\n this.#column++;\n }\n return this.#lastToken;\n }\n\n // ----- Context checks -----\n\n #inTemplate() {\n return this.#template && this.#templateBraceCount === 0;\n }\n\n #inCommandMode() {\n return !this.#inTemplate() || this.#templateMode === \"command\";\n }\n\n #possiblePrecedingSymbol() {\n return (\n this.#isAlpha(this.#lastToken) ||\n this.#isNumeric(this.#lastToken) ||\n this.#lastToken === \")\" ||\n this.#lastToken === \"\\\"\" ||\n this.#lastToken === \"'\" ||\n this.#lastToken === \"`\" ||\n this.#lastToken === \"}\" ||\n this.#lastToken === \"]\"\n );\n }\n\n #isValidSingleQuoteStringStart() {\n if (this.#tokens.length > 0) {\n var prev = this.#tokens.at(-1);\n if (prev.type === \"IDENTIFIER\" || prev.type === \"CLASS_REF\" || prev.type === \"ID_REF\") {\n return false;\n }\n if (prev.op && (prev.value === \">\" || prev.value === \")\")) {\n return false;\n }\n }\n return true;\n }\n\n // ----- Token constructors -----\n\n #makeToken(type, value) {\n return {\n type: type,\n value: value || \"\",\n start: this.#position,\n end: this.#position + 1,\n column: this.#column,\n line: this.#line,\n };\n }\n\n #makeOpToken(type, value) {\n var token = this.#makeToken(type, value);\n token.op = true;\n return token;\n }\n\n // ----- Consume methods -----\n\n #consumeComment() {\n while (this.#currentChar() && !this.#isNewline(this.#currentChar())) {\n this.#consumeChar();\n }\n this.#consumeChar();\n }\n\n #consumeWhitespace() {\n var ws = this.#makeToken(\"WHITESPACE\");\n var value = \"\";\n while (this.#currentChar() && this.#isWhitespace(this.#currentChar())) {\n if (this.#isNewline(this.#currentChar())) {\n this.#templateMode = \"indeterminant\";\n }\n value += this.#consumeChar();\n }\n ws.value = value;\n ws.end = this.#position;\n return ws;\n }\n\n #consumeClassReference() {\n var token = this.#makeToken(\"CLASS_REF\");\n var value = this.#consumeChar();\n if (this.#currentChar() === \"{\") {\n token.template = true;\n value += this.#consumeChar();\n while (this.#currentChar() && this.#currentChar() !== \"}\") {\n value += this.#consumeChar();\n }\n if (this.#currentChar() !== \"}\") {\n throw new Error(\"Unterminated class reference\");\n } else {\n value += this.#consumeChar();\n }\n } else {\n while (this.#isValidCSSChar(this.#currentChar()) || this.#currentChar() === \"\\\\\") {\n if (this.#currentChar() === \"\\\\\") this.#consumeChar();\n value += this.#consumeChar();\n }\n }\n token.value = value;\n token.end = this.#position;\n return token;\n }\n\n #consumeIdReference() {\n var token = this.#makeToken(\"ID_REF\");\n var value = this.#consumeChar();\n if (this.#currentChar() === \"{\") {\n token.template = true;\n value += this.#consumeChar();\n while (this.#currentChar() && this.#currentChar() !== \"}\") {\n value += this.#consumeChar();\n }\n if (this.#currentChar() !== \"}\") {\n throw new Error(\"Unterminated id reference\");\n } else {\n this.#consumeChar();\n }\n } else {\n while (this.#isValidCSSChar(this.#currentChar())) {\n value += this.#consumeChar();\n }\n }\n token.value = value;\n token.end = this.#position;\n return token;\n }\n\n #consumeAttributeReference() {\n var token = this.#makeToken(\"ATTRIBUTE_REF\");\n var value = this.#consumeChar();\n while (this.#position < this.#source.length && this.#currentChar() !== \"]\") {\n value += this.#consumeChar();\n }\n if (this.#currentChar() === \"]\") {\n value += this.#consumeChar();\n }\n token.value = value;\n token.end = this.#position;\n return token;\n }\n\n #consumeShortAttributeReference() {\n var token = this.#makeToken(\"ATTRIBUTE_REF\");\n var value = this.#consumeChar();\n while (this.#isValidCSSChar(this.#currentChar())) {\n value += this.#consumeChar();\n }\n if (this.#currentChar() === '=') {\n value += this.#consumeChar();\n if (this.#currentChar() === '\"' || this.#currentChar() === \"'\") {\n value += this.#consumeString().value;\n } else if (this.#isAlpha(this.#currentChar()) || this.#isNumeric(this.#currentChar()) || this.#isIdentifierChar(this.#currentChar())) {\n value += this.#consumeIdentifier().value;\n }\n }\n token.value = value;\n token.end = this.#position;\n return token;\n }\n\n #consumeStyleReference() {\n var token = this.#makeToken(\"STYLE_REF\");\n var value = this.#consumeChar();\n while (this.#isAlpha(this.#currentChar()) || this.#currentChar() === \"-\") {\n value += this.#consumeChar();\n }\n token.value = value;\n token.end = this.#position;\n return token;\n }\n\n #consumeTemplateLogic() {\n var token = this.#makeToken(\"IDENTIFIER\");\n this.#consumeChar(); // Don't need the '#'\n var value = \"\"\n\n while(this.#isAlpha(this.#currentChar())) {\n value += this.#consumeChar();\n }\n\n token.value = value;\n token.end = this.#position;\n\n return token;\n }\n\n #consumeTemplateLine() {\n var token = this.#makeToken(\"TEMPLATE_LINE\");\n token.value = \"TEMPLATE_LINE\";\n var content = \"\";\n while (this.#currentChar() && !this.#isNewline(this.#currentChar())) {\n content += this.#consumeChar();\n }\n if (this.#currentChar() && this.#isNewline(this.#currentChar())) {\n this.#consumeChar();\n content += \"\\n\";\n this.#templateMode = \"indeterminant\";\n }\n token.content = content;\n token.end = this.#position;\n return token;\n }\n\n #consumeTemplateIdentifier() {\n var token = this.#makeToken(\"IDENTIFIER\");\n var value = this.#consumeChar();\n var escaped = value === \"\\\\\";\n if (escaped) value = \"\";\n while (this.#isAlpha(this.#currentChar()) ||\n this.#isNumeric(this.#currentChar()) ||\n this.#isIdentifierChar(this.#currentChar()) ||\n this.#currentChar() === \"\\\\\" ||\n this.#currentChar() === \"{\" ||\n this.#currentChar() === \"}\") {\n if (this.#currentChar() === \"$\" && !escaped) {\n break;\n } else if (this.#currentChar() === \"\\\\\") {\n escaped = true;\n this.#consumeChar();\n } else {\n escaped = false;\n value += this.#consumeChar();\n }\n }\n if (this.#currentChar() === \"!\" && value === \"beep\") {\n value += this.#consumeChar();\n }\n token.value = value;\n token.end = this.#position;\n return token;\n }\n\n #consumeIdentifier() {\n var token = this.#makeToken(\"IDENTIFIER\");\n var value = this.#consumeChar();\n while (this.#isAlpha(this.#currentChar()) || this.#isNumeric(this.#currentChar()) || this.#isIdentifierChar(this.#currentChar())) {\n value += this.#consumeChar();\n }\n if (this.#currentChar() === \"!\" && value === \"beep\") {\n value += this.#consumeChar();\n }\n token.value = value;\n token.end = this.#position;\n return token;\n }\n\n #consumeNumber() {\n var token = this.#makeToken(\"NUMBER\");\n var value = this.#consumeChar();\n\n // consume integer part: XXX\n while (this.#isNumeric(this.#currentChar())) {\n value += this.#consumeChar();\n }\n\n // consume decimal part: .YYY\n if (this.#currentChar() === \".\" && this.#isNumeric(this.#nextChar())) {\n value += this.#consumeChar();\n }\n while (this.#isNumeric(this.#currentChar())) {\n value += this.#consumeChar();\n }\n\n // consume exponent: (e|E)[-]ZZZ\n if (this.#currentChar() === \"e\" || this.#currentChar() === \"E\") {\n if (this.#isNumeric(this.#nextChar())) {\n value += this.#consumeChar();\n } else if (this.#nextChar() === \"-\") {\n value += this.#consumeChar();\n value += this.#consumeChar();\n }\n }\n while (this.#isNumeric(this.#currentChar())) {\n value += this.#consumeChar();\n }\n\n token.value = value;\n token.end = this.#position;\n return token;\n }\n\n #consumeOp() {\n var token = this.#makeOpToken();\n var value = this.#consumeChar();\n while (this.#currentChar() && OP_TABLE[value + this.#currentChar()]) {\n value += this.#consumeChar();\n }\n token.type = OP_TABLE[value];\n token.value = value;\n token.end = this.#position;\n return token;\n }\n\n #consumeString() {\n var token = this.#makeToken(\"STRING\");\n var startChar = this.#consumeChar();\n token.template = startChar === \"`\";\n var value = \"\";\n while (this.#currentChar() && this.#currentChar() !== startChar) {\n if (this.#currentChar() === \"\\\\\") {\n this.#consumeChar();\n let next = this.#consumeChar();\n if (next === \"b\") value += \"\\b\";\n else if (next === \"f\") value += \"\\f\";\n else if (next === \"n\") value += \"\\n\";\n else if (next === \"r\") value += \"\\r\";\n else if (next === \"t\") value += \"\\t\";\n else if (next === \"v\") value += \"\\v\";\n else if (token.template && next === \"$\") value += \"\\\\$\";\n else if (next === \"x\") {\n const hex = this.#consumeHexEscape();\n if (Number.isNaN(hex)) {\n throw new Error(\"Invalid hexadecimal escape at [Line: \" + token.line + \", Column: \" + token.column + \"]\");\n }\n value += String.fromCharCode(hex);\n }\n else value += next;\n } else {\n value += this.#consumeChar();\n }\n }\n if (this.#currentChar() !== startChar) {\n throw new Error(\"Unterminated string at [Line: \" + token.line + \", Column: \" + token.column + \"]\");\n } else {\n this.#consumeChar();\n }\n token.value = value;\n token.end = this.#position;\n return token;\n }\n\n #consumeHexEscape() {\n if (!this.#currentChar()) return NaN;\n let result = 16 * Number.parseInt(this.#consumeChar(), 16);\n if (!this.#currentChar()) return NaN;\n result += Number.parseInt(this.#consumeChar(), 16);\n return result;\n }\n\n // ----- Main tokenization loop -----\n\n #isLineComment() {\n var c = this.#currentChar(), n = this.#nextChar(), n2 = this.#charAt(2);\n return (c === \"-\" && n === \"-\" && (this.#isWhitespace(n2) || n2 === \"\" || n2 === \"-\"))\n || (c === \"/\" && n === \"/\" && (this.#isWhitespace(n2) || n2 === \"\" || n2 === \"/\"));\n }\n\n #tokenize() {\n while (this.#position < this.#source.length) {\n if (this.#isLineComment()) {\n this.#consumeComment();\n } else if (this.#isWhitespace(this.#currentChar())) {\n this.#tokens.push(this.#consumeWhitespace());\n } else if (\n !this.#possiblePrecedingSymbol() &&\n this.#currentChar() === \".\" &&\n (this.#isAlpha(this.#nextChar()) || this.#nextChar() === \"{\" || this.#nextChar() === \"-\")\n ) {\n this.#tokens.push(this.#consumeClassReference());\n } else if (\n !this.#possiblePrecedingSymbol() &&\n this.#currentChar() === \"#\" &&\n (this.#isAlpha(this.#nextChar()) || this.#nextChar() === \"{\")\n ) {\n if (this.#template === \"lines\" && this.#templateMode === \"indeterminant\") {\n this.#templateMode = \"command\";\n this.#tokens.push(this.#consumeTemplateLogic());\n } else {\n this.#tokens.push(this.#consumeIdReference());\n }\n } else if (this.#template === \"lines\" && this.#templateMode === \"indeterminant\" && this.#templateBraceCount === 0) {\n this.#templateMode = \"template\";\n this.#tokens.push(this.#consumeTemplateLine());\n } else if (this.#currentChar() === \"[\" && this.#nextChar() === \"@\") {\n this.#tokens.push(this.#consumeAttributeReference());\n } else if (this.#currentChar() === \"@\") {\n this.#tokens.push(this.#consumeShortAttributeReference());\n } else if (this.#currentChar() === \"*\" && this.#isAlpha(this.#nextChar())) {\n this.#tokens.push(this.#consumeStyleReference());\n } else if (this.#inTemplate() &&\n (this.#isAlpha(this.#currentChar()) || this.#currentChar() === \"\\\\\") &&\n this.#templateMode !== \"command\"\n ) {\n this.#tokens.push(this.#consumeTemplateIdentifier());\n } else if (this.#inCommandMode() && (this.#isAlpha(this.#currentChar()) || this.#isIdentifierChar(this.#currentChar()))) {\n this.#tokens.push(this.#consumeIdentifier());\n } else if (this.#isNumeric(this.#currentChar())) {\n this.#tokens.push(this.#consumeNumber());\n } else if (this.#inCommandMode() && (this.#currentChar() === '\"' || this.#currentChar() === \"`\")) {\n this.#tokens.push(this.#consumeString());\n } else if (this.#inCommandMode() && this.#currentChar() === \"'\") {\n if (this.#isValidSingleQuoteStringStart()) {\n this.#tokens.push(this.#consumeString());\n } else {\n this.#tokens.push(this.#consumeOp());\n }\n } else if (OP_TABLE[this.#currentChar()]) {\n if (this.#lastToken === \"$\" && this.#currentChar() === \"{\") {\n this.#templateBraceCount++;\n }\n if (this.#currentChar() === \"}\") {\n this.#templateBraceCount--;\n }\n this.#tokens.push(this.#consumeOp());\n } else if (this.#inTemplate() || this.#isReservedChar(this.#currentChar())) {\n this.#tokens.push(this.#makeToken(\"RESERVED\", this.#consumeChar()));\n } else {\n if (this.#position < this.#source.length) {\n throw new Error(\"Unknown token: \" + this.#currentChar() + \" \");\n }\n }\n }\n\n return new Tokens(this.#tokens, this.#source);\n }\n}\n", "/**\n * Base classes for parse tree elements\n */\n\n/**\n * ParseElement - Root base class for all parse tree elements\n *\n * Provides common functionality for all parse tree nodes including\n * expressions, commands, and features.\n */\nexport class ParseElement {\n errors = [];\n\n collectErrors(visited) {\n if (!visited) visited = new Set();\n if (visited.has(this)) return [];\n visited.add(this);\n var all = [...this.errors];\n for (var key of Object.keys(this)) {\n for (var item of [this[key]].flat()) {\n if (item instanceof ParseElement) {\n all.push(...item.collectErrors(visited));\n }\n }\n }\n return all;\n }\n\n sourceFor() {\n return this.programSource.substring(this.startToken.start, this.endToken.end);\n }\n\n lineFor() {\n return this.programSource.split(\"\\n\")[this.startToken.line - 1];\n }\n\n static parseEventArgs(parser) {\n var args = [];\n // handle argument list (look ahead 3)\n if (\n parser.token(0).value === \"(\" &&\n (parser.token(1).value === \")\" || parser.token(2).value === \",\" || parser.token(2).value === \")\")\n ) {\n parser.matchOpToken(\"(\");\n do {\n args.push(parser.requireTokenType(\"IDENTIFIER\"));\n } while (parser.matchOpToken(\",\"));\n parser.requireOpToken(\")\");\n }\n return args;\n }\n}\n\n/**\n * Expression - Base class for all expressions\n *\n * Delegates to Runtime.unifiedEval for evaluation.\n * Subclasses define resolve() and args for their logic.\n * Type is auto-derived from static grammarName.\n */\nexport class Expression extends ParseElement {\n constructor() {\n super();\n if (this.constructor.grammarName) {\n this.type = this.constructor.grammarName;\n }\n }\n\n evaluate(context) {\n return context.meta.runtime.unifiedEval(this, context);\n }\n\n evalStatically() {\n throw new Error(\"This expression cannot be evaluated statically: \" + this.type);\n }\n}\n\n/**\n * Command - Base class for all commands\n *\n * Delegates to Runtime.unifiedExec for execution.\n * Subclasses define resolve() and args for their logic.\n * Type is auto-derived from static keyword + \"Command\".\n */\nexport class Command extends ParseElement {\n constructor() {\n super();\n if (this.constructor.keyword) {\n this.type = this.constructor.keyword + \"Command\";\n }\n }\n\n execute(context) {\n context.meta.command = this;\n return context.meta.runtime.unifiedExec(this, context);\n }\n\n findNext(context) {\n return context.meta.runtime.findNext(this, context);\n }\n\n}\n\n/**\n * Feature - Base class for all features\n *\n * Features define behavior that is installed on elements.\n * Subclasses implement install() method for their specific logic.\n * Type is auto-derived from static keyword + \"Feature\".\n */\nexport class Feature extends ParseElement {\n isFeature = true;\n\n constructor() {\n super();\n if (this.constructor.keyword) {\n this.type = this.constructor.keyword + \"Feature\";\n }\n }\n\n install(target, source, args, runtime) {\n // Subclasses override this method\n }\n\n /**\n * Parse optional catch/finally blocks after a command list.\n * Returns { errorHandler, errorSymbol, finallyHandler }\n */\n static parseErrorAndFinally(parser) {\n var errorSymbol, errorHandler, finallyHandler;\n if (parser.matchToken(\"catch\")) {\n errorSymbol = parser.requireTokenType(\"IDENTIFIER\").value;\n errorHandler = parser.requireElement(\"commandList\");\n parser.ensureTerminated(errorHandler);\n }\n if (parser.matchToken(\"finally\")) {\n finallyHandler = parser.requireElement(\"commandList\");\n parser.ensureTerminated(finallyHandler);\n }\n return { errorHandler, errorSymbol, finallyHandler };\n }\n}\n", "/**\n * Internal parse elements used by the kernel grammar\n */\n\nimport { ParseElement, Command, Feature } from './base.js';\n\n/**\n * EmptyCommandListCommand - Placeholder for empty command lists\n */\nexport class EmptyCommandListCommand extends Command {\n constructor() {\n super();\n this.type = \"emptyCommandListCommand\";\n }\n\n resolve(context) {\n return this.findNext(context);\n }\n}\n\n/**\n * UnlessStatementModifier - Wraps a command with an \"unless\" conditional\n */\nexport class UnlessStatementModifier extends Command {\n constructor(root, conditional) {\n super();\n this.type = \"unlessStatementModifier\";\n this.root = root;\n this.args = { conditional };\n }\n\n resolve(context, { conditional }) {\n if (conditional) {\n return this.next;\n } else {\n return this.root;\n }\n }\n}\n\n/**\n * HyperscriptProgram - Root node for a parsed hyperscript document\n */\nexport class HyperscriptProgram extends ParseElement {\n constructor(features) {\n super();\n this.type = \"hyperscript\";\n this.features = features;\n }\n\n apply(target, source, args, runtime) {\n for (const feature of this.features) {\n feature.install(target, source, args, runtime);\n }\n }\n}\n\n/**\n * FailedFeature - Placeholder for a feature that failed to parse.\n * Allows the parser to continue and collect more errors.\n * Never executed - element won't apply() if errors exist.\n */\nexport class FailedFeature extends Feature {\n constructor(error, keyword) {\n super();\n this.type = \"failedFeature\";\n this.keyword = keyword;\n this.errors.push(error);\n }\n\n install() {}\n}\n\n/**\n * FailedCommand - Placeholder for a command that failed to parse.\n * Allows the parser to continue and collect more errors.\n * Never executed - element won't apply() if errors exist.\n */\nexport class FailedCommand extends Command {\n constructor(error, keyword) {\n super();\n this.type = \"failedCommand\";\n this.keyword = keyword;\n this.errors.push(error);\n }\n\n resolve() {}\n}\n\n/**\n * ImplicitReturn - Terminates command lists without explicit returns\n */\nexport class ImplicitReturn extends Command {\n constructor() {\n super();\n this.type = \"implicitReturn\";\n }\n\n resolve(context) {\n context.meta.returned = true;\n if (context.meta.resolve) {\n context.meta.resolve();\n }\n return context.meta.runtime.HALT;\n }\n}\n", "/**\n * Literal parse tree elements\n * Simple value literals with no dependencies\n */\n\nimport { Expression } from '../base.js';\nimport { Tokenizer } from '../../core/tokenizer.js';\n\n/**\n * NakedString - Represents unquoted strings (consumed until whitespace)\n *\n * Parses: bareword text\n * Returns: string value\n */\nexport class NakedString extends Expression {\n static grammarName = \"nakedString\";\n\n constructor(tokens) {\n super();\n this.tokens = tokens;\n }\n\n static parse(parser) {\n if (parser.hasMore()) {\n var tokenArr = parser.consumeUntilWhitespace();\n parser.matchTokenType(\"WHITESPACE\");\n return new NakedString(tokenArr);\n }\n }\n\n evalStatically() {\n return this.resolve();\n }\n\n resolve(context) {\n return this.tokens\n .map(function (t) {\n return t.value;\n })\n .join(\"\");\n }\n}\n\n/**\n * BooleanLiteral - Represents true/false keywords\n *\n * Parses: true | false\n * Returns: boolean value\n */\nexport class BooleanLiteral extends Expression {\n static grammarName = \"boolean\";\n static expressionType = \"leaf\";\n\n constructor(value) {\n super();\n this.value = value;\n }\n\n static parse(parser) {\n var booleanLiteral = parser.matchToken(\"true\") || parser.matchToken(\"false\");\n if (!booleanLiteral) return;\n const value = booleanLiteral.value === \"true\";\n return new BooleanLiteral(value);\n }\n\n evalStatically() {\n return this.value;\n }\n\n resolve(context) {\n return this.value;\n }\n}\n\n/**\n * NullLiteral - Represents the null keyword\n *\n * Parses: null\n * Returns: null value\n */\nexport class NullLiteral extends Expression {\n static grammarName = \"null\";\n static expressionType = \"leaf\";\n\n constructor() {\n super();\n }\n\n static parse(parser) {\n if (parser.matchToken(\"null\")) {\n return new NullLiteral();\n }\n }\n\n evalStatically() {\n return null;\n }\n\n resolve(context) {\n return null;\n }\n}\n\n/**\n * NumberLiteral - Represents numeric values\n *\n * Parses: 42 | 3.14 | 1e10\n * Returns: number value\n */\nexport class NumberLiteral extends Expression {\n static grammarName = \"number\";\n static expressionType = \"leaf\";\n\n constructor(value, numberToken) {\n super();\n this.value = value;\n this.numberToken = numberToken;\n }\n\n static parse(parser) {\n var number = parser.matchTokenType(\"NUMBER\");\n if (!number) return;\n var numberToken = number;\n var value = parseFloat(/** @type {string} */ (number.value));\n return new NumberLiteral(value, numberToken);\n }\n\n evalStatically() {\n return this.value;\n }\n\n resolve(context) {\n return this.value;\n }\n}\n\n/**\n * StringLiteral - Represents string values (with optional template interpolation)\n *\n * Parses: \"hello\" | \"hello ${name}\"\n * Returns: string value\n */\nexport class StringLiteral extends Expression {\n static grammarName = \"string\";\n static expressionType = \"leaf\";\n\n constructor(stringToken, rawValue, args) {\n super();\n this.token = stringToken;\n this.rawValue = rawValue;\n this.args = args.length > 0 ? { parts: args } : null;\n }\n\n static parse(parser) {\n var stringToken = parser.matchTokenType(\"STRING\");\n if (!stringToken) return;\n var rawValue = /** @type {string} */ (stringToken.value);\n /** @type {any[]} */\n var args;\n if (stringToken.template) {\n var innerTokens = Tokenizer.tokenize(rawValue, true);\n var innerParser = parser.createChildParser(innerTokens);\n args = innerParser.parseStringTemplate();\n } else {\n args = [];\n }\n return new StringLiteral(stringToken, rawValue, args);\n }\n\n evalStatically() {\n if (this.args === null) return this.rawValue;\n return super.evalStatically();\n }\n\n resolve(context, { parts } = {}) {\n if (!parts || parts.length === 0) {\n return this.rawValue;\n }\n var returnStr = \"\";\n for (var i = 0; i < parts.length; i++) {\n var val = parts[i];\n if (val !== undefined) {\n returnStr += val;\n }\n }\n return returnStr;\n }\n}\n\n/**\n * ArrayLiteral - Represents array literals\n *\n * Parses: [1, 2, 3] | []\n * Returns: array value\n */\nexport class ArrayLiteral extends Expression {\n static grammarName = \"arrayLiteral\";\n static expressionType = \"leaf\";\n\n constructor(values) {\n super();\n this.values = values;\n this.args = { values };\n }\n\n static parse(parser) {\n if (!parser.matchOpToken(\"[\")) return;\n var values = [];\n if (!parser.matchOpToken(\"]\")) {\n do {\n var expr = parser.requireElement(\"expression\");\n values.push(expr);\n } while (parser.matchOpToken(\",\"));\n parser.requireOpToken(\"]\");\n }\n return new ArrayLiteral(values);\n }\n\n resolve(context, { values }) {\n return values;\n }\n}\n\n/**\n * ObjectKey - Represents an object key (string, identifier, or computed expression)\n *\n * Parses: \"key\" | key | [expression]\n * Returns: string key value\n */\nexport class ObjectKey extends Expression {\n static grammarName = \"objectKey\";\n\n constructor(key, expr, args) {\n super();\n this.key = key;\n this.expr = expr;\n this.args = args;\n }\n\n static parse(parser) {\n var token;\n if ((token = parser.matchTokenType(\"STRING\"))) {\n return new ObjectKey(token.value, null, null);\n } else if (parser.matchOpToken(\"[\")) {\n var expr = parser.parseElement(\"expression\");\n parser.requireOpToken(\"]\");\n return new ObjectKey(null, expr, { value: expr });\n } else {\n var key = \"\";\n do {\n token = parser.matchTokenType(\"IDENTIFIER\") || parser.matchOpToken(\"-\");\n if (token) key += token.value;\n } while (token);\n return new ObjectKey(key, null, null);\n }\n }\n\n evalStatically() {\n if (!this.expr) return this.key;\n return super.evalStatically();\n }\n\n resolve(ctx, { value } = {}) {\n if (this.expr) {\n return value;\n }\n return this.key;\n }\n}\n\n/**\n * ObjectLiteral - Represents object literals\n *\n * Parses: {foo: bar, baz: qux} | {}\n * Returns: object value\n */\nexport class ObjectLiteral extends Expression {\n static grammarName = \"objectLiteral\";\n static expressionType = \"leaf\";\n\n constructor(keyExpressions, valueExpressions) {\n super();\n this.keyExpressions = keyExpressions;\n this.valueExpressions = valueExpressions;\n this.args = { keys: keyExpressions, values: valueExpressions };\n }\n\n static parse(parser) {\n if (!parser.matchOpToken(\"{\")) return;\n var keyExpressions = [];\n var valueExpressions = [];\n if (!parser.matchOpToken(\"}\")) {\n do {\n var name = parser.requireElement(\"objectKey\");\n parser.requireOpToken(\":\");\n var value = parser.requireElement(\"expression\");\n valueExpressions.push(value);\n keyExpressions.push(name);\n } while (parser.matchOpToken(\",\") && !parser.peekToken(\"}\", 0, 'R_BRACE'));\n parser.requireOpToken(\"}\");\n }\n return new ObjectLiteral(keyExpressions, valueExpressions);\n }\n\n resolve(context, { keys, values }) {\n var returnVal = {};\n for (var i = 0; i < keys.length; i++) {\n returnVal[keys[i]] = values[i];\n }\n return returnVal;\n }\n}\n\n/**\n * NamedArgumentList - Represents named argument lists (with or without parentheses)\n *\n * Parses: foo: 1, bar: 2 or (foo: 1, bar: 2)\n * Returns: object with named arguments\n */\nexport class NamedArgumentList extends Expression {\n static grammarName = \"namedArgumentList\";\n\n constructor(fields, valueExpressions) {\n super();\n this.fields = fields;\n this.args = { values: valueExpressions };\n }\n\n static parseNaked(parser) {\n var fields = [];\n var valueExpressions = [];\n if (parser.currentToken().type === \"IDENTIFIER\") {\n do {\n var name = parser.requireTokenType(\"IDENTIFIER\");\n parser.requireOpToken(\":\");\n var value = parser.requireElement(\"expression\");\n valueExpressions.push(value);\n fields.push({ name: name, value: value });\n } while (parser.matchOpToken(\",\"));\n }\n return new NamedArgumentList(fields, valueExpressions);\n }\n\n static parse(parser) {\n if (!parser.matchOpToken(\"(\")) return;\n var elt = NamedArgumentList.parseNaked(parser);\n parser.requireOpToken(\")\");\n return elt;\n }\n\n resolve(context, { values }) {\n var returnVal = { _namedArgList_: true };\n for (var i = 0; i < values.length; i++) {\n var field = this.fields[i];\n returnVal[field.name.value] = values[i];\n }\n return returnVal;\n }\n}\n\n/**\n * NakedNamedArgumentList - Registration proxy for the naked (no parens) variant\n */\nexport class NakedNamedArgumentList extends Expression {\n static grammarName = \"nakedNamedArgumentList\";\n static parse = NamedArgumentList.parseNaked;\n}\n\n/**\n * StringLike - Matches either a quoted string or a naked string\n */\nexport class StringLike extends Expression {\n static grammarName = \"stringLike\";\n\n static parse(parser) {\n return parser.parseAnyOf([\"string\", \"nakedString\"]);\n }\n}", "// Parser - Unified API for parsing operations\n// Encapsulates both LanguageKernel and Tokens to provide a single parameter to grammar functions\n\nimport { ImplicitReturn } from '../parsetree/internals.js';\nimport { NakedString } from '../parsetree/expressions/literals.js';\n\n// ============================================================\n// Parse error types\n// ============================================================\n\nexport class ParseError {\n constructor(message, token, source, expected) {\n this.message = message;\n this.token = token;\n this.source = source;\n this.expected = expected || null;\n this.line = token?.line ?? null;\n this.column = token?.column ?? null;\n }\n}\n\nexport class ParseRecoverySentinel extends Error {\n constructor(parseError) {\n super(parseError.message);\n this.parseError = parseError;\n }\n}\n\nexport class Parser {\n #kernel;\n\n constructor(kernel, tokens) {\n this.#kernel = kernel;\n this.tokens = tokens;\n }\n\n toString() {\n this.tokens.matched\n }\n\n static formatErrors(errors) {\n if (!errors.length) return \"\";\n var source = errors[0].source;\n var lines = source.split(\"\\n\");\n\n var byLine = new Map();\n for (var e of errors) {\n var lineIdx = e.token?.line ? e.token.line - 1 : lines.length - 1;\n if (!byLine.has(lineIdx)) byLine.set(lineIdx, []);\n byLine.get(lineIdx).push(e);\n }\n\n var maxLine = Math.max(...byLine.keys()) + 1;\n var gutter = String(maxLine).length;\n var pad = \" \".repeat(gutter + 5);\n var sortedLines = [...byLine.entries()].sort((a, b) => a[0] - b[0]);\n var prevLineIdx = -1;\n var out = \"\";\n\n for (var [lineIdx, lineErrors] of sortedLines) {\n if (prevLineIdx !== -1 && lineIdx > prevLineIdx + 1) {\n out += \" \".repeat(gutter + 1) + \"...\\n\";\n } else if (prevLineIdx === -1 && lineIdx > 0) {\n out += \" \".repeat(gutter + 1) + \"...\\n\";\n }\n prevLineIdx = lineIdx;\n\n var lineNum = String(lineIdx + 1).padStart(gutter);\n var contextLine = lines[lineIdx] || \"\";\n out += \" \" + lineNum + \" | \" + contextLine + \"\\n\";\n\n lineErrors.sort((a, b) => (a.column || 0) - (b.column || 0));\n\n var underlineChars = Array(contextLine.length + 10).fill(\" \");\n for (var e of lineErrors) {\n var col = e.token?.line ? e.token.column : Math.max(0, contextLine.length - 1);\n var len = Math.max(1, e.token?.value?.length || 1);\n for (var i = 0; i < len; i++) underlineChars[col + i] = \"^\";\n }\n out += pad + underlineChars.join(\"\").trimEnd() + \"\\n\";\n\n for (var e of lineErrors) {\n var col = e.token?.line ? e.token.column : 0;\n out += pad + \" \".repeat(col) + e.message + \"\\n\";\n }\n }\n return out;\n }\n\n // ===========================\n // Token delegation methods\n // ===========================\n\n consumeWhitespace() {\n return this.tokens.consumeWhitespace();\n }\n\n requireOpToken(value) {\n var token = this.matchOpToken(value);\n if (token) return token;\n this.raiseExpected(value);\n }\n\n matchAnyOpToken(...ops) {\n return this.tokens.matchAnyOpToken(...ops);\n }\n\n matchAnyToken(...tokens) {\n return this.tokens.matchAnyToken(...tokens);\n }\n\n matchOpToken(value) {\n return this.tokens.matchOpToken(value);\n }\n\n requireTokenType(...types) {\n var token = this.matchTokenType(...types);\n if (token) return token;\n this.raiseExpected(...types);\n }\n\n matchTokenType(...types) {\n return this.tokens.matchTokenType(...types);\n }\n\n requireToken(value, type) {\n var token = this.matchToken(value, type);\n if (token) return token;\n this.raiseExpected(value);\n }\n\n peekToken(value, peek, type) {\n return this.tokens.peekToken(value, peek, type);\n }\n\n matchToken(value, type) {\n return this.tokens.matchToken(value, type);\n }\n\n consumeToken() {\n return this.tokens.consumeToken();\n }\n\n consumeUntil(value, type) {\n return this.tokens.consumeUntil(value, type);\n }\n\n lastWhitespace() {\n return this.tokens.lastWhitespace();\n }\n\n consumeUntilWhitespace() {\n return this.tokens.consumeUntilWhitespace();\n }\n\n hasMore() {\n return this.tokens.hasMore();\n }\n\n token(n, includeWhitespace) {\n return this.tokens.token(n, includeWhitespace);\n }\n\n currentToken() {\n return this.tokens.currentToken();\n }\n\n lastMatch() {\n return this.tokens.lastMatch();\n }\n\n pushFollow(str) {\n return this.tokens.pushFollow(str);\n }\n\n popFollow() {\n return this.tokens.popFollow();\n }\n\n pushFollows(...strs) {\n return this.tokens.pushFollows(...strs);\n }\n\n popFollows(count) {\n return this.tokens.popFollows(count);\n }\n\n clearFollows() {\n return this.tokens.clearFollows();\n }\n\n restoreFollows(f) {\n return this.tokens.restoreFollows(f);\n }\n\n get source() {\n return this.tokens.source;\n }\n\n get consumed() {\n return this.tokens.consumed;\n }\n\n get list() {\n return this.tokens.list;\n }\n\n createChildParser(tokens) {\n return new Parser(this.#kernel, tokens);\n }\n\n // ===========================\n // Kernel delegation methods\n // ===========================\n\n parseElement(type, root = null) {\n return this.#kernel.parseElement(type, this, root);\n }\n\n requireElement(type, message, root) {\n return this.#kernel.requireElement(type, this, message, root);\n }\n\n parseAnyOf(types) {\n return this.#kernel.parseAnyOf(types, this);\n }\n\n raiseError(message, expected) {\n message = message || \"Unexpected Token : \" + this.currentToken().value;\n var parseError = new ParseError(message, this.currentToken(), this.source, expected);\n throw new ParseRecoverySentinel(parseError);\n }\n\n raiseExpected(...expected) {\n var msg = expected.length === 1\n ? \"Expected '\" + expected[0] + \"' but found '\" + this.currentToken().value + \"'\"\n : \"Expected one of: \" + expected.map(e => \"'\" + e + \"'\").join(\", \");\n this.raiseError(msg, expected);\n }\n\n // ===========================\n // Parser-owned methods\n // ===========================\n\n parseStringTemplate() {\n var returnArr = [\"\"];\n do {\n returnArr.push(this.lastWhitespace());\n if (this.currentToken().value === \"$\") {\n this.consumeToken();\n var startingBrace = this.matchOpToken(\"{\");\n returnArr.push(this.requireElement(\"expression\"));\n if (startingBrace) {\n this.requireOpToken(\"}\");\n }\n returnArr.push(\"\");\n } else if (this.currentToken().value === \"\\\\\") {\n this.consumeToken(); // skip next\n this.consumeToken();\n } else {\n var token = this.consumeToken();\n returnArr[returnArr.length - 1] += token ? token.value : \"\";\n }\n } while (this.hasMore());\n returnArr.push(this.lastWhitespace());\n return returnArr;\n }\n\n commandBoundary(token) {\n if (\n token.value == \"end\" ||\n token.value == \"then\" ||\n token.value == \"else\" ||\n token.value == \"otherwise\" ||\n token.value == \")\" ||\n this.commandStart(token) ||\n this.featureStart(token) ||\n token.type == \"EOF\"\n ) {\n return true;\n }\n return false;\n }\n\n commandStart(token) {\n return this.#kernel.commandStart(token);\n }\n\n featureStart(token) {\n return this.#kernel.featureStart(token);\n }\n\n setParent(elt, parent) {\n if (typeof elt === 'object') {\n elt.parent = parent;\n if (typeof parent === 'object') {\n parent.children = (parent.children || new Set());\n parent.children.add(elt)\n }\n this.setParent(elt.next, parent);\n }\n }\n\n parseURLOrExpression() {\n var cur = this.currentToken();\n if (cur.value === \"/\" && cur.type === \"DIVIDE\") {\n // starts with / - naked URL\n var tokens = this.consumeUntilWhitespace();\n this.matchTokenType(\"WHITESPACE\");\n return new NakedString(tokens);\n }\n if (cur.type === \"IDENTIFIER\" && (cur.value === \"http\" || cur.value === \"https\" || cur.value === \"ws\" || cur.value === \"wss\")) {\n // starts with http/https - naked URL\n var tokens = this.consumeUntilWhitespace();\n this.matchTokenType(\"WHITESPACE\");\n return new NakedString(tokens);\n }\n return this.requireElement(\"expression\");\n }\n\n ensureTerminated(commandList) {\n var implicitReturn = new ImplicitReturn();\n var end = commandList;\n while (end.next) {\n end = end.next;\n }\n end.next = implicitReturn;\n }\n}\n", "// LanguageKernel - AST parsing for _hyperscript\nimport { Parser } from './parser.js';\nimport { EmptyCommandListCommand, UnlessStatementModifier, HyperscriptProgram, FailedFeature, FailedCommand } from '../parsetree/internals.js';\nimport { Command, Feature } from '../parsetree/base.js';\nimport { ParseRecoverySentinel } from './parser.js';\n\nexport class LanguageKernel {\n\n #grammar = {};\n #commands = {};\n #features = {};\n #leafExpressions = [];\n #indirectExpressions = [];\n #postfixExpressions = [];\n #unaryExpressions = [];\n #topExpressions = [];\n #assignableExpressions = [];\n\n constructor() {\n // Top-level program structure\n this.addGrammarElement(\"hyperscript\", this.parseHyperscriptProgram.bind(this));\n this.addGrammarElement(\"feature\", this.parseFeature.bind(this));\n\n // Command structure\n this.addGrammarElement(\"commandList\", this.parseCommandList.bind(this));\n this.addGrammarElement(\"command\", this.parseCommand.bind(this));\n this.addGrammarElement(\"indirectStatement\", this.parseIndirectStatement.bind(this));\n\n // Expression precedence chain (top to bottom)\n this.addGrammarElement(\"expression\", this.parseExpression.bind(this));\n this.addGrammarElement(\"assignableExpression\", this.parseAssignableExpression.bind(this));\n this.addGrammarElement(\"unaryExpression\", this.parseUnaryExpression.bind(this));\n this.addGrammarElement(\"postfixExpression\", this.parsePostfixExpression.bind(this));\n this.addGrammarElement(\"primaryExpression\", this.parsePrimaryExpression.bind(this));\n this.addGrammarElement(\"indirectExpression\", this.parseIndirectExpression.bind(this));\n this.addGrammarElement(\"leaf\", this.parseLeaf.bind(this));\n\n }\n\n parseFeature(parser) {\n if (parser.matchOpToken(\"(\")) {\n var featureElement = parser.requireElement(\"feature\");\n parser.requireOpToken(\")\");\n return featureElement;\n }\n var featureDefinition = this.#features[parser.currentToken().value || \"\"];\n if (featureDefinition) {\n return featureDefinition(parser);\n }\n }\n\n parseCommand(parser) {\n if (parser.matchOpToken(\"(\")) {\n const commandElement = parser.requireElement(\"command\");\n parser.requireOpToken(\")\");\n return commandElement;\n }\n var commandDefinition = this.#commands[parser.currentToken().value || \"\"];\n let commandElement;\n if (commandDefinition) {\n commandElement = commandDefinition(parser);\n } else if (parser.currentToken().type === \"IDENTIFIER\") {\n commandElement = parser.parseElement(\"pseudoCommand\");\n }\n if (commandElement) {\n return this.parseElement(\"indirectStatement\", parser, commandElement);\n }\n return commandElement;\n }\n\n parseCommandList(parser) {\n if (parser.hasMore()) {\n var keyword = parser.currentToken().value;\n var cmd;\n try {\n cmd = parser.parseElement(\"command\");\n } catch (e) {\n if (e instanceof ParseRecoverySentinel) {\n cmd = new FailedCommand(e.parseError, keyword);\n this.#syncToCommand(parser);\n } else {\n throw e;\n }\n }\n if (cmd) {\n parser.matchToken(\"then\");\n const next = parser.parseElement(\"commandList\");\n if (next) cmd.next = next;\n return cmd;\n }\n }\n return new EmptyCommandListCommand();\n }\n\n parseLeaf(parser) {\n var result = parser.parseAnyOf(this.#leafExpressions);\n // symbol is last so it doesn't consume any constants\n if (result == null) {\n return parser.parseElement(\"symbol\");\n }\n return result;\n }\n\n parseIndirectExpression(parser, root) {\n for (var i = 0; i < this.#indirectExpressions.length; i++) {\n var indirect = this.#indirectExpressions[i];\n root.endToken = parser.lastMatch();\n var result = this.parseElement(indirect, parser, root);\n if (result) {\n return result;\n }\n }\n return root;\n }\n\n parsePostfixExpression(parser) {\n var root = parser.parseElement(\"negativeNumber\");\n for (var i = 0; i < this.#postfixExpressions.length; i++) {\n var postfixType = this.#postfixExpressions[i];\n var result = this.parseElement(postfixType, parser, root);\n if (result) {\n return result;\n }\n }\n return root;\n }\n\n parseUnaryExpression(parser) {\n parser.matchToken(\"the\"); // optional \"the\"\n var result = parser.parseAnyOf(this.#unaryExpressions);\n if (result) return this.parseElement(\"indirectExpression\", parser, result);\n return parser.parseElement(\"postfixExpression\");\n }\n\n parseExpression(parser) {\n parser.matchToken(\"the\"); // optional \"the\"\n return parser.parseAnyOf(this.#topExpressions);\n }\n\n parseAssignableExpression(parser) {\n parser.matchToken(\"the\"); // optional \"the\"\n var expr = parser.parseElement(\"primaryExpression\");\n var checkExpr = expr;\n while (checkExpr && checkExpr.type === \"parenthesized\") {\n checkExpr = checkExpr.expr;\n }\n if (checkExpr && this.#assignableExpressions.includes(checkExpr.type)) {\n return expr;\n } else {\n parser.raiseError(\n \"A target expression must be writable. The expression type '\" + (checkExpr && checkExpr.type) + \"' is not.\"\n );\n }\n }\n\n parseIndirectStatement(parser, root) {\n if (parser.matchToken(\"unless\")) {\n root.endToken = parser.lastMatch();\n var conditional = parser.requireElement(\"expression\");\n var unless = new UnlessStatementModifier(root, conditional);\n root.parent = unless;\n return unless;\n }\n return root;\n }\n\n parsePrimaryExpression(parser) {\n var leaf = parser.parseElement(\"leaf\");\n if (leaf) {\n return this.parseElement(\"indirectExpression\", parser, leaf);\n }\n parser.raiseError(\"Unexpected value: \" + parser.currentToken().value);\n }\n\n parseHyperscriptProgram(parser) {\n var features = [];\n if (parser.hasMore()) {\n while (parser.currentToken().type !== \"EOF\") {\n var keyword = parser.currentToken().value;\n if (parser.featureStart(parser.currentToken()) || parser.currentToken().value === \"(\") {\n try {\n var feature = parser.requireElement(\"feature\");\n features.push(feature);\n parser.matchToken(\"end\"); // optional end\n } catch (e) {\n if (e instanceof ParseRecoverySentinel) {\n features.push(new FailedFeature(e.parseError, keyword));\n this.#syncToFeature(parser);\n } else {\n throw e;\n }\n }\n } else if (parser.currentToken().value === \"end\") {\n break; // scope terminator (e.g. behavior body) - leave for outer parser\n } else {\n // Unconsumed token between features - report and sync\n try {\n parser.raiseError();\n } catch (e) {\n if (e instanceof ParseRecoverySentinel) {\n features.push(new FailedFeature(e.parseError, keyword));\n this.#syncToFeature(parser);\n } else {\n throw e;\n }\n }\n }\n }\n }\n return new HyperscriptProgram(features);\n }\n\n use(plugin) {\n plugin(this)\n return this\n }\n\n initElt(parseElement, start, tokens) {\n parseElement.startToken = start;\n parseElement.programSource = tokens.source;\n }\n\n parseElement(type, parser, root = undefined) {\n var elementDefinition = this.#grammar[type];\n if (elementDefinition) {\n var tokens = parser.tokens;\n var start = tokens.currentToken();\n var parseElement = elementDefinition(parser, root);\n if (parseElement) {\n this.initElt(parseElement, start, tokens);\n parseElement.endToken = parseElement.endToken || tokens.lastMatch();\n var root = parseElement.root;\n while (root != null) {\n this.initElt(root, start, tokens);\n root = root.root;\n }\n }\n return parseElement;\n }\n }\n\n requireElement(type, parser, message, root) {\n var result = this.parseElement(type, parser, root);\n if (!result) parser.raiseError(message || \"Expected \" + type);\n return result;\n }\n\n parseAnyOf(types, parser) {\n for (var i = 0; i < types.length; i++) {\n var type = types[i];\n var expression = this.parseElement(type, parser);\n if (expression) {\n return expression;\n }\n }\n }\n\n addGrammarElement(name, definition) {\n if (this.#grammar[name]) {\n throw new Error(`Grammar element '${name}' already exists`);\n }\n this.#grammar[name] = definition;\n }\n\n addCommand(keyword, definition) {\n var commandGrammarType = keyword + \"Command\";\n this.#grammar[commandGrammarType] = definition;\n this.#commands[keyword] = definition;\n }\n\n addCommands(...commandClasses) {\n for (const CommandClass of commandClasses) {\n if (!CommandClass.keyword) {\n throw new Error(`Command class ${CommandClass.name} must have a static 'keyword' property`);\n }\n if (!CommandClass.parse) {\n throw new Error(`Command class ${CommandClass.name} must have a static 'parse' method`);\n }\n var keywords = Array.isArray(CommandClass.keyword) ? CommandClass.keyword : [CommandClass.keyword];\n for (var kw of keywords) this.addCommand(kw, CommandClass.parse);\n }\n }\n\n addFeatures(...featureClasses) {\n for (const FeatureClass of featureClasses) {\n if (!FeatureClass.keyword) {\n throw new Error(`Feature class ${FeatureClass.name} must have a static 'keyword' property`);\n }\n if (!FeatureClass.parse) {\n throw new Error(`Feature class ${FeatureClass.name} must have a static 'parse' method`);\n }\n this.addFeature(FeatureClass.keyword, FeatureClass.parse);\n }\n }\n\n addFeature(keyword, definition) {\n var featureGrammarType = keyword + \"Feature\";\n this.#grammar[featureGrammarType] = definition;\n this.#features[keyword] = definition;\n }\n\n /**\n * Register a parse element class based on its static metadata.\n * Commands need `static keyword`, expressions need `static grammarName`.\n */\n registerParseElement(ElementClass) {\n if (!ElementClass.parse) return;\n\n const parse = ElementClass.parse.bind(ElementClass);\n\n // Commands with keyword (supports string or array of strings)\n if (ElementClass.keyword && ElementClass.prototype instanceof Command) {\n var keywords = Array.isArray(ElementClass.keyword) ? ElementClass.keyword : [ElementClass.keyword];\n for (var kw of keywords) this.addCommand(kw, parse);\n return;\n }\n\n // Features with keyword (supports string or array of strings)\n if (ElementClass.keyword && ElementClass.prototype instanceof Feature) {\n var keywords = Array.isArray(ElementClass.keyword) ? ElementClass.keyword : [ElementClass.keyword];\n for (var kw of keywords) this.addFeature(kw, parse);\n return;\n }\n\n // Grammar elements with grammarName\n const name = ElementClass.grammarName;\n if (!name) return;\n\n switch (ElementClass.expressionType) {\n case 'leaf': this.addLeafExpression(name, parse); break;\n case 'indirect': this.addIndirectExpression(name, parse); break;\n case 'unary': this.addUnaryExpression(name, parse); break;\n case 'top': this.addTopExpression(name, parse); break;\n case 'postfix': this.addPostfixExpression(name, parse); break;\n default: this.addGrammarElement(name, parse); break;\n }\n\n if (ElementClass.assignable) {\n this.#assignableExpressions.push(name);\n }\n }\n\n /**\n * Register all exported parse element classes from a module.\n * Iterates over module exports and registers any class with\n * a static `parse` method and appropriate metadata.\n */\n registerModule(module) {\n for (const exported of Object.values(module)) {\n if (typeof exported === 'function' && exported.parse) {\n this.registerParseElement(exported);\n }\n }\n }\n\n addLeafExpression(name, definition) {\n this.#leafExpressions.push(name);\n this.addGrammarElement(name, definition);\n }\n\n addIndirectExpression(name, definition) {\n this.#indirectExpressions.push(name);\n this.addGrammarElement(name, definition);\n }\n\n addPostfixExpression(name, definition) {\n this.#postfixExpressions.push(name);\n this.addGrammarElement(name, definition);\n }\n\n addUnaryExpression(name, definition) {\n this.#unaryExpressions.push(name);\n this.addGrammarElement(name, definition);\n }\n\n addTopExpression(name, definition) {\n this.#topExpressions.push(name);\n this.addGrammarElement(name, definition);\n }\n\n commandStart(token) {\n return this.#commands[token.value || \"\"];\n }\n\n featureStart(token) {\n return this.#features[token.value || \"\"];\n }\n\n parseHyperScript(tokens) {\n var parser = new Parser(this, tokens);\n var result;\n var lastError = null;\n try {\n result = parser.parseElement(\"hyperscript\");\n if (tokens.hasMore()) parser.raiseError();\n } catch (e) {\n if (!(e instanceof ParseRecoverySentinel)) throw e;\n lastError = e.parseError;\n }\n if (!result) result = new HyperscriptProgram([]);\n result.errors = result.collectErrors();\n if (lastError) result.errors.push(lastError);\n return result;\n }\n\n #syncToFeature(parser) {\n parser.tokens.clearFollows();\n while (parser.hasMore() &&\n !parser.featureStart(parser.currentToken()) &&\n parser.currentToken().value !== \"end\" &&\n parser.currentToken().type !== \"EOF\") {\n parser.tokens.consumeToken();\n }\n }\n\n #syncToCommand(parser) {\n parser.tokens.clearFollows();\n while (parser.hasMore() &&\n !parser.commandBoundary(parser.currentToken())) {\n parser.tokens.consumeToken();\n }\n // consume 'then' if that's what we landed on\n if (parser.hasMore() && parser.currentToken().value === \"then\") {\n parser.tokens.consumeToken();\n }\n }\n\n parse(tokenizer, src) {\n var tokens = tokenizer.tokenize(src);\n var parser = new Parser(this, tokens);\n var result, lastError;\n try {\n if (parser.commandStart(tokens.currentToken())) {\n result = this.requireElement(\"commandList\", parser);\n if (tokens.hasMore()) parser.raiseError();\n parser.ensureTerminated(result);\n } else if (parser.featureStart(tokens.currentToken())) {\n result = this.requireElement(\"hyperscript\", parser);\n if (tokens.hasMore()) parser.raiseError();\n } else {\n result = this.requireElement(\"expression\", parser);\n if (tokens.hasMore()) parser.raiseError();\n }\n } catch (e) {\n if (!(e instanceof ParseRecoverySentinel)) throw e;\n lastError = e.parseError;\n }\n if (!result && lastError) {\n result = { type: \"empty\", errors: [lastError] };\n } else if (result) {\n result.errors = result.collectErrors();\n if (lastError) result.errors.push(lastError);\n }\n return result;\n }\n\n}\n", "// Configuration for _hyperscript\n\nexport const config = {\n attributes: \"_, script, data-script\",\n defaultTransition: \"all 500ms ease-in\",\n disableSelector: \"[disable-scripting], [data-disable-scripting]\",\n fetchThrowsOn: [/4.*/, /5.*/],\n hideShowStrategies: {},\n debugMode: false,\n logAll: false,\n mutatingMethods: {\n Array: [\"push\", \"pop\", \"shift\", \"unshift\", \"splice\", \"sort\", \"reverse\", \"fill\", \"copyWithin\"],\n Set: [\"add\", \"delete\", \"clear\"],\n Map: [\"set\", \"delete\", \"clear\"],\n },\n}\n", "// Type conversions for _hyperscript\n\nclass HyperscriptFormData {\n result = {};\n\n addElement(node) {\n if (node.name == undefined || node.value == undefined) return;\n if (node.type === \"radio\" && !node.checked) return;\n\n var name = node.name;\n var value;\n\n if (node.type === \"checkbox\") {\n value = node.checked ? [node.value] : undefined;\n } else if (node.type === \"select-multiple\") {\n value = Array.from(node.options).filter(o => o.selected).map(o => o.value);\n } else {\n value = node.value;\n }\n\n if (value == undefined) return;\n\n if (this.result[name] == undefined) {\n this.result[name] = value;\n } else {\n var existing = Array.isArray(this.result[name]) ? this.result[name] : [this.result[name]];\n this.result[name] = existing.concat(value);\n }\n }\n\n addContainer(node) {\n if (node.name != undefined && node.value != undefined) {\n this.addElement(node);\n return;\n }\n if (node.querySelectorAll) {\n node.querySelectorAll(\"input,select,textarea\").forEach(child => this.addElement(child));\n }\n }\n}\n\nfunction _toHTML(value) {\n if (value instanceof Array) {\n return value.map(item => _toHTML(item)).join(\"\");\n }\n if (value instanceof HTMLElement) {\n return value.outerHTML;\n }\n if (value instanceof NodeList) {\n var result = \"\";\n for (var i = 0; i < value.length; i++) {\n if (value[i] instanceof HTMLElement) {\n result += value[i].outerHTML;\n }\n }\n return result;\n }\n if (value.toString) {\n return value.toString();\n }\n return \"\";\n}\n\nexport const conversions = {\n dynamicResolvers: [\n // Fixed-point number conversion\n function(str, value) {\n if (str === \"Fixed\") {\n return Number(value).toFixed();\n } else if (str.startsWith(\"Fixed:\")) {\n let num = str.split(\":\")[1];\n return Number(value).toFixed(parseInt(num));\n }\n },\n // Values conversion - extracts form values from DOM nodes\n function(str, node, runtime) {\n if (str !== \"Values\") return;\n var formData = new HyperscriptFormData();\n runtime.implicitLoop(node, (node) => formData.addContainer(node));\n return formData.result;\n },\n ],\n String: function (val) {\n if (val.toString) {\n return val.toString();\n } else {\n return \"\" + val;\n }\n },\n Int: function (val) {\n return parseInt(val);\n },\n Float: function (val) {\n return parseFloat(val);\n },\n Number: function (val) {\n return Number(val);\n },\n Boolean: function (val) {\n return !!val;\n },\n Date: function (val) {\n return new Date(val);\n },\n Array: function (val) {\n return Array.from(val);\n },\n JSON: function (val) {\n if (typeof Response !== \"undefined\" && val instanceof Response) return val.json();\n return JSON.parse(val);\n },\n JSONString: function (val) {\n return JSON.stringify(val);\n },\n Object: function (val) {\n if (val instanceof String) {\n val = val.toString();\n }\n if (typeof val === \"string\") {\n return JSON.parse(val);\n } else {\n return Object.assign({}, val);\n }\n },\n FormEncoded: function (val) {\n return new URLSearchParams(val).toString();\n },\n Set: function (val) {\n return new Set(val);\n },\n Map: function (val) {\n return new Map(Object.entries(val));\n },\n Keys: function (val) {\n if (val instanceof Map) return Array.from(val.keys());\n return Object.keys(val);\n },\n Entries: function (val) {\n if (val instanceof Map) return Array.from(val.entries());\n return Object.entries(val);\n },\n Reversed: function (val) {\n return Array.from(val).reverse();\n },\n Unique: function (val) {\n return [...new Set(val)];\n },\n Flat: function (val) {\n return Array.from(val).flat();\n },\n HTML: _toHTML,\n Stream: function () {\n throw new Error(\"The Stream conversion requires the SSE extension. \" +\n \"Include dist/ext/sse.js or dist/ext/sse.esm.js after hyperscript.\");\n },\n Fragment: function (val, runtime) {\n var frag = document.createDocumentFragment();\n runtime.implicitLoop(val, (val) => {\n if (val instanceof Node) frag.append(val);\n else {\n var temp = document.createElement(\"template\");\n temp.innerHTML = val;\n frag.append(temp.content);\n }\n });\n return frag;\n },\n}\n", "// Cookie management for _hyperscript\n\nexport class CookieJar {\n #parseCookies() {\n if (!document.cookie) return [];\n return document.cookie.split(\"; \").map(entry => {\n var eq = entry.indexOf(\"=\");\n return { name: entry.slice(0, eq), value: decodeURIComponent(entry.slice(eq + 1)) };\n });\n }\n\n get(target, prop) {\n if (prop === 'then') {\n return null; // prevent Promise detection\n } else if (prop === 'length') {\n return this.#parseCookies().length;\n } else if (prop === 'clear') {\n return (name) => {\n document.cookie = name + \"=;expires=Thu, 01 Jan 1970 00:00:00 GMT\";\n };\n } else if (prop === 'clearAll') {\n return () => {\n for (const cookie of this.#parseCookies()) {\n document.cookie = cookie.name + \"=;expires=Thu, 01 Jan 1970 00:00:00 GMT\";\n }\n };\n } else if (prop === Symbol.iterator) {\n var cookies = this.#parseCookies();\n return cookies[Symbol.iterator].bind(cookies);\n } else if (typeof prop === \"string\") {\n if (!isNaN(prop)) {\n return this.#parseCookies()[parseInt(prop)];\n }\n var match = this.#parseCookies().find(c => c.name === prop);\n return match ? match.value : undefined;\n }\n }\n\n set(target, prop, value) {\n var parts = [];\n if (typeof value === 'string') {\n parts.push(encodeURIComponent(value));\n parts.push(\"samesite=lax\");\n } else {\n parts.push(encodeURIComponent(value.value));\n if (value.expires) parts.push(\"expires=\" + value.expires);\n if (value.maxAge) parts.push(\"max-age=\" + value.maxAge);\n if (value.partitioned) parts.push(\"partitioned=\" + value.partitioned);\n if (value.path) parts.push(\"path=\" + value.path);\n if (value.samesite) parts.push(\"samesite=\" + value.samesite);\n if (value.secure) parts.push(\"secure\");\n }\n document.cookie = String(prop) + \"=\" + parts.join(\";\");\n return true;\n }\n\n proxy() {\n return new Proxy({}, this)\n }\n}\n\n", "// Collection utilities for _hyperscript\n\nexport const SHOULD_AUTO_ITERATE_SYM = Symbol()\n\nexport class ElementCollection {\n constructor(css, relativeToElement, escape, runtime) {\n this._css = css;\n this.relativeToElement = relativeToElement;\n this.escape = escape;\n this._runtime = runtime;\n this[SHOULD_AUTO_ITERATE_SYM] = true;\n }\n\n get css() {\n if (this.escape) {\n return this._runtime.escapeSelector(this._css);\n } else {\n return this._css;\n }\n }\n\n get className() {\n return this._css.slice(1);\n }\n\n get id() {\n return this.className;\n }\n\n contains(elt) {\n for (let element of this) {\n if (element.contains(elt)) {\n return true;\n }\n }\n return false;\n }\n\n get length() {\n return this.selectMatches().length;\n }\n\n [Symbol.iterator]() {\n let query = this.selectMatches();\n return query [Symbol.iterator]();\n }\n\n /** @returns {NodeList} all elements matching this.css under the root node */\n selectMatches() {\n var root = this._runtime.getRootNode(this.relativeToElement);\n return this._runtime.resolveQuery(root, this.css);\n }\n}\n\nexport class TemplatedQueryElementCollection extends ElementCollection {\n constructor(css, relativeToElement, templateParts, runtime) {\n super(css, relativeToElement, false, runtime);\n this.templateParts = templateParts;\n this.elements = templateParts.filter(elt => elt instanceof Element);\n }\n\n get css() {\n let rv = \"\", i = 0\n for (const val of this.templateParts) {\n if (val instanceof Element) {\n rv += \"[data-hs-query-id='\" + i++ + \"']\";\n } else rv += val;\n }\n return rv;\n }\n\n [Symbol.iterator]() {\n this.elements.forEach((el, i) => el.dataset.hsQueryId = i);\n const rv = super[Symbol.iterator]();\n this.elements.forEach(el => el.removeAttribute('data-hs-query-id'));\n return rv;\n }\n}\n\nexport class RegExpIterator {\n constructor(re, str) {\n this.re = re;\n this.str = str;\n }\n\n next() {\n const match = this.re.exec(this.str);\n if (match === null) return { done: true };\n if (match[0].length === 0) this.re.lastIndex++;\n return { value: match };\n }\n}\n\nexport class RegExpIterable {\n constructor(re, flags, str) {\n this.re = re;\n this.flags = flags;\n this.str = str;\n }\n\n [Symbol.iterator]() {\n return new RegExpIterator(new RegExp(this.re, this.flags), this.str);\n }\n}\n\nexport class HyperscriptModule extends EventTarget {\n constructor(mod) {\n super();\n this.module = mod;\n }\n\n toString() {\n return this.module.id;\n }\n}\n", "// Runtime - Execution engine for _hyperscript\nimport { config } from '../config.js';\nimport { conversions } from './conversions.js';\nimport { CookieJar } from './cookies.js';\nimport { ElementCollection, SHOULD_AUTO_ITERATE_SYM } from './collections.js';\nimport { Parser } from '../parser.js';\n\n// cookie jar proxy for runtime\nlet cookies = new CookieJar().proxy();\n\nexport class Context {\n constructor(owner, feature, hyperscriptTarget, event, runtime, globalScope, kernel, tokenizer) {\n this.meta = {\n parser: kernel,\n tokenizer: tokenizer,\n runtime,\n owner: owner,\n feature: feature,\n iterators: {},\n ctx: this\n }\n this.locals = {\n cookies: cookies\n };\n if (typeof navigator !== \"undefined\" && navigator.clipboard) {\n Object.defineProperty(this.locals, 'clipboard', {\n get() { return navigator.clipboard.readText(); },\n set(v) { navigator.clipboard.writeText(String(v)); },\n enumerable: false,\n configurable: true\n });\n }\n if (typeof window !== \"undefined\" && window.getSelection) {\n Object.defineProperty(this.locals, 'selection', {\n get() { return window.getSelection().toString(); },\n enumerable: true,\n configurable: true\n });\n }\n this.me = hyperscriptTarget;\n this.you = undefined\n this.result = undefined\n this.beingTested = null\n this.event = event;\n this.target = event?.target ?? null;\n this.detail = event?.detail ?? null;\n this.sender = event?.detail?.sender ?? null;\n this.body = \"document\" in globalScope ? document.body : null;\n runtime.addFeatures(owner, this);\n }\n}\n\n\nexport class Runtime {\n\n static HALT = {};\n HALT = Runtime.HALT;\n\n #kernel;\n #tokenizer;\n #globalScope;\n #reactivity;\n #morphEngine;\n #scriptAttrs = null;\n\n constructor(globalScope, kernel, tokenizer, reactivity, morphEngine) {\n this.#globalScope = globalScope;\n this.#kernel = kernel;\n this.#tokenizer = tokenizer;\n this.#reactivity = reactivity;\n this.#morphEngine = morphEngine;\n }\n\n get globalScope() {\n return this.#globalScope;\n }\n\n get reactivity() {\n return this.#reactivity;\n }\n\n // =================================================================\n // Core execution engine\n // =================================================================\n\n unifiedExec(command, ctx) {\n while (true) {\n if (config.debugMode) {\n var target = ctx.meta.owner || ctx.me;\n var eventResult = this.triggerEvent(\n target,\n \"hyperscript:beforeEval\",\n { command: command, ctx: ctx },\n );\n if (!eventResult) {\n if (ctx.meta.onHalt) ctx.meta.onHalt();\n return;\n }\n }\n var afterFired = false;\n\n try {\n var next = this.unifiedEval(command, ctx);\n } catch (e) {\n if (config.debugMode) {\n this.triggerEvent(target, \"hyperscript:afterEval\", {\n command: command,\n ctx: ctx,\n error: e,\n });\n afterFired = true;\n }\n\n if (ctx.meta.handlingFinally) {\n console.error(\" Exception in finally block: \", e);\n next = Runtime.HALT;\n } else {\n this.registerHyperTrace(ctx, e);\n if (ctx.meta.errorHandler && !ctx.meta.handlingError) {\n ctx.meta.handlingError = true;\n ctx.locals[ctx.meta.errorSymbol] = e;\n command = ctx.meta.errorHandler;\n continue;\n } else {\n ctx.meta.currentException = e;\n next = Runtime.HALT;\n }\n }\n }\n\n if (config.debugMode && !afterFired) {\n this.triggerEvent(target, \"hyperscript:afterEval\", {\n command: command,\n ctx: ctx,\n next: next,\n });\n }\n\n if (next == null) {\n throw new Error(\"Command \" + (command.type || \"unknown\") + \" did not return a next element to execute\");\n } else if (next.then) {\n next.then(resolvedNext => {\n this.unifiedExec(resolvedNext, ctx);\n }).catch(reason => {\n this.unifiedExec({\n resolve: function(){\n throw reason;\n }\n }, ctx);\n });\n return;\n } else if (next === Runtime.HALT) {\n if (ctx.meta.finallyHandler && !ctx.meta.handlingFinally) {\n ctx.meta.handlingFinally = true;\n command = ctx.meta.finallyHandler;\n } else {\n if (ctx.meta.onHalt) {\n ctx.meta.onHalt();\n }\n if (ctx.meta.currentException) {\n if (ctx.meta.reject) {\n ctx.meta.reject(ctx.meta.currentException);\n return;\n } else {\n throw ctx.meta.currentException;\n }\n } else {\n return;\n }\n }\n } else {\n command = next;\n }\n }\n }\n\n unifiedEval(parseElement, ctx) {\n var async = false;\n var evaluatedArgs = {};\n\n if (parseElement.args) {\n for (var [name, argument] of Object.entries(parseElement.args)) {\n if (argument == null) {\n evaluatedArgs[name] = null;\n } else if (Array.isArray(argument)) {\n var arr = [];\n for (var j = 0; j < argument.length; j++) {\n var element = argument[j];\n if (element == null) {\n arr.push(null);\n } else if (element.evaluate) {\n var value = element.evaluate(ctx);\n if (value && value.then) {\n async = true;\n }\n arr.push(value);\n } else {\n arr.push(element);\n }\n }\n evaluatedArgs[name] = arr;\n } else if (argument.evaluate) {\n var value = argument.evaluate(ctx);\n if (value && value.then) {\n async = true;\n }\n evaluatedArgs[name] = value;\n } else {\n evaluatedArgs[name] = argument;\n }\n }\n }\n if (async) {\n return new Promise((resolve, reject) => {\n var keys = Object.keys(evaluatedArgs);\n var values = Object.values(evaluatedArgs).map(v =>\n Array.isArray(v) ? Promise.all(v) : v\n );\n Promise.all(values)\n .then(function (resolved) {\n try {\n var finalArgs = {};\n keys.forEach((k, i) => finalArgs[k] = resolved[i]);\n resolve(parseElement.resolve(ctx, finalArgs));\n } catch (e) {\n reject(e);\n }\n })\n .catch(function (reason) {\n reject(reason);\n });\n });\n } else {\n return parseElement.resolve(ctx, evaluatedArgs);\n }\n }\n\n findNext(command, context) {\n if (command) {\n if (command.resolveNext) {\n return command.resolveNext(context);\n } else if (command.next) {\n return command.next;\n } else {\n return this.findNext(command.parent, context);\n }\n }\n }\n\n\n // =================================================================\n // Context and scope\n // =================================================================\n\n makeContext(owner, feature, hyperscriptTarget, event) {\n return new Context(owner, feature, hyperscriptTarget, event, this, this.#globalScope, this.#kernel, this.#tokenizer)\n }\n\n getHyperscriptFeatures(elt) {\n var data = this.getInternalData(elt);\n if (!data.features) {\n data.features = {};\n }\n return data.features;\n }\n\n addFeatures(owner, ctx) {\n if (owner) {\n Object.assign(ctx.locals, this.getHyperscriptFeatures(owner));\n this.addFeatures(owner.parentElement, ctx);\n }\n }\n\n // =================================================================\n // Symbol and property resolution\n // =================================================================\n\n #isReservedWord(str) {\n return [\"meta\", \"it\", \"result\", \"locals\", \"event\", \"target\", \"detail\", \"sender\", \"body\"].includes(str)\n }\n\n #isHyperscriptContext(context) {\n return context instanceof Context;\n }\n\n resolveSymbol(str, context, type, targetElement) {\n if (str === \"me\" || str === \"my\" || str === \"I\") return context.me;\n if (str === \"it\" || str === \"its\") return context.beingTested ?? context.result;\n if (str === \"result\") return context.result;\n if (str === \"you\" || str === \"your\" || str === \"yourself\") return context.you;\n\n if (type === \"global\") {\n if (this.reactivity.isTracking) this.reactivity.trackGlobalSymbol(str);\n var val = this.#globalScope[str];\n this.#trackMutation(val);\n return val;\n }\n if (type === \"element\") {\n if (this.reactivity.isTracking) this.reactivity.trackElementSymbol(str, context.meta.owner);\n var val = this.#getElementScope(context)[str];\n this.#trackMutation(val);\n return val;\n }\n if (type === \"inherited\") {\n var inherited = this.#resolveInherited(str, context, targetElement);\n if (this.reactivity.isTracking) {\n var trackElement = inherited.element || targetElement || context.meta?.owner;\n if (trackElement) {\n this.reactivity.trackElementSymbol(str, trackElement);\n }\n }\n this.#trackMutation(inherited.value);\n return inherited.value;\n }\n // local scope resolution: meta.context (set only inside `on click[...]`\n // filter expressions to the current event) \u2192 locals \u2192 element \u2192 global.\n // Event destructuring in handler bodies is explicit: `on click(x, y)`\n // copies event/detail props into ctx.locals at handler entry, so in body\n // code `x` is a real local, not a fallback lookup.\n if (context.meta?.context) {\n var fromMetaContext = context.meta.context[str];\n if (typeof fromMetaContext !== \"undefined\") return fromMetaContext;\n if (context.meta.context.detail) {\n fromMetaContext = context.meta.context.detail[str];\n if (typeof fromMetaContext !== \"undefined\") return fromMetaContext;\n }\n }\n var fromContext = this.#isHyperscriptContext(context) && !this.#isReservedWord(str)\n ? context.locals[str] : context[str];\n if (typeof fromContext !== \"undefined\") return fromContext;\n\n // element scope\n var elementScope = this.#getElementScope(context);\n fromContext = elementScope[str];\n if (typeof fromContext !== \"undefined\") {\n if (this.reactivity.isTracking) this.reactivity.trackElementSymbol(str, context.meta.owner);\n this.#trackMutation(fromContext);\n return fromContext;\n }\n // global scope (or not found - track as global so we catch the first write)\n if (this.reactivity.isTracking) this.reactivity.trackGlobalSymbol(str);\n var val = this.#globalScope[str];\n this.#trackMutation(val);\n return val;\n }\n\n setSymbol(str, context, type, value, targetElement) {\n if (type === \"global\") {\n this.#globalScope[str] = value;\n this.reactivity.notifyGlobalSymbol(str);\n return;\n }\n if (type === \"element\") {\n this.#getElementScope(context)[str] = value;\n this.reactivity.notifyElementSymbol(str, context.meta.owner);\n return;\n }\n if (type === \"inherited\") {\n var inherited = this.#resolveInherited(str, context, targetElement);\n if (inherited.element) {\n this.getInternalData(inherited.element).elementScope[str] = value;\n this.reactivity.notifyElementSymbol(str, inherited.element);\n } else {\n var owner = targetElement || context.meta?.owner;\n if (owner) {\n var internalData = this.getInternalData(owner);\n if (!internalData.elementScope) internalData.elementScope = {};\n internalData.elementScope[str] = value;\n this.reactivity.notifyElementSymbol(str, owner);\n }\n }\n return;\n }\n // local scope resolution (tries locals \u2192 element \u2192 global chain)\n if (this.#isHyperscriptContext(context) && !this.#isReservedWord(str) && typeof context.locals[str] !== \"undefined\") {\n context.locals[str] = value;\n return;\n }\n var elementScope = this.#getElementScope(context);\n if (typeof elementScope[str] !== \"undefined\") {\n elementScope[str] = value;\n this.reactivity.notifyElementSymbol(str, context.meta.owner);\n } else if (this.#isHyperscriptContext(context) && !this.#isReservedWord(str)) {\n context.locals[str] = value;\n } else {\n context[str] = value;\n }\n }\n\n getInternalData(elt) {\n if (!elt._hyperscript) {\n elt._hyperscript = {};\n }\n return elt._hyperscript;\n }\n\n #resolveInherited(str, context, startElement) {\n var elt = startElement || (context.meta && context.meta.owner);\n while (elt) {\n var internalData = elt._hyperscript;\n if (internalData && internalData.elementScope && str in internalData.elementScope) {\n return { value: internalData.elementScope[str], element: elt };\n }\n // Check dom-scope attribute for scope control\n var domScope = elt.getAttribute && elt.getAttribute('dom-scope');\n if (domScope) {\n if (domScope === 'isolated') {\n return { value: undefined, element: null };\n }\n // \"closest \" - jump to matching ancestor\n var match = domScope.match(/^closest\\s+(.+)/);\n if (match) {\n elt = elt.parentElement && elt.parentElement.closest(match[1]);\n continue;\n }\n // \"parent of \" - jump to the parent of the nearest matching ancestor\n match = domScope.match(/^parent\\s+of\\s+(.+)/);\n if (match) {\n var target = elt.closest(match[1]);\n elt = target && target.parentElement;\n continue;\n }\n }\n elt = elt.parentElement;\n }\n return { value: undefined, element: null };\n }\n\n #getElementScope(context) {\n var elt = context.meta && context.meta.owner;\n if (elt) {\n var internalData = this.getInternalData(elt);\n var scopeName = \"elementScope\";\n if (context.meta.feature && context.meta.feature.behavior) {\n scopeName = context.meta.feature.behavior + \"Scope\";\n }\n var elementScope = internalData[scopeName];\n if (!elementScope) {\n elementScope = {};\n internalData[scopeName] = elementScope;\n }\n return elementScope;\n } else {\n return {};\n }\n }\n\n #flatGet(root, property, getter) {\n if (root != null) {\n var val = getter(root, property);\n if (typeof val !== \"undefined\") {\n return val;\n }\n if (this.shouldAutoIterate(root)) {\n var result = [];\n for (var component of root) {\n var componentValue = getter(component, property);\n result.push(componentValue);\n }\n return result;\n }\n }\n }\n\n resolveProperty(root, property) {\n if (this.reactivity.isTracking) this.reactivity.trackProperty(root, property);\n return this.#flatGet(root, property, (root, property) => root[property])\n }\n\n /**\n * Set a property on an object and notify the reactivity system.\n * @param {Object} obj - DOM element or plain JS object\n * @param {string} property\n * @param {any} value\n */\n setProperty(obj, property, value) {\n obj[property] = value;\n this.reactivity.notifyProperty(obj);\n }\n\n /**\n * Notify the reactivity system that an object was mutated in-place.\n * Call this after operations like push, splice, append, etc.\n * @param {Object} obj - The mutated object\n */\n notifyMutation(obj) {\n this.reactivity.notifyProperty(obj);\n }\n\n morph(elt, content) {\n this.#morphEngine.morph(elt, content, {\n beforeNodeRemoved: (node) => {\n if (node.nodeType === 1) this.cleanup(node);\n },\n afterNodeAdded: (node) => {\n if (node.nodeType === 1) this.processNode(node);\n },\n afterNodeMorphed: (node) => {\n if (node.nodeType === 1) this.processNode(node);\n }\n });\n }\n\n replaceInDom(target, value) {\n this.implicitLoop(target, (elt) => {\n var parent = elt.parentElement;\n if (value instanceof Node) {\n elt.replaceWith(value.cloneNode(true));\n } else {\n elt.replaceWith(this.convertValue(value, \"Fragment\"));\n }\n if (parent) this.processNode(parent);\n });\n }\n\n /**\n * Check if a method call is known to mutate its receiver, and notify if so.\n * @param {Object} target - The object the method was called on\n * @param {string} methodName - The method name\n */\n maybeNotify(target, methodName) {\n if (target == null || typeof target !== \"object\") return;\n var typeName = target.constructor && target.constructor.name;\n var methods = typeName && config.mutatingMethods[typeName];\n if (methods && methods.includes(methodName)) {\n this.notifyMutation(target);\n }\n }\n\n #trackMutation(val) {\n if (this.reactivity.isTracking && val != null && typeof val === \"object\") {\n this.reactivity.trackProperty(val, \"__mutation__\");\n }\n }\n\n resolveQuery(root, css) {\n if (this.reactivity.isTracking) this.reactivity.trackQuery(root);\n return root.querySelectorAll(css);\n }\n\n resolveAttribute(root, property) {\n if (this.reactivity.isTracking) this.reactivity.trackAttribute(root, property);\n return this.#flatGet(root, property, (root, property) => root.getAttribute && root.getAttribute(property))\n }\n\n resolveStyle(root, property) {\n return this.#flatGet(root, property, (root, property) => root.style && root.style[property])\n }\n\n resolveComputedStyle(root, property) {\n return this.#flatGet(root, property, (root, property) => getComputedStyle(root).getPropertyValue(property))\n }\n\n assignToNamespace(elt, nameSpace, name, value) {\n let root\n if (elt == null || (typeof document !== \"undefined\" && elt === document.body)) {\n root = this.#globalScope;\n } else {\n root = this.getHyperscriptFeatures(elt);\n }\n var propertyName;\n while ((propertyName = nameSpace.shift()) !== undefined) {\n var newRoot = root[propertyName];\n if (newRoot == null) {\n newRoot = {};\n root[propertyName] = newRoot;\n }\n root = newRoot;\n }\n root[name] = value;\n }\n\n // =================================================================\n // Collection and iteration utilities\n // =================================================================\n\n #isArrayLike(value) {\n return Array.isArray(value) ||\n (typeof NodeList !== 'undefined' && (value instanceof NodeList || value instanceof HTMLCollection || value instanceof FileList));\n }\n\n #isIterable(value) {\n return typeof value === 'object'\n && Symbol.iterator in value\n && typeof value[Symbol.iterator] === 'function';\n }\n\n shouldAutoIterate(value) {\n return (value != null && value[SHOULD_AUTO_ITERATE_SYM]) || this.#isArrayLike(value);\n }\n\n forEach(value, func) {\n if (value == null) {\n // do nothing\n } else if (this.#isIterable(value)) {\n for (const nth of value) {\n func(nth);\n }\n } else if (this.#isArrayLike(value)) {\n for (var i = 0; i < value.length; i++) {\n func(value[i]);\n }\n } else {\n func(value);\n }\n }\n\n implicitLoop(value, func) {\n if (this.shouldAutoIterate(value)) {\n for (const x of value) func(x);\n } else {\n func(value);\n }\n }\n\n /**\n * Iterate over targets with a when condition, applying forward or reverse per element.\n * Supports async conditions transparently -- returns a Promise if any condition is async.\n */\n implicitLoopWhen(targets, whenExpr, context, forwardFn, reverseFn) {\n var elements = [];\n this.implicitLoop(targets, function (elt) { elements.push(elt); });\n\n var conditions = elements.map(function (elt) {\n context.beingTested = elt;\n return whenExpr.evaluate(context);\n });\n context.beingTested = null;\n\n var hasPromise = conditions.some(function (c) { return c && typeof c.then === \"function\"; });\n if (hasPromise) {\n return Promise.all(conditions).then((results) => {\n context.result = this.#applyWhenResults(elements, results, forwardFn, reverseFn);\n });\n } else {\n context.result = this.#applyWhenResults(elements, conditions, forwardFn, reverseFn);\n }\n }\n\n #applyWhenResults(elements, results, forwardFn, reverseFn) {\n var matched = [];\n for (var i = 0; i < elements.length; i++) {\n if (results[i]) { forwardFn(elements[i]); matched.push(elements[i]); }\n else reverseFn(elements[i]);\n }\n return matched;\n }\n\n // =================================================================\n // Type system\n // =================================================================\n\n convertValue(value, type) {\n var dynamicResolvers = conversions.dynamicResolvers;\n for (var i = 0; i < dynamicResolvers.length; i++) {\n var dynamicResolver = dynamicResolvers[i];\n var converted = dynamicResolver(type, value, this);\n if (converted !== undefined) {\n return converted;\n }\n }\n if (value == null) {\n return null;\n }\n var converter = conversions[type];\n if (converter) {\n return converter(value, this);\n }\n throw new Error(\"Unknown conversion : \" + type);\n }\n\n evaluateNoPromise(elt, ctx) {\n let result = elt.evaluate(ctx);\n if (result && typeof result.then === \"function\") {\n throw new Error(elt.sourceFor() + \" returned a Promise in a context that they are not allowed.\");\n }\n return result;\n }\n\n typeCheck(value, typeString, nullOk) {\n if (value == null && nullOk) {\n return true;\n }\n var typeName = Object.prototype.toString.call(value).slice(8, -1);\n if (typeName === typeString) return true;\n // instanceof fallback for base classes\n var ctor = typeof globalThis !== \"undefined\" && globalThis[typeString];\n return typeof ctor === \"function\" && value instanceof ctor;\n }\n\n nullCheck(value, elt) {\n if (value == null) {\n throw new Error(\"'\" + elt.sourceFor() + \"' is null\");\n }\n }\n\n isEmpty(value) {\n return value == undefined || value.length === 0;\n }\n\n doesExist(value) {\n if(value == null){\n return false;\n }\n if (this.shouldAutoIterate(value)) {\n for (const elt of value) {\n return true;\n }\n return false;\n }\n return true;\n }\n\n // =================================================================\n // DOM operations\n // =================================================================\n\n matchesSelector(elt, selector) {\n return elt.matches && elt.matches(selector);\n }\n\n makeEvent(eventName, detail) {\n var evt = new Event(eventName, { bubbles: true, cancelable: true, composed: true });\n evt['detail'] = detail;\n return evt;\n }\n\n triggerEvent(elt, eventName, detail, sender) {\n detail = detail || {};\n detail[\"sender\"] = sender;\n var event = this.makeEvent(eventName, detail);\n if (config.logAll) {\n console.log(eventName, detail, elt);\n }\n var eventResult = elt.dispatchEvent(event);\n return eventResult;\n }\n\n getRootNode(node) {\n if (node && node instanceof Node) {\n var rv = node.getRootNode();\n if (rv instanceof Document || rv instanceof ShadowRoot) return rv;\n }\n return document;\n }\n\n escapeSelector(str) {\n return str.replace(/[:&()\\[\\]\\/]/g, function (str) {\n return \"\\\\\" + str;\n });\n }\n\n getEventQueueFor(elt, onFeature) {\n let internalData = this.getInternalData(elt);\n var eventQueuesForElt = internalData.eventQueues;\n if (eventQueuesForElt == null) {\n eventQueuesForElt = new Map();\n internalData.eventQueues = eventQueuesForElt;\n }\n var eventQueueForFeature = eventQueuesForElt.get(onFeature);\n if (eventQueueForFeature == null) {\n eventQueueForFeature = {queue:[], executing:false};\n eventQueuesForElt.set(onFeature, eventQueueForFeature);\n }\n return eventQueueForFeature;\n }\n\n // =================================================================\n // DOM initialization\n // =================================================================\n\n #getScriptAttributes() {\n if (this.#scriptAttrs == null) {\n this.#scriptAttrs = config.attributes.replaceAll(\" \", \"\").split(\",\");\n }\n return this.#scriptAttrs;\n }\n\n #getScript(elt) {\n var attrs = this.#getScriptAttributes();\n for (var i = 0; i < attrs.length; i++) {\n var scriptAttribute = attrs[i];\n if (elt.hasAttribute && elt.hasAttribute(scriptAttribute)) {\n return elt.getAttribute(scriptAttribute);\n }\n }\n if (elt instanceof HTMLScriptElement && elt.type === \"text/hyperscript\") {\n return elt.innerText;\n }\n return null;\n }\n\n #scriptSelector;\n #getScriptSelector() {\n if (!this.#scriptSelector) {\n this.#scriptSelector = this.#getScriptAttributes().map(a => \"[\" + a + \"]\").join(\", \");\n }\n return this.#scriptSelector;\n }\n\n #hashScript(str) {\n var hash = 5381;\n for (var i = 0; i < str.length; i++) {\n hash = ((hash << 5) + hash) + str.charCodeAt(i);\n }\n return hash;\n }\n\n cleanup(elt) {\n if (!elt._hyperscript) return;\n\n this.triggerEvent(elt, \"hyperscript:before:cleanup\");\n\n var data = elt._hyperscript;\n\n // Remove registered event listeners\n if (data.listeners) {\n for (var info of data.listeners) {\n info.target.removeEventListener(info.event, info.handler);\n }\n }\n\n // Disconnect observers\n if (data.observers) {\n for (var observer of data.observers) {\n observer.disconnect();\n }\n }\n\n // Clear debounce/throttle timers\n if (data.eventState) {\n for (var state of data.eventState.values()) {\n if (state.debounced) clearTimeout(state.debounced);\n }\n }\n\n // Stop reactive effects\n this.reactivity.stopElementEffects(elt);\n\n // Recursively clean children\n if (elt.querySelectorAll) {\n for (var child of elt.querySelectorAll('[data-hyperscript-powered]')) {\n this.cleanup(child);\n }\n }\n\n this.triggerEvent(elt, \"hyperscript:after:cleanup\");\n\n elt.removeAttribute('data-hyperscript-powered');\n delete elt._hyperscript;\n }\n\n #initElement(elt, target) {\n if (elt.closest && elt.closest(config.disableSelector)) {\n return;\n }\n var internalData = this.getInternalData(elt);\n var src = this.#getScript(elt);\n if (!src) return;\n var hash = this.#hashScript(src);\n if (internalData.initialized) {\n if (internalData.scriptHash === hash) {\n this.#resolveTemplateScopes(elt);\n return;\n }\n // Script changed (e.g. morph swap) - clean up and reinitialize\n this.cleanup(elt);\n internalData = this.getInternalData(elt);\n }\n\n if (!this.triggerEvent(elt, \"hyperscript:before:init\")) return;\n\n internalData.initialized = true;\n internalData.scriptHash = hash;\n try {\n var tokens = this.#tokenizer.tokenize(src);\n var hyperScript = this.#kernel.parseHyperScript(tokens);\n if (!hyperScript) return;\n\n if (hyperScript.errors?.length) {\n this.triggerEvent(elt, \"hyperscript:parse-error\", {\n errors: hyperScript.errors,\n });\n console.error(\n \"hyperscript: \" + hyperScript.errors.length + \" parse error(s) on:\",\n elt,\n \"\\n\\n\" + Parser.formatErrors(hyperScript.errors)\n );\n return;\n }\n\n this.#resolveTemplateScopes(elt);\n hyperScript.apply(target || elt, elt, null, this);\n elt.setAttribute('data-hyperscript-powered', 'true');\n this.triggerEvent(elt, \"hyperscript:after:init\");\n setTimeout(() => {\n this.triggerEvent(target || elt, \"load\", {\n hyperscript: true,\n });\n }, 1);\n } catch (e) {\n this.triggerEvent(elt, \"exception\", {\n error: e,\n });\n console.error(\n \"hyperscript errors were found on the following element:\",\n elt,\n \"\\n\\n\",\n e.message,\n e.stack\n );\n }\n }\n\n #resolveTemplateScopes(elt) {\n var root = elt.closest('[data-live-template], [dom-scope=\"isolated\"]');\n if (!root || !root.__hs_scopes) return;\n\n var matches = [];\n var node = elt;\n while (node && node !== root) {\n var prev = node.previousSibling;\n while (prev) {\n if (prev.nodeType === 8) {\n var text = prev.data;\n if (text.startsWith('hs-scope:')) {\n matches.push(text);\n break;\n }\n }\n prev = prev.previousSibling;\n }\n node = node.parentElement;\n }\n if (!matches.length) return;\n\n var internalData = this.getInternalData(elt);\n if (!internalData.elementScope) internalData.elementScope = {};\n\n for (var i = 0; i < matches.length; i++) {\n var parts = matches[i].split(':');\n var loopId = parts[1];\n var iter = parseInt(parts[2]);\n var scope = root.__hs_scopes[loopId];\n if (!scope) continue;\n internalData.elementScope[scope.identifier] = scope.source[iter];\n if (scope.indexIdentifier) {\n internalData.elementScope[scope.indexIdentifier] = iter;\n }\n }\n }\n\n #beforeProcessHooks = [];\n #afterProcessHooks = [];\n\n addBeforeProcessHook(fn) { this.#beforeProcessHooks.push(fn); }\n addAfterProcessHook(fn) { this.#afterProcessHooks.push(fn); }\n\n processNode(elt) {\n for (var fn of this.#beforeProcessHooks) fn(elt);\n\n var selector = this.#getScriptSelector();\n if (this.matchesSelector(elt, selector)) {\n this.#initElement(elt, elt);\n }\n if (elt instanceof HTMLScriptElement && elt.type === \"text/hyperscript\") {\n this.#initElement(elt, document.body);\n }\n if (elt.querySelectorAll) {\n this.forEach(elt.querySelectorAll(selector + \", [type='text/hyperscript']\"), elt => {\n this.#initElement(elt, elt instanceof HTMLScriptElement && elt.type === \"text/hyperscript\" ? document.body : elt);\n });\n }\n\n for (var fn of this.#afterProcessHooks) fn(elt);\n }\n\n // =================================================================\n // Debug and tracing\n // =================================================================\n\n getHyperTrace(ctx, thrown) {\n var trace = [];\n var root = ctx;\n while (root.meta.caller) {\n root = root.meta.caller;\n }\n if (root.meta.traceMap) {\n return root.meta.traceMap.get(thrown, trace);\n }\n }\n\n registerHyperTrace(ctx, thrown) {\n var trace = [];\n var root = null;\n while (ctx != null) {\n trace.push(ctx);\n root = ctx;\n ctx = ctx.meta.caller;\n }\n if (root.meta.traceMap == null) {\n root.meta.traceMap = new Map();\n }\n if (!root.meta.traceMap.get(thrown)) {\n var traceEntry = {\n trace: trace,\n print: function (logger) {\n logger = logger || console.error;\n logger(\"hypertrace /// \");\n var maxLen = 0;\n for (var i = 0; i < trace.length; i++) {\n maxLen = Math.max(maxLen, trace[i].meta.feature.displayName.length);\n }\n for (var i = 0; i < trace.length; i++) {\n var traceElt = trace[i];\n logger(\n \" ->\",\n traceElt.meta.feature.displayName.padEnd(maxLen + 2),\n \"-\",\n traceElt.meta.owner\n );\n }\n },\n };\n root.meta.traceMap.set(thrown, traceEntry);\n }\n }\n\n beepValueToConsole(element, expression, value) {\n if (this.triggerEvent(element, \"hyperscript:beep\", {element, expression, value})) {\n var typeName = !value ? \"object (null)\"\n : value instanceof ElementCollection ? \"ElementCollection\"\n : value.constructor?.name || \"unknown\";\n var logValue = typeName === \"String\" ? '\"' + value + '\"'\n : value instanceof ElementCollection ? Array.from(value)\n : value;\n console.log(\"///_ BEEP! The expression (\" + expression.sourceFor().replace(\"beep! \", \"\") + \") evaluates to:\", logValue, \"of type \" + typeName);\n }\n }\n\n}\n", "// Reactivity - Automatic dependency tracking for _hyperscript\n//\n// When an effect is active, reads via resolveSymbol, resolveProperty,\n// and resolveAttribute record dependencies. Writes via setSymbol\n// notify subscribers. Property and attribute changes are detected\n// via DOM events and MutationObserver.\n\n/**\n * A single tracked read, recording what was accessed during expression().\n *\n * @typedef {Object} Dependency\n * @property {string} type - \"symbol\" | \"property\" | \"attribute\" | \"query\"\n * @property {string} name - e.g. \"$price\", \"value\", \"data-title\"\n * @property {string} [scope] - \"global\" or \"element\" (symbol deps only)\n * @property {Element} [element] - Target element (element-scoped/property/attribute deps)\n */\n\n/** Object.is semantics: treats NaN===NaN and distinguishes +0/-0 */\nfunction _sameValue(a, b) {\n // eslint-disable-next-line no-self-compare\n return a === b ? (a !== 0 || 1 / a === 1 / b) : (a !== a && b !== b);\n}\n\n/**\n * A reactive effect. Re-runs when its dependencies change.\n * Owns its full lifecycle: initialize, run, stop.\n */\nclass Effect {\n /**\n * @param {() => any} expression - The watched expression\n * @param {(v: any) => void} handler - Called when value changes\n * @param {Element|null} element - Owner element; auto-stops when disconnected\n * @param {Reactivity} reactivity - The owning reactivity system\n */\n constructor(expression, handler, element, reactivity) {\n // What this effect does\n this.expression = expression; // () => value - the watched expression, re-evaluated on dep change\n this.handler = handler; // (value) => void - called when expression result changes\n\n // Where it lives\n this.element = element;\n this._reactivity = reactivity;\n\n // Tracked state\n this.dependencies = new Map();\n this._lastValue = undefined;\n this._isStopped = false;\n this._consecutiveTriggers = 0;\n }\n\n /**\n * First evaluation: track deps, subscribe, call handler if non-null.\n * Both undefined and null are treated as \"no value yet\" to support\n * left-side-wins initialization in bind.\n */\n initialize() {\n var reactivity = this._reactivity;\n\n // Evaluate expression with tracking enabled - any symbol, property,\n // or attribute reads during this call are recorded as dependencies.\n var prev = reactivity._currentEffect;\n reactivity._currentEffect = this;\n try {\n this._lastValue = this.expression();\n } catch (e) {\n console.error(\"Error in reactive expression:\", e);\n }\n reactivity._currentEffect = prev;\n\n // Wire up subscriptions so we're notified when dependencies change\n reactivity._subscribeEffect(this);\n\n // If we got a value, push it to the handler immediately.\n // null/undefined means \"no value yet\" - skip to let the other\n // side of a bind initialize first (left-side-wins semantics).\n if (this._lastValue != null) {\n try {\n this.handler(this._lastValue);\n } catch (e) {\n console.error(\"Error in reactive handler:\", e);\n }\n }\n }\n\n /**\n * Re-evaluate expression with dependency tracking, compare with last\n * value, and call handler if changed. Returns false if circular\n * guard tripped (caller should skip this effect).\n * @returns {boolean} Whether the effect ran successfully\n */\n run() {\n this._consecutiveTriggers++;\n if (this._consecutiveTriggers > 100) {\n console.error(\n \"Reactivity loop detected: an effect triggered 100 consecutive \" +\n \"times without settling. This usually means an effect is modifying \" +\n \"a variable it also depends on.\",\n this.element || this\n );\n return false;\n }\n\n var reactivity = this._reactivity;\n\n // Unsubscribe from current deps\n reactivity._unsubscribeEffect(this);\n\n // Re-run expression with tracking\n var oldDeps = this.dependencies;\n this.dependencies = new Map();\n\n var prev = reactivity._currentEffect;\n reactivity._currentEffect = this;\n var newValue;\n try {\n newValue = this.expression();\n } catch (e) {\n console.error(\"Error in reactive expression:\", e);\n // Restore old dependencies on error\n this.dependencies = oldDeps;\n reactivity._currentEffect = prev;\n reactivity._subscribeEffect(this);\n return true;\n }\n reactivity._currentEffect = prev;\n\n // Subscribe to new deps\n reactivity._subscribeEffect(this);\n\n // Clean up empty subscription entries for deps that were dropped\n reactivity._cleanupOrphanedDeps(oldDeps);\n\n // Compare and fire (Object.is semantics: NaN === NaN, +0 !== -0)\n if (!_sameValue(newValue, this._lastValue)) {\n this._lastValue = newValue;\n try {\n this.handler(newValue);\n } catch (e) {\n console.error(\"Error in reactive handler:\", e);\n }\n }\n return true;\n }\n\n /** Reset circular guard after cascade settles. */\n resetTriggerCount() {\n this._consecutiveTriggers = 0;\n }\n\n /** Stop this effect and clean up all subscriptions. */\n stop() {\n if (this._isStopped) return;\n this._isStopped = true;\n this._reactivity._unsubscribeEffect(this);\n this._reactivity._cleanupOrphanedDeps(this.dependencies);\n this._reactivity._pendingEffects.delete(this);\n }\n}\n\nexport class Reactivity {\n constructor() {\n /** Per-object reactive state, keyed by any object (DOM element or plain JS object) */\n this._objectState = new WeakMap();\n\n // Symbol subscriptions \u2014 global $variables (e.g. $count, $theme)\n // Element-scoped :variables are stored in _getObjectState(element).subscriptions\n this._globalSymbolSubscriptions = new Map(); // symbolName -> Set\n\n // Attribute subscriptions \u2014 DOM attributes (@data-theme, @aria-hidden)\n this._attributeSubscriptions = new Map(); // \"attrName:elementId\" -> Set\n\n // Property subscriptions \u2014 JS properties (element.value, element.checked)\n this._propertySubscriptions = new Map(); // elementId -> Set\n\n // Query subscriptions \u2014 CSS selector results (<:checked/> in #myTable)\n this._querySubscriptions = new Map(); // root -> Set\n\n /** Next ID to assign to an object for dependency dedup keys */\n this._nextId = 0;\n\n /** @type {Effect|null} The effect currently being evaluated */\n this._currentEffect = null;\n\n /** @type {Set} Effects waiting to run in the next microtask */\n this._pendingEffects = new Set();\n\n /** @type {boolean} Whether a microtask is scheduled to run pending effects */\n this._isRunScheduled = false;\n\n }\n\n /**\n * Get or create the reactive state object for any object.\n * Assigns a stable unique ID on first access.\n * @param {Object} obj - DOM element or plain JS object\n * @returns {{ id: string, subscriptions: Map|null }}\n */\n _getObjectState(obj) {\n var state = this._objectState.get(obj);\n if (!state) {\n this._objectState.set(obj, state = {\n id: String(++this._nextId),\n subscriptions: null,\n });\n }\n return state;\n }\n\n /**\n * Whether an effect is currently evaluating its expression().\n * When true, reads (symbol/property/attribute) are recorded as dependencies.\n * @returns {boolean}\n */\n get isTracking() {\n return this._currentEffect !== null;\n }\n\n /**\n * Track a global variable read as a dependency.\n * @param {string} name - Variable name\n */\n trackGlobalSymbol(name) {\n this._currentEffect.dependencies.set(\"symbol:global:\" + name,\n { type: \"symbol\", name: name, scope: \"global\" });\n }\n\n /**\n * Track an element-scoped variable read as a dependency.\n * @param {string} name - Variable name\n * @param {Element} element - Owning element\n */\n trackElementSymbol(name, element) {\n if (!element) return;\n var elementId = this._getObjectState(element).id;\n this._currentEffect.dependencies.set(\"symbol:element:\" + name + \":\" + elementId,\n { type: \"symbol\", name: name, scope: \"element\", element: element });\n }\n\n /**\n * Track a property read as a dependency.\n * Subscription is coarse-grained (one handler per object, not per property),\n * so the dep key uses \"*\" rather than the property name.\n * @param {Object} obj - DOM element or plain JS object\n * @param {string} name - Property name\n */\n trackProperty(obj, name) {\n if (obj == null || typeof obj !== \"object\" || obj._hsSkipTracking) return;\n this._currentEffect.dependencies.set(\"property:\" + this._getObjectState(obj).id,\n { type: \"property\", object: obj, name: name });\n }\n\n /**\n * Track a DOM attribute read as a dependency.\n * @param {Element} element\n * @param {string} name - Attribute name\n */\n trackAttribute(element, name) {\n if (!(element instanceof Element)) return;\n this._currentEffect.dependencies.set(\"attribute:\" + name + \":\" + this._getObjectState(element).id,\n { type: \"attribute\", element: element, name: name });\n }\n\n /**\n * Track a DOM query as a dependency. Re-evaluates when any DOM\n * change occurs within root or its descendants.\n * @param {Element|Document} root - the element querySelectorAll runs on (e.g. #myTable in `<:checked/> in #myTable`)\n */\n trackQuery(root) {\n if (!this._currentEffect) return;\n root = root || document;\n var key = \"query:\" + this._getObjectState(root).id;\n this._currentEffect.dependencies.set(key,\n { type: \"query\", root: root });\n }\n\n /**\n * Notify that a global variable was written.\n * @param {string} name - Variable name\n */\n notifyGlobalSymbol(name) {\n var subs = this._globalSymbolSubscriptions.get(name);\n if (subs) {\n for (var effect of subs) {\n this._scheduleEffect(effect);\n }\n }\n }\n\n /**\n * Notify that an element-scoped variable was written.\n * @param {string} name - Variable name\n * @param {Element} element - Owning element\n */\n notifyElementSymbol(name, element) {\n if (!element) return;\n var state = this._getObjectState(element);\n if (state.subscriptions) {\n var subs = state.subscriptions.get(name);\n if (subs) {\n for (var effect of subs) {\n this._scheduleEffect(effect);\n }\n }\n }\n }\n\n /**\n * Notify that a property was written programmatically.\n * Schedules all effects watching properties on this object.\n * @param {Object} obj - DOM element or plain JS object\n */\n notifyProperty(obj) {\n if (obj == null || typeof obj !== \"object\" || obj._hsSkipTracking) return;\n var state = this._objectState.get(obj);\n if (state) {\n var subs = this._propertySubscriptions.get(state.id);\n if (subs) {\n for (var effect of subs) {\n this._scheduleEffect(effect);\n }\n }\n }\n }\n\n /**\n * Add an effect to the pending set.\n * Schedules a microtask to run them if one isn't already scheduled.\n * @param {Effect} effect\n */\n _scheduleEffect(effect) {\n if (effect._isStopped) return;\n this._pendingEffects.add(effect);\n if (!this._isRunScheduled) {\n this._isRunScheduled = true;\n var self = this;\n queueMicrotask(function () { self._runPendingEffects(); });\n }\n }\n\n /**\n * Set up the single global MutationObserver and delegated input/change\n * listeners that power attribute, property, and query tracking.\n */\n _initGlobalObserver() {\n if (typeof document === \"undefined\") return;\n\n if (!this._observer) {\n var reactivity = this;\n this._observer = new MutationObserver(function (mutations) {\n reactivity._handleMutations(mutations);\n });\n this._inputHandler = function (e) { reactivity._handleDOMEvent(e); };\n this._changeHandler = function (e) { reactivity._handleDOMEvent(e); };\n }\n\n // observe() replaces any prior observation on this target\n this._observer.observe(document, {\n attributes: true,\n childList: true,\n subtree: true\n });\n\n // addEventListener is idempotent for the same listener+capture combo\n document.addEventListener(\"input\", this._inputHandler, true);\n document.addEventListener(\"change\", this._changeHandler, true);\n }\n\n /**\n * Handle MutationObserver callbacks. Dispatches to attribute and query\n * subscriptions based on mutation type.\n * @param {MutationRecord[]} mutations\n */\n _handleMutations(mutations) {\n var hasQueries = this._querySubscriptions.size > 0;\n var queryTargets = hasQueries ? new Set() : null;\n for (var i = 0; i < mutations.length; i++) {\n var mutation = mutations[i];\n if (mutation.type === \"attributes\") {\n this._scheduleAttributeEffects(mutation.target, mutation.attributeName);\n }\n if (queryTargets) queryTargets.add(mutation.target);\n }\n if (queryTargets) this._scheduleQueryEffects(queryTargets);\n }\n\n /**\n * Handle delegated input/change events. Dispatches to property and\n * query subscriptions.\n * @param {Event} event\n */\n _handleDOMEvent(event) {\n var el = event.target;\n if (!(el instanceof Element)) return;\n var state = this._objectState.get(el);\n if (state) {\n var subs = this._propertySubscriptions.get(state.id);\n if (subs) {\n for (var effect of subs) {\n this._scheduleEffect(effect);\n }\n }\n }\n this._scheduleQueryEffects(el);\n }\n\n /**\n * Schedule effects watching a specific attribute on a specific element.\n * @param {Element} element\n * @param {string} attrName\n */\n _scheduleAttributeEffects(element, attrName) {\n var state = this._objectState.get(element);\n if (!state) return;\n var key = attrName + \":\" + state.id;\n var subs = this._attributeSubscriptions.get(key);\n if (subs) {\n for (var effect of subs) {\n this._scheduleEffect(effect);\n }\n }\n }\n\n /**\n * Schedule effects with query deps whose root includes any of the mutated elements.\n * @param {Set|Element} mutated - Element(s) where DOM changes occurred\n */\n _scheduleQueryEffects(mutated) {\n if (this._querySubscriptions.size === 0) return;\n for (var [root, effects] of this._querySubscriptions) {\n if (this._containsTarget(root, mutated)) {\n for (var effect of effects) {\n this._scheduleEffect(effect);\n }\n }\n }\n }\n\n /** Check if any of the mutated elements are inside root. */\n _containsTarget(root, mutated) {\n if (mutated instanceof Set) {\n for (var el of mutated) {\n if (root.contains(el)) return true;\n }\n return false;\n }\n return root.contains(mutated);\n }\n\n /**\n * Run all pending effects. Called once per microtask batch.\n * Effects that re-trigger during this run are queued for the next batch.\n */\n _runPendingEffects() {\n this._isRunScheduled = false;\n // Copy because effects may re-schedule themselves during this run\n var effects = Array.from(this._pendingEffects);\n this._pendingEffects.clear();\n for (var i = 0; i < effects.length; i++) {\n var effect = effects[i];\n if (effect._isStopped) continue;\n // Auto-stop if owning element is disconnected\n if (effect.element && !effect.element.isConnected) {\n effect.stop();\n continue;\n }\n effect.run();\n }\n // Reset trigger counts when the cascade settles (no more pending\n // effects). Legitimate re-triggers on future user events start\n // fresh, while infinite cross-microtask loops accumulate to 100.\n if (this._pendingEffects.size === 0) {\n for (var i = 0; i < effects.length; i++) {\n if (!effects[i]._isStopped) effects[i].resetTriggerCount();\n }\n }\n }\n\n /**\n * Subscribe an effect to all its current deps.\n * Symbols go into per-element/global subscription maps.\n * Attributes, properties, and queries use flat lookup maps\n * dispatched by the global observer.\n * @param {Effect} effect\n */\n _subscribeEffect(effect) {\n var reactivity = this;\n var needsGlobalObserver = false;\n\n for (var [depKey, dep] of effect.dependencies) {\n if (dep.type === \"symbol\" && dep.scope === \"global\") {\n if (!reactivity._globalSymbolSubscriptions.has(dep.name)) {\n reactivity._globalSymbolSubscriptions.set(dep.name, new Set());\n }\n reactivity._globalSymbolSubscriptions.get(dep.name).add(effect);\n\n } else if (dep.type === \"symbol\" && dep.scope === \"element\") {\n var state = reactivity._getObjectState(dep.element);\n if (!state.subscriptions) {\n state.subscriptions = new Map();\n }\n if (!state.subscriptions.has(dep.name)) {\n state.subscriptions.set(dep.name, new Set());\n }\n state.subscriptions.get(dep.name).add(effect);\n\n } else if (dep.type === \"attribute\") {\n var attrState = reactivity._getObjectState(dep.element);\n var attrKey = dep.name + \":\" + attrState.id;\n if (!reactivity._attributeSubscriptions.has(attrKey)) {\n reactivity._attributeSubscriptions.set(attrKey, new Set());\n }\n reactivity._attributeSubscriptions.get(attrKey).add(effect);\n needsGlobalObserver = true;\n\n } else if (dep.type === \"property\") {\n var propState = reactivity._getObjectState(dep.object);\n if (!reactivity._propertySubscriptions.has(propState.id)) {\n reactivity._propertySubscriptions.set(propState.id, new Set());\n }\n reactivity._propertySubscriptions.get(propState.id).add(effect);\n needsGlobalObserver = true;\n\n } else if (dep.type === \"query\") {\n if (!reactivity._querySubscriptions.has(dep.root)) {\n reactivity._querySubscriptions.set(dep.root, new Set());\n }\n reactivity._querySubscriptions.get(dep.root).add(effect);\n needsGlobalObserver = true;\n }\n }\n\n // Lazily initialize the global observer only when an effect\n // actually depends on DOM state (attributes, properties, or queries).\n // Symbol-only effects (e.g. `when $count changes`) skip this entirely.\n if (needsGlobalObserver) {\n reactivity._initGlobalObserver();\n }\n }\n\n /** @param {Effect} effect */\n _unsubscribeEffect(effect) {\n var reactivity = this;\n for (var [depKey, dep] of effect.dependencies) {\n if (dep.type === \"symbol\" && dep.scope === \"global\") {\n var subs = reactivity._globalSymbolSubscriptions.get(dep.name);\n if (subs) {\n subs.delete(effect);\n if (subs.size === 0) {\n reactivity._globalSymbolSubscriptions.delete(dep.name);\n }\n }\n } else if (dep.type === \"symbol\" && dep.scope === \"element\") {\n var state = reactivity._getObjectState(dep.element);\n if (state.subscriptions) {\n var subs = state.subscriptions.get(dep.name);\n if (subs) {\n subs.delete(effect);\n if (subs.size === 0) {\n state.subscriptions.delete(dep.name);\n }\n }\n }\n } else if (dep.type === \"attribute\" && dep.element) {\n var attrState = reactivity._getObjectState(dep.element);\n var attrKey = dep.name + \":\" + attrState.id;\n var subs = reactivity._attributeSubscriptions.get(attrKey);\n if (subs) {\n subs.delete(effect);\n if (subs.size === 0) {\n reactivity._attributeSubscriptions.delete(attrKey);\n }\n }\n } else if (dep.type === \"property\" && dep.object) {\n var propState = reactivity._getObjectState(dep.object);\n var subs = reactivity._propertySubscriptions.get(propState.id);\n if (subs) {\n subs.delete(effect);\n if (subs.size === 0) {\n reactivity._propertySubscriptions.delete(propState.id);\n }\n }\n } else if (dep.type === \"query\") {\n var subs = reactivity._querySubscriptions.get(dep.root);\n if (subs) {\n subs.delete(effect);\n if (subs.size === 0) {\n reactivity._querySubscriptions.delete(dep.root);\n }\n }\n }\n }\n reactivity._maybeStopGlobalObserver();\n }\n\n /**\n * Disconnect the global observer and delegated listeners when no\n * effects depend on DOM state (attributes, properties, or queries).\n */\n _maybeStopGlobalObserver() {\n if (!this._observer) return;\n if (this._attributeSubscriptions.size > 0) return;\n if (this._propertySubscriptions.size > 0) return;\n if (this._querySubscriptions.size > 0) return;\n this._observer.disconnect();\n document.removeEventListener(\"input\", this._inputHandler, true);\n document.removeEventListener(\"change\", this._changeHandler, true);\n }\n\n /**\n * Remove empty entries from subscription maps for deps that were dropped.\n * Query deps need no cleanup here \u2014 _unsubscribeEffect handles them directly.\n * @param {Map} deps\n */\n _cleanupOrphanedDeps(deps) {\n var reactivity = this;\n for (var [depKey, dep] of deps) {\n if (dep.type === \"attribute\" && dep.element) {\n var attrState = reactivity._objectState.get(dep.element);\n if (attrState) {\n var attrKey = dep.name + \":\" + attrState.id;\n var subs = reactivity._attributeSubscriptions.get(attrKey);\n if (subs && subs.size === 0) {\n reactivity._attributeSubscriptions.delete(attrKey);\n }\n }\n } else if (dep.type === \"property\" && dep.object) {\n var propState = reactivity._objectState.get(dep.object);\n if (propState) {\n var subs = reactivity._propertySubscriptions.get(propState.id);\n if (subs && subs.size === 0) {\n reactivity._propertySubscriptions.delete(propState.id);\n }\n }\n }\n }\n }\n\n /**\n * Create a reactive effect with automatic dependency tracking.\n * @param {() => any} expression - The watched expression\n * @param {(value: any) => void} handler - Called when the value changes\n * @param {Object} [options]\n * @param {Element} [options.element] - Auto-stop when element disconnects\n * @returns {() => void} Stop function\n */\n createEffect(expression, handler, options) {\n var effect = new Effect(\n expression,\n handler,\n (options && options.element) || null,\n this\n );\n\n effect.initialize();\n\n // Track effect by element for cleanup\n if (effect.element) {\n var data = effect.element._hyperscript ??= {};\n data.effects ??= new Set();\n data.effects.add(effect);\n }\n\n return function () {\n effect.stop();\n };\n }\n\n /** Stop all reactive effects owned by an element. */\n stopElementEffects(element) {\n var data = element._hyperscript;\n if (!data || !data.effects) return;\n for (var effect of data.effects) {\n effect.stop();\n }\n delete data.effects;\n }\n}\n\n// Reactivity instance is created by Runtime, not here.\n// See runtime.js constructor.\n", "// Morph - DOM morphing algorithm for _hyperscript\n//\n// Based on the htmx4 morph algorithm by Michael West.\n// Efficiently updates an existing DOM tree to match new content,\n// preserving node identity, focus, scroll position, and event listeners\n// where possible.\n\n/**\n * @typedef {Object} MorphCallbacks\n * @property {function(Node): void} [beforeNodeRemoved] - Called before a node is removed\n * @property {function(Node): void} [afterNodeAdded] - Called after a new node is inserted\n * @property {function(Node): void} [afterNodeMorphed] - Called after an existing node is morphed\n */\n\nexport class Morph {\n\n /**\n * Morph oldNode to match content.\n * @param {Element} oldNode - The existing DOM element to morph\n * @param {string|Element|DocumentFragment} content - The new content\n * @param {MorphCallbacks} [callbacks] - Optional lifecycle callbacks\n */\n morph(oldNode, content, callbacks = {}) {\n var fragment;\n if (typeof content === \"string\") {\n var temp = document.createElement(\"template\");\n temp.innerHTML = content;\n fragment = temp.content;\n } else if (content instanceof DocumentFragment) {\n fragment = content;\n } else if (content instanceof Element) {\n fragment = document.createDocumentFragment();\n fragment.append(content.cloneNode(true));\n } else {\n throw new Error(\"morph requires an HTML string, element, or document fragment\");\n }\n\n // If the fragment has a single root element matching the target tag,\n // treat as outer morph: sync attributes, then morph children\n var newRoot = fragment.firstElementChild;\n if (newRoot && !newRoot.nextElementSibling && newRoot.tagName === oldNode.tagName) {\n _copyAttributes(oldNode, newRoot);\n fragment = newRoot;\n }\n\n var { persistentIds, idMap } = _createIdMaps(oldNode, fragment);\n var pantry = document.createElement(\"div\");\n pantry.hidden = true;\n (document.body || oldNode.parentElement).after(pantry);\n var ctx = { target: oldNode, idMap, persistentIds, pantry, futureMatches: new WeakSet(), callbacks };\n\n _morphChildren(ctx, oldNode, fragment);\n\n // clean up orphaned nodes in pantry\n callbacks.beforeNodeRemoved?.(pantry);\n pantry.remove();\n }\n}\n\nfunction _morphChildren(ctx, oldParent, newParent, insertionPoint = null, endPoint = null) {\n if (oldParent instanceof HTMLTemplateElement && newParent instanceof HTMLTemplateElement) {\n oldParent = oldParent.content;\n newParent = newParent.content;\n }\n insertionPoint ||= oldParent.firstChild;\n\n let newChild = newParent.firstChild;\n while (newChild) {\n let matchedNode;\n if (insertionPoint && insertionPoint !== endPoint) {\n matchedNode = _findBestMatch(ctx, newChild, insertionPoint, endPoint);\n if (matchedNode && matchedNode !== insertionPoint) {\n let cursor = insertionPoint;\n while (cursor && cursor !== matchedNode) {\n let tempNode = cursor;\n cursor = cursor.nextSibling;\n if (tempNode instanceof Element && (ctx.idMap.has(tempNode) || _matchesUpcomingSibling(ctx, tempNode, newChild))) {\n _moveBefore(oldParent, tempNode, endPoint);\n } else {\n _removeNode(ctx, tempNode);\n }\n }\n }\n }\n\n if (!matchedNode && newChild instanceof Element && ctx.persistentIds.has(newChild.id)) {\n let escapedId = CSS.escape(newChild.id);\n matchedNode = (ctx.target.id === newChild.id && ctx.target) ||\n ctx.target.querySelector('[id=\"' + escapedId + '\"]') ||\n ctx.pantry.querySelector('[id=\"' + escapedId + '\"]');\n let element = matchedNode;\n while ((element = element.parentNode)) {\n let idSet = ctx.idMap.get(element);\n if (idSet) {\n idSet.delete(matchedNode.id);\n if (!idSet.size) ctx.idMap.delete(element);\n }\n }\n _moveBefore(oldParent, matchedNode, insertionPoint);\n }\n\n if (matchedNode) {\n _morphNode(matchedNode, newChild, ctx);\n insertionPoint = matchedNode.nextSibling;\n newChild = newChild.nextSibling;\n continue;\n }\n\n let nextNewChild = newChild.nextSibling;\n if (ctx.idMap.has(newChild)) {\n let placeholder = document.createElement(newChild.tagName);\n oldParent.insertBefore(placeholder, insertionPoint);\n _morphNode(placeholder, newChild, ctx);\n insertionPoint = placeholder.nextSibling;\n } else {\n oldParent.insertBefore(newChild, insertionPoint);\n ctx.callbacks.afterNodeAdded?.(newChild);\n insertionPoint = newChild.nextSibling;\n }\n newChild = nextNewChild;\n }\n\n while (insertionPoint && insertionPoint !== endPoint) {\n let tempNode = insertionPoint;\n insertionPoint = insertionPoint.nextSibling;\n _removeNode(ctx, tempNode);\n }\n}\n\nfunction _morphNode(oldNode, newNode, ctx) {\n if (!(oldNode instanceof Element)) return;\n _copyAttributes(oldNode, newNode);\n if (oldNode instanceof HTMLTextAreaElement && oldNode.defaultValue !== newNode.defaultValue) {\n oldNode.value = newNode.value;\n }\n if (!oldNode.isEqualNode(newNode) || newNode.tagName === \"TEMPLATE\" || newNode.querySelector?.(\"template\")) {\n _morphChildren(ctx, oldNode, newNode);\n }\n ctx.callbacks.afterNodeMorphed?.(oldNode);\n}\n\nfunction _findBestMatch(ctx, node, startPoint, endPoint) {\n if (!(node instanceof Element)) return null;\n var softMatch = null, displaceMatchCount = 0, scanLimit = 10;\n var newSet = ctx.idMap.get(node), nodeMatchCount = newSet?.size || 0;\n if (node.id && !newSet) return null;\n var cursor = startPoint;\n while (cursor && cursor !== endPoint) {\n var oldSet = ctx.idMap.get(cursor);\n if (_isSoftMatch(cursor, node)) {\n if (oldSet && newSet && [...oldSet].some(id => newSet.has(id))) return cursor;\n if (!oldSet) {\n if (scanLimit > 0 && cursor.isEqualNode(node)) return cursor;\n if (!softMatch) softMatch = cursor;\n }\n }\n displaceMatchCount += oldSet?.size || 0;\n if (displaceMatchCount > nodeMatchCount) break;\n if (cursor.contains(document.activeElement)) break;\n if (--scanLimit < 1 && nodeMatchCount === 0) break;\n cursor = cursor.nextSibling;\n }\n if (softMatch && _matchesUpcomingSibling(ctx, softMatch, node)) return null;\n return softMatch;\n}\n\nfunction _matchesUpcomingSibling(ctx, oldElt, startNode) {\n if (ctx.futureMatches.has(oldElt)) return true;\n for (var sibling = startNode.nextSibling, i = 0; sibling && i < 10; sibling = sibling.nextSibling, i++) {\n if (sibling instanceof Element && oldElt.isEqualNode(sibling)) {\n ctx.futureMatches.add(oldElt);\n return true;\n }\n }\n return false;\n}\n\nfunction _removeNode(ctx, node) {\n if (ctx.idMap.has(node)) {\n _moveBefore(ctx.pantry, node, null);\n } else {\n ctx.callbacks.beforeNodeRemoved?.(node);\n node.remove();\n }\n}\n\nfunction _moveBefore(parentNode, element, after) {\n if (parentNode.moveBefore) {\n try { parentNode.moveBefore(element, after); return; } catch (e) {}\n }\n parentNode.insertBefore(element, after);\n}\n\nfunction _copyAttributes(destination, source) {\n for (var attr of source.attributes) {\n if (destination.getAttribute(attr.name) !== attr.value) {\n destination.setAttribute(attr.name, attr.value);\n if (attr.name === \"value\" && destination instanceof HTMLInputElement && destination.type !== \"file\") {\n destination.value = attr.value;\n }\n }\n }\n for (var i = destination.attributes.length - 1; i >= 0; i--) {\n var attr = destination.attributes[i];\n if (attr && !source.hasAttribute(attr.name)) {\n destination.removeAttribute(attr.name);\n }\n }\n}\n\nfunction _isSoftMatch(oldNode, newNode) {\n if (!(oldNode instanceof Element) || oldNode.tagName !== newNode.tagName) return false;\n if (oldNode.tagName === \"SCRIPT\" && !oldNode.isEqualNode(newNode)) return false;\n return !oldNode.id || oldNode.id === newNode.id;\n}\n\nfunction _createIdMaps(oldNode, newContent) {\n var oldIdElements = _queryEltAndDescendants(oldNode, \"[id]\");\n var newIdElements = newContent.querySelectorAll(\"[id]\");\n var persistentIds = _createPersistentIds(oldIdElements, newIdElements);\n var idMap = new Map();\n _populateIdMapWithTree(idMap, persistentIds, oldNode.parentElement, oldIdElements);\n _populateIdMapWithTree(idMap, persistentIds, newContent, newIdElements);\n return { persistentIds, idMap };\n}\n\nfunction _createPersistentIds(oldIdElements, newIdElements) {\n var duplicateIds = new Set(), oldIdTagNameMap = new Map();\n for (var { id, tagName } of oldIdElements) {\n if (oldIdTagNameMap.has(id)) duplicateIds.add(id);\n else if (id) oldIdTagNameMap.set(id, tagName);\n }\n var persistentIds = new Set();\n for (var { id, tagName } of newIdElements) {\n if (persistentIds.has(id)) duplicateIds.add(id);\n else if (oldIdTagNameMap.get(id) === tagName) persistentIds.add(id);\n }\n for (var id of duplicateIds) persistentIds.delete(id);\n return persistentIds;\n}\n\nfunction _populateIdMapWithTree(idMap, persistentIds, root, elements) {\n for (var elt of elements) {\n if (persistentIds.has(elt.id)) {\n var current = elt;\n while (current && current !== root) {\n var idSet = idMap.get(current);\n if (idSet == null) { idSet = new Set(); idMap.set(current, idSet); }\n idSet.add(elt.id);\n current = current.parentElement;\n }\n }\n }\n}\n\nfunction _queryEltAndDescendants(elt, selector) {\n var results = [...(elt.querySelectorAll?.(selector) ?? [])];\n if (elt.matches?.(selector)) results.unshift(elt);\n return results;\n}\n", "/**\n * HtmxCompat - hyperscript/htmx integration layer\n *\n * Handles bidirectional processing (htmx content \u2192 hyperscript, hyperscript content \u2192 htmx)\n * and the hs-include extension for bridging element variables into htmx requests.\n */\nexport class HtmxCompat {\n\n #processingFromHtmx = false;\n\n constructor(globalScope, hyperscript) {\n this.globalScope = globalScope;\n this.hyperscript = hyperscript;\n }\n\n init() {\n var self = this;\n var globalScope = this.globalScope;\n var _hyperscript = this.hyperscript;\n\n // htmx -> hyperscript: process new htmx content\n globalScope.document.addEventListener(\"htmx:load\", function (evt) {\n self.#processingFromHtmx = true;\n _hyperscript.process(evt.detail.elt);\n self.#processingFromHtmx = false;\n });\n globalScope.document.addEventListener(\"htmx:after:process\", function (evt) {\n self.#processingFromHtmx = true;\n _hyperscript.process(evt.target);\n self.#processingFromHtmx = false;\n });\n\n // hyperscript -> htmx: notify htmx about hyperscript-inserted content\n if (typeof htmx !== 'undefined') {\n _hyperscript.addAfterProcessHook(function (elt) {\n if (!self.#processingFromHtmx) htmx.process(elt);\n });\n\n // hs-include: bridge hyperscript element variables into htmx requests (htmx 4+ only)\n if (htmx.version?.startsWith('4')) {\n htmx.registerExtension('hs-include', {\n htmx_config_request: function (elt, detail) {\n var ctx = detail?.ctx;\n if (!ctx) return;\n var sourceElt = ctx.sourceElement || elt;\n\n var found = HtmxCompat.#findHsInclude(sourceElt);\n if (!found) return;\n\n var vars = HtmxCompat.#resolveSpecifiers(found.value, found.scopeElt);\n var body = ctx.request?.body;\n if (body instanceof FormData) {\n for (var k in vars) body.set(k, vars[k]);\n }\n }\n });\n }\n }\n }\n\n // ----- hs-include helpers -----\n\n static #findHsInclude(sourceElt) {\n var attr = sourceElt.getAttribute('hs-include');\n if (attr !== null) return { value: attr, scopeElt: sourceElt };\n var elt = sourceElt.parentElement;\n while (elt) {\n attr = elt.getAttribute('hs-include:inherited');\n if (attr !== null) return { value: attr, scopeElt: elt };\n elt = elt.parentElement;\n }\n return null;\n }\n\n static #readScope(elt) {\n return elt?._hyperscript?.elementScope || {};\n }\n\n static #serialize(value) {\n if (value == null) return '';\n if (typeof value === 'object') {\n try { return JSON.stringify(value); }\n catch (_) { return ''; }\n }\n return String(value);\n }\n\n static #resolveInherited(scopeKey, startElt) {\n var elt = startElt;\n while (elt) {\n var scope = elt._hyperscript?.elementScope;\n if (scope && scopeKey in scope) return scope[scopeKey];\n elt = elt.parentElement;\n }\n }\n\n static #resolveSpecifiers(attrValue, scopeElt) {\n var result = {};\n var raw = attrValue.trim();\n\n if (raw === '*') {\n var scope = this.#readScope(scopeElt);\n for (var k in scope) {\n if (Object.prototype.hasOwnProperty.call(scope, k)) {\n result[k[0] === ':' ? k.slice(1) : k] = this.#serialize(scope[k]);\n }\n }\n return result;\n }\n\n var self = this;\n raw.split(',').forEach(function (part) {\n part = part.trim();\n if (!part) return;\n if (part[0] === ':') {\n var name = part.slice(1);\n var scope = self.#readScope(scopeElt);\n var scopeKey = ':' + name;\n if (scopeKey in scope) result[name] = self.#serialize(scope[scopeKey]);\n } else if (part[0] === '^') {\n var name = part.slice(1);\n var val = self.#resolveInherited(':' + name, scopeElt);\n if (val !== undefined) result[name] = self.#serialize(val);\n } else if (part[0] === '#') {\n var colonIdx = part.lastIndexOf(':');\n if (colonIdx > 0) {\n var selector = part.slice(0, colonIdx);\n var name = part.slice(colonIdx + 1);\n var targetElt = document.querySelector(selector);\n if (targetElt) {\n var scope = self.#readScope(targetElt);\n var scopeKey = ':' + name;\n if (scopeKey in scope) result[name] = self.#serialize(scope[scopeKey]);\n }\n }\n }\n });\n return result;\n }\n}\n", "/**\n * Basic expression parse tree elements\n */\n\nimport { Expression } from '../base.js';\n\n/**\n * ParenthesizedExpression - Wraps an expression in parentheses\n *\n * Parses: (expression)\n * Returns: the inner expression\n */\nexport class ParenthesizedExpression extends Expression {\n static grammarName = \"parenthesized\";\n static expressionType = \"leaf\";\n\n constructor(expr) {\n super();\n this.expr = expr;\n this.args = { value: expr };\n }\n\n static parse(parser) {\n if (parser.matchOpToken(\"(\")) {\n var follows = parser.clearFollows();\n try {\n var expr = parser.requireElement(\"expression\");\n } finally {\n parser.restoreFollows(follows);\n }\n parser.requireOpToken(\")\");\n return new ParenthesizedExpression(expr);\n }\n }\n\n resolve(context, { value }) {\n return value;\n }\n}\n\n/**\n * BlockLiteral - Represents lambda-style block expressions\n *\n * Parses: \\x -> expr or \\x, y -> expr\n * Returns: function that evaluates the expression with bound arguments\n */\nexport class BlockLiteral extends Expression {\n static grammarName = \"blockLiteral\";\n static expressionType = \"leaf\";\n\n constructor(params, expr) {\n super();\n this.params = params;\n this.expr = expr;\n }\n\n static parse(parser) {\n if (!parser.matchOpToken(\"\\\\\")) return;\n var params = [];\n var arg1 = parser.matchTokenType(\"IDENTIFIER\");\n if (arg1) {\n params.push(arg1);\n while (parser.matchOpToken(\",\")) {\n params.push(parser.requireTokenType(\"IDENTIFIER\"));\n }\n }\n // TODO compound op token\n parser.requireOpToken(\"-\");\n parser.requireOpToken(\">\");\n var expr = parser.requireElement(\"expression\");\n return new BlockLiteral(params, expr);\n }\n\n resolve(ctx) {\n var params = this.params;\n var expr = this.expr;\n return function () {\n //TODO - push scope\n for (var i = 0; i < params.length; i++) {\n ctx.locals[params[i].value] = arguments[i];\n }\n return expr.evaluate(ctx); //OK\n };\n }\n}\n\n/**\n * NegativeNumber - Represents unary minus operator\n *\n * Parses: -expression\n * Returns: negated numeric value\n */\nexport class NegativeNumber extends Expression {\n static grammarName = \"negativeNumber\";\n\n constructor(root) {\n super();\n this.root = root;\n this.args = { value: root };\n }\n\n static parse(parser) {\n if (parser.matchOpToken(\"-\")) {\n var root = parser.requireElement(\"negativeNumber\");\n return new NegativeNumber(root);\n } else {\n return parser.requireElement(\"primaryExpression\");\n }\n }\n\n resolve(context, { value }) {\n return -value;\n }\n}\n\n/**\n * LogicalNot - Represents logical NOT operator\n *\n * Parses: not expression\n * Returns: boolean negation\n */\nexport class LogicalNot extends Expression {\n static grammarName = \"logicalNot\";\n static expressionType = \"unary\";\n\n constructor(root) {\n super();\n this.root = root;\n this.args = { value: root };\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"not\")) return;\n var root = parser.requireElement(\"unaryExpression\");\n return new LogicalNot(root);\n }\n\n resolve(context, { value: val }) {\n return !val;\n }\n}\n\n/**\n * SymbolRef - Represents variable/symbol references\n *\n * Parses: identifier | global identifier | local identifier | :identifier | $identifier\n * Returns: resolved symbol value\n */\nexport class SymbolRef extends Expression {\n static grammarName = \"symbol\";\n static assignable = true;\n\n constructor(token, scope, name, targetExpr) {\n super();\n this.token = token;\n this.scope = scope;\n this.name = name;\n this.targetExpr = targetExpr || null;\n }\n\n static parse(parser) {\n var scope = null;\n if (parser.matchToken(\"global\")) {\n scope = \"global\";\n } else if (parser.matchToken(\"element\")) {\n scope = \"element\";\n // optional possessive\n if (parser.matchOpToken(\"'\")) {\n parser.requireToken(\"s\");\n }\n } else if (parser.matchToken(\"dom\")) {\n scope = \"inherited\";\n } else if (parser.matchToken(\"local\")) {\n scope = \"local\";\n }\n\n // TODO better look ahead here\n let eltPrefix = parser.matchOpToken(\":\");\n let caretPrefix = !eltPrefix && parser.matchOpToken(\"^\");\n let identifier = parser.matchTokenType(\"IDENTIFIER\");\n if (identifier && identifier.value) {\n var name = identifier.value;\n if (eltPrefix) {\n name = \":\" + name;\n } else if (caretPrefix) {\n name = \"^\" + name;\n }\n // Resolve final scope: explicit keyword > name prefix > local default\n if (scope === null) {\n if (name.startsWith(\"$\")) {\n scope = \"global\";\n } else if (name.startsWith(\":\")) {\n scope = \"element\";\n } else if (name.startsWith(\"^\")) {\n scope = \"inherited\";\n } else {\n scope = \"local\";\n }\n }\n var targetExpr = null;\n if (scope === \"inherited\" && parser.matchToken(\"on\")) {\n var follows = parser.pushFollows(\"to\", \"into\", \"before\", \"after\", \"then\");\n try {\n targetExpr = parser.requireElement(\"expression\");\n } finally {\n parser.popFollows(follows);\n }\n }\n return new SymbolRef(identifier, scope, name, targetExpr);\n }\n }\n\n resolve(context) {\n return context.meta.runtime.resolveSymbol(this.name, context, this.scope,\n this.targetExpr ? this.targetExpr.evaluate(context) : null);\n }\n\n get lhs() { return {}; }\n\n set(ctx, lhs, value) {\n ctx.meta.runtime.setSymbol(this.name, ctx, this.scope, value,\n this.targetExpr ? this.targetExpr.evaluate(ctx) : null);\n }\n}\n\n/**\n * BeepExpression - Debug operator that logs expression values\n *\n * Parses: beep! expression\n * Returns: expression value (after logging to console)\n */\nexport class BeepExpression extends Expression {\n static grammarName = \"beepExpression\";\n static expressionType = \"unary\";\n\n constructor(expression) {\n super();\n this.expression = expression;\n this.expression['booped'] = true;\n this.args = { value: expression };\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"beep!\")) return;\n var expression = parser.parseElement(\"unaryExpression\");\n if (expression) {\n return new BeepExpression(expression);\n }\n }\n\n resolve(ctx, { value }) {\n ctx.meta.runtime.beepValueToConsole(ctx.me, this.expression, value);\n return value;\n }\n}\n\n/**\n * PropertyAccess - Represents dot notation property access\n *\n * Parses: expression.property\n * Returns: property value from the root expression\n */\nexport class PropertyAccess extends Expression {\n static grammarName = \"propertyAccess\";\n static expressionType = \"indirect\";\n static assignable = true;\n\n constructor(root, prop) {\n super();\n this.root = root;\n this.prop = prop;\n this.args = { root };\n }\n\n static parse(parser, root) {\n if (!parser.matchOpToken(\".\")) return;\n var prop = parser.requireTokenType(\"IDENTIFIER\");\n var propertyAccess = new PropertyAccess(root, prop);\n return parser.parseElement(\"indirectExpression\", propertyAccess);\n }\n\n resolve(context, { root: rootVal }) {\n return context.meta.runtime.resolveProperty(rootVal, this.prop.value);\n }\n\n get lhs() { return { root: this.root }; }\n\n set(ctx, lhs, value) {\n ctx.meta.runtime.nullCheck(lhs.root, this.root);\n var runtime = ctx.meta.runtime;\n runtime.implicitLoop(lhs.root, elt => {\n runtime.setProperty(elt, this.prop.value, value);\n });\n }\n\n delete(ctx, lhs) {\n ctx.meta.runtime.nullCheck(lhs.root, this.root);\n var runtime = ctx.meta.runtime;\n var prop = this.prop.value;\n runtime.implicitLoop(lhs.root, elt => {\n delete elt[prop];\n runtime.notifyMutation(elt);\n });\n }\n}\n\n/**\n * OfExpression - Represents reversed property access (property of object)\n *\n * Parses: property of expression\n * Returns: property value from the expression\n */\nexport class OfExpression extends Expression {\n static grammarName = \"ofExpression\";\n static expressionType = \"indirect\";\n static assignable = true;\n\n constructor(prop, newRoot, attribute, expression, args, urRoot) {\n super();\n this.prop = prop; // token\n this.root = newRoot;\n this.attribute = attribute;\n this.expression = expression;\n this.args = args;\n this._urRoot = urRoot;\n this._prop = urRoot.name;\n this._isAttribute = urRoot.type === \"attributeRef\";\n this._isStyle = urRoot.type === \"styleRef\";\n this._isComputed = urRoot.type === \"computedStyleRef\";\n }\n\n static parse(parser, root) {\n if (!parser.matchToken(\"of\")) return;\n var newRoot = parser.requireElement(\"unaryExpression\");\n // find the urroot\n var childOfUrRoot = null;\n var urRoot = root;\n while (urRoot.root) {\n childOfUrRoot = urRoot;\n urRoot = urRoot.root;\n }\n var validOfRoots = [\"symbol\", \"attributeRef\", \"styleRef\", \"computedStyleRef\"];\n if (!validOfRoots.includes(urRoot.type)) {\n parser.raiseError(\"Cannot take a property of a non-symbol: \" + urRoot.type);\n }\n var attribute = urRoot.type === \"attributeRef\";\n var style = urRoot.type === \"styleRef\" || urRoot.type === \"computedStyleRef\";\n var attributeElt = (attribute || style) ? urRoot : null;\n var prop = urRoot.name;\n\n var propertyAccess = new OfExpression(\n urRoot.token, // can be undefined for attributeRef\n newRoot,\n attributeElt,\n root,\n { root: newRoot },\n urRoot\n );\n\n if (urRoot.type === \"attributeRef\") {\n propertyAccess.attribute = urRoot;\n }\n if (childOfUrRoot) {\n childOfUrRoot.root = propertyAccess;\n childOfUrRoot.args = { root: propertyAccess };\n } else {\n root = propertyAccess;\n }\n\n return parser.parseElement(\"indirectExpression\", root);\n }\n\n resolve(context, { root: rootVal }) {\n if (this._isAttribute) {\n return context.meta.runtime.resolveAttribute(rootVal, this._prop);\n } else if (this._isComputed) {\n return context.meta.runtime.resolveComputedStyle(rootVal, this._prop);\n } else if (this._isStyle) {\n return context.meta.runtime.resolveStyle(rootVal, this._prop);\n } else {\n return context.meta.runtime.resolveProperty(rootVal, this._prop);\n }\n }\n\n get lhs() { return { root: this.root }; }\n\n set(ctx, lhs, value) {\n ctx.meta.runtime.nullCheck(lhs.root, this.root);\n if (this._isAttribute) {\n ctx.meta.runtime.implicitLoop(lhs.root, elt => {\n value == null ? elt.removeAttribute(this._prop) : elt.setAttribute(this._prop, value);\n });\n } else if (this._isStyle) {\n ctx.meta.runtime.implicitLoop(lhs.root, elt => { elt.style[this._prop] = value; });\n } else {\n var runtime = ctx.meta.runtime;\n runtime.implicitLoop(lhs.root, elt => {\n runtime.setProperty(elt, this._prop, value);\n });\n }\n }\n\n delete(ctx, lhs) {\n ctx.meta.runtime.nullCheck(lhs.root, this.root);\n var runtime = ctx.meta.runtime;\n var prop = this._prop;\n if (this._isAttribute) {\n runtime.implicitLoop(lhs.root, elt => elt.removeAttribute(prop));\n } else if (this._isStyle) {\n runtime.implicitLoop(lhs.root, elt => elt.style.removeProperty(prop));\n } else {\n runtime.implicitLoop(lhs.root, elt => {\n delete elt[prop];\n runtime.notifyMutation(elt);\n });\n }\n }\n}\n\n/**\n * PossessiveExpression - Represents possessive property access\n *\n * Parses: expression's property | my property | its property\n * Returns: property value\n */\nexport class PossessiveExpression extends Expression {\n static grammarName = \"possessive\";\n static expressionType = \"indirect\";\n static assignable = true;\n\n constructor(root, attribute, prop) {\n super();\n this.root = root;\n this.attribute = attribute;\n this.prop = prop;\n this.args = { root };\n }\n\n static parse(parser, root) {\n var apostrophe = parser.matchOpToken(\"'\");\n if (\n apostrophe ||\n (root.type === \"symbol\" &&\n (root.name === \"my\" || root.name === \"its\" || root.name === \"your\") &&\n (parser.currentToken().type === \"IDENTIFIER\" || parser.currentToken().type === \"ATTRIBUTE_REF\" || parser.currentToken().type === \"STYLE_REF\"))\n ) {\n if (apostrophe) {\n parser.requireToken(\"s\");\n }\n\n var attribute, style, prop;\n attribute = parser.parseElement(\"attributeRef\");\n if (attribute == null) {\n style = parser.parseElement(\"styleRef\");\n if (style == null) {\n prop = parser.requireTokenType(\"IDENTIFIER\");\n }\n }\n var propertyAccess = new PossessiveExpression(root, attribute || style, prop);\n return parser.parseElement(\"indirectExpression\", propertyAccess);\n }\n }\n\n resolve(context, { root: rootVal }) {\n var value;\n if (this.attribute) {\n if (this.attribute.type === 'computedStyleRef') {\n value = context.meta.runtime.resolveComputedStyle(rootVal, this.attribute['name']);\n } else if (this.attribute.type === 'styleRef') {\n value = context.meta.runtime.resolveStyle(rootVal, this.attribute['name']);\n } else {\n value = context.meta.runtime.resolveAttribute(rootVal, this.attribute.name);\n }\n } else {\n value = context.meta.runtime.resolveProperty(rootVal, this.prop.value);\n }\n return value;\n }\n\n get lhs() { return { root: this.root }; }\n\n set(ctx, lhs, value) {\n ctx.meta.runtime.nullCheck(lhs.root, this.root);\n if (this.attribute) {\n var name = this.attribute.name;\n if (this.attribute.type === 'styleRef') {\n ctx.meta.runtime.implicitLoop(lhs.root, elt => { elt.style[name] = value; });\n } else {\n ctx.meta.runtime.implicitLoop(lhs.root, elt => {\n value == null ? elt.removeAttribute(name) : elt.setAttribute(name, value);\n });\n }\n } else {\n var runtime = ctx.meta.runtime;\n var prop = this.prop.value;\n runtime.implicitLoop(lhs.root, elt => {\n runtime.setProperty(elt, prop, value);\n });\n }\n }\n}\n\n/**\n * InExpression - Represents containment check\n *\n * Parses: expression in target\n * Returns: filtered elements or boolean\n */\nexport class InExpression extends Expression {\n static grammarName = \"inExpression\";\n static expressionType = \"indirect\";\n static assignable = true;\n\n constructor(root, target) {\n super();\n this.root = root;\n this.target = target;\n this.args = { root, target };\n }\n\n static parse(parser, root) {\n if (!parser.matchToken(\"in\")) return;\n var target = parser.requireElement(\"unaryExpression\");\n var result = new InExpression(root, target);\n if (parser.matchToken(\"where\")) {\n result = new WhereExpression(result, CollectionExpression.parseOperand(parser));\n }\n return parser.parseElement(\"indirectExpression\", result);\n }\n\n resolve(context, { root: rootVal, target }) {\n if (rootVal == null) return [];\n var returnArr = [];\n if (rootVal.css) {\n context.meta.runtime.implicitLoop(target, function (targetElt) {\n var results = context.meta.runtime.resolveQuery(targetElt, rootVal.css);\n for (var i = 0; i < results.length; i++) {\n returnArr.push(results[i]);\n }\n });\n } else if (rootVal instanceof Element) {\n var within = false;\n context.meta.runtime.implicitLoop(target, function (targetElt) {\n if (targetElt.contains(rootVal)) {\n within = true;\n }\n });\n if(within) {\n return rootVal;\n }\n } else {\n context.meta.runtime.implicitLoop(rootVal, function (rootElt) {\n context.meta.runtime.implicitLoop(target, function (targetElt) {\n if (rootElt === targetElt) {\n returnArr.push(rootElt);\n }\n });\n });\n }\n return returnArr;\n }\n\n get lhs() { return { root: this.root, target: this.target }; }\n set(ctx, lhs, value) {\n var targets = this.resolve(ctx, lhs);\n ctx.meta.runtime.replaceInDom(targets, value);\n }\n}\n\n/**\n * AsExpression - Type conversion expression\n *\n * Parses: expression as Type [| Type]*\n * Returns: converted value\n */\nexport class AsExpression extends Expression {\n static grammarName = \"asExpression\";\n static expressionType = \"indirect\";\n\n constructor(root, conversion) {\n super();\n this.root = root;\n this.conversion = conversion;\n this.args = { root };\n }\n\n static parse(parser, root) {\n if (!parser.matchToken(\"as\")) return;\n parser.matchToken(\"a\") || parser.matchToken(\"an\");\n var conversion = parser.requireElement(\"dotOrColonPath\").evalStatically();\n var asExpr = new AsExpression(root, conversion);\n while (parser.matchOpToken(\"|\")) {\n conversion = parser.requireElement(\"dotOrColonPath\").evalStatically();\n asExpr = new AsExpression(asExpr, conversion);\n }\n return parser.parseElement(\"indirectExpression\", asExpr);\n }\n\n resolve(context, { root: rootVal }) {\n return context.meta.runtime.convertValue(rootVal, this.conversion);\n }\n}\n\n/**\n * FunctionCall - Represents function call expressions\n *\n * Parses: function(args) or object.method(args)\n * Returns: function result\n */\nexport class FunctionCall extends Expression {\n static grammarName = \"functionCall\";\n static expressionType = \"indirect\";\n\n constructor(root, argExpressions, args, isMethodCall) {\n super();\n this.root = root;\n this.argExpressions = argExpressions;\n this.args = args;\n this._isMethodCall = isMethodCall;\n this._parseRoot = root; // Store original root from parse time (before mutations)\n }\n\n static parse(parser, root) {\n if (!parser.matchOpToken(\"(\")) return;\n var args = [];\n if (!parser.matchOpToken(\")\")) {\n do {\n args.push(parser.requireElement(\"expression\"));\n } while (parser.matchOpToken(\",\"));\n parser.requireOpToken(\")\");\n }\n\n var functionCall;\n if (root.root) {\n functionCall = new FunctionCall(root, args, { target: root.root, argVals: args }, true);\n } else {\n functionCall = new FunctionCall(root, args, { target: root, argVals: args }, false);\n }\n return parser.parseElement(\"indirectExpression\", functionCall);\n }\n\n resolve(context, { target, argVals }) {\n if (this._isMethodCall) {\n context.meta.runtime.nullCheck(target, this._parseRoot.root);\n var methodName = this._parseRoot.prop.value;\n var func = target[methodName];\n context.meta.runtime.nullCheck(func, this._parseRoot);\n if (func.hyperfunc) {\n argVals.push(context);\n }\n var result = func.apply(target, argVals);\n context.meta.runtime.maybeNotify(target, methodName);\n return result;\n } else {\n context.meta.runtime.nullCheck(target, this._parseRoot);\n if (target.hyperfunc) {\n argVals.push(context);\n }\n return target(...argVals);\n }\n }\n}\n\n/**\n * AttributeRefAccess - Attribute reference on an expression\n *\n * Parses: expression@attribute\n * Returns: attribute value\n */\nexport class AttributeRefAccess extends Expression {\n static grammarName = \"attributeRefAccess\";\n static expressionType = \"indirect\";\n static assignable = true;\n\n constructor(root, attribute) {\n super();\n this.root = root;\n this.attribute = attribute;\n this.args = { root };\n }\n\n static parse(parser, root) {\n var attribute = parser.parseElement(\"attributeRef\");\n if (!attribute) return;\n return new AttributeRefAccess(root, attribute);\n }\n\n resolve(_ctx, { root: rootVal }) {\n return _ctx.meta.runtime.resolveAttribute(rootVal, this.attribute.name);\n }\n\n get lhs() { return { root: this.root }; }\n\n set(ctx, lhs, value) {\n ctx.meta.runtime.nullCheck(lhs.root, this.root);\n ctx.meta.runtime.implicitLoop(lhs.root, elt => {\n value == null ? elt.removeAttribute(this.attribute.name) : elt.setAttribute(this.attribute.name, value);\n });\n }\n}\n\n/**\n * ArrayIndex - Array/object indexing and slicing\n *\n * Parses: array[index] | array[start..end] | array[..end] | array[start..]\n * Returns: indexed value or slice\n */\nexport class ArrayIndex extends Expression {\n static grammarName = \"arrayIndex\";\n static expressionType = \"indirect\";\n static assignable = true;\n\n constructor(root, firstIndex, secondIndex, andBefore, andAfter) {\n super();\n this.root = root;\n this.prop = firstIndex;\n this.firstIndex = firstIndex;\n this.secondIndex = secondIndex;\n this.andBefore = andBefore;\n this.andAfter = andAfter;\n this.args = { root, firstIndex, secondIndex };\n }\n\n static parse(parser, root) {\n if (!parser.matchOpToken(\"[\")) return;\n var andBefore = false;\n var andAfter = false;\n var firstIndex = null;\n var secondIndex = null;\n\n if (parser.matchOpToken(\"..\")) {\n andBefore = true;\n firstIndex = parser.requireElement(\"expression\");\n } else {\n firstIndex = parser.requireElement(\"expression\");\n\n if (parser.matchOpToken(\"..\")) {\n andAfter = true;\n var current = parser.currentToken();\n if (current.type !== \"R_BRACKET\") {\n secondIndex = parser.parseElement(\"expression\");\n }\n }\n }\n parser.requireOpToken(\"]\");\n\n var arrayIndex = new ArrayIndex(root, firstIndex, secondIndex, andBefore, andAfter);\n return parser.parseElement(\"indirectExpression\", arrayIndex);\n }\n\n resolve(_ctx, { root, firstIndex, secondIndex }) {\n if (root == null) {\n return null;\n }\n if (this.andBefore) {\n if (firstIndex < 0) {\n firstIndex = root.length + firstIndex;\n }\n return root.slice(0, firstIndex + 1); // returns all items from beginning to firstIndex (inclusive)\n } else if (this.andAfter) {\n if (secondIndex != null) {\n if (secondIndex < 0) {\n secondIndex = root.length + secondIndex;\n }\n return root.slice(firstIndex, secondIndex + 1); // returns all items from firstIndex to secondIndex (inclusive)\n } else {\n return root.slice(firstIndex); // returns from firstIndex to end of array\n }\n } else {\n return root[firstIndex];\n }\n }\n\n get lhs() { return { root: this.root, index: this.firstIndex }; }\n\n set(ctx, lhs, value) {\n ctx.meta.runtime.nullCheck(lhs.root, this.root);\n lhs.root[lhs.index] = value;\n }\n\n delete(ctx, lhs) {\n if (this.andBefore || this.andAfter) {\n throw new Error(\"Cannot remove a slice - use a single index\");\n }\n ctx.meta.runtime.nullCheck(lhs.root, this.root);\n var runtime = ctx.meta.runtime;\n var root = lhs.root;\n var idx = lhs.index;\n if (Array.isArray(root)) {\n if (idx < 0) idx = root.length + idx;\n root.splice(idx, 1);\n } else {\n delete root[idx];\n }\n runtime.notifyMutation(root);\n }\n}\n\n/**\n * MathOperator - Binary math operations\n *\n * Parses: expr + expr | expr - expr | expr * expr | expr / expr | expr mod expr\n * Returns: computed result\n * Note: Enforces same operator throughout chain (must parenthesize mixed operators)\n */\nexport class MathOperator extends Expression {\n static grammarName = \"mathOperator\";\n\n constructor(lhs, operator, rhs) {\n super();\n this.lhs = lhs;\n this.rhs = rhs;\n this.operator = operator;\n this.args = { lhs, rhs };\n }\n\n static parse(parser) {\n var expr = parser.parseElement(\"collectionExpression\");\n var mathOp, initialMathOp = null;\n mathOp = parser.matchAnyOpToken(\"+\", \"-\", \"*\", \"/\") || parser.matchToken('mod');\n while (mathOp) {\n initialMathOp = initialMathOp || mathOp;\n var operator = mathOp.value;\n if (initialMathOp.value !== operator) {\n parser.raiseError(\"You must parenthesize math operations with different operators\");\n }\n var rhs = parser.parseElement(\"collectionExpression\");\n expr = new MathOperator(expr, operator, rhs);\n mathOp = parser.matchAnyOpToken(\"+\", \"-\", \"*\", \"/\") || parser.matchToken('mod');\n }\n return expr;\n }\n\n resolve(context, { lhs: lhsVal, rhs: rhsVal }) {\n if (this.operator === \"+\") {\n if (Array.isArray(lhsVal)) return lhsVal.concat(rhsVal);\n return lhsVal + rhsVal;\n } else if (this.operator === \"-\") {\n return lhsVal - rhsVal;\n } else if (this.operator === \"*\") {\n return lhsVal * rhsVal;\n } else if (this.operator === \"/\") {\n return lhsVal / rhsVal;\n } else if (this.operator === \"mod\") {\n return lhsVal % rhsVal;\n }\n }\n}\n\n/**\n * ComparisonOperator - Comparison operations\n *\n * Parses: expr == expr | expr < expr | expr is empty | expr matches pattern | etc.\n * Returns: boolean result\n * Supports: ==, !=, ===, !==, <, >, <=, >=, is, am, match, contain, include, start with, end with, between, precede, follow, exist, empty\n */\nexport class ComparisonOperator extends Expression {\n static grammarName = \"comparisonOperator\";\n\n constructor(lhs, operator, rhs, typeName, nullOk, ignoringCase, rhs2) {\n super();\n this.operator = operator;\n this.typeName = typeName;\n this.nullOk = nullOk;\n this.ignoringCase = ignoringCase;\n this.lhs = lhs;\n this.rhs = rhs;\n this.rhs2 = rhs2;\n this.args = { lhs, rhs, rhs2 };\n }\n\n sloppyContains(src, container, value) {\n if (container['contains']) {\n return container.contains(value);\n } else if (container['includes']) {\n return container.includes(value);\n } else {\n throw new Error(\"The value of \" + src.sourceFor() + \" does not have a contains or includes method on it\");\n }\n }\n\n sloppyMatches(src, target, toMatch) {\n if (target['match']) {\n return !!target.match(toMatch);\n } else if (target['matches']) {\n return target.matches(toMatch);\n } else {\n throw new Error(\"The value of \" + src.sourceFor() + \" does not have a match or matches method on it\");\n }\n }\n\n static parse(parser) {\n var expr = parser.parseElement(\"mathOperator\");\n var comparisonToken = parser.matchAnyOpToken(\"<\", \">\", \"<=\", \">=\", \"==\", \"===\", \"!=\", \"!==\");\n var operator = comparisonToken ? comparisonToken.value : null;\n var hasRightValue = true;\n var typeCheck = false;\n\n if (operator == null) {\n if (parser.matchToken(\"is\") || parser.matchToken(\"am\")) {\n if (parser.matchToken(\"not\")) {\n if (parser.matchToken(\"in\")) {\n operator = \"not in\";\n } else if (parser.matchToken(\"a\") || parser.matchToken(\"an\")) {\n operator = \"not a\";\n typeCheck = true;\n } else if (parser.matchToken(\"empty\")) {\n operator = \"not empty\";\n hasRightValue = false;\n } else if (parser.matchToken(\"between\")) {\n operator = \"not between\";\n } else if (parser.matchToken(\"really\")) {\n operator = \"!==\";\n if (parser.matchToken(\"equal\")) parser.matchToken(\"to\");\n } else if (parser.matchToken(\"equal\")) {\n parser.matchToken(\"to\");\n operator = \"!=\";\n } else {\n operator = \"is not\";\n }\n } else if (parser.matchToken(\"in\")) {\n operator = \"in\";\n } else if (parser.matchToken(\"a\") || parser.matchToken(\"an\")) {\n operator = \"a\";\n typeCheck = true;\n } else if (parser.matchToken(\"empty\")) {\n operator = \"empty\";\n hasRightValue = false;\n } else if (parser.matchToken(\"between\")) {\n operator = \"between\";\n } else if (parser.matchToken(\"less\")) {\n parser.requireToken(\"than\");\n if (parser.matchToken(\"or\")) {\n parser.requireToken(\"equal\");\n parser.requireToken(\"to\");\n operator = \"<=\";\n } else {\n operator = \"<\";\n }\n } else if (parser.matchToken(\"greater\")) {\n parser.requireToken(\"than\");\n if (parser.matchToken(\"or\")) {\n parser.requireToken(\"equal\");\n parser.requireToken(\"to\");\n operator = \">=\";\n } else {\n operator = \">\";\n }\n } else if (parser.matchToken(\"really\")) {\n operator = \"===\";\n if (parser.matchToken(\"equal\")) parser.matchToken(\"to\");\n } else if (parser.matchToken(\"equal\")) {\n parser.matchToken(\"to\");\n operator = \"==\";\n } else {\n operator = \"is\";\n }\n } else if (parser.matchToken(\"equals\")) {\n operator = \"==\";\n } else if (parser.matchToken(\"really\")) {\n parser.requireToken(\"equals\")\n operator = \"===\";\n } else if (parser.matchToken(\"exist\") || parser.matchToken(\"exists\")) {\n operator = \"exist\";\n hasRightValue = false;\n } else if (parser.matchToken(\"matches\") || parser.matchToken(\"match\")) {\n operator = \"match\";\n } else if (parser.matchToken(\"contains\") || parser.matchToken(\"contain\")) {\n operator = \"contain\";\n } else if (parser.matchToken(\"includes\") || parser.matchToken(\"include\")) {\n operator = \"include\";\n } else if (parser.matchToken(\"starts\")) {\n parser.requireToken(\"with\");\n operator = \"start with\";\n } else if (parser.matchToken(\"ends\")) {\n parser.requireToken(\"with\");\n operator = \"end with\";\n } else if (parser.matchToken(\"precedes\") || parser.matchToken(\"precede\")) {\n operator = \"precede\";\n } else if (parser.matchToken(\"follows\") || parser.matchToken(\"follow\")) {\n operator = \"follow\";\n } else if (parser.matchToken(\"do\") || parser.matchToken(\"does\")) {\n parser.requireToken(\"not\");\n if (parser.matchToken(\"matches\") || parser.matchToken(\"match\")) {\n operator = \"not match\";\n } else if (parser.matchToken(\"contains\") || parser.matchToken(\"contain\")) {\n operator = \"not contain\";\n } else if (parser.matchToken(\"exist\")) {\n operator = \"not exist\";\n hasRightValue = false;\n } else if (parser.matchToken(\"include\")) {\n operator = \"not include\";\n } else if (parser.matchToken(\"start\")) {\n parser.requireToken(\"with\");\n operator = \"not start with\";\n } else if (parser.matchToken(\"end\")) {\n parser.requireToken(\"with\");\n operator = \"not end with\";\n } else if (parser.matchToken(\"precede\")) {\n operator = \"not precede\";\n } else if (parser.matchToken(\"follow\")) {\n operator = \"not follow\";\n } else {\n parser.raiseExpected('matches', 'contains', 'starts with', 'ends with', 'precede', 'follow');\n }\n }\n }\n\n if (operator) {\n var typeName, nullOk, rhs;\n if (typeCheck) {\n typeName = parser.requireTokenType(\"IDENTIFIER\");\n nullOk = !parser.matchOpToken(\"!\");\n } else if (hasRightValue) {\n rhs = parser.requireElement(\"mathOperator\");\n if (operator === \"match\" || operator === \"not match\") {\n rhs = rhs.css ? rhs.css : rhs;\n }\n }\n var rhs2 = null;\n if (operator === \"between\" || operator === \"not between\") {\n parser.requireToken(\"and\");\n rhs2 = parser.requireElement(\"mathOperator\");\n }\n var ignoringCase = false;\n if (parser.matchToken(\"ignoring\")) {\n parser.requireToken(\"case\");\n ignoringCase = true;\n }\n var lhs = expr;\n expr = new ComparisonOperator(lhs, operator, rhs, typeName, nullOk, ignoringCase, rhs2);\n }\n return expr;\n }\n\n resolve(context, { lhs: lhsVal, rhs: rhsVal, rhs2: rhs2Val }) {\n const operator = this.operator;\n const lhs = this.lhs;\n const rhs = this.rhs;\n const typeName = this.typeName;\n const nullOk = this.nullOk;\n\n if (this.ignoringCase) {\n if (typeof lhsVal === \"string\") lhsVal = lhsVal.toLowerCase();\n if (typeof rhsVal === \"string\") rhsVal = rhsVal.toLowerCase();\n }\n\n if (operator === \"is\") {\n if (rhsVal === undefined && rhs.type === \"symbol\" && rhs.scope === \"local\"\n && rhs.name !== \"undefined\" && rhs.name !== \"null\") {\n return !!context.meta.runtime.resolveProperty(lhsVal, rhs.name);\n }\n return lhsVal == rhsVal;\n }\n if (operator === \"is not\") {\n if (rhsVal === undefined && rhs.type === \"symbol\" && rhs.scope === \"local\"\n && rhs.name !== \"undefined\" && rhs.name !== \"null\") {\n return !context.meta.runtime.resolveProperty(lhsVal, rhs.name);\n }\n return lhsVal != rhsVal;\n }\n if (operator === \"==\") return lhsVal == rhsVal;\n if (operator === \"!=\") return lhsVal != rhsVal;\n if (operator === \"===\") return lhsVal === rhsVal;\n if (operator === \"!==\") return lhsVal !== rhsVal;\n if (operator === \"<\") return lhsVal < rhsVal;\n if (operator === \">\") return lhsVal > rhsVal;\n if (operator === \"<=\") return lhsVal <= rhsVal;\n if (operator === \">=\") return lhsVal >= rhsVal;\n if (operator === \"match\") return lhsVal != null && this.sloppyMatches(lhs, lhsVal, rhsVal);\n if (operator === \"not match\") return lhsVal == null || !this.sloppyMatches(lhs, lhsVal, rhsVal);\n if (operator === \"in\") return rhsVal != null && this.sloppyContains(rhs, rhsVal, lhsVal);\n if (operator === \"not in\") return rhsVal == null || !this.sloppyContains(rhs, rhsVal, lhsVal);\n if (operator === \"contain\" || operator === \"include\") return lhsVal != null && this.sloppyContains(lhs, lhsVal, rhsVal);\n if (operator === \"not contain\" || operator === \"not include\") return lhsVal == null || !this.sloppyContains(lhs, lhsVal, rhsVal);\n if (operator === \"start with\") return lhsVal != null && String(lhsVal).startsWith(rhsVal);\n if (operator === \"not start with\") return lhsVal == null || !String(lhsVal).startsWith(rhsVal);\n if (operator === \"end with\") return lhsVal != null && String(lhsVal).endsWith(rhsVal);\n if (operator === \"not end with\") return lhsVal == null || !String(lhsVal).endsWith(rhsVal);\n if (operator === \"between\") return lhsVal >= rhsVal && lhsVal <= rhs2Val;\n if (operator === \"not between\") return lhsVal < rhsVal || lhsVal > rhs2Val;\n if (operator === \"precede\") return lhsVal != null && rhsVal != null && (lhsVal.compareDocumentPosition(rhsVal) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0;\n if (operator === \"not precede\") return lhsVal == null || rhsVal == null || (lhsVal.compareDocumentPosition(rhsVal) & Node.DOCUMENT_POSITION_FOLLOWING) === 0;\n if (operator === \"follow\") return lhsVal != null && rhsVal != null && (lhsVal.compareDocumentPosition(rhsVal) & Node.DOCUMENT_POSITION_PRECEDING) !== 0;\n if (operator === \"not follow\") return lhsVal == null || rhsVal == null || (lhsVal.compareDocumentPosition(rhsVal) & Node.DOCUMENT_POSITION_PRECEDING) === 0;\n if (operator === \"empty\") return context.meta.runtime.isEmpty(lhsVal);\n if (operator === \"not empty\") return !context.meta.runtime.isEmpty(lhsVal);\n if (operator === \"exist\") return context.meta.runtime.doesExist(lhsVal);\n if (operator === \"not exist\") return !context.meta.runtime.doesExist(lhsVal);\n if (operator === \"a\") return context.meta.runtime.typeCheck(lhsVal, typeName.value, nullOk);\n if (operator === \"not a\") return !context.meta.runtime.typeCheck(lhsVal, typeName.value, nullOk);\n throw new Error(\"Unknown comparison : \" + operator);\n }\n}\n\n/**\n * LogicalOperator - Logical and/or operations\n *\n * Parses: expr and expr | expr or expr\n * Returns: boolean result\n * Note: Enforces same operator throughout chain (must parenthesize mixed operators)\n */\nexport class LogicalOperator extends Expression {\n static grammarName = \"logicalOperator\";\n static expressionType = \"top\";\n\n constructor(lhs, operator, rhs) {\n super();\n this.operator = operator;\n this.lhs = lhs;\n this.rhs = rhs;\n this.args = { lhs, rhs };\n }\n\n static parse(parser) {\n var expr = parser.parseElement(\"comparisonOperator\");\n var logicalOp, initialLogicalOp = null;\n logicalOp = parser.matchToken(\"and\") || parser.matchToken(\"or\");\n while (logicalOp) {\n initialLogicalOp = initialLogicalOp || logicalOp;\n if (initialLogicalOp.value !== logicalOp.value) {\n parser.raiseError(\"You must parenthesize logical operations with different operators\");\n }\n var rhs = parser.requireElement(\"comparisonOperator\");\n const operator = logicalOp.value;\n expr = new LogicalOperator(expr, operator, rhs);\n logicalOp = parser.matchToken(\"and\") || parser.matchToken(\"or\");\n }\n return expr;\n }\n\n resolve(context, { lhs: lhsVal, rhs: rhsVal }) {\n if (this.operator === \"and\") {\n return lhsVal && rhsVal;\n } else {\n return lhsVal || rhsVal;\n }\n }\n\n // override to handle promise-compatible and/or short-circuiting\n evaluate(context) {\n var self = this;\n var shortCircuitValue = this.operator === \"or\"; // `or` short-circuits on truthy, `and` on falsy\n var lhsVal = this.lhs.evaluate(context);\n\n var continueWith = function (resolvedLhs) {\n if (!!resolvedLhs === shortCircuitValue) {\n return resolvedLhs;\n }\n var rhsVal = self.rhs.evaluate(context);\n if (rhsVal && rhsVal.then) {\n return rhsVal.then(r => self.resolve(context, { lhs: resolvedLhs, rhs: r }));\n }\n return self.resolve(context, { lhs: resolvedLhs, rhs: rhsVal });\n };\n\n if (lhsVal && lhsVal.then) {\n return lhsVal.then(continueWith);\n }\n return continueWith(lhsVal);\n }\n}\n\n\n/**\n * DotOrColonPath - Path with dots or colons\n *\n * Parses: identifier.identifier.identifier OR identifier:identifier:identifier\n * Returns: joined path string\n */\nclass DotOrColonPathNode extends Expression {\n constructor(path, separator) {\n super();\n this.type = \"dotOrColonPath\";\n this.path = path;\n this.separator = separator;\n }\n\n evalStatically() {\n return this.path.join(this.separator ? this.separator : \"\");\n }\n\n resolve() {\n return this.evalStatically();\n }\n}\n\n/**\n * CollectionOp - Centralized parser for collection postfix expressions.\n *\n * Handles: where, sorted by, mapped to, split by, joined by\n *\n * All collection keywords live in one list. When parsing the operand of any\n * collection op, the OTHER keywords are pushed as follows so they act as\n * boundaries. This is the single place to update when adding new collection ops.\n */\n/**\n * CollectionExpression - Collection operations (where, sorted by, mapped to, etc.)\n *\n * Lives above unaryExpression in the grammar so that collection ops bind\n * more loosely than indirect expressions like `in`, `.property`, `[index]`.\n *\n * Parses: where \n * sorted by [descending]\n * mapped to \n * split by \n * joined by \n */\nexport class CollectionExpression extends Expression {\n static grammarName = \"collectionExpression\";\n static KEYWORDS = [\"where\", \"sorted\", \"mapped\", \"split\", \"joined\"];\n\n static parseOperand(parser) {\n var count = parser.pushFollows(...this.KEYWORDS);\n try {\n return parser.requireElement(\"expression\");\n } finally {\n parser.popFollows(count);\n }\n }\n\n static parse(parser) {\n var root = parser.parseElement(\"unaryExpression\");\n var changed = true;\n while (changed) {\n changed = false;\n if (parser.matchToken(\"where\")) {\n root = new WhereExpression(root, this.parseOperand(parser));\n changed = true;\n } else if (parser.matchToken(\"sorted\")) {\n parser.requireToken(\"by\");\n var key = this.parseOperand(parser);\n var descending = parser.matchToken(\"descending\");\n root = new SortedByExpression(root, key, !!descending);\n changed = true;\n } else if (parser.matchToken(\"mapped\")) {\n parser.requireToken(\"to\");\n root = new MappedToExpression(root, this.parseOperand(parser));\n changed = true;\n } else if (parser.matchToken(\"split\")) {\n parser.requireToken(\"by\");\n root = new SplitByExpression(root, this.parseOperand(parser));\n changed = true;\n } else if (parser.matchToken(\"joined\")) {\n parser.requireToken(\"by\");\n root = new JoinedByExpression(root, this.parseOperand(parser));\n changed = true;\n }\n }\n return root;\n }\n}\n\n/** Filter a collection: where */\nclass WhereExpression extends Expression {\n constructor(root, condition) {\n super();\n this.root = root;\n this.condition = condition;\n this.args = { root };\n }\n\n resolve(context, { root: collection }) {\n if (!collection) return collection;\n var result = [];\n var items = Array.from(collection);\n for (var i = 0; i < items.length; i++) {\n context.beingTested = items[i];\n if (this.varName) context.locals[this.varName] = items[i];\n if (this.condition.evaluate(context)) {\n result.push(items[i]);\n }\n }\n context.beingTested = null;\n return result;\n }\n}\n\n/** Sort a collection: sorted by [descending] */\nclass SortedByExpression extends Expression {\n constructor(root, key, descending) {\n super();\n this.root = root;\n this.key = key;\n this.descending = descending;\n this.args = { root };\n }\n\n resolve(context, { root: collection }) {\n if (!collection) return collection;\n var items = Array.from(collection);\n var keys = [];\n for (var i = 0; i < items.length; i++) {\n context.beingTested = items[i];\n keys.push(this.key.evaluate(context));\n }\n context.beingTested = null;\n var indices = items.map(function (_, i) { return i; });\n var dir = this.descending ? -1 : 1;\n indices.sort(function (a, b) {\n var ka = keys[a], kb = keys[b];\n if (ka == kb) return 0;\n return (ka < kb ? -1 : 1) * dir;\n });\n return indices.map(function (i) { return items[i]; });\n }\n}\n\n/** Map a collection: mapped to */\nclass MappedToExpression extends Expression {\n constructor(root, projection) {\n super();\n this.root = root;\n this.projection = projection;\n this.args = { root };\n }\n\n resolve(context, { root: collection }) {\n if (!collection) return collection;\n var items = Array.from(collection);\n var result = [];\n for (var i = 0; i < items.length; i++) {\n context.beingTested = items[i];\n result.push(this.projection.evaluate(context));\n }\n context.beingTested = null;\n return result;\n }\n}\n\n/** Split a string: split by */\nclass SplitByExpression extends Expression {\n constructor(root, delimiter) {\n super();\n this.args = { root, delimiter };\n }\n\n resolve(context, { root, delimiter }) {\n if (!root) return root;\n return String(root).split(delimiter);\n }\n}\n\n/** Join an array: joined by */\nclass JoinedByExpression extends Expression {\n constructor(root, delimiter) {\n super();\n this.args = { root, delimiter };\n }\n\n resolve(context, { root, delimiter }) {\n if (!root) return root;\n return Array.from(root).join(delimiter);\n }\n}\n\nexport class DotOrColonPath extends Expression {\n static grammarName = \"dotOrColonPath\";\n\n static parse(parser) {\n var root = parser.matchTokenType(\"IDENTIFIER\");\n if (root) {\n var path = [root.value];\n\n var separator = parser.matchOpToken(\".\") || parser.matchOpToken(\":\");\n if (separator) {\n do {\n path.push(parser.requireTokenType(\"IDENTIFIER\", \"NUMBER\").value);\n } while (parser.matchOpToken(separator.value));\n }\n\n return new DotOrColonPathNode(path, separator ? separator.value : null);\n }\n }\n}", "/**\n * Web-related literal parse tree elements\n * References to DOM elements and attributes\n */\nimport { ElementCollection, TemplatedQueryElementCollection } from '../../core/runtime/collections.js';\nimport { Expression } from '../base.js';\nimport { Tokenizer } from '../../core/tokenizer.js';\n\n/**\n * IdRef - Represents ID references (#foo or #${expr})\n *\n * Parses: #elementId | #${expression}\n * Returns: Element with matching ID\n */\nexport class IdRef extends Expression {\n static grammarName = \"idRef\";\n static expressionType = \"leaf\";\n static assignable = true;\n\n constructor(variant, css, value, innerExpression) {\n super();\n this.variant = variant;\n this.type = variant === \"template\" ? \"idRefTemplate\" : \"idRef\";\n this.css = css;\n this.value = value;\n this.args = variant === \"template\" ? { expr: innerExpression } : null;\n }\n\n static parse(parser) {\n\n var elementId = parser.matchTokenType(\"ID_REF\");\n if (!elementId) return;\n if (!elementId.value) return;\n if (elementId.template) {\n var templateValue = elementId.value.substring(2);\n var innerTokens = Tokenizer.tokenize(templateValue);\n var innerParser = parser.createChildParser(innerTokens);\n var innerExpression = innerParser.requireElement(\"expression\");\n return new IdRef(\"template\", null, null, innerExpression);\n } else {\n const value = elementId.value.substring(1);\n return new IdRef(\"static\", elementId.value, value, null);\n }\n }\n\n resolve(context, { expr } = {}) {\n if (this.variant === \"template\") {\n return context.meta.runtime.getRootNode(context.me).getElementById(expr);\n } else {\n return context.meta.runtime.getRootNode(context.me).getElementById(this.value);\n }\n }\n\n get lhs() { return {}; }\n set(ctx, lhs, value) {\n var target = this.resolve(ctx);\n if (target) ctx.meta.runtime.replaceInDom(target, value);\n }\n}\n\n/**\n * ClassRef - Represents class references (.foo or .${expr})\n *\n * Parses: .className | .${expression}\n * Returns: ElementCollection with matching class\n */\nexport class ClassRef extends Expression {\n static grammarName = \"classRef\";\n static expressionType = \"leaf\";\n static assignable = true;\n\n constructor(variant, css, className, innerExpression) {\n super();\n this.variant = variant;\n this.type = variant === \"template\" ? \"classRefTemplate\" : \"classRef\";\n this.css = css;\n this.className = className;\n this.args = variant === \"template\" ? { expr: innerExpression } : null;\n }\n\n static parse(parser) {\n\n var classRef = parser.matchTokenType(\"CLASS_REF\");\n if (!classRef) return;\n if (!classRef.value) return;\n if (classRef.template) {\n var templateValue = classRef.value.substring(2);\n var innerTokens = Tokenizer.tokenize(templateValue);\n var innerParser = parser.createChildParser(innerTokens);\n var innerExpression = innerParser.requireElement(\"expression\");\n return new ClassRef(\"template\", null, null, innerExpression);\n } else {\n const css = classRef.value;\n const className = css.slice(1);\n return new ClassRef(\"static\", css, className, null);\n }\n }\n\n resolve(context, { expr } = {}) {\n if (this.variant === \"template\") {\n return new ElementCollection(\".\" + expr, context.me, true, context.meta.runtime);\n } else {\n return new ElementCollection(this.css, context.me, true, context.meta.runtime);\n }\n }\n\n get lhs() { return {}; }\n set(ctx, lhs, value) {\n var targets = Array.from(this.resolve(ctx));\n ctx.meta.runtime.replaceInDom(targets, value);\n }\n}\n\n/**\n * QueryRef - Represents query selector references ()\n *\n * Parses:
| <.foo/> | <#bar/>\n * Returns: ElementCollection matching query\n */\nexport class QueryRef extends Expression {\n static grammarName = \"queryRef\";\n static expressionType = \"leaf\";\n static assignable = true;\n\n constructor(css, args, template) {\n super();\n this.css = css;\n this.templateArgs = args;\n this.args = template ? { parts: args } : null;\n this.template = template;\n }\n\n static parse(parser) {\n\n\n var queryStart = parser.matchOpToken(\"<\");\n if (!queryStart) return;\n var queryTokens = parser.consumeUntil(\"/\");\n parser.requireOpToken(\"/\");\n parser.requireOpToken(\">\");\n var queryValue = queryTokens\n .map(function (t) {\n if (t.type === \"STRING\") {\n return '\"' + t.value + '\"';\n } else {\n return t.value;\n }\n })\n .join(\"\");\n\n var template, innerTokens, args;\n if (/\\$[^=]/.test(queryValue)) {\n template = true;\n innerTokens = Tokenizer.tokenize(queryValue, true);\n var innerParser = parser.createChildParser(innerTokens);\n args = innerParser.parseStringTemplate();\n }\n\n return new QueryRef(queryValue, args, template);\n }\n\n resolve(context, { parts } = {}) {\n if (this.template) {\n return new TemplatedQueryElementCollection(this.css, context.me, parts, context.meta.runtime);\n } else {\n return new ElementCollection(this.css, context.me, false, context.meta.runtime);\n }\n }\n\n get lhs() { return this.template ? { parts: this.templateArgs } : {}; }\n set(ctx, lhs, value) {\n var targets = Array.from(this.resolve(ctx, lhs));\n ctx.meta.runtime.replaceInDom(targets, value);\n }\n}\n\n/**\n * AttributeRef - Represents attribute references (@attr or [@attr=\"value\"])\n *\n * Parses: @name | @name=\"value\"\n * Returns: Attribute value or ElementCollection\n */\nexport class AttributeRef extends Expression {\n static grammarName = \"attributeRef\";\n static expressionType = \"leaf\";\n static assignable = true;\n\n constructor(name, css, value) {\n super();\n this.name = name;\n this.css = css;\n this.value = value;\n }\n\n static parse(parser) {\n var attributeRef = parser.matchTokenType(\"ATTRIBUTE_REF\");\n if (!attributeRef) return;\n if (!attributeRef.value) return;\n var outerVal = attributeRef.value;\n if (outerVal.startsWith(\"[\")) {\n var innerValue = outerVal.substring(2, outerVal.length - 1);\n } else {\n var innerValue = outerVal.substring(1);\n }\n var css = \"[\" + innerValue + \"]\";\n var split = innerValue.split(\"=\");\n var name = split[0];\n var value = split[1];\n if (value) {\n // strip quotes\n if (value.startsWith('\"') || value.startsWith(\"'\")) {\n value = value.substring(1, value.length - 1);\n }\n }\n return new AttributeRef(name, css, value);\n }\n\n resolve(context) {\n var target = context.you || context.me;\n if (target) {\n return context.meta.runtime.resolveAttribute(target, this.name);\n }\n }\n\n get lhs() { return {}; }\n\n set(ctx, lhs, value) {\n var target = ctx.you || ctx.me;\n if (target) {\n value == null ? target.removeAttribute(this.name) : target.setAttribute(this.name, value);\n }\n }\n}\n\n/**\n * StyleRef - Represents style references (*prop or *computed-prop)\n *\n * Parses: *color | *computed-width\n * Returns: Style property value (regular or computed)\n */\nexport class StyleRef extends Expression {\n static grammarName = \"styleRef\";\n static expressionType = \"leaf\";\n static assignable = true;\n\n constructor(variant, name) {\n super();\n this.variant = variant;\n this.type = variant === \"computed\" ? \"computedStyleRef\" : \"styleRef\";\n this.name = name;\n }\n\n static parse(parser) {\n var styleRef = parser.matchTokenType(\"STYLE_REF\");\n if (!styleRef) return;\n if (!styleRef.value) return;\n var styleProp = styleRef.value.slice(1);\n if (styleProp.startsWith(\"computed-\")) {\n styleProp = styleProp.slice(\"computed-\".length);\n return new StyleRef(\"computed\", styleProp);\n } else {\n return new StyleRef(\"style\", styleProp);\n }\n }\n\n resolve(context) {\n var target = context.you || context.me;\n if (target) {\n if (this.variant === \"computed\") {\n return context.meta.runtime.resolveComputedStyle(target, this.name);\n } else {\n return context.meta.runtime.resolveStyle(target, this.name);\n }\n }\n }\n\n get lhs() { return {}; }\n\n set(ctx, lhs, value) {\n var target = ctx.you || ctx.me;\n if (target) { target.style[this.name] = value; }\n }\n}\n\n/**\n * StyleLiteral - Represents templated style strings\n *\n * Parses: { css-text-with-$variables }\n * Returns: Interpolated CSS string\n */\nexport class StyleLiteral extends Expression {\n static grammarName = \"styleLiteral\";\n\n constructor(stringParts, exprs) {\n super();\n this.stringParts = stringParts;\n this.args = { exprs };\n }\n\n static parse(parser) {\n if (!parser.matchOpToken(\"{\")) return;\n\n var stringParts = [\"\"]\n var exprs = []\n\n while (parser.hasMore()) {\n if (parser.matchOpToken(\"\\\\\")) {\n parser.consumeToken();\n } else if (parser.matchOpToken(\"}\")) {\n break;\n } else if (parser.matchToken(\"$\")) {\n var opencurly = parser.matchOpToken(\"{\");\n var expr = parser.parseElement(\"expression\");\n if (opencurly) parser.requireOpToken(\"}\");\n\n exprs.push(expr)\n stringParts.push(\"\")\n } else {\n var tok = parser.consumeToken();\n stringParts[stringParts.length-1] += parser.source.substring(tok.start, tok.end);\n }\n\n stringParts[stringParts.length-1] += parser.lastWhitespace();\n }\n\n return new StyleLiteral(stringParts, exprs);\n }\n\n resolve(ctx, { exprs }) {\n var rv = \"\";\n const stringParts = this.stringParts;\n\n stringParts.forEach(function (part, idx) {\n rv += part;\n if (idx in exprs) rv += exprs[idx];\n });\n\n return rv;\n }\n}\n", "/**\n * Postfix expression parse tree elements\n * Handles CSS units, time expressions, and type checking\n */\n\nimport { Expression } from '../base.js';\n\n// CSS unit postfixes\n// taken from https://drafts.csswg.org/css-values-4/#relative-length\n// and https://drafts.csswg.org/css-values-4/#absolute-length\n// (NB: we do not support `in` due to conflicts w/ the hyperscript grammar)\nconst STRING_POSTFIXES = [\n 'em', 'ex', 'cap', 'ch', 'ic', 'rem', 'lh', 'rlh', 'vw', 'vh', 'vi', 'vb', 'vmin', 'vmax',\n 'cm', 'mm', 'Q', 'pc', 'pt', 'px'\n];\n\nexport class StringPostfixExpression extends Expression {\n static grammarName = \"stringPostfixExpression\";\n static expressionType = \"postfix\";\n\n constructor(root, postfix) {\n super();\n this.postfix = postfix;\n this.args = { value: root };\n }\n\n static parse(parser, root) {\n let stringPostfix = parser.matchAnyToken(...STRING_POSTFIXES) || parser.matchOpToken(\"%\");\n if (!stringPostfix) return;\n\n return new StringPostfixExpression(root, stringPostfix.value);\n }\n\n resolve(context, { value: val }) {\n return \"\" + val + this.postfix;\n }\n}\n\nexport class TimeExpression extends Expression {\n static grammarName = \"timeExpression\";\n static expressionType = \"postfix\";\n\n constructor(root, timeFactor) {\n super();\n this.time = root;\n this.factor = timeFactor;\n this.args = { value: root };\n }\n\n static parse(parser, root) {\n var timeFactor = null;\n if (parser.matchToken(\"s\") || parser.matchToken(\"seconds\")) {\n timeFactor = 1000;\n } else if (parser.matchToken(\"ms\") || parser.matchToken(\"milliseconds\")) {\n timeFactor = 1;\n }\n if (!timeFactor) return;\n\n return new TimeExpression(root, timeFactor);\n }\n\n evalStatically() {\n return this.time.evalStatically() * this.factor;\n }\n\n resolve(context, { value: val }) {\n return val * this.factor;\n }\n}\n\nexport class TypeCheckExpression extends Expression {\n static grammarName = \"typeCheckExpression\";\n static expressionType = \"postfix\";\n\n constructor(root, typeName, nullOk) {\n super();\n this.typeName = typeName;\n this.nullOk = nullOk;\n this.args = { value: root };\n }\n\n static parse(parser, root) {\n if (!parser.matchOpToken(\":\")) return;\n\n var typeName = parser.requireTokenType(\"IDENTIFIER\");\n if (!typeName.value) return;\n var nullOk = !parser.matchOpToken(\"!\");\n\n return new TypeCheckExpression(root, typeName, nullOk);\n }\n\n resolve(context, { value: val }) {\n var passed = context.meta.runtime.typeCheck(val, this.typeName.value, this.nullOk);\n if (passed) {\n return val;\n } else {\n throw new Error(\"Typecheck failed! Expected: \" + this.typeName.value);\n }\n }\n}\n", "/**\n * Positional expression parse tree elements\n * DOM query and position-based selections\n */\n\nimport { Expression } from '../base.js';\nimport { AttributeRefAccess } from './expressions.js';\n\n/**\n * RelativePositionalExpression - Relative DOM navigation\n *\n * Parses: next
| previous

| next

from in \n * Returns: matching element relative to starting point\n */\nexport class RelativePositionalExpression extends Expression {\n static grammarName = \"relativePositionalExpression\";\n static expressionType = \"unary\";\n\n constructor(thingElt, from, forwardSearch, inSearch, wrapping, inElt, withinElt, operator) {\n super();\n this.thingElt = thingElt;\n this.from = from;\n this.forwardSearch = forwardSearch;\n this.inSearch = inSearch;\n this.wrapping = wrapping;\n this.inElt = inElt;\n this.withinElt = withinElt;\n this.operator = operator;\n this.args = { thing: thingElt, from, inElt, withinElt };\n }\n\n static parse(parser) {\n var op = parser.matchAnyToken(\"next\", \"previous\");\n if (!op) return;\n var forwardSearch = op.value === \"next\";\n\n var thingElt = parser.parseElement(\"leaf\");\n\n if (parser.matchToken(\"from\")) {\n parser.pushFollow(\"in\");\n try {\n var from = parser.requireElement(\"unaryExpression\");\n } finally {\n parser.popFollow();\n }\n } else {\n var from = parser.requireElement(\"implicitMeTarget\");\n }\n\n var inSearch = false;\n var withinElt;\n if (parser.matchToken(\"in\")) {\n inSearch = true;\n var inElt = parser.requireElement(\"unaryExpression\");\n } else if (parser.matchToken(\"within\")) {\n withinElt = parser.requireElement(\"unaryExpression\");\n } else {\n withinElt = null; // resolved to document.body at runtime\n }\n\n var wrapping = false;\n if (parser.matchToken(\"with\")) {\n parser.requireToken(\"wrapping\")\n wrapping = true;\n }\n\n return new RelativePositionalExpression(\n thingElt,\n from,\n forwardSearch,\n inSearch,\n wrapping,\n inElt,\n withinElt,\n op.value\n );\n }\n\n scanForwardQuery(start, root, match, wrap) {\n var results = root.querySelectorAll(match);\n for (var i = 0; i < results.length; i++) {\n var elt = results[i];\n if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_PRECEDING) {\n return elt;\n }\n }\n if (wrap) {\n return results[0];\n }\n }\n\n scanBackwardsQuery(start, root, match, wrap) {\n var results = root.querySelectorAll(match);\n for (var i = results.length - 1; i >= 0; i--) {\n var elt = results[i];\n if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_FOLLOWING) {\n return elt;\n }\n }\n if (wrap) {\n return results[results.length - 1];\n }\n }\n\n scanForwardArray(start, array, match, wrap) {\n var matches = [];\n for (var elt of array) {\n if (elt.matches(match) || elt === start) {\n matches.push(elt);\n }\n }\n for (var i = 0; i < matches.length - 1; i++) {\n var elt = matches[i];\n if (elt === start) {\n return matches[i + 1];\n }\n }\n if (wrap) {\n var first = matches[0];\n if (first && first.matches(match)) {\n return first;\n }\n }\n }\n\n scanBackwardsArray(start, array, match, wrap) {\n return this.scanForwardArray(start, Array.from(array).reverse(), match, wrap);\n }\n\n resolve(context, { thing, from, inElt, withinElt }) {\n var css = thing.css;\n if (css == null) {\n throw new Error(\"Expected a CSS value to be returned by \" + this.thingElt.sourceFor());\n }\n\n if(this.inSearch) {\n if (inElt) {\n if (this.forwardSearch) {\n return this.scanForwardArray(from, inElt, css, this.wrapping);\n } else {\n return this.scanBackwardsArray(from, inElt, css, this.wrapping);\n }\n }\n } else {\n var root = withinElt ?? document.body;\n if (this.forwardSearch) {\n return this.scanForwardQuery(from, root, css, this.wrapping);\n } else {\n return this.scanBackwardsQuery(from, root, css, this.wrapping);\n }\n }\n }\n\n}\n\n/**\n * PositionalExpression - Array/collection position selection\n *\n * Parses: first | last | random \n * Returns: selected element from collection\n */\nexport class PositionalExpression extends Expression {\n static grammarName = \"positionalExpression\";\n static expressionType = \"unary\";\n\n constructor(rhs, operator) {\n super();\n this.rhs = rhs;\n this.operator = operator;\n this.args = { value: rhs };\n }\n\n static parse(parser) {\n var op = parser.matchAnyToken(\"first\", \"last\", \"random\");\n if (!op) return;\n parser.matchAnyToken(\"in\", \"from\", \"of\");\n var rhs = parser.requireElement(\"unaryExpression\");\n return new PositionalExpression(rhs, op.value);\n }\n\n resolve(context, { value: rhsVal }) {\n if (rhsVal && !Array.isArray(rhsVal)) {\n if (rhsVal.children) {\n rhsVal = rhsVal.children;\n } else {\n rhsVal = Array.from(rhsVal);\n }\n }\n if (rhsVal) {\n if (this.operator === \"first\") {\n return rhsVal[0];\n } else if (this.operator === \"last\") {\n return rhsVal[rhsVal.length - 1];\n } else if (this.operator === \"random\") {\n return rhsVal[Math.floor(Math.random() * rhsVal.length)];\n }\n }\n }\n\n}\n\n/**\n * ClosestExprNode - Closest ancestor matching selector node\n */\nclass ClosestExprNode extends Expression {\n constructor(parentSearch, expr, css, to) {\n super();\n this.type = \"closestExpr\";\n this.parentSearch = parentSearch;\n this.expr = expr;\n this.css = css;\n this.to = to;\n this.args = { to };\n }\n\n resolve(ctx, { to }) {\n if (to == null) return null;\n let result = [];\n const css = this.css;\n const parentSearch = this.parentSearch;\n ctx.meta.runtime.implicitLoop(to, function(to){\n if (parentSearch) {\n result.push(to.parentElement ? to.parentElement.closest(css) : null);\n } else {\n result.push(to.closest(css));\n }\n })\n return ctx.meta.runtime.shouldAutoIterate(to) ? result : result[0];\n }\n\n get lhs() { return { to: this.to }; }\n set(ctx, lhs, value) {\n var target = this.resolve(ctx, lhs);\n if (target) ctx.meta.runtime.replaceInDom(target, value);\n }\n}\n\n/**\n * ClosestExpr - Finds closest ancestor matching selector\n *\n * Parses: closest [to element] | closest parent [to element]\n * Returns: Closest matching element\n */\nexport class ClosestExpr extends Expression {\n static grammarName = \"closestExpr\";\n static expressionType = \"leaf\";\n static assignable = true;\n\n static parse(parser) {\n if (!parser.matchToken(\"closest\")) return;\n\n var parentSearch = false;\n if (parser.matchToken(\"parent\")) {\n parentSearch = true;\n }\n\n var css = null;\n var attributeRef = null;\n if (parser.currentToken().type === \"ATTRIBUTE_REF\") {\n attributeRef = parser.requireElement(\"attributeRefAccess\", null);\n css = \"[\" + attributeRef.attribute.name + \"]\";\n }\n\n if (css == null) {\n var expr = parser.requireElement(\"unaryExpression\");\n if (expr.css == null) {\n parser.raiseError(\"Expected a CSS expression\");\n } else {\n css = expr.css;\n }\n }\n\n if (parser.matchToken(\"to\")) {\n var to = parser.parseElement(\"expression\");\n } else {\n var to = parser.parseElement(\"implicitMeTarget\");\n }\n\n var closestExpr = new ClosestExprNode(parentSearch, expr, css, to);\n\n // If we parsed an attributeRef, create a new AttributeRefAccess with the correct root\n if (attributeRef) {\n return new AttributeRefAccess(closestExpr, attributeRef.attribute);\n } else {\n return closestExpr;\n }\n }\n}\n", "/**\n * Existential expression parse tree elements\n * Expressions that check for existence, emptiness, etc.\n */\n\nimport { Expression } from '../base.js';\n\n/**\n * NoExpression - Represents the \"no\" keyword for empty/null checks\n *\n * Parses: no \n * Returns: true if the expression is empty/null, false otherwise\n */\nexport class NoExpression extends Expression {\n static grammarName = \"noExpression\";\n static expressionType = \"unary\";\n\n constructor(root) {\n super();\n this.root = root;\n this.args = { value: root };\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"no\")) return;\n var root = parser.requireElement(\"collectionExpression\");\n return new NoExpression(root);\n }\n\n resolve(context, { value: val }) {\n return context.meta.runtime.isEmpty(val);\n }\n}\n\n/**\n * SomeExpression - Represents the \"some\" keyword for existential checks\n *\n * Parses: some \n * Returns: true if the expression is not empty/null, false otherwise\n */\nexport class SomeExpression extends Expression {\n static grammarName = \"some\";\n static expressionType = \"leaf\";\n\n constructor(root) {\n super();\n this.root = root;\n this.args = { value: root };\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"some\")) return;\n var root = parser.requireElement(\"expression\");\n return new SomeExpression(root);\n }\n\n resolve(context, { value: val }) {\n return !context.meta.runtime.isEmpty(val);\n }\n}\n", "/**\n * Target expression parse tree elements\n * Elements that represent targets for operations (me, you, etc.)\n */\n\nimport { Expression } from '../base.js';\n\n/**\n * ImplicitMeTarget - Represents the implicit \"me\" or \"you\" target\n *\n * Parses: implicit me/you reference\n * Returns: context.you || context.me\n */\nexport class ImplicitMeTarget extends Expression {\n static grammarName = \"implicitMeTarget\";\n\n constructor() {\n super();\n }\n\n static parse(parser) {\n return new ImplicitMeTarget();\n }\n\n resolve(context) {\n return context.you || context.me;\n }\n}\n", "/**\n * Basic command parse tree elements\n * Simple commands with no control flow, plus data manipulation commands\n */\n\nimport { RegExpIterable } from '../../core/runtime/collections.js';\nimport { Command, Expression } from '../base.js';\nimport { config } from '../../core/config.js';\n\n/**\n * ImplicitResultSymbol - Represents the implicit \"result\" symbol\n */\nclass ImplicitResultSymbol extends Expression {\n constructor() {\n super();\n this.type = \"symbol\";\n }\n\n resolve(context) {\n return context.meta.runtime.resolveSymbol(\"result\", context);\n }\n\n get lhs() { return {}; }\n\n set(ctx, lhs, value) {\n ctx.meta.runtime.setSymbol(\"result\", ctx, null, value);\n }\n}\n\n/**\n * LogCommand - Log values to console\n *\n * Parses: log expr1, expr2, ... [with customLogger]\n * Executes: Logs values to console or custom logger\n */\nexport class LogCommand extends Command {\n static keyword = \"log\";\n\n constructor(exprs, withExpr) {\n super();\n this.exprs = exprs;\n this.withExpr = withExpr;\n this.args = { logger: withExpr, values: exprs };\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"log\")) return;\n var exprs = [parser.parseElement(\"expression\")];\n while (parser.matchOpToken(\",\")) {\n exprs.push(parser.requireElement(\"expression\"));\n }\n if (parser.matchToken(\"with\")) {\n var withExpr = parser.requireElement(\"expression\");\n }\n return new LogCommand(exprs, withExpr);\n }\n\n resolve(ctx, { logger, values }) {\n if (logger) {\n logger(...values);\n } else {\n console.log(...values);\n }\n return this.findNext(ctx);\n }\n}\n\n/**\n * BeepCommand - Debug beep to console\n *\n * Parses: beep! expr1, expr2, ...\n * Executes: Logs values with debug formatting\n */\nexport class BeepCommand extends Command {\n static keyword = \"beep!\";\n\n constructor(exprs) {\n super();\n this.exprs = exprs;\n this.args = { values: exprs };\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"beep!\")) return;\n var exprs = [parser.parseElement(\"expression\")];\n while (parser.matchOpToken(\",\")) {\n exprs.push(parser.requireElement(\"expression\"));\n }\n return new BeepCommand(exprs);\n }\n\n resolve(ctx, { values }) {\n for (let i = 0; i < this.exprs.length; i++) {\n const expr = this.exprs[i];\n const val = values[i];\n ctx.meta.runtime.beepValueToConsole(ctx.me, expr, val);\n }\n return this.findNext(ctx);\n }\n}\n\n/**\n * ThrowCommand - Throw an error\n *\n * Parses: throw expression\n * Executes: Throws the evaluated expression\n */\nexport class ThrowCommand extends Command {\n static keyword = \"throw\";\n\n constructor(expr) {\n super();\n this.expr = expr;\n this.args = { value: expr };\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"throw\")) return;\n var expr = parser.requireElement(\"expression\");\n return new ThrowCommand(expr);\n }\n\n resolve(ctx, { value }) {\n ctx.meta.runtime.registerHyperTrace(ctx, value);\n throw value;\n }\n}\n\n/**\n * ReturnCommand - Return a value from handler\n *\n * Parses: return expression\n * Executes: Returns value and halts execution\n */\nexport class ReturnCommand extends Command {\n static keyword = \"return\";\n\n constructor(value) {\n super();\n this.value = value;\n this.args = { value };\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"return\")) return;\n var value;\n if (!parser.commandBoundary(parser.currentToken())) {\n value = parser.requireElement(\"expression\");\n }\n return new ReturnCommand(value);\n }\n\n resolve(context, { value }) {\n var resolve = context.meta.resolve;\n context.meta.returned = true;\n context.meta.returnValue = value;\n if (resolve) resolve(value);\n return context.meta.runtime.HALT;\n }\n}\n\n/**\n * ExitCommand - Exit handler without returning value\n *\n * Parses: exit\n * Executes: Exits and halts execution\n */\nexport class ExitCommand extends Command {\n static keyword = \"exit\";\n\n static parse(parser) {\n if (!parser.matchToken(\"exit\")) return;\n return new ExitCommand();\n }\n\n resolve(context) {\n var resolve = context.meta.resolve;\n context.meta.returned = true;\n context.meta.returnValue = null;\n if (resolve) {\n resolve();\n }\n return context.meta.runtime.HALT;\n }\n}\n\n/**\n * HaltCommand - Halt event bubbling/default behavior\n *\n * Parses: halt [the event] [bubbling|default]\n * Executes: Stops event propagation/default\n */\nexport class HaltCommand extends Command {\n static keyword = \"halt\";\n\n constructor(bubbling, haltDefault, keepExecuting, exit) {\n super();\n this.keepExecuting = keepExecuting;\n this.bubbling = bubbling;\n this.haltDefault = haltDefault;\n this.exit = exit;\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"halt\")) return;\n if (parser.matchToken(\"the\")) {\n parser.requireToken(\"event\");\n // optional possessive\n if (parser.matchOpToken(\"'\")) {\n parser.requireToken(\"s\");\n }\n var keepExecuting = true;\n }\n if (parser.matchToken(\"bubbling\")) {\n var bubbling = true;\n } else if (parser.matchToken(\"default\")) {\n var haltDefault = true;\n }\n var exit = new ExitCommand();\n return new HaltCommand(bubbling, haltDefault, keepExecuting, exit);\n }\n\n resolve(ctx) {\n if (ctx.event) {\n if (this.bubbling) {\n ctx.event.stopPropagation();\n } else if (this.haltDefault) {\n ctx.event.preventDefault();\n } else {\n ctx.event.stopPropagation();\n ctx.event.preventDefault();\n }\n }\n if (this.keepExecuting) {\n return this.findNext(ctx);\n } else {\n return this.exit;\n }\n }\n}\n\n/**\n * MakeCommand - Create/instantiate objects or elements\n *\n * Parses: make [a|an] [from ] [called ]\n * Executes: Creates DOM elements from query refs or instantiates objects\n */\nexport class MakeCommand extends Command {\n static keyword = \"make\";\n\n constructor(variant, expr, constructorArgs, target) {\n super();\n this.variant = variant;\n this.expr = expr;\n this.constructorArgs = constructorArgs;\n this.target = target;\n this.args = variant === \"queryRef\" ? null : { expr, constructorArgs };\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"make\")) return;\n parser.matchToken(\"a\") || parser.matchToken(\"an\");\n\n var expr = parser.requireElement(\"expression\");\n\n var args = [];\n if (expr.type !== \"queryRef\" && parser.matchToken(\"from\")) {\n do {\n args.push(parser.requireElement(\"expression\"));\n } while (parser.matchOpToken(\",\"));\n }\n\n if (parser.matchToken(\"called\")) {\n var target = parser.requireElement(\"symbol\");\n }\n\n if (expr.type === \"queryRef\") {\n return new MakeCommand(\"queryRef\", expr, null, target);\n } else {\n return new MakeCommand(\"constructor\", expr, args, target);\n }\n }\n\n resolve(ctx, { expr, constructorArgs } = {}) {\n if (this.variant === \"queryRef\") {\n var match,\n tagname = \"div\",\n id,\n classes = [];\n var re = /(?:(^|#|\\.)([^#\\. ]+))/g;\n while ((match = re.exec(this.expr.css))) {\n if (match[1] === \"\") tagname = match[2].trim();\n else if (match[1] === \"#\") id = match[2].trim();\n else classes.push(match[2].trim());\n }\n\n var result = document.createElement(tagname);\n if (id !== undefined) result.id = id;\n result.classList.add(...classes);\n\n ctx.result = result;\n } else {\n ctx.result = new expr(...constructorArgs);\n }\n\n if (this.target) {\n ctx.meta.runtime.setSymbol(this.target.name, ctx, this.target.scope, ctx.result);\n }\n\n return this.findNext(ctx);\n }\n}\n\n/**\n * AppendCommand - Append to collection/string/DOM\n *\n * Parses: append [to ]\n * Executes: Appends value to array, string, or DOM element\n */\nexport class AppendCommand extends Command {\n static keyword = \"append\";\n\n constructor(value, targetExpr, assignable) {\n super();\n this.value = value;\n this._target = targetExpr;\n this.assignable = assignable;\n if (assignable) {\n this.args = { target: targetExpr, value, ...targetExpr.lhs };\n } else {\n this.args = { target: targetExpr, value };\n }\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"append\")) return;\n var targetExpr = null;\n\n var value = parser.requireElement(\"expression\");\n\n if (parser.matchToken(\"to\")) {\n targetExpr = parser.requireElement(\"expression\");\n } else {\n targetExpr = new ImplicitResultSymbol();\n }\n\n var checkTarget = targetExpr;\n while (checkTarget.type === \"parenthesized\") checkTarget = checkTarget.expr;\n var assignable = checkTarget.set != null;\n\n return new AppendCommand(value, targetExpr, assignable);\n }\n\n resolve(context, args) {\n var { target, value, ...lhs } = args;\n if (Array.isArray(target)) {\n target.push(value);\n context.meta.runtime.notifyMutation(target);\n } else if (target instanceof Set) {\n target.add(value);\n context.meta.runtime.notifyMutation(target);\n } else if (target instanceof Element) {\n if (value instanceof Element) {\n target.insertAdjacentElement(\"beforeend\", value);\n } else {\n target.insertAdjacentHTML(\"beforeend\", value);\n }\n context.meta.runtime.processNode(target);\n } else if(this.assignable) {\n this._target.set(context, lhs, (target || \"\") + value);\n } else {\n throw new Error(\"Unable to append a value!\")\n }\n return this.findNext(context);\n }\n}\n\n/**\n * PickCommand - Extract items from collections\n *\n * Parses: pick first of \n * pick last of \n * pick random [] of \n * pick [the] item[s]|character[s] of|from \n * pick [the] match[es] [of] [|] of|from \n */\nexport class PickCommand extends Command {\n static keyword = \"pick\";\n\n constructor(variant, root, range, re, flags, count) {\n super();\n this.variant = variant;\n this.range = range;\n this.flags = flags;\n if (variant === \"range\") {\n this.args = { root, from: range.from, to: range.to };\n } else if (variant === \"first\" || variant === \"last\" || variant === \"random\") {\n this.args = { root, count };\n } else {\n this.args = { root, re };\n }\n }\n\n static parsePickRange(parser) {\n parser.matchToken(\"at\") || parser.matchToken(\"from\");\n var rv = { includeStart: true, includeEnd: false };\n\n rv.from = parser.matchToken(\"start\") ? 0 : parser.requireElement(\"expression\");\n\n if (parser.matchToken(\"to\") || parser.matchOpToken(\"..\")) {\n if (parser.matchToken(\"end\")) {\n rv.toEnd = true;\n } else {\n rv.to = parser.requireElement(\"expression\");\n }\n }\n\n if (parser.matchToken(\"inclusive\")) rv.includeEnd = true;\n else if (parser.matchToken(\"exclusive\")) rv.includeStart = false;\n\n return rv;\n }\n\n static parseSource(parser) {\n if (!parser.matchAnyToken(\"of\", \"from\")) {\n parser.raiseExpected('of', 'from');\n }\n return parser.requireElement(\"expression\");\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"pick\")) return;\n\n parser.matchToken(\"the\");\n\n if (parser.matchToken(\"first\")) {\n var follows = parser.pushFollows(\"of\", \"from\");\n try { var count = parser.requireElement(\"expression\"); }\n finally { parser.popFollows(follows); }\n var root = PickCommand.parseSource(parser);\n return new PickCommand(\"first\", root, null, null, null, count);\n }\n\n if (parser.matchToken(\"last\")) {\n var follows = parser.pushFollows(\"of\", \"from\");\n try { var count = parser.requireElement(\"expression\"); }\n finally { parser.popFollows(follows); }\n var root = PickCommand.parseSource(parser);\n return new PickCommand(\"last\", root, null, null, null, count);\n }\n\n if (parser.matchToken(\"random\")) {\n var count = null;\n if (parser.currentToken().type === \"NUMBER\") {\n var follows = parser.pushFollows(\"of\", \"from\");\n try { count = parser.requireElement(\"expression\"); }\n finally { parser.popFollows(follows); }\n }\n var root = PickCommand.parseSource(parser);\n return new PickCommand(\"random\", root, null, null, null, count);\n }\n\n if (parser.matchToken(\"item\") || parser.matchToken(\"items\")\n || parser.matchToken(\"character\") || parser.matchToken(\"characters\")) {\n var follows = parser.pushFollows(\"of\", \"from\");\n try { var range = PickCommand.parsePickRange(parser); }\n finally { parser.popFollows(follows); }\n var root = PickCommand.parseSource(parser);\n return new PickCommand(\"range\", root, range, null, null);\n }\n\n if (parser.matchToken(\"match\")) {\n parser.matchToken(\"of\");\n var follows = parser.pushFollows(\"of\", \"from\");\n try {\n var re = parser.parseElement(\"expression\");\n var flags = \"\";\n if (parser.matchOpToken(\"|\")) {\n flags = parser.requireTokenType(\"IDENTIFIER\").value;\n }\n } finally { parser.popFollows(follows); }\n var root = PickCommand.parseSource(parser);\n return new PickCommand(\"match\", root, null, re, flags);\n }\n\n if (parser.matchToken(\"matches\")) {\n parser.matchToken(\"of\");\n var follows = parser.pushFollows(\"of\", \"from\");\n try {\n var re = parser.parseElement(\"expression\");\n var flags = \"gu\";\n if (parser.matchOpToken(\"|\")) {\n flags = 'g' + parser.requireTokenType(\"IDENTIFIER\").value.replace('g', '');\n }\n } finally { parser.popFollows(follows); }\n var root = PickCommand.parseSource(parser);\n return new PickCommand(\"matches\", root, null, re, flags);\n }\n }\n\n resolve(ctx, { root, from, to, re, count }) {\n if (root == null) { ctx.result = root; return this.findNext(ctx); }\n if (this.variant === \"first\") {\n ctx.result = root.slice(0, count);\n } else if (this.variant === \"last\") {\n ctx.result = root.slice(-count);\n } else if (this.variant === \"random\") {\n if (count == null) {\n ctx.result = root[Math.floor(Math.random() * root.length)];\n } else {\n var copy = Array.from(root);\n var result = [];\n for (var i = 0; i < count && copy.length > 0; i++) {\n var idx = Math.floor(Math.random() * copy.length);\n result.push(copy.splice(idx, 1)[0]);\n }\n ctx.result = result;\n }\n } else if (this.variant === \"range\") {\n if (this.range.toEnd) to = root.length;\n if (!this.range.includeStart) from++;\n if (this.range.includeEnd) to++;\n if (to == null) to = from + 1;\n ctx.result = root.slice(from, to);\n } else if (this.variant === \"match\") {\n ctx.result = new RegExp(re, this.flags).exec(root);\n } else {\n ctx.result = new RegExpIterable(re, this.flags, root);\n }\n return this.findNext(ctx);\n }\n}\n\n/**\n * FetchCommand - HTTP fetch\n *\n * Parses: fetch [as ] [with ]\n * Executes: Performs HTTP fetch with optional response conversion\n */\nexport class FetchCommand extends Command {\n static keyword = \"fetch\";\n\n constructor(url, argExprs, conversionType, conversion, dontThrow) {\n super();\n this.url = url;\n this.argExpressions = argExprs;\n this.args = { url, options: argExprs };\n this.conversionType = conversionType;\n this.conversion = conversion;\n this.dontThrow = dontThrow;\n }\n\n static parseConversionInfo(parser) {\n var type = \"text\";\n var conversion;\n parser.matchToken(\"a\") || parser.matchToken(\"an\");\n if (parser.matchToken(\"json\") || parser.matchToken(\"JSON\") || parser.matchToken(\"Object\")) {\n type = \"json\";\n } else if (parser.matchToken(\"response\") || parser.matchToken(\"Response\")) {\n type = \"response\";\n } else if (parser.matchToken(\"html\") || parser.matchToken(\"HTML\")) {\n type = \"html\";\n } else if (parser.matchToken(\"text\") || parser.matchToken(\"Text\") || parser.matchToken(\"String\")) {\n // default\n } else {\n conversion = parser.requireElement(\"dotOrColonPath\").evalStatically();\n }\n return {type, conversion};\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"fetch\")) return;\n var url = parser.parseURLOrExpression();\n\n if (parser.matchToken(\"as\")) {\n var conversionInfo = FetchCommand.parseConversionInfo(parser);\n }\n\n if (parser.matchToken(\"with\") && parser.currentToken().value !== \"{\") {\n var argExprs = parser.parseElement(\"nakedNamedArgumentList\");\n } else {\n var argExprs = parser.parseElement(\"objectLiteral\");\n }\n\n if (conversionInfo == null && parser.matchToken(\"as\")) {\n conversionInfo = FetchCommand.parseConversionInfo(parser);\n }\n\n var dontThrow = false;\n if (parser.matchToken(\"do\")) {\n parser.requireToken(\"not\");\n parser.requireToken(\"throw\");\n dontThrow = true;\n } else if (parser.currentToken().value === \"don\" &&\n parser.token(1).value === \"'\" &&\n parser.token(2).value === \"t\" &&\n parser.token(1).start === parser.currentToken().end &&\n parser.token(2).start === parser.token(1).end) {\n parser.consumeToken(); // don\n parser.consumeToken(); // '\n parser.consumeToken(); // t\n parser.requireToken(\"throw\");\n dontThrow = true;\n }\n\n var type = conversionInfo ? conversionInfo.type : \"text\";\n var conversion = conversionInfo ? conversionInfo.conversion : null;\n\n return new FetchCommand(url, argExprs, type, conversion, dontThrow);\n }\n\n resolve(context, { url, options }) {\n var detail = options || {};\n detail.sender = context.me;\n detail.headers = detail.headers || {};\n detail.conversion = this.conversion || this.conversionType;\n var abortController = new AbortController();\n var abortListener = () => abortController.abort();\n context.me.addEventListener('fetch:abort', abortListener, {once: true});\n detail.signal = abortController.signal;\n context.meta.runtime.triggerEvent(context.me, \"hyperscript:beforeFetch\", detail);\n context.meta.runtime.triggerEvent(context.me, \"fetch:beforeRequest\", detail);\n var finished = false;\n if (detail.timeout) {\n setTimeout(() => { if (!finished) abortController.abort(); }, detail.timeout);\n }\n\n var complete = (result) => {\n context.result = result;\n context.meta.runtime.triggerEvent(context.me, \"fetch:afterRequest\", {result});\n finished = true;\n return this.findNext(context);\n };\n\n var checkThrow = !this.dontThrow && this.conversionType !== \"response\";\n\n return fetch(url, detail)\n .then((resp) => {\n var resultDetails = {response: resp};\n context.meta.runtime.triggerEvent(context.me, \"fetch:afterResponse\", resultDetails);\n resp = resultDetails.response;\n\n if (checkThrow) {\n var statusStr = String(resp.status);\n var patterns = config.fetchThrowsOn || [];\n for (var i = 0; i < patterns.length; i++) {\n if (patterns[i].test(statusStr)) {\n var err = new Error(\"fetch failed: \" + resp.status + \" \" + resp.statusText + \" (\" + url + \")\");\n err.response = resp;\n err.status = resp.status;\n throw err;\n }\n }\n }\n\n if (this.conversionType === \"response\") return complete(resp);\n if (this.conversionType === \"json\") return resp.json().then(complete);\n if (this.conversion) {\n var convFn = config.conversions[this.conversion];\n if (convFn && convFn._rawResponse) {\n return complete(convFn(resp, context.meta.runtime, context));\n }\n }\n return resp.text().then((result) => {\n if (this.conversion) result = context.meta.runtime.convertValue(result, this.conversion);\n if (this.conversionType === \"html\") result = context.meta.runtime.convertValue(result, \"Fragment\");\n return complete(result);\n });\n })\n .catch((reason) => {\n context.meta.runtime.triggerEvent(context.me, \"fetch:error\", {reason});\n throw reason;\n })\n .finally(() => {\n context.me.removeEventListener('fetch:abort', abortListener);\n });\n }\n}\n\nfunction _parseScrollModifiers(parser) {\n parser.matchToken(\"the\");\n var verticalPosition = parser.matchAnyToken(\"top\", \"middle\", \"bottom\");\n var horizontalPosition = parser.matchAnyToken(\"left\", \"center\", \"right\");\n if (verticalPosition || horizontalPosition) {\n parser.requireToken(\"of\");\n }\n var target = parser.requireElement(\"unaryExpression\");\n\n var plusOrMinus = parser.matchAnyOpToken(\"+\", \"-\");\n var offset;\n if (plusOrMinus) {\n parser.pushFollow(\"px\");\n try {\n offset = parser.requireElement(\"expression\");\n } finally {\n parser.popFollow();\n }\n }\n parser.matchToken(\"px\");\n\n var container;\n if (parser.matchToken(\"in\")) {\n container = parser.requireElement(\"unaryExpression\");\n }\n\n var smoothness = parser.matchAnyToken(\"smoothly\", \"instantly\");\n\n var scrollOptions = { block: \"start\", inline: \"nearest\" };\n\n var blockMap = { top: \"start\", bottom: \"end\", middle: \"center\" };\n var inlineMap = { left: \"start\", center: \"center\", right: \"end\" };\n var behaviorMap = { smoothly: \"smooth\", instantly: \"instant\" };\n if (verticalPosition) scrollOptions.block = blockMap[verticalPosition.value];\n if (horizontalPosition) scrollOptions.inline = inlineMap[horizontalPosition.value];\n if (smoothness) scrollOptions.behavior = behaviorMap[smoothness.value];\n\n return { target, offset, plusOrMinus, scrollOptions, container };\n}\n\nfunction _parseSmoothness(parser) {\n var smoothness = parser.matchAnyToken(\"smoothly\", \"instantly\");\n if (!smoothness) return undefined;\n return smoothness.value === \"smoothly\" ? \"smooth\" : \"instant\";\n}\n\nfunction _resolveScroll(ctx, to, offset, plusOrMinus, scrollOptions, container) {\n ctx.meta.runtime.implicitLoop(to, function (target) {\n if (target === window) target = document.body;\n\n // \"scroll to #item in #container\" - scroll within a specific container\n if (container) {\n var ctr = container instanceof Element ? container : container;\n var top = target.offsetTop - ctr.offsetTop;\n var left = target.offsetLeft - ctr.offsetLeft;\n if (plusOrMinus) {\n var adj = plusOrMinus.value === \"+\" ? offset : offset * -1;\n top += adj;\n }\n ctr.scrollTo({ top, left, behavior: scrollOptions.behavior || \"auto\" });\n return;\n }\n\n if (plusOrMinus) {\n var boundingRect = target.getBoundingClientRect();\n var scrollShim = document.createElement(\"div\");\n var actualOffset = plusOrMinus.value === \"+\" ? offset : offset * -1;\n var offsetX = scrollOptions.inline == \"start\" || scrollOptions.inline == \"end\" ? actualOffset : 0;\n var offsetY = scrollOptions.block == \"start\" || scrollOptions.block == \"end\" ? actualOffset : 0;\n\n scrollShim.style.position = \"absolute\";\n scrollShim.style.top = (boundingRect.top + window.scrollY + offsetY) + \"px\";\n scrollShim.style.left = (boundingRect.left + window.scrollX + offsetX) + \"px\";\n scrollShim.style.height = boundingRect.height + \"px\";\n scrollShim.style.width = boundingRect.width + \"px\";\n scrollShim.style.zIndex = \"\" + Number.MIN_SAFE_INTEGER;\n scrollShim.style.opacity = \"0\";\n\n document.body.appendChild(scrollShim);\n setTimeout(function () { document.body.removeChild(scrollShim); }, 100);\n target = scrollShim;\n }\n\n target.scrollIntoView(scrollOptions);\n });\n}\n\n/**\n * ScrollCommand - Scroll to element or scroll by amount\n *\n * Parses:\n * scroll to [the] [top|middle|bottom] [left|center|right] [of] [+/- px] [in ] [smoothly|instantly]\n * scroll [] [up|down|left|right] by px [smoothly|instantly]\n */\nexport class ScrollCommand extends Command {\n static keyword = \"scroll\";\n\n constructor(target, offset, plusOrMinus, scrollOptions, container, byMode) {\n super();\n this.target = target;\n this.plusOrMinus = plusOrMinus;\n this.scrollOptions = scrollOptions;\n this.byMode = byMode;\n this.args = { target, offset, container };\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"scroll\")) return;\n\n // scroll to ... form\n if (parser.matchToken(\"to\")) {\n var scroll = _parseScrollModifiers(parser);\n return new ScrollCommand(scroll.target, scroll.offset, scroll.plusOrMinus, scroll.scrollOptions, scroll.container);\n }\n\n // scroll [] [up|down|left|right] by px [smoothly|instantly]\n var direction = parser.matchAnyToken(\"up\", \"down\", \"left\", \"right\");\n var target;\n\n if (!direction && parser.currentToken().value !== \"by\") {\n target = parser.requireElement(\"unaryExpression\");\n direction = parser.matchAnyToken(\"up\", \"down\", \"left\", \"right\");\n }\n\n parser.requireToken(\"by\");\n\n parser.pushFollow(\"px\");\n var offset;\n try {\n offset = parser.requireElement(\"expression\");\n } finally {\n parser.popFollow();\n }\n parser.matchToken(\"px\");\n\n var behavior = _parseSmoothness(parser);\n var scrollOptions = {};\n if (behavior) scrollOptions.behavior = behavior;\n\n var byMode = { direction: direction ? direction.value : \"down\" };\n return new ScrollCommand(target, offset, null, scrollOptions, null, byMode);\n }\n\n resolve(ctx, { target, offset, container }) {\n if (this.byMode) {\n var el = target || document.documentElement;\n var dir = this.byMode.direction;\n var top = 0, left = 0;\n if (dir === \"up\") top = -offset;\n else if (dir === \"down\") top = offset;\n else if (dir === \"left\") left = -offset;\n else if (dir === \"right\") left = offset;\n var opts = { top, left };\n if (this.scrollOptions.behavior) opts.behavior = this.scrollOptions.behavior;\n el.scrollBy(opts);\n } else {\n _resolveScroll(ctx, target, offset, this.plusOrMinus, this.scrollOptions, container);\n }\n return this.findNext(ctx);\n }\n}\n\n/**\n * GoCommand - Navigate or scroll (scroll form deprecated, use ScrollCommand)\n *\n * Parses: go back | go [to] [in new window]\n */\nexport class GoCommand extends Command {\n static keyword = \"go\";\n\n constructor(target, offset, back, newWindow, plusOrMinus, scrollOptions) {\n super();\n this.target = target;\n this.args = { target, offset };\n this.back = back;\n this.newWindow = newWindow;\n this.plusOrMinus = plusOrMinus;\n this.scrollOptions = scrollOptions;\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"go\")) return;\n\n if (parser.matchToken(\"back\")) {\n return new GoCommand(null, null, true);\n }\n\n parser.matchToken(\"to\");\n\n // deprecated: go [to] url \n if (parser.matchToken(\"url\")) {\n var target = parser.requireElement(\"stringLike\");\n var newWindow = false;\n if (parser.matchToken(\"in\")) {\n parser.requireToken(\"new\");\n parser.requireToken(\"window\");\n newWindow = true;\n }\n return new GoCommand(target, null, false, newWindow);\n }\n\n // deprecated: go [to] [the] top/middle/bottom ... of (scroll form)\n var cur = parser.currentToken();\n if (cur.value === \"the\" || cur.value === \"top\" || cur.value === \"middle\" || cur.value === \"bottom\"\n || cur.value === \"left\" || cur.value === \"center\" || cur.value === \"right\") {\n var scroll = _parseScrollModifiers(parser);\n return new GoCommand(scroll.target, scroll.offset, false, false, scroll.plusOrMinus, scroll.scrollOptions);\n }\n\n // new: go [to] [in new window]\n var target = parser.parseURLOrExpression();\n var newWindow = false;\n if (parser.matchToken(\"in\")) {\n parser.requireToken(\"new\");\n parser.requireToken(\"window\");\n newWindow = true;\n }\n return new GoCommand(target, null, false, newWindow);\n }\n\n resolve(ctx, { target: to, offset }) {\n if (this.back) {\n window.history.back();\n } else if (this.scrollOptions) {\n // deprecated scroll form\n _resolveScroll(ctx, to, offset, this.plusOrMinus, this.scrollOptions);\n } else if (to != null) {\n if (to instanceof Element) {\n // element -> scroll (backwards compat)\n to.scrollIntoView({ block: \"start\", inline: \"nearest\" });\n } else {\n var str = String(to);\n if (str.startsWith(\"#\")) {\n window.location.hash = str;\n } else if (this.newWindow) {\n window.open(str);\n } else {\n window.location.href = str;\n }\n }\n }\n return this.findNext(ctx);\n }\n}\n", "/**\n * Setter command parse tree elements\n * Commands that modify values (set, default, increment, decrement, put)\n */\n\nimport { Command } from '../base.js';\n\n/**\n * SetCommand - Set variable or property\n *\n * Parses: set {obj} on target OR set target to value\n * Executes: Assigns value to target using target's lhs/set() contract\n */\nexport class SetCommand extends Command {\n static keyword = \"set\";\n\n constructor(target, valueExpr, objectLiteral = null) {\n super();\n this.target = target;\n this.objectLiteral = objectLiteral;\n\n if (objectLiteral) {\n this.args = { obj: objectLiteral, setTarget: target };\n } else {\n this.args = { ...target.lhs, value: valueExpr };\n }\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"set\")) return;\n\n // set {obj} on target form\n if (parser.currentToken().type === \"L_BRACE\") {\n var obj = parser.requireElement(\"objectLiteral\");\n parser.requireToken(\"on\");\n var target = parser.requireElement(\"expression\");\n return new SetCommand(target, null, obj);\n }\n\n // set target to value form\n try {\n parser.pushFollow(\"to\");\n var target = parser.requireElement(\"assignableExpression\");\n } finally {\n parser.popFollow();\n }\n // Unwrap parenthesized expressions\n while (target.type === \"parenthesized\") target = target.expr;\n parser.requireToken(\"to\");\n var value = parser.requireElement(\"expression\");\n return new SetCommand(target, value);\n }\n\n resolve(context, args) {\n if (this.objectLiteral) {\n var { obj, setTarget } = args;\n Object.assign(setTarget, obj);\n } else {\n var { value, ...lhs } = args;\n this.target.set(context, lhs, value);\n }\n return this.findNext(context);\n }\n}\n\n/**\n * DefaultCommand - Set default value if undefined\n *\n * Parses: default target to value\n * Executes: Sets target to value only if target is null or undefined\n */\nexport class DefaultCommand extends Command {\n static keyword = \"default\";\n\n constructor(target, setter) {\n super();\n this.target = target;\n this.setter = setter;\n this.args = { targetValue: target };\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"default\")) return;\n try {\n parser.pushFollow(\"to\");\n var target = parser.requireElement(\"assignableExpression\");\n } finally {\n parser.popFollow();\n }\n // Unwrap parenthesized expressions\n while (target.type === \"parenthesized\") target = target.expr;\n parser.requireToken(\"to\");\n\n var value = parser.requireElement(\"expression\");\n\n var setter = new SetCommand(target, value);\n var defaultCmd = new DefaultCommand(target, setter);\n setter.parent = defaultCmd;\n return defaultCmd;\n }\n\n resolve(context, { targetValue }) {\n if (targetValue != null && targetValue !== \"\") {\n return this.findNext(context);\n } else {\n return this.setter;\n }\n }\n}\n\n/**\n * IncrementCommand - Increment numeric value\n *\n * Parses: increment target [by amount]\n * Executes: Adds amount (default 1) to target\n */\nexport class IncrementCommand extends Command {\n static keyword = \"increment\";\n\n constructor(target, amountExpr) {\n super();\n this.target = target;\n this.amountExpr = amountExpr;\n this.args = { targetValue: target, amount: amountExpr, ...target.lhs };\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"increment\")) return;\n var amountExpr;\n\n // This is optional. Defaults to \"result\"\n var target = parser.parseElement(\"assignableExpression\");\n // Unwrap parenthesized expressions\n while (target.type === \"parenthesized\") target = target.expr;\n\n // This is optional. Defaults to 1.\n if (parser.matchToken(\"by\")) {\n amountExpr = parser.requireElement(\"expression\");\n }\n\n return new IncrementCommand(target, amountExpr);\n }\n\n resolve(context, args) {\n var { targetValue, amount, ...lhs } = args;\n targetValue = targetValue ? parseFloat(targetValue) : 0;\n amount = this.amountExpr ? parseFloat(amount) : 1;\n var newValue = targetValue + amount;\n context.result = newValue;\n this.target.set(context, lhs, newValue);\n return this.findNext(context);\n }\n}\n\n/**\n * DecrementCommand - Decrement numeric value\n *\n * Parses: decrement target [by amount]\n * Executes: Subtracts amount (default 1) from target\n */\nexport class DecrementCommand extends Command {\n static keyword = \"decrement\";\n\n constructor(target, amountExpr) {\n super();\n this.target = target;\n this.amountExpr = amountExpr;\n this.args = { targetValue: target, amount: amountExpr, ...target.lhs };\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"decrement\")) return;\n var amountExpr;\n\n // This is optional. Defaults to \"result\"\n try {\n parser.pushFollow(\"by\");\n var target = parser.parseElement(\"assignableExpression\");\n } finally {\n parser.popFollow();\n }\n // Unwrap parenthesized expressions\n while (target.type === \"parenthesized\") target = target.expr;\n\n // This is optional. Defaults to 1.\n if (parser.matchToken(\"by\")) {\n amountExpr = parser.requireElement(\"expression\");\n }\n\n return new DecrementCommand(target, amountExpr);\n }\n\n resolve(context, args) {\n var { targetValue, amount, ...lhs } = args;\n targetValue = targetValue ? parseFloat(targetValue) : 0;\n amount = this.amountExpr ? parseFloat(amount) : 1;\n var newValue = targetValue - amount;\n context.result = newValue;\n this.target.set(context, lhs, newValue);\n return this.findNext(context);\n }\n}\n\n/**\n * SwapCommand - Swap two values\n *\n * Parses: swap target1 with target2\n * Executes: Swaps the values of two assignable expressions\n */\nexport class SwapCommand extends Command {\n static keyword = \"swap\";\n\n constructor(target1, target2) {\n super();\n this.target1 = target1;\n this.target2 = target2;\n this.args = {\n value1: target1, value2: target2,\n root1: target1.lhs.root, index1: target1.lhs.index,\n root2: target2.lhs.root, index2: target2.lhs.index,\n };\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"swap\")) return;\n try {\n parser.pushFollow(\"with\");\n var target1 = parser.requireElement(\"assignableExpression\");\n } finally {\n parser.popFollow();\n }\n while (target1.type === \"parenthesized\") target1 = target1.expr;\n parser.requireToken(\"with\");\n var target2 = parser.requireElement(\"assignableExpression\");\n while (target2.type === \"parenthesized\") target2 = target2.expr;\n return new SwapCommand(target1, target2);\n }\n\n resolve(context, { value1, value2, root1, index1, root2, index2 }) {\n if (value1 instanceof Element && value2 instanceof Element) {\n // DOM swap needs placeholders to avoid position interference\n var placeholder = document.createComment('');\n value1.replaceWith(placeholder);\n value2.replaceWith(value1);\n placeholder.replaceWith(value2);\n } else {\n this.target1.set(context, { root: root1, index: index1 }, value2);\n this.target2.set(context, { root: root2, index: index2 }, value1);\n }\n return this.findNext(context);\n }\n}\n\n/**\n * PutCommand - Put value into/before/after target (web-specific)\n *\n * Parses: put expr into target | put expr before/after target | put expr at start/end of target\n * Executes: Inserts value into DOM or variable\n */\nexport class PutCommand extends Command {\n static keyword = \"put\";\n\n constructor(target, operation, value, rootExpr) {\n super();\n this.target = target;\n this.operation = operation;\n this.value = value;\n this.rootExpr = rootExpr;\n\n // Derive flags from target type\n this.symbolWrite = target.type === \"symbol\" && operation === \"into\";\n this.arrayIndex = target.type === \"arrayIndex\";\n this.attributeWrite = (target.type === \"attributeRef\" ||\n (target.attribute && target.attribute.type === \"attributeRef\")) &&\n operation === \"into\";\n this.styleWrite = (target.type === \"styleRef\" ||\n (target.attribute && target.attribute.type === \"styleRef\")) &&\n operation === \"into\";\n\n // Derive property/prop\n if (this.arrayIndex) {\n this.prop = target.prop;\n } else if (this.symbolWrite) {\n this.prop = target.name;\n } else if (target.type === \"attributeRef\" || target.type === \"styleRef\") {\n this.prop = target.name;\n } else if (target.attribute) {\n this.prop = target.attribute.name;\n } else if (target.prop) {\n this.prop = target.prop.value;\n } else {\n this.prop = null;\n }\n\n this.args = { root: rootExpr, prop: this.prop, value };\n }\n\n static parse(parser) {\n if (!parser.matchToken(\"put\")) return;\n\n var value = parser.requireElement(\"expression\");\n\n var operationToken = parser.matchAnyToken(\"into\", \"before\", \"after\");\n\n if (operationToken == null && parser.matchToken(\"at\")) {\n parser.matchToken(\"the\"); // optional \"the\"\n operationToken = parser.matchAnyToken(\"start\", \"end\");\n parser.requireToken(\"of\");\n }\n\n if (operationToken == null) {\n parser.raiseExpected('into', 'before', 'at start of', 'at end of', 'after');\n }\n var target = parser.requireElement(\"expression\");\n // Unwrap parenthesized expressions\n while (target.type === \"parenthesized\") target = target.expr;\n\n var operation = operationToken.value;\n\n // Determine rootExpr based on target type\n var rootExpr;\n if (target.type === \"arrayIndex\" && operation === \"into\") {\n rootExpr = target.root;\n } else if (target.prop && target.root && operation === \"into\") {\n rootExpr = target.root;\n } else if (target.type === \"symbol\" && operation === \"into\") {\n rootExpr = null;\n } else if (target.type === \"attributeRef\" && operation === \"into\") {\n rootExpr = parser.requireElement(\"implicitMeTarget\");\n } else if (target.type === \"styleRef\" && operation === \"into\") {\n rootExpr = parser.requireElement(\"implicitMeTarget\");\n } else if (target.attribute && operation === \"into\") {\n rootExpr = target.root;\n } else {\n rootExpr = target;\n }\n\n return new PutCommand(target, operation, value, rootExpr);\n }\n\n putInto(context, root, prop, valueToPut) {\n if (root == null) {\n var value = context.meta.runtime.resolveSymbol(prop, context);\n } else {\n var value = root;\n }\n if ((root == null || prop == null) && (value instanceof Element || value instanceof Document)) {\n while (value.firstChild) value.removeChild(value.firstChild);\n value.append(context.meta.runtime.convertValue(valueToPut, \"Fragment\"));\n context.meta.runtime.processNode(value);\n } else {\n if (root == null) {\n context.meta.runtime.setSymbol(prop, context, null, valueToPut);\n } else {\n root[prop] = valueToPut\n }\n }\n }\n\n resolve(context, { root, prop, value: valueToPut }) {\n if (this.symbolWrite) {\n this.putInto(context, root, prop, valueToPut);\n } else {\n context.meta.runtime.nullCheck(root, this.rootExpr);\n if (this.operation === \"into\") {\n if (this.attributeWrite) {\n context.meta.runtime.implicitLoop(root, function (elt) {\n if (valueToPut == null) {\n elt.removeAttribute(prop);\n } else {\n elt.setAttribute(prop, valueToPut);\n }\n });\n } else if (this.styleWrite) {\n context.meta.runtime.implicitLoop(root, function (elt) {\n elt.style[prop] = valueToPut;\n });\n } else if (this.arrayIndex) {\n root[prop] = valueToPut;\n } else {\n var cmd = this;\n context.meta.runtime.implicitLoop(root, function (elt) {\n cmd.putInto(context, elt, prop, valueToPut);\n });\n }\n } else if (Array.isArray(root)) {\n if (this.operation === \"start\") {\n root.unshift(valueToPut);\n } else {\n root.push(valueToPut);\n }\n context.meta.runtime.notifyMutation(root);\n } else {\n var ops = { before: Element.prototype.before, after: Element.prototype.after,\n start: Element.prototype.prepend, end: Element.prototype.append };\n var op = ops[this.operation] || Element.prototype.append;\n\n context.meta.runtime.implicitLoop(root, function (elt) {\n op.call(\n elt,\n valueToPut instanceof Node\n ? valueToPut\n : context.meta.runtime.convertValue(valueToPut, \"Fragment\")\n );\n // process any new content\n if (elt.parentElement) {\n context.meta.runtime.processNode(elt.parentElement);\n } else {\n context.meta.runtime.processNode(elt);\n }\n });\n }\n }\n return this.findNext(context);\n }\n}\n", "/**\n * Event-related command parse tree elements\n * Commands for event handling and waiting (wait, trigger, send)\n */\n\nimport { Command, Expression, ParseElement } from '../base.js';\n\n/**\n * WaitCommand - Wait for time or event\n *\n * Parses: wait [a tick] | wait