sprae 5.1.0 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -40,22 +40,20 @@
40
40
  :onchange="e => (item.done = !item.done, save(todos))"
41
41
  />
42
42
  <label :text="item.text"></label>
43
- <button class="destroy" :onclick="e => save(todos = todos.filter(i => i !== item))"></button>
43
+ <button class="destroy" :onclick="e => (save(todos = todos.filter(i => i.text !== item.text)))"></button>
44
44
  </div>
45
45
  <input class=edit
46
46
  :value="item.text"
47
- :on="{
48
- input(e) { item.text = e.target.value; save(todos) },
49
- blur(e) { li.classList.remove('editing'); },
50
- keypress(e) { e.key === 'Enter' ? e.target.blur() : null },
51
- }"
47
+ :oninput="e => {item.text = e.target.value; save(todos)}"
48
+ :onblur="e => li.classList.remove('editing');"
49
+ :onkeypress="(e) => { e.key === 'Enter' ? e.target.blur() : null }"
52
50
  />
53
51
  </li>
54
52
  </ul>
55
53
  </section>
56
54
  <footer class="footer">
57
55
  <span class="todo-count">
58
- <strong :text="count">#</strong> <span :text="plur('item', count)">items</span> left
56
+ <strong :text="count()">#</strong> <span :text="plur('item', count())">items</span> left
59
57
  </span>
60
58
  <ul class="filters">
61
59
  <li :each="label, key in {'#/': 'All', '#/active': 'Active', '#/completed': 'Completed'}">
@@ -77,19 +75,19 @@
77
75
  <script src="https://unpkg.com/todomvc-common/base.js"></script>
78
76
  <script type="importmap"> {
79
77
  "imports": {
80
- "sprae": "https://cdn.skypack.dev/sprae",
78
+ "sprae": "https://cdn.jsdelivr.net/npm/sprae/sprae.min.js",
81
79
  "plur": "https://cdn.skypack.dev/plur"
82
80
  }
83
81
  }
84
82
  </script>
85
83
  <script type="module">
86
- import sprae from 'sprae'
84
+ import sprae from '../sprae.js'
87
85
  import plur from 'plur'
88
86
 
89
87
  let state = sprae(document.body, {
90
88
  plur,
91
89
  todos: JSON.parse(localStorage.getItem('todomvc.items') || '[]'),
92
- get count() { return this.todos.filter(item => !item.done).length },
90
+ count() { return this.todos.filter(item => !item.done).length },
93
91
  hash: window.location.hash || '#/',
94
92
  save: items => localStorage.setItem('todomvc.items', JSON.stringify(items))
95
93
  })
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sprae",
3
3
  "description": "DOM microhydration.",
4
- "version": "5.1.0",
4
+ "version": "5.3.0",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
7
7
  "type": "module",
package/r&d.md CHANGED
@@ -225,13 +225,14 @@
225
225
  - slows down domdiff
226
226
  - can be solved as `<x :if="xxx" :="xxx && (...)'">` automatically
227
227
 
228
- ## [x] :onmount/onunmount? -> for now no reasons
228
+ ## [ ] :onmount/onunmount? ->
229
229
 
230
230
  + useful for :if, :each
231
231
  + useful to dispose listeners via :onunmount (opposed to hidden symbols)
232
232
  - doesn't really solve disposal: if element is attached again, it would need to reattach removed listeners
233
233
  -> can be dolved via teardowns returned from updators
234
234
  -> nah, event listeners don't need collection, just make sure no refs to element remain
235
+ + can be useful for lazy-loadings
235
236
 
236
237
  ## [x] :focus="direct code", :evt="direct code" -> nah, too messy.
237
238
 
@@ -374,7 +375,18 @@
374
375
  - still need that syntax for filters, maps etc
375
376
  + can be made async by default
376
377
  - illicit `event` object
378
+ + we don't seem to ever need that event argument, many cases are covered by `.prevent` or `.stop`
379
+ ~+ generally `e=>` seem to conflict logically with modifiers sense
380
+ + `e=>` brings syntax burden - we may not ever need functions
381
+ + less problem detecting `const/let` in code
377
382
  - conflicts with regular attrs logic: the code is immediately invoked and can assign a function.
383
+ - `:oninput="e => (handleCaret(e), updateTimecodes())"`
384
+ - `:onbeforeinput="handleBeforeInput"`
385
+ - `:ondragenter..ondragleave:ondragenter..ondrop="e=>(this.classList.add('w-dragover'),e=>this.classList.remove('w-dragover'))"`
386
+ - `:ondrop="e=>console.log(e.dataTransfer.types)||e.preventDefault()"`
387
+ - `:onfocus="e => (e.relatedTarget ? e.relatedTarget.focus() : e.target.blur())"`
388
+ - `:onpopstate.window="e => goto(e.state)"`
389
+ - `:onclick.toggle="play"`
378
390
 
379
391
  ## [x] Should getters convert to computed? -> yes, that's relatively cheap and useful
380
392
 
@@ -448,8 +460,6 @@
448
460
  * onkey.after, onkey.microtask, onkey.defer, onkey.immediate
449
461
  * onkey.tick-1?
450
462
 
451
- ### [ ] onkey.unfocusable
452
-
453
463
  ## [ ] Prop modifiers
454
464
 
455
465
  - overall seems code complication without much benefit
@@ -475,6 +485,7 @@
475
485
  * [ ] x.watch-a-b-c - update by change of any of the deps
476
486
  * [ ] :x.always - update by _any_ dep change
477
487
  * [ ] :class.active="active"
488
+ * [ ] :x.persist="v"
478
489
 
479
490
  ## [x] Writing props on elements (like ones in :each) -> nah, just use `:x="this.x=abc"`
480
491
 
package/readme.md CHANGED
@@ -2,32 +2,24 @@
2
2
 
3
3
  > DOM microhydration with `:` attributes.
4
4
 
5
- _Sprae_ is tiny progressive enhancement lib, a minimal essential alternative to [alpine](https://github.com/alpinejs/alpine), [petite-vue](https://github.com/vuejs/petite-vue) or [template-parts](https://github.com/github/template-parts) with improved ergonomics. It enables simple markup logic without external scripts. Perfect for small websites or prototypes.
5
+ _Sprae_ is tiny progressive enhancement framework, a minimal essential alternative to [alpine](https://github.com/alpinejs/alpine)/[lucia](https://github.com/aidenybai/lucia), [petite-vue](https://github.com/vuejs/petite-vue) or [template-parts](https://github.com/github/template-parts) with improved ergonomics[*](#justification). It enables simple markup logic without external scripts. Perfect for small websites, prototypes or UI logic.
6
6
 
7
- ## Install
7
+ ## Use
8
+
9
+ ### Autoinit
8
10
 
9
- To autoinit on document, include [`sprae.auto.js`](./sprae.auto.js):
11
+ To autoinit document, include [`sprae.auto.js`](./sprae.auto.js):
10
12
 
11
13
  ```html
12
14
  <!-- <script src="https://cdn.jsdelivr.net/npm/sprae/sprae.auto.js" defer></script> -->
13
15
  <script src="./path/to/sprae.auto.js" defer></script>
14
16
 
15
- <a :each="id in ['a','b','c']" :href="`#${id}`"></a>
16
- ```
17
-
18
- To use as module, import [`sprae.js`](./sprae.js):
19
-
20
- ```html
21
- <script type="module">
22
- // import sprae from 'https://cdn.jsdelivr.net/npm/sprae/sprae.js';
23
- import sprae from './path/to/sprae.js';
24
- sprae(el, {foo: 'bar'});
25
- </script>
17
+ <a :each="id in ['a','b','c']" :href="`#${id}`" :text="id"></a>
26
18
  ```
27
19
 
28
- ## Use
20
+ ### Manual init
29
21
 
30
- Sprae evaluates attributes starting with `:`:
22
+ To use as module, import [`sprae.js`](./sprae.js):
31
23
 
32
24
  ```html
33
25
  <div id="container" :if="user">
@@ -35,15 +27,15 @@ Sprae evaluates attributes starting with `:`:
35
27
  </div>
36
28
 
37
29
  <script type="module">
38
- import sprae from 'sprae';
30
+ // import sprae from 'https://cdn.jsdelivr.net/npm/sprae/sprae.js';
31
+ import sprae from './path/to/sprae.js';
39
32
 
40
33
  const state = sprae(container, { user: { displayName: 'Dmitry Ivanov' } });
41
- state.user.displayName = 'dy'; // automatically updates DOM
34
+ state.user.displayName = 'dy'; // updates DOM
42
35
  </script>
43
36
  ```
44
37
 
45
- * `sprae` initializes container's subtree with data and immediately evaporates `:` attrs.
46
- * `state` object reflects current values, changing any props rerenders subtree next tick.
38
+ Sprae evaluates `:`-attributes and evaporates them. Reactive `state` reflects current values, can be updated directly.
47
39
 
48
40
 
49
41
  ## Attributes
@@ -63,14 +55,12 @@ Control flow of elements.
63
55
  Multiply element. `index` value starts from 1.
64
56
 
65
57
  ```html
66
- <ul>
67
- <li :each="item in items" :text="item">Untitled</li>
68
- </ul>
58
+ <ul><li :each="item in items" :text="item"></ul>
69
59
 
70
60
  <!-- Cases -->
71
61
  <li :each="item, idx in list" />
72
62
  <li :each="val, key in obj" />
73
- <li :each="idx, idx0 in number" />
63
+ <li :each="idx0, idx in number" />
74
64
 
75
65
  <!-- Loop by condition -->
76
66
  <li :if="items" :each="item in items" :text="item" />
@@ -78,9 +68,6 @@ Multiply element. `index` value starts from 1.
78
68
 
79
69
  <!-- Key items to reuse elements -->
80
70
  <li :each="item in items" :key="item.id" :text="item.value" />
81
-
82
- <!-- To avoid FOUC -->
83
- <style>[:each]{visibility: hidden}</style>
84
71
  ```
85
72
 
86
73
  #### `:text="value"`
@@ -131,7 +118,7 @@ Set value of an input, textarea or select. Takes handle of `checked` and `select
131
118
 
132
119
  #### `:<prop>="value?"`
133
120
 
134
- Set any attribute value or run effect.
121
+ Set any attribute value or run an effect.
135
122
 
136
123
  ```html
137
124
  <!-- Single property -->
@@ -166,10 +153,8 @@ Add event listener or events chain.
166
153
  <!-- Sequence of events -->
167
154
  <button :onfocus..onblur="e => {
168
155
  // onfocus
169
- let id = setInterval(track,200)
170
156
  return e => {
171
157
  // onblur
172
- clearInterval(id)
173
158
  }
174
159
  }">
175
160
 
@@ -223,6 +208,7 @@ Expose element to current data scope with the `id`:
223
208
  ```html
224
209
  <!-- single item -->
225
210
  <textarea :ref="text" placeholder="Enter text..."></textarea>
211
+ <span :text="text.value"></span>
226
212
 
227
213
  <!-- iterable items -->
228
214
  <ul>
@@ -230,14 +216,6 @@ Expose element to current data scope with the `id`:
230
216
  <input :onfocus..onblur="e => (item.classList.add('editing'), e => item.classList.remove('editing'))"/>
231
217
  </li>
232
218
  </ul>
233
-
234
- <script type="module">
235
- import sprae from 'sprae';
236
- let state = sprae(document, {items: ['a','b','c']})
237
-
238
- // element is in the state
239
- state.text // <textarea></textarea>
240
- </script>
241
219
  ```
242
220
 
243
221
  ## Sandbox
@@ -252,27 +230,37 @@ Expressions are sandboxed, ie. have no access to global or window (since sprae c
252
230
  Default sandbox provides: _window_, _document_, _console_, _history_, _location_, _Array_, _Object_, _Number_, _String_, _Boolean_, _Date_, _Set_, _Map_.<br/>
253
231
  Sandbox can be extended as `Object.assign(sprae.globals, { BigInt })`.
254
232
 
233
+ ## FOUC
234
+
235
+ To avoid _flash of unstyled content_, you can hide sprae attribute or add a custom effect, eg. `:hidden` - that will be removed once sprae is initialized:
236
+
237
+ ```html
238
+ <div :hidden></div>
239
+ <style>[:each],[:hidden] {visibility: hidden}</style>
240
+ ```
241
+
255
242
  ## Examples
256
243
 
257
244
  * TODO MVC: [demo](https://dy.github.io/sprae/examples/todomvc), [code](https://github.com/dy/sprae/blob/main/examples/todomvc.html)
258
245
  * Wavearea: [demo](https://dy.github.io/wavearea?src=//cdn.freesound.org/previews/586/586281_2332564-lq.mp3), [code](https://github.com/dy/wavearea)
246
+ * Prostogreen [demo](http://web-being.org/prostogreen/), [code](https://github.com/web-being/prostogreen/)
259
247
 
260
- ## Justification
248
+ ## Justification & alternatives
261
249
 
262
- * [Template-parts](https://github.com/dy/template-parts) / [templize](https://github.com/dy/templize) is progressive, but is stuck with native HTML quirks ([parsing table](https://github.com/github/template-parts/issues/24), [svg attributes](https://github.com/github/template-parts/issues/25), [liquid syntax](https://shopify.github.io/liquid/tags/template/#raw) conflict etc). Also ergonomics of `attr="{{}}"` is inferior to `:attr=""` since it creates flash of uninitialized values.
263
- * [Alpine](https://github.com/alpinejs/alpine) / [vue](https://github.com/vuejs/petite-vue) / [lit](https://github.com/lit/lit/tree/main/packages/lit-html) escapes native HTML quirks, but the syntax is a bit scattered: `:attr`, `v-*`,`x-*`, `@evt`, `{{}}` can be expressed with single convention. Besides, functionality is too broad and can be reduced to essence: perfection is when there's nothing to take away, not add. Also they tend to [self-encapsulate](https://github.com/alpinejs/alpine/discussions/3223), making interop hard.
250
+ * [Template-parts](https://github.com/dy/template-parts) / [templize](https://github.com/dy/templize) is progressive, but is stuck with native HTML quirks ([parsing table](https://github.com/github/template-parts/issues/24), [SVG attributes](https://github.com/github/template-parts/issues/25), [liquid syntax](https://shopify.github.io/liquid/tags/template/#raw) conflict etc). Also ergonomics of `attr="{{}}"` is inferior to `:attr=""` since it creates flash of uninitialized values.
251
+ * [Alpine](https://github.com/alpinejs/alpine) / [vue](https://github.com/vuejs/petite-vue) / [lit](https://github.com/lit/lit/tree/main/packages/lit-html) / [lucia](https://github.com/aidenybai/lucia) escapes native HTML quirks, but the syntax is a bit scattered: `:attr`, `v-*`,`x-*`, `l-*` `@evt`, `{{}}` can be expressed with single convention. `{{}}` also conflicts with template-parts and liquid/django. Besides, functionality is too broad and can be reduced to essence: perfection is when there's nothing to take away, not add. Also they tend to [self-encapsulate](https://github.com/alpinejs/alpine/discussions/3223), making interop hard.
264
252
  * React/[preact](https://ghub.io/preact) does the job wiring up JS to HTML, but with an extreme of migrating HTML to JSX and enforcing SPA, which is not organic for HTML. Also it doesn't support reactive fields (needs render call).
265
253
 
266
- _Sprae_ takes convention of _templize directives_ (_alpine_/_vue_ attrs) and builds upon <del>[_@preact/signals_](https://ghub.io/@preact/signals)</del> simple reacti.
254
+ _Sprae_ takes idea of _templize directives_/_alpine_/_vue_ attrs and builds upon <del>[_@preact/signals_](https://ghub.io/@preact/signals)</del> simple reactive state.
267
255
 
268
- * It doesn't break or modify initial static html markup.
256
+ * It doesn't break or modify static html markup.
269
257
  * It falls back to element content if uninitialized.
270
258
  * It doesn't enforce SPA nor JSX.
271
259
  * It enables island hydration.
272
260
  * It reserves minimal syntax space as `:` convention (keeping tree neatly decorated, not scattered).
273
261
  * Expressions are naturally reactive and incur minimal updates.
274
- * Input data may contain [signals](https://ghub.io/@preact/signals) or [reactive values](https://ghub.io/sube).
275
262
  * Elements / data API is open and enable easy interop.
263
+ * It's just nice separation: attributes belong to client and template fields to server.
276
264
 
277
265
  It is reminiscent of [XSLT](https://www.w3schools.com/xml/xsl_intro.asp), considered a [buried treasure](https://github.com/bahrus/be-restated) by web-connoisseurs.
278
266
 
package/sprae.auto.js CHANGED
@@ -108,6 +108,14 @@
108
108
  let proxy = new Proxy(obj, handler);
109
109
  targetProxy.set(obj, proxy);
110
110
  proxyTarget.set(proxy, obj);
111
+ let descriptors = Object.getOwnPropertyDescriptors(obj);
112
+ for (let name in descriptors) {
113
+ let desc = descriptors[name];
114
+ if (desc.get) {
115
+ if (desc.get)
116
+ desc.get = desc.get.bind(proxy), Object.defineProperty(obj, name, desc);
117
+ }
118
+ }
111
119
  obj[_parent] = parent ? state(parent) : sandbox;
112
120
  return proxy;
113
121
  };
@@ -215,12 +223,6 @@
215
223
  }
216
224
  };
217
225
  };
218
- primary["with"] = (el, expr, rootState) => {
219
- let evaluate = parseExpr(el, expr, ":with");
220
- const localState = evaluate(rootState);
221
- let state2 = state(localState, rootState);
222
- sprae(el, state2);
223
- };
224
226
  var _each = Symbol(":each");
225
227
  primary["each"] = (tpl, expr) => {
226
228
  let each = parseForExpression(expr);
@@ -241,7 +243,7 @@
241
243
  if (!list)
242
244
  list = [];
243
245
  else if (typeof list === "number")
244
- list = Array.from({ length: list }, (_, i) => [i, i + 1]);
246
+ list = Array.from({ length: list }, (_, i) => [i + 1, i]);
245
247
  else if (Array.isArray(list))
246
248
  list = list.map((item, i) => [i + 1, item]);
247
249
  else if (typeof list === "object")
@@ -271,6 +273,12 @@
271
273
  }
272
274
  };
273
275
  };
276
+ primary["with"] = (el, expr, rootState) => {
277
+ let evaluate = parseExpr(el, expr, ":with");
278
+ const localState = evaluate(rootState);
279
+ let state2 = state(localState, rootState);
280
+ sprae(el, state2);
281
+ };
274
282
  primary["ref"] = (el, expr, state2) => {
275
283
  state2[expr] = el;
276
284
  };
@@ -310,8 +318,19 @@
310
318
  let initClassName = el.getAttribute("class");
311
319
  return (state2) => {
312
320
  let v = evaluate(state2);
313
- let className = typeof v === "string" ? v : (Array.isArray(v) ? v : Object.entries(v).map(([k, v2]) => v2 ? k : "")).filter(Boolean).join(" ");
314
- el.setAttribute("class", [initClassName, className].filter(Boolean).join(" "));
321
+ let className = [initClassName];
322
+ if (v) {
323
+ if (typeof v === "string")
324
+ className.push(v);
325
+ else if (Array.isArray(v))
326
+ className.push(...v);
327
+ else
328
+ className.push(...Object.entries(v).map(([k, v2]) => v2 ? k : ""));
329
+ }
330
+ if (className = className.filter(Boolean).join(" "))
331
+ el.setAttribute("class", className);
332
+ else
333
+ el.removeAttribute("class");
315
334
  };
316
335
  };
317
336
  secondary["style"] = (el, expr) => {
package/sprae.auto.min.js CHANGED
@@ -1 +1 @@
1
- (()=>{var e,t,r=Promise.prototype.then.bind(Promise.resolve()),n=new WeakMap,l=class extends Map{#e=new FinalizationRegistry((e=>super.delete(e)));get size(){return[...this].length}constructor(e=[]){super();for(const[t,r]of e)this.set(t,r)}get(e){return super.get(e)?.deref()}set(e,t){let r=super.get(e);return r&&this.#e.unregister(r),r=(e=>n.get(e)||(e=>{const t=new WeakRef(e);return n.set(e,t),t})(e))(t),this.#e.register(t,e,r),super.set(e,r)}},s=new Set,i=new WeakMap,o=new WeakMap,a=new WeakMap,u=Symbol("parent"),c={Array:Array,Object:Object,Number:Number,String:String,Boolean:Boolean,Date:Date,console:console,window:window,document:document,history:history,location:location},f={has:()=>!0,get(t,r){if("symbol"==typeof r)return t[r];if(!(r in t))return t[u]?.[r];if(r in Object.prototype||Array.isArray(t)&&r in Array.prototype&&"length"!==r)return t[r];let n=t[r];if(e){let n=i.get(t);n||i.set(t,n={}),n[r]?n[r].includes(e)||n[r].push(e):n[r]=[e]}if(n&&n.constructor===Object||Array.isArray(n)){let e=o.get(n);return e||o.set(n,e=new Proxy(n,f)),e}return n},set(e,t,r){if(!(t in e)&&e[u]&&t in e[u])return e[u][t]=r;if(!Array.isArray(e)&&Object.is(e[t],r))return!0;e[t]=r;let n=i.get(e)?.[t];if(n)for(let e of n)s.add(e);return h(),!0},deleteProperty:(e,t)=>(e[t]=void 0,delete e[t],!0)},p=(e,t)=>{if(o.has(e))return o.get(e);if(a.has(e))return e;let r=new Proxy(e,f);return o.set(e,r),a.set(r,e),e[u]=t?p(t):c,r},y=t=>{const r=()=>{let n=e;e=r,t(),e=n};return r(),r},h=()=>{t||(t=!0,r((()=>{for(let e of s)e.call();s.clear(),t=!1})))},d={},g={};d.if=(e,t)=>{let r=document.createTextNode(""),n=[E(e,t,":if")],l=[e],s=e;for(;(s=e.nextElementSibling)&&s.hasAttribute(":else");)s.removeAttribute(":else"),(t=s.getAttribute(":if"))?(s.removeAttribute(":if"),s.remove(),l.push(s),n.push(E(e,t,":else :if"))):(s.remove(),l.push(s),n.push((()=>1)));return e.replaceWith(s=r),e=>{let t=n.findIndex((t=>t(e)));l[t]!=s&&((s[m]||s).replaceWith(s=l[t]||r),$(s,e))}},d.with=(e,t,r)=>{const n=E(e,t,":with")(r);$(e,p(n,r))};var m=Symbol(":each");d.each=(e,t)=>{let r=function(e){let t=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,r=e.match(/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/);if(!r)return;let n=r[2].trim(),l=r[1].replace(/^\s*\(|\)\s*$/g,"").trim(),s=l.match(t);return s?[l.replace(t,"").trim(),s[1].trim(),n]:[l,"",n]}(t);if(!r)return N(new Error,e,t,":each");const n=e[m]=document.createTextNode("");e.replaceWith(n);const s=E(e,r[2],":each"),i=e.getAttribute(":key"),o=i?E(null,i,":each"):null;e.removeAttribute(":key");const a=e.getAttribute(":ref"),u=new l,c=new l;let f=[];return l=>{let i=s(l);i?"number"==typeof i?i=Array.from({length:i},((e,t)=>[t,t+1])):Array.isArray(i)?i=i.map(((e,t)=>[t+1,e])):"object"==typeof i?i=Object.entries(i):N(Error("Bad list value"),e,t,":each"):i=[];let y=[],h=[];for(let[t,n]of i){let s,i,f=o?.({[r[0]]:n,[r[1]]:t});null==f?s=e.cloneNode(!0):(s=c.get(f))||c.set(f,s=e.cloneNode(!0)),y.push(s),null!=f&&(i=u.get(f))?i[r[0]]=n:(i=p({[r[0]]:n,[a||""]:null,[r[1]]:t},l),null!=f&&u.set(f,i)),h.push(i)}!function(e,t,r,n){const l=new Map,s=new Map;let i,o;for(i=0;i<t.length;i++)l.set(t[i],i);for(i=0;i<r.length;i++)s.set(r[i],i);for(i=o=0;i!==t.length||o!==r.length;){var a=t[i],u=r[o];if(null===a)i++;else if(r.length<=o)e.removeChild(t[i]),i++;else if(t.length<=i)e.insertBefore(u,t[i]||n),o++;else if(a===u)i++,o++;else{var c=s.get(a),f=l.get(u);void 0===c?(e.removeChild(t[i]),i++):void 0===f?(e.insertBefore(u,t[i]||n),o++):(e.insertBefore(t[f],t[i]||n),t[f]=null,f>i+1&&i++,o++)}}}(n.parentNode,f,y,n),f=y;for(let e=0;e<y.length;e++)$(y[e],h[e])}},d.ref=(e,t,r)=>{r[t]=e},g.render=(e,t,r)=>{let n=E(e,t,":render")(r);n||N(new Error("Template not found"),e,t,":render");let l=n.content.cloneNode(!0);e.replaceChildren(l),$(e,r)},g.id=(e,t)=>{let r=E(e,t,":id");return t=>{return n=r(t),e.id=n||0===n?n:"";var n}},g.class=(e,t)=>{let r=E(e,t,":class"),n=e.getAttribute("class");return t=>{let l=r(t),s="string"==typeof l?l:(Array.isArray(l)?l:Object.entries(l).map((([e,t])=>t?e:""))).filter(Boolean).join(" ");e.setAttribute("class",[n,s].filter(Boolean).join(" "))}},g.style=(e,t)=>{let r=E(e,t,":style"),n=e.getAttribute("style")||"";return n.endsWith(";")||(n+="; "),t=>{let l=r(t);if("string"==typeof l)e.setAttribute("style",n+l);else{e.setAttribute("style",n);for(let t in l)e.style.setProperty(t,l[t])}}},g.text=(e,t)=>{let r=E(e,t,":text");return t=>{let n=r(t);e.textContent=null==n?"":n}},g[""]=(e,t)=>{let r=E(e,t,":");if(r)return t=>{let n=r(t);for(let t in n)x(e,t.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(e=>"-"+e.toLowerCase())),n[t])}},g.value=(e,t)=>{let r,n,l=E(e,t,":value"),s="text"===e.type||""===e.type?t=>e.setAttribute("value",e.value=null==t?"":t):"TEXTAREA"===e.tagName||"text"===e.type||""===e.type?t=>(r=e.selectionStart,n=e.selectionEnd,e.setAttribute("value",e.value=null==t?"":t),r&&e.setSelectionRange(r,n)):"checkbox"===e.type?t=>(e.value=t?"on":"",x(e,"checked",t)):"select-one"===e.type?t=>{for(let t in e.options)t.removeAttribute("selected");e.value=t,e.selectedOptions[0]?.setAttribute("selected","")}:t=>e.value=t;return e=>s(l(e))},g.on=(e,t)=>{let r=E(e,t,":on");return t=>{let n=r(t),l=[];for(let t in n)l.push(v(e,t,n[t]));return()=>{for(let e of l)e()}}};var b=(e,t,r,n)=>{let l=n.startsWith("on")&&n.slice(2),s=E(e,t,":"+n);if(s)return l?t=>{let r=s(t)||(()=>{});return v(e,l,r)}:t=>x(e,n,s(t))},v=(e,t,r)=>{if(!r)return;let n=t.split("..").map((t=>{let r={evt:"",target:e,test:()=>!0};return r.evt=(t.startsWith("on")?t.slice(2):t).replace(/\.(\w+)?-?([-\w]+)?/g,((e,t,n="")=>(r.test=A[t]?.(r,...n.split("-"))||r.test,""))),r}));if(1==n.length)return i(r,n[0]);const l=(t,r=0)=>{let s;return s=i((i=>{r&&s();let o=t.call(e,i);"function"!=typeof o&&(o=()=>{}),r+1<n.length&&l(o,r?r+1:1)}),n[r])};let s=l(r);return()=>s();function i(e,{evt:t,target:r,test:n,defer:l,stop:s,prevent:i,...o}){l&&(e=l(e));let a=t=>n(t)&&(s&&t.stopPropagation(),i&&t.preventDefault(),e.call(r,t));return r.addEventListener(t,a,o),()=>r.removeEventListener(t,a,o)}},A={prevent(e){e.prevent=!0},stop(e){e.stop=!0},once(e){e.once=!0},passive(e){e.passive=!0},capture(e){e.capture=!0},window(e){e.target=window},document(e){e.target=document},toggle(e){e.defer=(t,r)=>n=>r?(r.call?.(e.target,n),r=null):r=t()},throttle(e,t){e.defer=e=>k(e,t?Number(t)||0:108)},debounce(e,t){e.defer=e=>W(e,t?Number(t)||0:108)},outside:e=>t=>{let r=e.target;return!(r.contains(t.target)||!1===t.target.isConnected||r.offsetWidth<1&&r.offsetHeight<1)},self:e=>t=>t.target===e.target,ctrl:(e,...t)=>e=>w.ctrl(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),shift:(e,...t)=>e=>w.shift(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),alt:(e,...t)=>e=>w.alt(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),meta:(e,...t)=>e=>w.meta(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),arrow:e=>w.arrow,enter:e=>w.enter,escape:e=>w.escape,tab:e=>w.tab,space:e=>w.space,backspace:e=>w.backspace,delete:e=>w.delete,digit:e=>w.digit,letter:e=>w.letter,character:e=>w.character},w={ctrl:e=>e.ctrlKey||"Control"===e.key||"Ctrl"===e.key,shift:e=>e.shiftKey||"Shift"===e.key,alt:e=>e.altKey||"Alt"===e.key,meta:e=>e.metaKey||"Meta"===e.key||"Command"===e.key,arrow:e=>e.key.startsWith("Arrow"),enter:e=>"Enter"===e.key,escape:e=>e.key.startsWith("Esc"),tab:e=>"Tab"===e.key,space:e=>" "===e.key||"Space"===e.key||" "===e.key,backspace:e=>"Backspace"===e.key,delete:e=>"Delete"===e.key,digit:e=>/^\d$/.test(e.key),letter:e=>/^[a-zA-Z]$/.test(e.key),character:e=>/^\S$/.test(e.key)},k=(e,t)=>{let r,n,l=s=>{r=!0,setTimeout((()=>{if(r=!1,n)return n=!1,l(s),e(s)}),t)};return t=>r?n=!0:(l(t),e(t))},W=(e,t)=>{let r;return n=>{clearTimeout(r),r=setTimeout((()=>{r=null,e(n)}),t)}},x=(e,t,r)=>{null==r||!1===r?e.removeAttribute(t):e.setAttribute(t,!0===r?"":"number"==typeof r||"string"==typeof r?r:"")},S={};function E(e,t,r){let n=S[t];if(!n){let l=/^[\n\s]*if.*\(.*\)/.test(t)||/\b(let|const)\s/.test(t)&&!r.startsWith(":on")?`(() => {${t}})()`:t;try{n=S[t]=new Function("__scope",`with (__scope) { return ${l.trim()} };`)}catch(n){return N(n,e,t,r)}}return l=>{let s;try{s=n.call(e,l)}catch(n){return N(n,e,t,r)}return s}}function N(e,t,n,l){Object.assign(e,{element:t,expression:n}),console.warn(`∴ ${e.message}\n\n${l}=${n?`"${n}"\n\n`:""}`,t),r((()=>{throw e}),0)}$.globals=c;var j=new WeakMap;function $(e,t){if(!e.children)return;if(j.has(e))return Object.assign(j.get(e),t);const r=p(t||{}),n=[],l=(e,t=e.parentNode)=>{for(let l in d){let s=":"+l;if(e.hasAttribute?.(s)){let i=e.getAttribute(s);if(e.removeAttribute(s),n.push(d[l](e,i,r,l)),j.has(e))return;if(e.parentNode!==t)return!1}}if(e.attributes)for(let t=0;t<e.attributes.length;){let l=e.attributes[t];if(":"!==l.name[0]){t++;continue}e.removeAttribute(l.name);let s=l.value,i=l.name.slice(1).split(":");for(let t of i){let l=g[t]||b;n.push(l(e,s,r,t))}}for(let t,r=0;t=e.children[r];r++)!1===l(t,e)&&r--};l(e);for(let e of n)if(e){let t;y((()=>{"function"==typeof t&&t(),t=e(r)}))}return j.set(e,r),r}document.currentScript&&$(document.documentElement)})();
1
+ (()=>{var e,t,r=Promise.prototype.then.bind(Promise.resolve()),n=new WeakMap,l=class extends Map{#e=new FinalizationRegistry((e=>super.delete(e)));get size(){return[...this].length}constructor(e=[]){super();for(const[t,r]of e)this.set(t,r)}get(e){return super.get(e)?.deref()}set(e,t){let r=super.get(e);return r&&this.#e.unregister(r),r=(e=>n.get(e)||(e=>{const t=new WeakRef(e);return n.set(e,t),t})(e))(t),this.#e.register(t,e,r),super.set(e,r)}},s=new Set,i=new WeakMap,o=new WeakMap,a=new WeakMap,u=Symbol("parent"),c={Array:Array,Object:Object,Number:Number,String:String,Boolean:Boolean,Date:Date,console:console,window:window,document:document,history:history,location:location},f={has:()=>!0,get(t,r){if("symbol"==typeof r)return t[r];if(!(r in t))return t[u]?.[r];if(r in Object.prototype||Array.isArray(t)&&r in Array.prototype&&"length"!==r)return t[r];let n=t[r];if(e){let n=i.get(t);n||i.set(t,n={}),n[r]?n[r].includes(e)||n[r].push(e):n[r]=[e]}if(n&&n.constructor===Object||Array.isArray(n)){let e=o.get(n);return e||o.set(n,e=new Proxy(n,f)),e}return n},set(e,t,r){if(!(t in e)&&e[u]&&t in e[u])return e[u][t]=r;if(!Array.isArray(e)&&Object.is(e[t],r))return!0;e[t]=r;let n=i.get(e)?.[t];if(n)for(let e of n)s.add(e);return h(),!0},deleteProperty:(e,t)=>(e[t]=void 0,delete e[t],!0)},p=(e,t)=>{if(o.has(e))return o.get(e);if(a.has(e))return e;let r=new Proxy(e,f);o.set(e,r),a.set(r,e);let n=Object.getOwnPropertyDescriptors(e);for(let t in n){let l=n[t];l.get&&l.get&&(l.get=l.get.bind(r),Object.defineProperty(e,t,l))}return e[u]=t?p(t):c,r},y=t=>{const r=()=>{let n=e;e=r,t(),e=n};return r(),r},h=()=>{t||(t=!0,r((()=>{for(let e of s)e.call();s.clear(),t=!1})))},g={},d={};g.if=(e,t)=>{let r=document.createTextNode(""),n=[j(e,t,":if")],l=[e],s=e;for(;(s=e.nextElementSibling)&&s.hasAttribute(":else");)s.removeAttribute(":else"),(t=s.getAttribute(":if"))?(s.removeAttribute(":if"),s.remove(),l.push(s),n.push(j(e,t,":else :if"))):(s.remove(),l.push(s),n.push((()=>1)));return e.replaceWith(s=r),e=>{let t=n.findIndex((t=>t(e)));l[t]!=s&&((s[m]||s).replaceWith(s=l[t]||r),O(s,e))}};var m=Symbol(":each");g.each=(e,t)=>{let r=function(e){let t=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,r=e.match(/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/);if(!r)return;let n=r[2].trim(),l=r[1].replace(/^\s*\(|\)\s*$/g,"").trim(),s=l.match(t);return s?[l.replace(t,"").trim(),s[1].trim(),n]:[l,"",n]}(t);if(!r)return E(new Error,e,t,":each");const n=e[m]=document.createTextNode("");e.replaceWith(n);const s=j(e,r[2],":each"),i=e.getAttribute(":key"),o=i?j(null,i,":each"):null;e.removeAttribute(":key");const a=e.getAttribute(":ref"),u=new l,c=new l;let f=[];return l=>{let i=s(l);i?"number"==typeof i?i=Array.from({length:i},((e,t)=>[t+1,t])):Array.isArray(i)?i=i.map(((e,t)=>[t+1,e])):"object"==typeof i?i=Object.entries(i):E(Error("Bad list value"),e,t,":each"):i=[];let y=[],h=[];for(let[t,n]of i){let s,i,f=o?.({[r[0]]:n,[r[1]]:t});null==f?s=e.cloneNode(!0):(s=c.get(f))||c.set(f,s=e.cloneNode(!0)),y.push(s),null!=f&&(i=u.get(f))?i[r[0]]=n:(i=p({[r[0]]:n,[a||""]:null,[r[1]]:t},l),null!=f&&u.set(f,i)),h.push(i)}!function(e,t,r,n){const l=new Map,s=new Map;let i,o;for(i=0;i<t.length;i++)l.set(t[i],i);for(i=0;i<r.length;i++)s.set(r[i],i);for(i=o=0;i!==t.length||o!==r.length;){var a=t[i],u=r[o];if(null===a)i++;else if(r.length<=o)e.removeChild(t[i]),i++;else if(t.length<=i)e.insertBefore(u,t[i]||n),o++;else if(a===u)i++,o++;else{var c=s.get(a),f=l.get(u);void 0===c?(e.removeChild(t[i]),i++):void 0===f?(e.insertBefore(u,t[i]||n),o++):(e.insertBefore(t[f],t[i]||n),t[f]=null,f>i+1&&i++,o++)}}}(n.parentNode,f,y,n),f=y;for(let e=0;e<y.length;e++)O(y[e],h[e])}},g.with=(e,t,r)=>{const n=j(e,t,":with")(r);O(e,p(n,r))},g.ref=(e,t,r)=>{r[t]=e},d.render=(e,t,r)=>{let n=j(e,t,":render")(r);n||E(new Error("Template not found"),e,t,":render");let l=n.content.cloneNode(!0);e.replaceChildren(l),O(e,r)},d.id=(e,t)=>{let r=j(e,t,":id");return t=>{return n=r(t),e.id=n||0===n?n:"";var n}},d.class=(e,t)=>{let r=j(e,t,":class"),n=e.getAttribute("class");return t=>{let l=r(t),s=[n];l&&("string"==typeof l?s.push(l):Array.isArray(l)?s.push(...l):s.push(...Object.entries(l).map((([e,t])=>t?e:"")))),(s=s.filter(Boolean).join(" "))?e.setAttribute("class",s):e.removeAttribute("class")}},d.style=(e,t)=>{let r=j(e,t,":style"),n=e.getAttribute("style")||"";return n.endsWith(";")||(n+="; "),t=>{let l=r(t);if("string"==typeof l)e.setAttribute("style",n+l);else{e.setAttribute("style",n);for(let t in l)e.style.setProperty(t,l[t])}}},d.text=(e,t)=>{let r=j(e,t,":text");return t=>{let n=r(t);e.textContent=null==n?"":n}},d[""]=(e,t)=>{let r=j(e,t,":");if(r)return t=>{let n=r(t);for(let t in n)x(e,t.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(e=>"-"+e.toLowerCase())),n[t])}},d.value=(e,t)=>{let r,n,l=j(e,t,":value"),s="text"===e.type||""===e.type?t=>e.setAttribute("value",e.value=null==t?"":t):"TEXTAREA"===e.tagName||"text"===e.type||""===e.type?t=>(r=e.selectionStart,n=e.selectionEnd,e.setAttribute("value",e.value=null==t?"":t),r&&e.setSelectionRange(r,n)):"checkbox"===e.type?t=>(e.value=t?"on":"",x(e,"checked",t)):"select-one"===e.type?t=>{for(let t in e.options)t.removeAttribute("selected");e.value=t,e.selectedOptions[0]?.setAttribute("selected","")}:t=>e.value=t;return e=>s(l(e))},d.on=(e,t)=>{let r=j(e,t,":on");return t=>{let n=r(t),l=[];for(let t in n)l.push(v(e,t,n[t]));return()=>{for(let e of l)e()}}};var b=(e,t,r,n)=>{let l=n.startsWith("on")&&n.slice(2),s=j(e,t,":"+n);if(s)return l?t=>{let r=s(t)||(()=>{});return v(e,l,r)}:t=>x(e,n,s(t))},v=(e,t,r)=>{if(!r)return;let n=t.split("..").map((t=>{let r={evt:"",target:e,test:()=>!0};return r.evt=(t.startsWith("on")?t.slice(2):t).replace(/\.(\w+)?-?([-\w]+)?/g,((e,t,n="")=>(r.test=A[t]?.(r,...n.split("-"))||r.test,""))),r}));if(1==n.length)return i(r,n[0]);const l=(t,r=0)=>{let s;return s=i((i=>{r&&s();let o=t.call(e,i);"function"!=typeof o&&(o=()=>{}),r+1<n.length&&l(o,r?r+1:1)}),n[r])};let s=l(r);return()=>s();function i(e,{evt:t,target:r,test:n,defer:l,stop:s,prevent:i,...o}){l&&(e=l(e));let a=t=>n(t)&&(s&&t.stopPropagation(),i&&t.preventDefault(),e.call(r,t));return r.addEventListener(t,a,o),()=>r.removeEventListener(t,a,o)}},A={prevent(e){e.prevent=!0},stop(e){e.stop=!0},once(e){e.once=!0},passive(e){e.passive=!0},capture(e){e.capture=!0},window(e){e.target=window},document(e){e.target=document},toggle(e){e.defer=(t,r)=>n=>r?(r.call?.(e.target,n),r=null):r=t()},throttle(e,t){e.defer=e=>k(e,t?Number(t)||0:108)},debounce(e,t){e.defer=e=>W(e,t?Number(t)||0:108)},outside:e=>t=>{let r=e.target;return!(r.contains(t.target)||!1===t.target.isConnected||r.offsetWidth<1&&r.offsetHeight<1)},self:e=>t=>t.target===e.target,ctrl:(e,...t)=>e=>w.ctrl(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),shift:(e,...t)=>e=>w.shift(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),alt:(e,...t)=>e=>w.alt(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),meta:(e,...t)=>e=>w.meta(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),arrow:e=>w.arrow,enter:e=>w.enter,escape:e=>w.escape,tab:e=>w.tab,space:e=>w.space,backspace:e=>w.backspace,delete:e=>w.delete,digit:e=>w.digit,letter:e=>w.letter,character:e=>w.character},w={ctrl:e=>e.ctrlKey||"Control"===e.key||"Ctrl"===e.key,shift:e=>e.shiftKey||"Shift"===e.key,alt:e=>e.altKey||"Alt"===e.key,meta:e=>e.metaKey||"Meta"===e.key||"Command"===e.key,arrow:e=>e.key.startsWith("Arrow"),enter:e=>"Enter"===e.key,escape:e=>e.key.startsWith("Esc"),tab:e=>"Tab"===e.key,space:e=>" "===e.key||"Space"===e.key||" "===e.key,backspace:e=>"Backspace"===e.key,delete:e=>"Delete"===e.key,digit:e=>/^\d$/.test(e.key),letter:e=>/^[a-zA-Z]$/.test(e.key),character:e=>/^\S$/.test(e.key)},k=(e,t)=>{let r,n,l=s=>{r=!0,setTimeout((()=>{if(r=!1,n)return n=!1,l(s),e(s)}),t)};return t=>r?n=!0:(l(t),e(t))},W=(e,t)=>{let r;return n=>{clearTimeout(r),r=setTimeout((()=>{r=null,e(n)}),t)}},x=(e,t,r)=>{null==r||!1===r?e.removeAttribute(t):e.setAttribute(t,!0===r?"":"number"==typeof r||"string"==typeof r?r:"")},S={};function j(e,t,r){let n=S[t];if(!n){let l=/^[\n\s]*if.*\(.*\)/.test(t)||/\b(let|const)\s/.test(t)&&!r.startsWith(":on")?`(() => {${t}})()`:t;try{n=S[t]=new Function("__scope",`with (__scope) { return ${l.trim()} };`)}catch(n){return E(n,e,t,r)}}return l=>{let s;try{s=n.call(e,l)}catch(n){return E(n,e,t,r)}return s}}function E(e,t,n,l){Object.assign(e,{element:t,expression:n}),console.warn(`∴ ${e.message}\n\n${l}=${n?`"${n}"\n\n`:""}`,t),r((()=>{throw e}),0)}O.globals=c;var N=new WeakMap;function O(e,t){if(!e.children)return;if(N.has(e))return Object.assign(N.get(e),t);const r=p(t||{}),n=[],l=(e,t=e.parentNode)=>{for(let l in g){let s=":"+l;if(e.hasAttribute?.(s)){let i=e.getAttribute(s);if(e.removeAttribute(s),n.push(g[l](e,i,r,l)),N.has(e))return;if(e.parentNode!==t)return!1}}if(e.attributes)for(let t=0;t<e.attributes.length;){let l=e.attributes[t];if(":"!==l.name[0]){t++;continue}e.removeAttribute(l.name);let s=l.value,i=l.name.slice(1).split(":");for(let t of i){let l=d[t]||b;n.push(l(e,s,r,t))}}for(let t,r=0;t=e.children[r];r++)!1===l(t,e)&&r--};l(e);for(let e of n)if(e){let t;y((()=>{"function"==typeof t&&t(),t=e(r)}))}return N.set(e,r),r}document.currentScript&&O(document.documentElement)})();
package/sprae.js CHANGED
@@ -107,6 +107,14 @@ var state = (obj, parent) => {
107
107
  let proxy = new Proxy(obj, handler);
108
108
  targetProxy.set(obj, proxy);
109
109
  proxyTarget.set(proxy, obj);
110
+ let descriptors = Object.getOwnPropertyDescriptors(obj);
111
+ for (let name in descriptors) {
112
+ let desc = descriptors[name];
113
+ if (desc.get) {
114
+ if (desc.get)
115
+ desc.get = desc.get.bind(proxy), Object.defineProperty(obj, name, desc);
116
+ }
117
+ }
110
118
  obj[_parent] = parent ? state(parent) : sandbox;
111
119
  return proxy;
112
120
  };
@@ -214,12 +222,6 @@ primary["if"] = (el, expr) => {
214
222
  }
215
223
  };
216
224
  };
217
- primary["with"] = (el, expr, rootState) => {
218
- let evaluate = parseExpr(el, expr, ":with");
219
- const localState = evaluate(rootState);
220
- let state2 = state(localState, rootState);
221
- sprae(el, state2);
222
- };
223
225
  var _each = Symbol(":each");
224
226
  primary["each"] = (tpl, expr) => {
225
227
  let each = parseForExpression(expr);
@@ -240,7 +242,7 @@ primary["each"] = (tpl, expr) => {
240
242
  if (!list)
241
243
  list = [];
242
244
  else if (typeof list === "number")
243
- list = Array.from({ length: list }, (_, i) => [i, i + 1]);
245
+ list = Array.from({ length: list }, (_, i) => [i + 1, i]);
244
246
  else if (Array.isArray(list))
245
247
  list = list.map((item, i) => [i + 1, item]);
246
248
  else if (typeof list === "object")
@@ -270,6 +272,12 @@ primary["each"] = (tpl, expr) => {
270
272
  }
271
273
  };
272
274
  };
275
+ primary["with"] = (el, expr, rootState) => {
276
+ let evaluate = parseExpr(el, expr, ":with");
277
+ const localState = evaluate(rootState);
278
+ let state2 = state(localState, rootState);
279
+ sprae(el, state2);
280
+ };
273
281
  primary["ref"] = (el, expr, state2) => {
274
282
  state2[expr] = el;
275
283
  };
@@ -309,8 +317,19 @@ secondary["class"] = (el, expr) => {
309
317
  let initClassName = el.getAttribute("class");
310
318
  return (state2) => {
311
319
  let v = evaluate(state2);
312
- let className = typeof v === "string" ? v : (Array.isArray(v) ? v : Object.entries(v).map(([k, v2]) => v2 ? k : "")).filter(Boolean).join(" ");
313
- el.setAttribute("class", [initClassName, className].filter(Boolean).join(" "));
320
+ let className = [initClassName];
321
+ if (v) {
322
+ if (typeof v === "string")
323
+ className.push(v);
324
+ else if (Array.isArray(v))
325
+ className.push(...v);
326
+ else
327
+ className.push(...Object.entries(v).map(([k, v2]) => v2 ? k : ""));
328
+ }
329
+ if (className = className.filter(Boolean).join(" "))
330
+ el.setAttribute("class", className);
331
+ else
332
+ el.removeAttribute("class");
314
333
  };
315
334
  };
316
335
  secondary["style"] = (el, expr) => {
package/sprae.min.js CHANGED
@@ -1 +1 @@
1
- var e,t,r=Promise.prototype.then.bind(Promise.resolve()),n=new WeakMap,l=class extends Map{#e=new FinalizationRegistry((e=>super.delete(e)));get size(){return[...this].length}constructor(e=[]){super();for(const[t,r]of e)this.set(t,r)}get(e){return super.get(e)?.deref()}set(e,t){let r=super.get(e);return r&&this.#e.unregister(r),r=(e=>n.get(e)||(e=>{const t=new WeakRef(e);return n.set(e,t),t})(e))(t),this.#e.register(t,e,r),super.set(e,r)}},s=new Set,i=new WeakMap,o=new WeakMap,a=new WeakMap,u=Symbol("parent"),c={Array:Array,Object:Object,Number:Number,String:String,Boolean:Boolean,Date:Date,console:console,window:window,document:document,history:history,location:location},f={has:()=>!0,get(t,r){if("symbol"==typeof r)return t[r];if(!(r in t))return t[u]?.[r];if(r in Object.prototype||Array.isArray(t)&&r in Array.prototype&&"length"!==r)return t[r];let n=t[r];if(e){let n=i.get(t);n||i.set(t,n={}),n[r]?n[r].includes(e)||n[r].push(e):n[r]=[e]}if(n&&n.constructor===Object||Array.isArray(n)){let e=o.get(n);return e||o.set(n,e=new Proxy(n,f)),e}return n},set(e,t,r){if(!(t in e)&&e[u]&&t in e[u])return e[u][t]=r;if(!Array.isArray(e)&&Object.is(e[t],r))return!0;e[t]=r;let n=i.get(e)?.[t];if(n)for(let e of n)s.add(e);return h(),!0},deleteProperty:(e,t)=>(e[t]=void 0,delete e[t],!0)},p=(e,t)=>{if(o.has(e))return o.get(e);if(a.has(e))return e;let r=new Proxy(e,f);return o.set(e,r),a.set(r,e),e[u]=t?p(t):c,r},y=t=>{const r=()=>{let n=e;e=r,t(),e=n};return r(),r},h=()=>{t||(t=!0,r((()=>{for(let e of s)e.call();s.clear(),t=!1})))},d={},g={};d.if=(e,t)=>{let r=document.createTextNode(""),n=[E(e,t,":if")],l=[e],s=e;for(;(s=e.nextElementSibling)&&s.hasAttribute(":else");)s.removeAttribute(":else"),(t=s.getAttribute(":if"))?(s.removeAttribute(":if"),s.remove(),l.push(s),n.push(E(e,t,":else :if"))):(s.remove(),l.push(s),n.push((()=>1)));return e.replaceWith(s=r),e=>{let t=n.findIndex((t=>t(e)));l[t]!=s&&((s[m]||s).replaceWith(s=l[t]||r),$(s,e))}},d.with=(e,t,r)=>{const n=E(e,t,":with")(r);$(e,p(n,r))};var m=Symbol(":each");d.each=(e,t)=>{let r=function(e){let t=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,r=e.match(/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/);if(!r)return;let n=r[2].trim(),l=r[1].replace(/^\s*\(|\)\s*$/g,"").trim(),s=l.match(t);return s?[l.replace(t,"").trim(),s[1].trim(),n]:[l,"",n]}(t);if(!r)return N(new Error,e,t,":each");const n=e[m]=document.createTextNode("");e.replaceWith(n);const s=E(e,r[2],":each"),i=e.getAttribute(":key"),o=i?E(null,i,":each"):null;e.removeAttribute(":key");const a=e.getAttribute(":ref"),u=new l,c=new l;let f=[];return l=>{let i=s(l);i?"number"==typeof i?i=Array.from({length:i},((e,t)=>[t,t+1])):Array.isArray(i)?i=i.map(((e,t)=>[t+1,e])):"object"==typeof i?i=Object.entries(i):N(Error("Bad list value"),e,t,":each"):i=[];let y=[],h=[];for(let[t,n]of i){let s,i,f=o?.({[r[0]]:n,[r[1]]:t});null==f?s=e.cloneNode(!0):(s=c.get(f))||c.set(f,s=e.cloneNode(!0)),y.push(s),null!=f&&(i=u.get(f))?i[r[0]]=n:(i=p({[r[0]]:n,[a||""]:null,[r[1]]:t},l),null!=f&&u.set(f,i)),h.push(i)}!function(e,t,r,n){const l=new Map,s=new Map;let i,o;for(i=0;i<t.length;i++)l.set(t[i],i);for(i=0;i<r.length;i++)s.set(r[i],i);for(i=o=0;i!==t.length||o!==r.length;){var a=t[i],u=r[o];if(null===a)i++;else if(r.length<=o)e.removeChild(t[i]),i++;else if(t.length<=i)e.insertBefore(u,t[i]||n),o++;else if(a===u)i++,o++;else{var c=s.get(a),f=l.get(u);void 0===c?(e.removeChild(t[i]),i++):void 0===f?(e.insertBefore(u,t[i]||n),o++):(e.insertBefore(t[f],t[i]||n),t[f]=null,f>i+1&&i++,o++)}}}(n.parentNode,f,y,n),f=y;for(let e=0;e<y.length;e++)$(y[e],h[e])}},d.ref=(e,t,r)=>{r[t]=e},g.render=(e,t,r)=>{let n=E(e,t,":render")(r);n||N(new Error("Template not found"),e,t,":render");let l=n.content.cloneNode(!0);e.replaceChildren(l),$(e,r)},g.id=(e,t)=>{let r=E(e,t,":id");return t=>{return n=r(t),e.id=n||0===n?n:"";var n}},g.class=(e,t)=>{let r=E(e,t,":class"),n=e.getAttribute("class");return t=>{let l=r(t),s="string"==typeof l?l:(Array.isArray(l)?l:Object.entries(l).map((([e,t])=>t?e:""))).filter(Boolean).join(" ");e.setAttribute("class",[n,s].filter(Boolean).join(" "))}},g.style=(e,t)=>{let r=E(e,t,":style"),n=e.getAttribute("style")||"";return n.endsWith(";")||(n+="; "),t=>{let l=r(t);if("string"==typeof l)e.setAttribute("style",n+l);else{e.setAttribute("style",n);for(let t in l)e.style.setProperty(t,l[t])}}},g.text=(e,t)=>{let r=E(e,t,":text");return t=>{let n=r(t);e.textContent=null==n?"":n}},g[""]=(e,t)=>{let r=E(e,t,":");if(r)return t=>{let n=r(t);for(let t in n)x(e,t.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(e=>"-"+e.toLowerCase())),n[t])}},g.value=(e,t)=>{let r,n,l=E(e,t,":value"),s="text"===e.type||""===e.type?t=>e.setAttribute("value",e.value=null==t?"":t):"TEXTAREA"===e.tagName||"text"===e.type||""===e.type?t=>(r=e.selectionStart,n=e.selectionEnd,e.setAttribute("value",e.value=null==t?"":t),r&&e.setSelectionRange(r,n)):"checkbox"===e.type?t=>(e.value=t?"on":"",x(e,"checked",t)):"select-one"===e.type?t=>{for(let t in e.options)t.removeAttribute("selected");e.value=t,e.selectedOptions[0]?.setAttribute("selected","")}:t=>e.value=t;return e=>s(l(e))},g.on=(e,t)=>{let r=E(e,t,":on");return t=>{let n=r(t),l=[];for(let t in n)l.push(v(e,t,n[t]));return()=>{for(let e of l)e()}}};var b=(e,t,r,n)=>{let l=n.startsWith("on")&&n.slice(2),s=E(e,t,":"+n);if(s)return l?t=>{let r=s(t)||(()=>{});return v(e,l,r)}:t=>x(e,n,s(t))},v=(e,t,r)=>{if(!r)return;let n=t.split("..").map((t=>{let r={evt:"",target:e,test:()=>!0};return r.evt=(t.startsWith("on")?t.slice(2):t).replace(/\.(\w+)?-?([-\w]+)?/g,((e,t,n="")=>(r.test=A[t]?.(r,...n.split("-"))||r.test,""))),r}));if(1==n.length)return i(r,n[0]);const l=(t,r=0)=>{let s;return s=i((i=>{r&&s();let o=t.call(e,i);"function"!=typeof o&&(o=()=>{}),r+1<n.length&&l(o,r?r+1:1)}),n[r])};let s=l(r);return()=>s();function i(e,{evt:t,target:r,test:n,defer:l,stop:s,prevent:i,...o}){l&&(e=l(e));let a=t=>n(t)&&(s&&t.stopPropagation(),i&&t.preventDefault(),e.call(r,t));return r.addEventListener(t,a,o),()=>r.removeEventListener(t,a,o)}},A={prevent(e){e.prevent=!0},stop(e){e.stop=!0},once(e){e.once=!0},passive(e){e.passive=!0},capture(e){e.capture=!0},window(e){e.target=window},document(e){e.target=document},toggle(e){e.defer=(t,r)=>n=>r?(r.call?.(e.target,n),r=null):r=t()},throttle(e,t){e.defer=e=>k(e,t?Number(t)||0:108)},debounce(e,t){e.defer=e=>W(e,t?Number(t)||0:108)},outside:e=>t=>{let r=e.target;return!(r.contains(t.target)||!1===t.target.isConnected||r.offsetWidth<1&&r.offsetHeight<1)},self:e=>t=>t.target===e.target,ctrl:(e,...t)=>e=>w.ctrl(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),shift:(e,...t)=>e=>w.shift(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),alt:(e,...t)=>e=>w.alt(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),meta:(e,...t)=>e=>w.meta(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),arrow:e=>w.arrow,enter:e=>w.enter,escape:e=>w.escape,tab:e=>w.tab,space:e=>w.space,backspace:e=>w.backspace,delete:e=>w.delete,digit:e=>w.digit,letter:e=>w.letter,character:e=>w.character},w={ctrl:e=>e.ctrlKey||"Control"===e.key||"Ctrl"===e.key,shift:e=>e.shiftKey||"Shift"===e.key,alt:e=>e.altKey||"Alt"===e.key,meta:e=>e.metaKey||"Meta"===e.key||"Command"===e.key,arrow:e=>e.key.startsWith("Arrow"),enter:e=>"Enter"===e.key,escape:e=>e.key.startsWith("Esc"),tab:e=>"Tab"===e.key,space:e=>" "===e.key||"Space"===e.key||" "===e.key,backspace:e=>"Backspace"===e.key,delete:e=>"Delete"===e.key,digit:e=>/^\d$/.test(e.key),letter:e=>/^[a-zA-Z]$/.test(e.key),character:e=>/^\S$/.test(e.key)},k=(e,t)=>{let r,n,l=s=>{r=!0,setTimeout((()=>{if(r=!1,n)return n=!1,l(s),e(s)}),t)};return t=>r?n=!0:(l(t),e(t))},W=(e,t)=>{let r;return n=>{clearTimeout(r),r=setTimeout((()=>{r=null,e(n)}),t)}},x=(e,t,r)=>{null==r||!1===r?e.removeAttribute(t):e.setAttribute(t,!0===r?"":"number"==typeof r||"string"==typeof r?r:"")},S={};function E(e,t,r){let n=S[t];if(!n){let l=/^[\n\s]*if.*\(.*\)/.test(t)||/\b(let|const)\s/.test(t)&&!r.startsWith(":on")?`(() => {${t}})()`:t;try{n=S[t]=new Function("__scope",`with (__scope) { return ${l.trim()} };`)}catch(n){return N(n,e,t,r)}}return l=>{let s;try{s=n.call(e,l)}catch(n){return N(n,e,t,r)}return s}}function N(e,t,n,l){Object.assign(e,{element:t,expression:n}),console.warn(`∴ ${e.message}\n\n${l}=${n?`"${n}"\n\n`:""}`,t),r((()=>{throw e}),0)}$.globals=c;var j=new WeakMap;function $(e,t){if(!e.children)return;if(j.has(e))return Object.assign(j.get(e),t);const r=p(t||{}),n=[],l=(e,t=e.parentNode)=>{for(let l in d){let s=":"+l;if(e.hasAttribute?.(s)){let i=e.getAttribute(s);if(e.removeAttribute(s),n.push(d[l](e,i,r,l)),j.has(e))return;if(e.parentNode!==t)return!1}}if(e.attributes)for(let t=0;t<e.attributes.length;){let l=e.attributes[t];if(":"!==l.name[0]){t++;continue}e.removeAttribute(l.name);let s=l.value,i=l.name.slice(1).split(":");for(let t of i){let l=g[t]||b;n.push(l(e,s,r,t))}}for(let t,r=0;t=e.children[r];r++)!1===l(t,e)&&r--};l(e);for(let e of n)if(e){let t;y((()=>{"function"==typeof t&&t(),t=e(r)}))}return j.set(e,r),r}var C=$;document.currentScript&&$(document.documentElement);export{C as default};
1
+ var e,t,r=Promise.prototype.then.bind(Promise.resolve()),n=new WeakMap,l=class extends Map{#e=new FinalizationRegistry((e=>super.delete(e)));get size(){return[...this].length}constructor(e=[]){super();for(const[t,r]of e)this.set(t,r)}get(e){return super.get(e)?.deref()}set(e,t){let r=super.get(e);return r&&this.#e.unregister(r),r=(e=>n.get(e)||(e=>{const t=new WeakRef(e);return n.set(e,t),t})(e))(t),this.#e.register(t,e,r),super.set(e,r)}},s=new Set,i=new WeakMap,o=new WeakMap,a=new WeakMap,u=Symbol("parent"),c={Array:Array,Object:Object,Number:Number,String:String,Boolean:Boolean,Date:Date,console:console,window:window,document:document,history:history,location:location},f={has:()=>!0,get(t,r){if("symbol"==typeof r)return t[r];if(!(r in t))return t[u]?.[r];if(r in Object.prototype||Array.isArray(t)&&r in Array.prototype&&"length"!==r)return t[r];let n=t[r];if(e){let n=i.get(t);n||i.set(t,n={}),n[r]?n[r].includes(e)||n[r].push(e):n[r]=[e]}if(n&&n.constructor===Object||Array.isArray(n)){let e=o.get(n);return e||o.set(n,e=new Proxy(n,f)),e}return n},set(e,t,r){if(!(t in e)&&e[u]&&t in e[u])return e[u][t]=r;if(!Array.isArray(e)&&Object.is(e[t],r))return!0;e[t]=r;let n=i.get(e)?.[t];if(n)for(let e of n)s.add(e);return h(),!0},deleteProperty:(e,t)=>(e[t]=void 0,delete e[t],!0)},p=(e,t)=>{if(o.has(e))return o.get(e);if(a.has(e))return e;let r=new Proxy(e,f);o.set(e,r),a.set(r,e);let n=Object.getOwnPropertyDescriptors(e);for(let t in n){let l=n[t];l.get&&l.get&&(l.get=l.get.bind(r),Object.defineProperty(e,t,l))}return e[u]=t?p(t):c,r},y=t=>{const r=()=>{let n=e;e=r,t(),e=n};return r(),r},h=()=>{t||(t=!0,r((()=>{for(let e of s)e.call();s.clear(),t=!1})))},g={},d={};g.if=(e,t)=>{let r=document.createTextNode(""),n=[j(e,t,":if")],l=[e],s=e;for(;(s=e.nextElementSibling)&&s.hasAttribute(":else");)s.removeAttribute(":else"),(t=s.getAttribute(":if"))?(s.removeAttribute(":if"),s.remove(),l.push(s),n.push(j(e,t,":else :if"))):(s.remove(),l.push(s),n.push((()=>1)));return e.replaceWith(s=r),e=>{let t=n.findIndex((t=>t(e)));l[t]!=s&&((s[m]||s).replaceWith(s=l[t]||r),O(s,e))}};var m=Symbol(":each");g.each=(e,t)=>{let r=function(e){let t=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,r=e.match(/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/);if(!r)return;let n=r[2].trim(),l=r[1].replace(/^\s*\(|\)\s*$/g,"").trim(),s=l.match(t);return s?[l.replace(t,"").trim(),s[1].trim(),n]:[l,"",n]}(t);if(!r)return E(new Error,e,t,":each");const n=e[m]=document.createTextNode("");e.replaceWith(n);const s=j(e,r[2],":each"),i=e.getAttribute(":key"),o=i?j(null,i,":each"):null;e.removeAttribute(":key");const a=e.getAttribute(":ref"),u=new l,c=new l;let f=[];return l=>{let i=s(l);i?"number"==typeof i?i=Array.from({length:i},((e,t)=>[t+1,t])):Array.isArray(i)?i=i.map(((e,t)=>[t+1,e])):"object"==typeof i?i=Object.entries(i):E(Error("Bad list value"),e,t,":each"):i=[];let y=[],h=[];for(let[t,n]of i){let s,i,f=o?.({[r[0]]:n,[r[1]]:t});null==f?s=e.cloneNode(!0):(s=c.get(f))||c.set(f,s=e.cloneNode(!0)),y.push(s),null!=f&&(i=u.get(f))?i[r[0]]=n:(i=p({[r[0]]:n,[a||""]:null,[r[1]]:t},l),null!=f&&u.set(f,i)),h.push(i)}!function(e,t,r,n){const l=new Map,s=new Map;let i,o;for(i=0;i<t.length;i++)l.set(t[i],i);for(i=0;i<r.length;i++)s.set(r[i],i);for(i=o=0;i!==t.length||o!==r.length;){var a=t[i],u=r[o];if(null===a)i++;else if(r.length<=o)e.removeChild(t[i]),i++;else if(t.length<=i)e.insertBefore(u,t[i]||n),o++;else if(a===u)i++,o++;else{var c=s.get(a),f=l.get(u);void 0===c?(e.removeChild(t[i]),i++):void 0===f?(e.insertBefore(u,t[i]||n),o++):(e.insertBefore(t[f],t[i]||n),t[f]=null,f>i+1&&i++,o++)}}}(n.parentNode,f,y,n),f=y;for(let e=0;e<y.length;e++)O(y[e],h[e])}},g.with=(e,t,r)=>{const n=j(e,t,":with")(r);O(e,p(n,r))},g.ref=(e,t,r)=>{r[t]=e},d.render=(e,t,r)=>{let n=j(e,t,":render")(r);n||E(new Error("Template not found"),e,t,":render");let l=n.content.cloneNode(!0);e.replaceChildren(l),O(e,r)},d.id=(e,t)=>{let r=j(e,t,":id");return t=>{return n=r(t),e.id=n||0===n?n:"";var n}},d.class=(e,t)=>{let r=j(e,t,":class"),n=e.getAttribute("class");return t=>{let l=r(t),s=[n];l&&("string"==typeof l?s.push(l):Array.isArray(l)?s.push(...l):s.push(...Object.entries(l).map((([e,t])=>t?e:"")))),(s=s.filter(Boolean).join(" "))?e.setAttribute("class",s):e.removeAttribute("class")}},d.style=(e,t)=>{let r=j(e,t,":style"),n=e.getAttribute("style")||"";return n.endsWith(";")||(n+="; "),t=>{let l=r(t);if("string"==typeof l)e.setAttribute("style",n+l);else{e.setAttribute("style",n);for(let t in l)e.style.setProperty(t,l[t])}}},d.text=(e,t)=>{let r=j(e,t,":text");return t=>{let n=r(t);e.textContent=null==n?"":n}},d[""]=(e,t)=>{let r=j(e,t,":");if(r)return t=>{let n=r(t);for(let t in n)x(e,t.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(e=>"-"+e.toLowerCase())),n[t])}},d.value=(e,t)=>{let r,n,l=j(e,t,":value"),s="text"===e.type||""===e.type?t=>e.setAttribute("value",e.value=null==t?"":t):"TEXTAREA"===e.tagName||"text"===e.type||""===e.type?t=>(r=e.selectionStart,n=e.selectionEnd,e.setAttribute("value",e.value=null==t?"":t),r&&e.setSelectionRange(r,n)):"checkbox"===e.type?t=>(e.value=t?"on":"",x(e,"checked",t)):"select-one"===e.type?t=>{for(let t in e.options)t.removeAttribute("selected");e.value=t,e.selectedOptions[0]?.setAttribute("selected","")}:t=>e.value=t;return e=>s(l(e))},d.on=(e,t)=>{let r=j(e,t,":on");return t=>{let n=r(t),l=[];for(let t in n)l.push(v(e,t,n[t]));return()=>{for(let e of l)e()}}};var b=(e,t,r,n)=>{let l=n.startsWith("on")&&n.slice(2),s=j(e,t,":"+n);if(s)return l?t=>{let r=s(t)||(()=>{});return v(e,l,r)}:t=>x(e,n,s(t))},v=(e,t,r)=>{if(!r)return;let n=t.split("..").map((t=>{let r={evt:"",target:e,test:()=>!0};return r.evt=(t.startsWith("on")?t.slice(2):t).replace(/\.(\w+)?-?([-\w]+)?/g,((e,t,n="")=>(r.test=A[t]?.(r,...n.split("-"))||r.test,""))),r}));if(1==n.length)return i(r,n[0]);const l=(t,r=0)=>{let s;return s=i((i=>{r&&s();let o=t.call(e,i);"function"!=typeof o&&(o=()=>{}),r+1<n.length&&l(o,r?r+1:1)}),n[r])};let s=l(r);return()=>s();function i(e,{evt:t,target:r,test:n,defer:l,stop:s,prevent:i,...o}){l&&(e=l(e));let a=t=>n(t)&&(s&&t.stopPropagation(),i&&t.preventDefault(),e.call(r,t));return r.addEventListener(t,a,o),()=>r.removeEventListener(t,a,o)}},A={prevent(e){e.prevent=!0},stop(e){e.stop=!0},once(e){e.once=!0},passive(e){e.passive=!0},capture(e){e.capture=!0},window(e){e.target=window},document(e){e.target=document},toggle(e){e.defer=(t,r)=>n=>r?(r.call?.(e.target,n),r=null):r=t()},throttle(e,t){e.defer=e=>k(e,t?Number(t)||0:108)},debounce(e,t){e.defer=e=>W(e,t?Number(t)||0:108)},outside:e=>t=>{let r=e.target;return!(r.contains(t.target)||!1===t.target.isConnected||r.offsetWidth<1&&r.offsetHeight<1)},self:e=>t=>t.target===e.target,ctrl:(e,...t)=>e=>w.ctrl(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),shift:(e,...t)=>e=>w.shift(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),alt:(e,...t)=>e=>w.alt(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),meta:(e,...t)=>e=>w.meta(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),arrow:e=>w.arrow,enter:e=>w.enter,escape:e=>w.escape,tab:e=>w.tab,space:e=>w.space,backspace:e=>w.backspace,delete:e=>w.delete,digit:e=>w.digit,letter:e=>w.letter,character:e=>w.character},w={ctrl:e=>e.ctrlKey||"Control"===e.key||"Ctrl"===e.key,shift:e=>e.shiftKey||"Shift"===e.key,alt:e=>e.altKey||"Alt"===e.key,meta:e=>e.metaKey||"Meta"===e.key||"Command"===e.key,arrow:e=>e.key.startsWith("Arrow"),enter:e=>"Enter"===e.key,escape:e=>e.key.startsWith("Esc"),tab:e=>"Tab"===e.key,space:e=>" "===e.key||"Space"===e.key||" "===e.key,backspace:e=>"Backspace"===e.key,delete:e=>"Delete"===e.key,digit:e=>/^\d$/.test(e.key),letter:e=>/^[a-zA-Z]$/.test(e.key),character:e=>/^\S$/.test(e.key)},k=(e,t)=>{let r,n,l=s=>{r=!0,setTimeout((()=>{if(r=!1,n)return n=!1,l(s),e(s)}),t)};return t=>r?n=!0:(l(t),e(t))},W=(e,t)=>{let r;return n=>{clearTimeout(r),r=setTimeout((()=>{r=null,e(n)}),t)}},x=(e,t,r)=>{null==r||!1===r?e.removeAttribute(t):e.setAttribute(t,!0===r?"":"number"==typeof r||"string"==typeof r?r:"")},S={};function j(e,t,r){let n=S[t];if(!n){let l=/^[\n\s]*if.*\(.*\)/.test(t)||/\b(let|const)\s/.test(t)&&!r.startsWith(":on")?`(() => {${t}})()`:t;try{n=S[t]=new Function("__scope",`with (__scope) { return ${l.trim()} };`)}catch(n){return E(n,e,t,r)}}return l=>{let s;try{s=n.call(e,l)}catch(n){return E(n,e,t,r)}return s}}function E(e,t,n,l){Object.assign(e,{element:t,expression:n}),console.warn(`∴ ${e.message}\n\n${l}=${n?`"${n}"\n\n`:""}`,t),r((()=>{throw e}),0)}O.globals=c;var N=new WeakMap;function O(e,t){if(!e.children)return;if(N.has(e))return Object.assign(N.get(e),t);const r=p(t||{}),n=[],l=(e,t=e.parentNode)=>{for(let l in g){let s=":"+l;if(e.hasAttribute?.(s)){let i=e.getAttribute(s);if(e.removeAttribute(s),n.push(g[l](e,i,r,l)),N.has(e))return;if(e.parentNode!==t)return!1}}if(e.attributes)for(let t=0;t<e.attributes.length;){let l=e.attributes[t];if(":"!==l.name[0]){t++;continue}e.removeAttribute(l.name);let s=l.value,i=l.name.slice(1).split(":");for(let t of i){let l=d[t]||b;n.push(l(e,s,r,t))}}for(let t,r=0;t=e.children[r];r++)!1===l(t,e)&&r--};l(e);for(let e of n)if(e){let t;y((()=>{"function"==typeof t&&t(),t=e(r)}))}return N.set(e,r),r}var $=O;document.currentScript&&O(document.documentElement);export{$ as default};
package/src/directives.js CHANGED
@@ -45,14 +45,6 @@ primary['if'] = (el, expr) => {
45
45
  }
46
46
  }
47
47
 
48
- // :with must come before :each, but :if has primary importance
49
- primary['with'] = (el, expr, rootState) => {
50
- let evaluate = parseExpr(el, expr, ':with')
51
- const localState = evaluate(rootState)
52
- let state = createState(localState, rootState)
53
- sprae(el, state);
54
- }
55
-
56
48
  const _each = Symbol(':each')
57
49
 
58
50
  // :each must init before :ref, :id or any others, since it defines scope
@@ -82,7 +74,7 @@ primary['each'] = (tpl, expr) => {
82
74
  let list = evaluate(state)
83
75
 
84
76
  if (!list) list = []
85
- else if (typeof list === 'number') list = Array.from({length: list}, (_, i)=>[i, i+1])
77
+ else if (typeof list === 'number') list = Array.from({length: list}, (_, i)=>[i+1, i])
86
78
  else if (Array.isArray(list)) list = list.map((item,i) => [i+1, item])
87
79
  else if (typeof list === 'object') list = Object.entries(list)
88
80
  else exprError(Error('Bad list value'), tpl, expr, ':each', list)
@@ -120,6 +112,15 @@ primary['each'] = (tpl, expr) => {
120
112
  }
121
113
  }
122
114
 
115
+ // `:each` can redefine scope as `:each="a in {myScope}"`,
116
+ // same time per-item scope as `:each="..." :with="{collapsed:true}"` is useful
117
+ primary['with'] = (el, expr, rootState) => {
118
+ let evaluate = parseExpr(el, expr, ':with')
119
+ const localState = evaluate(rootState)
120
+ let state = createState(localState, rootState)
121
+ sprae(el, state);
122
+ }
123
+
123
124
  // ref must be last within primaries, since that must be skipped by :each, but before secondaries
124
125
  primary['ref'] = (el, expr, state) => {
125
126
  // FIXME: wait for complex ref use-case
@@ -172,8 +173,14 @@ secondary['class'] = (el, expr) => {
172
173
  let initClassName = el.getAttribute('class')
173
174
  return (state) => {
174
175
  let v = evaluate(state)
175
- let className = typeof v === 'string' ? v : (Array.isArray(v) ? v : Object.entries(v).map(([k,v])=>v?k:'')).filter(Boolean).join(' ')
176
- el.setAttribute('class',[initClassName, className].filter(Boolean).join(' '));
176
+ let className = [initClassName]
177
+ if (v) {
178
+ if (typeof v === 'string') className.push(v)
179
+ else if (Array.isArray(v)) className.push(...v)
180
+ else className.push(...Object.entries(v).map(([k,v])=>v?k:''))
181
+ }
182
+ if (className = className.filter(Boolean).join(' ')) el.setAttribute('class', className);
183
+ else el.removeAttribute('class')
177
184
  }
178
185
  }
179
186
 
package/src/state.js CHANGED
@@ -50,6 +50,7 @@ const handler = {
50
50
  },
51
51
 
52
52
  set(target, prop, value) {
53
+ // "fake" prototype chain, since regular one doesn't fit
53
54
  if (!(prop in target) && (target[_parent] && prop in target[_parent])) return target[_parent][prop] = value
54
55
 
55
56
  // avoid bumping unchanged values
@@ -84,6 +85,16 @@ export const state = (obj, parent) => {
84
85
  targetProxy.set(obj, proxy)
85
86
  proxyTarget.set(proxy, obj)
86
87
 
88
+ // bind all getters here to proxy
89
+ // FIXME: alternatively we can store getters somewhere
90
+ let descriptors = Object.getOwnPropertyDescriptors(obj)
91
+ for (let name in descriptors) {
92
+ let desc = descriptors[name]
93
+ if (desc.get) {
94
+ if (desc.get) desc.get = desc.get.bind(proxy), Object.defineProperty(obj, name, desc)
95
+ }
96
+ }
97
+
87
98
  // inherit from parent state
88
99
  obj[_parent] = parent ? state(parent) : sandbox
89
100
 
package/test/dom.js CHANGED
@@ -109,6 +109,12 @@ test('class', async () => {
109
109
  // is(el.outerHTML, `<x class="base x"></x><y class="y w"></y><z class="b c"></z>`);
110
110
  })
111
111
 
112
+ test('class: undefined value', async () => {
113
+ let el = h`<x :class="a"></x><y :class="[b]"></y><z :class="{c}"></z>`
114
+ sprae(el, {a:undefined, b:undefined, c:undefined})
115
+ is(el.outerHTML, `<x></x><y></y><z></z>`)
116
+ })
117
+
112
118
  test('class: old svg fun', async () => {
113
119
  // raw html creates svganimatedstring
114
120
  let el = document.createElement('div')
@@ -441,10 +447,10 @@ test('each: loop within condition', async () => {
441
447
 
442
448
  const params = sprae(el, { a: 1 })
443
449
 
444
- is(el.innerHTML, '<x><y>1</y></x>')
450
+ is(el.innerHTML, '<x><y>0</y></x>')
445
451
  params.a = 2
446
452
  await tick()
447
- is(el.innerHTML, '<x><y>-1</y><y>-2</y></x>')
453
+ is(el.innerHTML, '<x><y>0</y><y>-1</y></x>')
448
454
  params.a = 0
449
455
  await tick()
450
456
  is(el.innerHTML, '')
@@ -475,7 +481,7 @@ test('each: next items have own "this", not single one', async () => {
475
481
  let el = h`<div><x :each="x in 3" :data-x="x" :x="log.push(x, this.dataset.x)"></x></div>`
476
482
  let log = []
477
483
  let state = sprae(el, {log})
478
- is(state.log, [1,'1',2,'2',3,'3'])
484
+ is(state.log, [0,'0',1,'1',2,'2'])
479
485
  })
480
486
 
481
487
  test('each: unkeyed', async () => {
@@ -514,7 +520,7 @@ test('each: keyed', async () => {
514
520
  test('each: wrapped source', async () => {
515
521
  let el = h`<div><x :each="i in (x || 2)" :text="i"></x></div>`
516
522
  sprae(el, {x:0})
517
- is(el.innerHTML, `<x>1</x><x>2</x>`)
523
+ is(el.innerHTML, `<x>0</x><x>1</x>`)
518
524
  })
519
525
 
520
526
  test('each: unmounted elements remove listeners', async () => {
@@ -558,6 +564,12 @@ test('each: key-based caching is in-sync with direct elements', () => {
558
564
  is(el.outerHTML, el2.outerHTML)
559
565
  })
560
566
 
567
+ test('each: with :with', () => {
568
+ let el = h`<ul><li :each="i in 3" :with="{x:i}" :text="x"></li></ul>`
569
+ sprae(el)
570
+ is(el.outerHTML, `<ul><li>0</li><li>1</li><li>2</li></ul>`)
571
+ })
572
+
561
573
  test('on: base', async () => {
562
574
  let el = h`<div :on="{click(e){log.push('click')}, x}"></div>`
563
575
  let log = []
@@ -1015,18 +1027,25 @@ test(':: scope directives must come first', async () => {
1015
1027
  is(a.outerHTML, `<x>1</x>`)
1016
1028
  })
1017
1029
 
1018
- test.skip('getters', async () => {
1019
- let x = h`<x>
1020
- <h2 :if="doubledCount > 10">YAY!</h2>
1021
- <button :text="count" :on="{click:increment}"/>
1022
- <button :text="doubledCount" :on="{click:increment}"/>
1023
- </x>`
1024
- document.body.appendChild(x)
1030
+ test.todo('immediate scope', async () => {
1031
+ let el = h`<x :with="{arr:[], inc(){ arr.push(1) }}" :onx="e=>inc()" :text="arr[0]"></x>`
1032
+ sprae(el)
1033
+ is(el.outerHTML, `<x></x>`)
1034
+ el.dispatchEvent(new CustomEvent('x'))
1035
+ await tick()
1036
+ is(el.outerHTML, `<x>1</x>`)
1037
+ })
1038
+
1039
+ test('getters', async () => {
1040
+ let x = h`<h2 :text="doubledCount >= 1 ? 1 : 0"></h2>`
1025
1041
  let state = sprae(x, {
1026
1042
  count:0,
1027
- get doubledCount(){ console.log(this); return this.count * 2},
1028
- increment(){ this.count++ }
1043
+ get doubledCount(){ return this.count * 2 }
1029
1044
  })
1045
+ is(x.outerHTML, `<h2>0</h2>`)
1046
+ state.count++
1047
+ await tick()
1048
+ is(x.outerHTML, `<h2>1</h2>`)
1030
1049
  })
1031
1050
 
1032
1051
  test('sandbox', async () => {
package/todo.md CHANGED
@@ -48,4 +48,7 @@
48
48
  * [x] parallel chains
49
49
  * [x] Sandbox
50
50
  * [x] Autorun
51
- * [x] There's some bug with prostogreen not triggering effect. (caused by special array.length case)
51
+ * [x] There's some bug with prostogreen not triggering effect. (caused by special array.length case)
52
+ * [x] Getters must become evaluable
53
+ * [ ] `:with="{likes:[], like(){ /* likes should not be undefined here */ }}"`
54
+ * [ ] `<li :each="item in items" :with="{collapsed:true}"><button :onclick='e=>collapsed=false'></li>`