sprae 5.2.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.
package/sprae.js CHANGED
@@ -222,12 +222,6 @@ primary["if"] = (el, expr) => {
222
222
  }
223
223
  };
224
224
  };
225
- primary["with"] = (el, expr, rootState) => {
226
- let evaluate = parseExpr(el, expr, ":with");
227
- const localState = evaluate(rootState);
228
- let state2 = state(localState, rootState);
229
- sprae(el, state2);
230
- };
231
225
  var _each = Symbol(":each");
232
226
  primary["each"] = (tpl, expr) => {
233
227
  let each = parseForExpression(expr);
@@ -248,7 +242,7 @@ primary["each"] = (tpl, expr) => {
248
242
  if (!list)
249
243
  list = [];
250
244
  else if (typeof list === "number")
251
- list = Array.from({ length: list }, (_, i) => [i, i + 1]);
245
+ list = Array.from({ length: list }, (_, i) => [i + 1, i]);
252
246
  else if (Array.isArray(list))
253
247
  list = list.map((item, i) => [i + 1, item]);
254
248
  else if (typeof list === "object")
@@ -278,6 +272,12 @@ primary["each"] = (tpl, expr) => {
278
272
  }
279
273
  };
280
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
+ };
281
281
  primary["ref"] = (el, expr, state2) => {
282
282
  state2[expr] = el;
283
283
  };
@@ -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++) {
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);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))}},g.with=(e,t,r)=>{const n=j(e,t,":with")(r);O(e,p(n,r))};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,t+1])):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.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};
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}var $=O;document.currentScript&&O(document.documentElement);export{$ as default};
package/src/core.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { state as createState, fx, sandbox } from './state.js';
2
- import defaultDirective, { primary, secondary } from './directives.js';
2
+ import defaultDirective, { primary, secondary, on } from './directives.js';
3
3
 
4
4
  sprae.globals=sandbox
5
5
 
@@ -39,18 +39,23 @@ export default function sprae(container, values) {
39
39
  // catch other attributes as secondary
40
40
  if (el.attributes) {
41
41
  for (let i = 0; i < el.attributes.length;) {
42
- let attr = el.attributes[i]
43
- if (attr.name[0] !== ':') {i++; continue}
44
- el.removeAttribute(attr.name)
45
- let expr = attr.value
42
+ let attr = el.attributes[i], prefix = attr.name[0]
46
43
 
47
- // multiple attributes like :id:for=""
48
- let attrNames = attr.name.slice(1).split(':')
49
- for (let attrName of attrNames) {
50
- let dir = secondary[attrName] || defaultDirective;
51
- updates.push(dir(el, expr, state, attrName));
52
- // NOTE: secondary directives don't stop flow nor extend state, so no need to check
44
+ if (prefix === ':' || prefix === '@') {
45
+ el.removeAttribute(attr.name)
46
+ let expr = prefix === '@' ? `event=>{${attr.value}}` : attr.value,
47
+ names = attr.name.slice(1).split(prefix)
48
+
49
+ // multiple attributes like :id:for="" or @click@touchstart
50
+ for (let name of names) {
51
+ // @click forwards to :onclick=event=>{...inline}
52
+ if (prefix === '@') name = `on` + name
53
+ let dir = secondary[name] || defaultDirective;
54
+ updates.push(dir(el, expr, state, name));
55
+ // NOTE: secondary directives don't stop flow nor extend state, so no need to check
56
+ }
53
57
  }
58
+ else i++;
54
59
  }
55
60
  }
56
61
 
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
@@ -239,18 +240,6 @@ secondary['value'] = (el, expr) => {
239
240
  return (state) => update(evaluate(state))
240
241
  }
241
242
 
242
- secondary['on'] = (el, expr) => {
243
- let evaluate = parseExpr(el, expr, ':on')
244
-
245
- return (state) => {
246
- let listeners = evaluate(state);
247
- let offs = []; for (let evt in listeners) offs.push(on(el, evt, listeners[evt]));
248
- return () => {
249
- for (let off of offs) off()
250
- }
251
- }
252
- }
253
-
254
243
  // any unknown directive
255
244
  export default (el, expr, state, name) => {
256
245
  let evt = name.startsWith('on') && name.slice(2)
@@ -267,50 +256,30 @@ export default (el, expr, state, name) => {
267
256
  return state => attr(el, name, evaluate(state))
268
257
  }
269
258
 
270
- // bind event to target
271
- const on = (target, evt, origFn) => {
272
- if (!origFn) return
273
-
274
- // ona..onb
275
- let ctxs = evt.split('..').map(e => {
276
- let ctx = { evt:'', target, test:()=>true };
277
- // onevt.debounce-108 -> evt.debounce-108
278
- ctx.evt = (e.startsWith('on') ? e.slice(2) : e).replace(/\.(\w+)?-?([-\w]+)?/g,
279
- (match, mod, param='') => (ctx.test = mods[mod]?.(ctx, ...param.split('-')) || ctx.test, '')
280
- );
281
- return ctx;
282
- });
283
-
284
- // single event bind
285
- if (ctxs.length == 1) return addListenerWithMods(origFn, ctxs[0])
286
-
287
- // events chain cycler
288
- const onFn = (fn, cur=0) => {
289
- let off
290
- let curListener = e => {
291
- if (cur) off(); // don't remove entry listener - we must keep chain entry always open
292
- let nextFn = fn.call(target, e)
293
- if (typeof nextFn !== 'function') nextFn = ()=>{}
294
- if (cur+1 < ctxs.length) onFn(nextFn, !cur ? 1 : cur+1);
295
- }
296
- return off = addListenerWithMods(curListener, ctxs[cur])
297
- }
298
- let rootOff = onFn(origFn)
299
- return () => rootOff()
259
+ // bind event to a target
260
+ export const on = (el, e, fn) => {
261
+ if (!fn) return
262
+
263
+ const ctx = { evt:'', target:el, test:()=>true };
264
+
265
+ // onevt.debounce-108 -> evt.debounce-108
266
+ ctx.evt = (e.startsWith('on') ? e.slice(2) : e).replace(/\.(\w+)?-?([-\w]+)?/g,
267
+ (match, mod, param='') => (ctx.test = mods[mod]?.(ctx, ...param.split('-')) || ctx.test, '')
268
+ );
300
269
 
301
270
  // add listener applying the context
302
- function addListenerWithMods(fn, {evt, target, test, defer, stop, prevent, ...opts} ) {
303
- if (defer) fn = defer(fn)
304
-
305
- let cb = e => test(e) && (
306
- stop&&e.stopPropagation(),
307
- prevent&&e.preventDefault(),
308
- fn.call(target, e)
309
- )
310
-
311
- target.addEventListener(evt, cb, opts)
312
- return () => target.removeEventListener(evt, cb, opts)
313
- };
271
+ const {evt, target, test, defer, stop, prevent, ...opts} = ctx;
272
+
273
+ if (defer) fn = defer(fn)
274
+
275
+ const cb = e => test(e) && (
276
+ stop&&e.stopPropagation(),
277
+ prevent&&e.preventDefault(),
278
+ fn.call(target, e)
279
+ )
280
+
281
+ target.addEventListener(evt, cb, opts)
282
+ return () => target.removeEventListener(evt, cb, opts)
314
283
  }
315
284
 
316
285
  // event modifiers
@@ -328,8 +297,6 @@ const mods = {
328
297
  window(ctx) { ctx.target = window },
329
298
  document(ctx) { ctx.target = document },
330
299
 
331
- toggle(ctx) { ctx.defer = (fn, out) => (e => out ? (out.call?.(ctx.target, e), out=null) : (out = fn())) },
332
-
333
300
  throttle(ctx, limit) { ctx.defer = fn => throttle(fn, limit ? Number(limit) || 0 : 108) },
334
301
  debounce(ctx, wait) { ctx.defer = fn => debounce(fn, wait ? Number(wait) || 0 : 108) },
335
302