sprae 5.3.0 → 6.0.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.
@@ -14,39 +14,38 @@
14
14
  <section class="todoapp">
15
15
  <header class="header">
16
16
  <h1>todos</h1>
17
- <input class="new-todo" placeholder="What needs to be done?" autofocus :onkeypress="e => {
18
- if (e.key !== 'Enter') return
19
- save(todos = [...todos, { text: e.target.value, done: false}])
20
- e.target.value = ''
21
- }">
17
+ <input class="new-todo" placeholder="What needs to be done?" autofocus @keypress.enter="
18
+ save(todos = [...todos, { text: this.value, done: false}])
19
+ this.value = ''
20
+ ">
22
21
  </header>
23
22
  <section class="main">
24
- <input id="toggle-all" class="toggle-all" type="checkbox" :onclick="e => {
23
+ <input id="toggle-all" class="toggle-all" type="checkbox" @click="
25
24
  const all = todos.every(item => item.done)
26
25
  save(todos = todos.map(item => (item.done = !all, item)))
27
- }">
26
+ ">
28
27
  <label for="toggle-all">Mark all as complete</label>
29
28
  <ul class="todo-list">
30
29
  <li :ref="li" :each="item in todos"
31
30
  :class="{completed: item.done}"
32
31
  :hidden="hash === '#/active' ? item.done : hash === '#/completed' ? !item.done : false"
33
- :ondblclick="e => {
32
+ @dblclick="
34
33
  li.querySelector('.edit').focus()
35
34
  li.classList.add('editing')
36
- }">
35
+ ">
37
36
  <div class="view">
38
37
  <input class="toggle" type="checkbox"
39
38
  :checked="item.done"
40
- :onchange="e => (item.done = !item.done, save(todos))"
39
+ @change="item.done = !item.done, save(todos)"
41
40
  />
42
41
  <label :text="item.text"></label>
43
- <button class="destroy" :onclick="e => (save(todos = todos.filter(i => i.text !== item.text)))"></button>
42
+ <button class="destroy" @click="save(todos = todos.filter(i => i.text !== item.text))"></button>
44
43
  </div>
45
44
  <input class=edit
46
45
  :value="item.text"
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 }"
46
+ @input="item.text = this.value; save(todos)"
47
+ @blur="li.classList.remove('editing');"
48
+ @keypress.enter="this.blur()"
50
49
  />
51
50
  </li>
52
51
  </ul>
@@ -62,7 +61,7 @@
62
61
  </ul>
63
62
  <button class="clear-completed"
64
63
  :hidden="todos.every(item => !item.done)"
65
- :onclick="e => save(todos = todos.filter(item => !item.done ? true : false))">
64
+ @click="save(todos = todos.filter(item => !item.done ? true : false))">
66
65
  Clear completed
67
66
  </button>
68
67
  </footer>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sprae",
3
3
  "description": "DOM microhydration.",
4
- "version": "5.3.0",
4
+ "version": "6.0.0",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
7
7
  "type": "module",
@@ -12,7 +12,7 @@
12
12
  "@preact/signals": "^1.1.3",
13
13
  "es-module-shims": "^1.6.2",
14
14
  "esbuild": "^0.15.14",
15
- "hyperf": "^1.5.0",
15
+ "hyperf": "^1.6.2",
16
16
  "jsdom": "^21.1.0",
17
17
  "signal-struct": "^1.10.0",
18
18
  "terser": "^5.15.1",
package/r&d.md CHANGED
@@ -381,12 +381,57 @@
381
381
  + less problem detecting `const/let` in code
382
382
  - conflicts with regular attrs logic: the code is immediately invoked and can assign a function.
383
383
  - `:oninput="e => (handleCaret(e), updateTimecodes())"`
384
+ ~ `:oninput="handleCaret(event), updateTimecodes()"`
384
385
  - `:onbeforeinput="handleBeforeInput"`
386
+ ~ `:onbeforeinput="handleBeforeInput(event)"`
385
387
  - `:ondragenter..ondragleave:ondragenter..ondrop="e=>(this.classList.add('w-dragover'),e=>this.classList.remove('w-dragover'))"`
388
+ ~ `:ondragenter..ondragleave:ondragenter..ondrop="this.classList.add('w-dragover'), e=>this.classList.remove('w-dragover')"`
386
389
  - `:ondrop="e=>console.log(e.dataTransfer.types)||e.preventDefault()"`
390
+ ~ `:ondrop.prevent="console.log(event.dataTransfer.types)"`
387
391
  - `:onfocus="e => (e.relatedTarget ? e.relatedTarget.focus() : e.target.blur())"`
392
+ ~ `:onfocus="event.relatedTarget ? event.relatedTarget.focus() : event.target.blur()"`
388
393
  - `:onpopstate.window="e => goto(e.state)"`
394
+ ~ `:onpopstate.window="goto(event.state)"`
389
395
  - `:onclick.toggle="play"`
396
+ ~ `:onclick.toggle="play()"`
397
+
398
+ ## [ ] Should we introduce `@click` for short-notation events?
399
+ + gives shorter code for majority of cases
400
+ + can be non-conflicting
401
+ + compatible with all frameworks (vue, alpine, lucia, lit)
402
+ + gives better meaning to modifiers - moves them outside of `:` attribute
403
+ - multiple events `@input@change="code"` is not nice
404
+ ~ that's fine and even meaningful
405
+ - chain of events `@focus..@blur="return (e)=>{}"` creates confusing `return` outside of body, as well as inconsistent chain pattern
406
+ ? remove that pattern
407
+ + it's still unsatisfactory: `@mousedown.document..@mouseup.document="e=> (isMouseDown = true, e=> isMouseDown = false)"` works, but what if we want to add `@touchstart..@touchend`, or
408
+ . `@click..@click="play" @keydown.alt-space..@keydown.alt-space="play"`, so in other words we need cross-reaction `@click_or_altspace..@click_or_altspace`, not just one single chain.
409
+ + actually here `@click@keydown.space..@click@keydown.space` is possible, unlike `:ona..onb` case, the question is how to provide sequence in attribute
410
+ + the thing is that local state, introduced by initiator events, is not useful by itself, detached from scope.
411
+ . the state is better reflected in data scope, rather than by initiator event.
412
+ ? what about temporaries like `@a..@b="id=setTimeout(),()=>clearTimeout(id)"` or `@a.toggle="stop=play(),stop"`
413
+ . use `:with={id:null} @a="id=setTimeout()" @b="clearTimeout(id)"`
414
+ - introduces illicit `event` variable ~ although compatible with standard, still obscure
415
+ - `@` prefix is unchangeable ~ can be removed, not set, but still on the verge.
416
+ - `@click.toggle="code"` has same problem as `@a..@b` - how can we make code separation in attribute?
417
+ + remove toggle
418
+ + overall less code
419
+
420
+ ## [x] Multiple chain events resolution -> redirect to main event for now
421
+ * Consider
422
+ ```
423
+ :onclick..onclick="play"
424
+ :onkeydown.document.alt-space..onkeydown.document.alt-space="play"
425
+ ```
426
+ * When started by click and ended by alt-space, it doesn't clear the onclick
427
+ * We actually want here `:onclick:onkeydown.document.alt-space..onclick:onkeydown.document.alt-space`.
428
+ - this makes inconsistency of `..onclick` - colon is missing
429
+ - also it makes precedence of `:` and `..` unclear - what comes before what after.
430
+
431
+ ? Can we use `:onclick.toggle="play"`?
432
+ - it doesn't help with switch-over
433
+ ? Some 'or' character `:onclick--onkeydown`
434
+ ? We can redirect to main event, that's it for now
390
435
 
391
436
  ## [x] Should getters convert to computed? -> yes, that's relatively cheap and useful
392
437
 
@@ -513,22 +558,6 @@
513
558
  5. `:x="this.x=value"`
514
559
  + yepyepyep
515
560
 
516
- ## [x] Multiple chain events resolution -> redirect to main event for now
517
- * Consider
518
- ```
519
- :onclick..onclick="play"
520
- :onkeydown.document.alt-space..onkeydown.document.alt-space="play"
521
- ```
522
- * When started by click and ended by alt-space, it doesn't clear the onclick
523
- * We actually want here `:onclick:onkeydown.document.alt-space..onclick:onkeydown.document.alt-space`.
524
- - this makes inconsistency of `..onclick` - colon is missing
525
- - also it makes precedence of `:` and `..` unclear - what comes before what after.
526
-
527
- ? Can we use `:onclick.toggle="play"`?
528
- - it doesn't help with switch-over
529
- ? Some 'or' character `:onclick--onkeydown`
530
- ? We can redirect to main event, that's it for now
531
-
532
561
  ## [x] Insert content by reusing the node/template -> use `:render="ref" :with="data"`
533
562
 
534
563
  * Makes easy use of repeatable fragments, instead of web-components
package/readme.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > DOM microhydration with `:` attributes.
4
4
 
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.
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--alternatives). It enables simple markup logic without external scripts. Perfect for small websites, prototypes or UI logic.
6
6
 
7
7
  ## Use
8
8
 
@@ -116,63 +116,6 @@ Set value of an input, textarea or select. Takes handle of `checked` and `select
116
116
  </select>
117
117
  ```
118
118
 
119
- #### `:<prop>="value?"`
120
-
121
- Set any attribute value or run an effect.
122
-
123
- ```html
124
- <!-- Single property -->
125
- <label :for="name" :text="name" />
126
-
127
- <!-- Multiple properties -->
128
- <input :id:name="name" />
129
-
130
- <!-- Effect (triggers any time bar changes) -->
131
- <div :fx="void bar()" ></div>
132
- ```
133
-
134
- #### `:="props?"`
135
-
136
- Spread multiple attibures.
137
-
138
- ```html
139
- <input :="{ id: name, name, type:'text', value }" />
140
- ```
141
-
142
- #### `:on<event>="handler"`, `:on<in>..on<out>="handler"`
143
-
144
- Add event listener or events chain.
145
-
146
- ```html
147
- <!-- Single event -->
148
- <input type="checkbox" :onchange="e => isChecked=e.target.value">
149
-
150
- <!-- Multiple events -->
151
- <input :value="text" :oninput:onchange="e => text=e.target.value">
152
-
153
- <!-- Sequence of events -->
154
- <button :onfocus..onblur="e => {
155
- // onfocus
156
- return e => {
157
- // onblur
158
- }
159
- }">
160
-
161
- <!-- Event modifiers -->
162
- <button :onclick.throttle-500="handler">Not too often</button>
163
- ```
164
-
165
- ##### Event modifiers
166
-
167
- * `.once`, `.passive`, `.capture` – listener [options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options).
168
- * `.prevent`, `.stop` – prevent default or stop propagation.
169
- * `.window`, `.document`, `.outside`, `.self` – specify event target.
170
- * `.throttle-<ms>`, `.debounce-<ms>` – defer function call with one of the methods.
171
- * `.toggle` – run function and its result in turn.
172
- * `.ctrl`, `.shift`, `.alt`, `.meta`, `.arrow`, `.enter`, `.escape`, `.tab`, `.space`, `.backspace`, `.delete`, `.digit`, `.letter`, `.character` – filter by [`event.key`](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values).
173
- * `.ctrl-<key>, .alt-<key>, .meta-<key>, .shift-<key>` – key combinations, eg. `.ctrl-alt-delete` or `.meta-x`.
174
- * `.*` – any other modifier has no effect, but allows binding multiple handlers to same event (like jQuery event classes).
175
-
176
119
  #### `:with="data"`
177
120
 
178
121
  Define or extend data scope for a subtree.
@@ -197,8 +140,8 @@ Include template as element content.
197
140
  ```html
198
141
  <template :ref="foo"><span :text="foo"></span></template>
199
142
 
200
- <div :render="foo" :with="{foo:'bar'}">unknown</div>
201
- <div :render="foo" :with="{foo:'baz'}">unknown</div>
143
+ <div :render="foo" :with="{foo:'bar'}">...inserted here...</div>
144
+ <div :render="foo" :with="{foo:'baz'}">...inserted here...</div>
202
145
  ```
203
146
 
204
147
  #### `:ref="id"`
@@ -213,18 +156,73 @@ Expose element to current data scope with the `id`:
213
156
  <!-- iterable items -->
214
157
  <ul>
215
158
  <li :each="item in items" :ref="item">
216
- <input :onfocus..onblur="e => (item.classList.add('editing'), e => item.classList.remove('editing'))"/>
159
+ <input @focus="item.classList.add('editing')" @blur="item.classList.remove('editing')"/>
217
160
  </li>
218
161
  </ul>
219
162
  ```
220
163
 
164
+ #### `:="props?"`
165
+
166
+ Spread multiple attibures.
167
+
168
+ ```html
169
+ <input :="{ id: name, name, type:'text', value }" />
170
+ ```
171
+
172
+ #### `:<prop>="value?"`
173
+
174
+ Set any attribute value or run an effect.
175
+
176
+ ```html
177
+ <!-- Single property -->
178
+ <label :for="name" :text="name" />
179
+
180
+ <!-- Multiple properties -->
181
+ <input :id:name="name" />
182
+
183
+ <!-- Effect - returns undefined, triggers any time bar changes -->
184
+ <div :fx="void bar()" ></div>
185
+
186
+ <!-- Raw event listener (see events) -->
187
+ <div :onclick="e=>e.preventDefault()"></div>
188
+ ```
189
+
190
+
191
+ ## Events
192
+
193
+ #### `@<event>="handler"`, `@<foo>@<bar>.<baz>="handler"`
194
+
195
+ Attach event listener or multiple listeners with possible modifiers, `event` variable holds current event.
196
+
197
+ ```html
198
+ <!-- Single event -->
199
+ <input type="checkbox" @change="isChecked = event.target.value">
200
+
201
+ <!-- Multiple events -->
202
+ <input :value="text" @input@change="text = event.target.value">
203
+
204
+ <!-- Event modifiers -->
205
+ <button @click.throttle-500="handler(event)">Not too often</button>
206
+ ```
207
+
208
+ ##### Event modifiers
209
+
210
+ * `.once`, `.passive`, `.capture` – listener [options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options).
211
+ * `.prevent`, `.stop` – prevent default or stop propagation.
212
+ * `.window`, `.document`, `.outside`, `.self` – specify event target.
213
+ * `.throttle-<ms>`, `.debounce-<ms>` – defer function call with one of the methods.
214
+ * `.ctrl`, `.shift`, `.alt`, `.meta`, `.arrow`, `.enter`, `.escape`, `.tab`, `.space`, `.backspace`, `.delete`, `.digit`, `.letter`, `.character` – filter by [`event.key`](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values).
215
+ * `.ctrl-<key>, .alt-<key>, .meta-<key>, .shift-<key>` – key combinations, eg. `.ctrl-alt-delete` or `.meta-x`.
216
+ * `.*` – any other modifier has no effect, but allows binding multiple handlers to same event (like jQuery event classes).
217
+
218
+
221
219
  ## Sandbox
222
220
 
223
221
  Expressions are sandboxed, ie. have no access to global or window (since sprae can be run in server environment).
224
222
 
225
223
  ```html
226
- <div :x="window.x"></div>
227
- <!-- window is undefined -->
224
+ <div :x="global.x"></div>
225
+ <!-- global is undefined -->
228
226
  ```
229
227
 
230
228
  Default sandbox provides: _window_, _document_, _console_, _history_, _location_, _Array_, _Object_, _Number_, _String_, _Boolean_, _Date_, _Set_, _Map_.<br/>
@@ -247,8 +245,8 @@ To avoid _flash of unstyled content_, you can hide sprae attribute or add a cust
247
245
 
248
246
  ## Justification & alternatives
249
247
 
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.
248
+ * [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. Also it's just nice to keep `{{}}` generic, regardless of markup, and attributes as part of markup.
249
+ * [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) escape native HTML quirks, but the syntax (`:attr`, `v-*`,`x-*`, `l-*` `@evt`, `{{}}`) and 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, invent own tooling or complex reactivity.
252
250
  * 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).
253
251
 
254
252
  _Sprae_ takes idea of _templize directives_/_alpine_/_vue_ attrs and builds upon <del>[_@preact/signals_](https://ghub.io/@preact/signals)</del> simple reactive state.
@@ -260,7 +258,6 @@ _Sprae_ takes idea of _templize directives_/_alpine_/_vue_ attrs and builds upon
260
258
  * It reserves minimal syntax space as `:` convention (keeping tree neatly decorated, not scattered).
261
259
  * Expressions are naturally reactive and incur minimal updates.
262
260
  * Elements / data API is open and enable easy interop.
263
- * It's just nice separation: attributes belong to client and template fields to server.
264
261
 
265
262
  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.
266
263
 
package/sprae.auto.js CHANGED
@@ -376,19 +376,6 @@
376
376
  } : (value) => el.value = value;
377
377
  return (state2) => update(evaluate(state2));
378
378
  };
379
- secondary["on"] = (el, expr) => {
380
- let evaluate = parseExpr(el, expr, ":on");
381
- return (state2) => {
382
- let listeners = evaluate(state2);
383
- let offs = [];
384
- for (let evt in listeners)
385
- offs.push(on(el, evt, listeners[evt]));
386
- return () => {
387
- for (let off of offs)
388
- off();
389
- };
390
- };
391
- };
392
379
  var directives_default = (el, expr, state2, name) => {
393
380
  let evt = name.startsWith("on") && name.slice(2);
394
381
  let evaluate = parseExpr(el, expr, ":" + name);
@@ -402,43 +389,20 @@
402
389
  };
403
390
  return (state3) => attr(el, name, evaluate(state3));
404
391
  };
405
- var on = (target, evt, origFn) => {
406
- if (!origFn)
392
+ var on = (el, e, fn) => {
393
+ if (!fn)
407
394
  return;
408
- let ctxs = evt.split("..").map((e) => {
409
- let ctx = { evt: "", target, test: () => true };
410
- ctx.evt = (e.startsWith("on") ? e.slice(2) : e).replace(
411
- /\.(\w+)?-?([-\w]+)?/g,
412
- (match, mod, param = "") => (ctx.test = mods[mod]?.(ctx, ...param.split("-")) || ctx.test, "")
413
- );
414
- return ctx;
415
- });
416
- if (ctxs.length == 1)
417
- return addListenerWithMods(origFn, ctxs[0]);
418
- const onFn = (fn, cur = 0) => {
419
- let off;
420
- let curListener = (e) => {
421
- if (cur)
422
- off();
423
- let nextFn = fn.call(target, e);
424
- if (typeof nextFn !== "function")
425
- nextFn = () => {
426
- };
427
- if (cur + 1 < ctxs.length)
428
- onFn(nextFn, !cur ? 1 : cur + 1);
429
- };
430
- return off = addListenerWithMods(curListener, ctxs[cur]);
431
- };
432
- let rootOff = onFn(origFn);
433
- return () => rootOff();
434
- function addListenerWithMods(fn, { evt: evt2, target: target2, test, defer, stop, prevent, ...opts }) {
435
- if (defer)
436
- fn = defer(fn);
437
- let cb = (e) => test(e) && (stop && e.stopPropagation(), prevent && e.preventDefault(), fn.call(target2, e));
438
- target2.addEventListener(evt2, cb, opts);
439
- return () => target2.removeEventListener(evt2, cb, opts);
440
- }
441
- ;
395
+ const ctx = { evt: "", target: el, test: () => true };
396
+ ctx.evt = (e.startsWith("on") ? e.slice(2) : e).replace(
397
+ /\.(\w+)?-?([-\w]+)?/g,
398
+ (match, mod, param = "") => (ctx.test = mods[mod]?.(ctx, ...param.split("-")) || ctx.test, "")
399
+ );
400
+ const { evt, target, test, defer, stop, prevent, ...opts } = ctx;
401
+ if (defer)
402
+ fn = defer(fn);
403
+ const cb = (e2) => test(e2) && (stop && e2.stopPropagation(), prevent && e2.preventDefault(), fn.call(target, e2));
404
+ target.addEventListener(evt, cb, opts);
405
+ return () => target.removeEventListener(evt, cb, opts);
442
406
  };
443
407
  var mods = {
444
408
  prevent(ctx) {
@@ -462,9 +426,6 @@
462
426
  document(ctx) {
463
427
  ctx.target = document;
464
428
  },
465
- toggle(ctx) {
466
- ctx.defer = (fn, out) => (e) => out ? (out.call?.(ctx.target, e), out = null) : out = fn();
467
- },
468
429
  throttle(ctx, limit) {
469
430
  ctx.defer = (fn) => throttle(fn, limit ? Number(limit) || 0 : 108);
470
431
  },
@@ -606,18 +567,18 @@ ${directive}=${expression ? `"${expression}"
606
567
  }
607
568
  if (el.attributes) {
608
569
  for (let i = 0; i < el.attributes.length; ) {
609
- let attr2 = el.attributes[i];
610
- if (attr2.name[0] !== ":") {
570
+ let attr2 = el.attributes[i], prefix = attr2.name[0];
571
+ if (prefix === ":" || prefix === "@") {
572
+ el.removeAttribute(attr2.name);
573
+ let expr = prefix === "@" ? `event=>{${attr2.value}}` : attr2.value, names = attr2.name.slice(1).split(prefix);
574
+ for (let name of names) {
575
+ if (prefix === "@")
576
+ name = `on` + name;
577
+ let dir = secondary[name] || directives_default;
578
+ updates.push(dir(el, expr, state2, name));
579
+ }
580
+ } else
611
581
  i++;
612
- continue;
613
- }
614
- el.removeAttribute(attr2.name);
615
- let expr = attr2.value;
616
- let attrNames = attr2.name.slice(1).split(":");
617
- for (let attrName of attrNames) {
618
- let dir = secondary[attrName] || directives_default;
619
- updates.push(dir(el, expr, state2, attrName));
620
- }
621
582
  }
622
583
  }
623
584
  for (let i = 0, child; child = el.children[i]; i++) {
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);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)})();
1
+ (()=>{var e,t,r=Promise.prototype.then.bind(Promise.resolve()),n=new WeakMap,s=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)}},l=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)l.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 s=n[t];s.get&&s.get&&(s.get=s.get.bind(r),Object.defineProperty(e,t,s))}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 l)e.call();l.clear(),t=!1})))},d={},g={};d.if=(e,t)=>{let r=document.createTextNode(""),n=[j(e,t,":if")],s=[e],l=e;for(;(l=e.nextElementSibling)&&l.hasAttribute(":else");)l.removeAttribute(":else"),(t=l.getAttribute(":if"))?(l.removeAttribute(":if"),l.remove(),s.push(l),n.push(j(e,t,":else :if"))):(l.remove(),s.push(l),n.push((()=>1)));return e.replaceWith(l=r),e=>{let t=n.findIndex((t=>t(e)));s[t]!=l&&((l[b]||l).replaceWith(l=s[t]||r),O(l,e))}};var b=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(),s=r[1].replace(/^\s*\(|\)\s*$/g,"").trim(),l=s.match(t);return l?[s.replace(t,"").trim(),l[1].trim(),n]:[s,"",n]}(t);if(!r)return E(new Error,e,t,":each");const n=e[b]=document.createTextNode("");e.replaceWith(n);const l=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 s,c=new s;let f=[];return s=>{let i=l(s);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 l,i,f=o?.({[r[0]]:n,[r[1]]:t});null==f?l=e.cloneNode(!0):(l=c.get(f))||c.set(f,l=e.cloneNode(!0)),y.push(l),null!=f&&(i=u.get(f))?i[r[0]]=n:(i=p({[r[0]]:n,[a||""]:null,[r[1]]:t},s),null!=f&&u.set(f,i)),h.push(i)}!function(e,t,r,n){const s=new Map,l=new Map;let i,o;for(i=0;i<t.length;i++)s.set(t[i],i);for(i=0;i<r.length;i++)l.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=l.get(a),f=s.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])}},d.with=(e,t,r)=>{const n=j(e,t,":with")(r);O(e,p(n,r))},d.ref=(e,t,r)=>{r[t]=e},g.render=(e,t,r)=>{let n=j(e,t,":render")(r);n||E(new Error("Template not found"),e,t,":render");let s=n.content.cloneNode(!0);e.replaceChildren(s),O(e,r)},g.id=(e,t)=>{let r=j(e,t,":id");return t=>{return n=r(t),e.id=n||0===n?n:"";var n}},g.class=(e,t)=>{let r=j(e,t,":class"),n=e.getAttribute("class");return t=>{let s=r(t),l=[n];s&&("string"==typeof s?l.push(s):Array.isArray(s)?l.push(...s):l.push(...Object.entries(s).map((([e,t])=>t?e:"")))),(l=l.filter(Boolean).join(" "))?e.setAttribute("class",l):e.removeAttribute("class")}},g.style=(e,t)=>{let r=j(e,t,":style"),n=e.getAttribute("style")||"";return n.endsWith(";")||(n+="; "),t=>{let s=r(t);if("string"==typeof s)e.setAttribute("style",n+s);else{e.setAttribute("style",n);for(let t in s)e.style.setProperty(t,s[t])}}},g.text=(e,t)=>{let r=j(e,t,":text");return t=>{let n=r(t);e.textContent=null==n?"":n}},g[""]=(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])}},g.value=(e,t)=>{let r,n,s=j(e,t,":value"),l="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=>l(s(e))};var m=(e,t,r,n)=>{let s=n.startsWith("on")&&n.slice(2),l=j(e,t,":"+n);if(l)return s?t=>{let r=l(t)||(()=>{});return v(e,s,r)}:t=>x(e,n,l(t))},v=(e,t,r)=>{if(!r)return;const n={evt:"",target:e,test:()=>!0};n.evt=(t.startsWith("on")?t.slice(2):t).replace(/\.(\w+)?-?([-\w]+)?/g,((e,t,r="")=>(n.test=A[t]?.(n,...r.split("-"))||n.test,"")));const{evt:s,target:l,test:i,defer:o,stop:a,prevent:u,...c}=n;o&&(r=o(r));const f=e=>i(e)&&(a&&e.stopPropagation(),u&&e.preventDefault(),r.call(l,e));return l.addEventListener(s,f,c),()=>l.removeEventListener(s,f,c)},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},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,s=l=>{r=!0,setTimeout((()=>{if(r=!1,n)return n=!1,s(l),e(l)}),t)};return t=>r?n=!0:(s(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 s=/^[\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 ${s.trim()} };`)}catch(n){return E(n,e,t,r)}}return s=>{let l;try{l=n.call(e,s)}catch(n){return E(n,e,t,r)}return l}}function E(e,t,n,s){Object.assign(e,{element:t,expression:n}),console.warn(`∴ ${e.message}\n\n${s}=${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=[],s=(e,t=e.parentNode)=>{for(let s in d){let l=":"+s;if(e.hasAttribute?.(l)){let i=e.getAttribute(l);if(e.removeAttribute(l),n.push(d[s](e,i,r,s)),N.has(e))return;if(e.parentNode!==t)return!1}}if(e.attributes)for(let t=0;t<e.attributes.length;){let s=e.attributes[t],l=s.name[0];if(":"===l||"@"===l){e.removeAttribute(s.name);let t="@"===l?`event=>{${s.value}}`:s.value,i=s.name.slice(1).split(l);for(let s of i){"@"===l&&(s="on"+s);let i=g[s]||m;n.push(i(e,t,r,s))}}else t++}for(let t,r=0;t=e.children[r];r++)!1===s(t,e)&&r--};s(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
@@ -375,19 +375,6 @@ secondary["value"] = (el, expr) => {
375
375
  } : (value) => el.value = value;
376
376
  return (state2) => update(evaluate(state2));
377
377
  };
378
- secondary["on"] = (el, expr) => {
379
- let evaluate = parseExpr(el, expr, ":on");
380
- return (state2) => {
381
- let listeners = evaluate(state2);
382
- let offs = [];
383
- for (let evt in listeners)
384
- offs.push(on(el, evt, listeners[evt]));
385
- return () => {
386
- for (let off of offs)
387
- off();
388
- };
389
- };
390
- };
391
378
  var directives_default = (el, expr, state2, name) => {
392
379
  let evt = name.startsWith("on") && name.slice(2);
393
380
  let evaluate = parseExpr(el, expr, ":" + name);
@@ -401,43 +388,20 @@ var directives_default = (el, expr, state2, name) => {
401
388
  };
402
389
  return (state3) => attr(el, name, evaluate(state3));
403
390
  };
404
- var on = (target, evt, origFn) => {
405
- if (!origFn)
391
+ var on = (el, e, fn) => {
392
+ if (!fn)
406
393
  return;
407
- let ctxs = evt.split("..").map((e) => {
408
- let ctx = { evt: "", target, test: () => true };
409
- ctx.evt = (e.startsWith("on") ? e.slice(2) : e).replace(
410
- /\.(\w+)?-?([-\w]+)?/g,
411
- (match, mod, param = "") => (ctx.test = mods[mod]?.(ctx, ...param.split("-")) || ctx.test, "")
412
- );
413
- return ctx;
414
- });
415
- if (ctxs.length == 1)
416
- return addListenerWithMods(origFn, ctxs[0]);
417
- const onFn = (fn, cur = 0) => {
418
- let off;
419
- let curListener = (e) => {
420
- if (cur)
421
- off();
422
- let nextFn = fn.call(target, e);
423
- if (typeof nextFn !== "function")
424
- nextFn = () => {
425
- };
426
- if (cur + 1 < ctxs.length)
427
- onFn(nextFn, !cur ? 1 : cur + 1);
428
- };
429
- return off = addListenerWithMods(curListener, ctxs[cur]);
430
- };
431
- let rootOff = onFn(origFn);
432
- return () => rootOff();
433
- function addListenerWithMods(fn, { evt: evt2, target: target2, test, defer, stop, prevent, ...opts }) {
434
- if (defer)
435
- fn = defer(fn);
436
- let cb = (e) => test(e) && (stop && e.stopPropagation(), prevent && e.preventDefault(), fn.call(target2, e));
437
- target2.addEventListener(evt2, cb, opts);
438
- return () => target2.removeEventListener(evt2, cb, opts);
439
- }
440
- ;
394
+ const ctx = { evt: "", target: el, test: () => true };
395
+ ctx.evt = (e.startsWith("on") ? e.slice(2) : e).replace(
396
+ /\.(\w+)?-?([-\w]+)?/g,
397
+ (match, mod, param = "") => (ctx.test = mods[mod]?.(ctx, ...param.split("-")) || ctx.test, "")
398
+ );
399
+ const { evt, target, test, defer, stop, prevent, ...opts } = ctx;
400
+ if (defer)
401
+ fn = defer(fn);
402
+ const cb = (e2) => test(e2) && (stop && e2.stopPropagation(), prevent && e2.preventDefault(), fn.call(target, e2));
403
+ target.addEventListener(evt, cb, opts);
404
+ return () => target.removeEventListener(evt, cb, opts);
441
405
  };
442
406
  var mods = {
443
407
  prevent(ctx) {
@@ -461,9 +425,6 @@ var mods = {
461
425
  document(ctx) {
462
426
  ctx.target = document;
463
427
  },
464
- toggle(ctx) {
465
- ctx.defer = (fn, out) => (e) => out ? (out.call?.(ctx.target, e), out = null) : out = fn();
466
- },
467
428
  throttle(ctx, limit) {
468
429
  ctx.defer = (fn) => throttle(fn, limit ? Number(limit) || 0 : 108);
469
430
  },
@@ -605,18 +566,18 @@ function sprae(container, values) {
605
566
  }
606
567
  if (el.attributes) {
607
568
  for (let i = 0; i < el.attributes.length; ) {
608
- let attr2 = el.attributes[i];
609
- if (attr2.name[0] !== ":") {
569
+ let attr2 = el.attributes[i], prefix = attr2.name[0];
570
+ if (prefix === ":" || prefix === "@") {
571
+ el.removeAttribute(attr2.name);
572
+ let expr = prefix === "@" ? `event=>{${attr2.value}}` : attr2.value, names = attr2.name.slice(1).split(prefix);
573
+ for (let name of names) {
574
+ if (prefix === "@")
575
+ name = `on` + name;
576
+ let dir = secondary[name] || directives_default;
577
+ updates.push(dir(el, expr, state2, name));
578
+ }
579
+ } else
610
580
  i++;
611
- continue;
612
- }
613
- el.removeAttribute(attr2.name);
614
- let expr = attr2.value;
615
- let attrNames = attr2.name.slice(1).split(":");
616
- for (let attrName of attrNames) {
617
- let dir = secondary[attrName] || directives_default;
618
- updates.push(dir(el, expr, state2, attrName));
619
- }
620
581
  }
621
582
  }
622
583
  for (let i = 0, child; child = el.children[i]; i++) {