mancha 0.5.3 → 0.5.4
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/dist/core.d.ts +3 -4
- package/dist/core.js +14 -8
- package/dist/mancha.js +1 -1
- package/dist/plugins.js +10 -12
- package/dist/reactive.d.ts +1 -1
- package/dist/reactive.js +1 -1
- package/package.json +1 -1
package/dist/core.d.ts
CHANGED
|
@@ -4,15 +4,14 @@ export type EvalListener = (result: any, dependencies: string[]) => any;
|
|
|
4
4
|
export declare function traverse(root: Node | DocumentFragment | Document, skip?: Set<Node>): Generator<ChildNode>;
|
|
5
5
|
export declare function dirname(fpath: string): string;
|
|
6
6
|
export declare function isRelativePath(fpath: string): boolean;
|
|
7
|
-
export declare function makeEvalFunction(code: string, args?:
|
|
8
|
-
|
|
9
|
-
}): Function;
|
|
10
|
-
export declare function safeEval(code: string, context: any, args?: {
|
|
7
|
+
export declare function makeEvalFunction(code: string, args?: string[]): Function;
|
|
8
|
+
export declare function safeEval(context: any, code: string, args?: {
|
|
11
9
|
[key: string]: any;
|
|
12
10
|
}): Promise<any>;
|
|
13
11
|
export declare abstract class IRenderer extends ReactiveProxyStore {
|
|
14
12
|
protected debugging: boolean;
|
|
15
13
|
protected readonly dirpath: string;
|
|
14
|
+
protected readonly evalkeys: string[];
|
|
16
15
|
protected readonly expressionCache: Map<string, Function>;
|
|
17
16
|
protected readonly evalCallbacks: Map<string, EvalListener[]>;
|
|
18
17
|
readonly skipNodes: Set<Node>;
|
package/dist/core.js
CHANGED
|
@@ -39,11 +39,11 @@ function isRelativePath(fpath) {
|
|
|
39
39
|
!fpath.startsWith("data:"));
|
|
40
40
|
}
|
|
41
41
|
exports.isRelativePath = isRelativePath;
|
|
42
|
-
function makeEvalFunction(code, args =
|
|
43
|
-
return new Function(...
|
|
42
|
+
function makeEvalFunction(code, args = []) {
|
|
43
|
+
return new Function(...args, `with (this) { return (async () => (${code}))(); }`);
|
|
44
44
|
}
|
|
45
45
|
exports.makeEvalFunction = makeEvalFunction;
|
|
46
|
-
function safeEval(
|
|
46
|
+
function safeEval(context, code, args = {}) {
|
|
47
47
|
const inner = `with (this) { return (async () => (${code}))(); }`;
|
|
48
48
|
return new Function(...Object.keys(args), inner).call(context, ...Object.values(args));
|
|
49
49
|
}
|
|
@@ -51,6 +51,7 @@ exports.safeEval = safeEval;
|
|
|
51
51
|
class IRenderer extends reactive_1.ReactiveProxyStore {
|
|
52
52
|
debugging = false;
|
|
53
53
|
dirpath = "";
|
|
54
|
+
evalkeys = ["$elem", "$event"];
|
|
54
55
|
expressionCache = new Map();
|
|
55
56
|
evalCallbacks = new Map();
|
|
56
57
|
skipNodes = new Set();
|
|
@@ -94,20 +95,25 @@ class IRenderer extends reactive_1.ReactiveProxyStore {
|
|
|
94
95
|
if (this.debugging)
|
|
95
96
|
console.debug(...args);
|
|
96
97
|
}
|
|
97
|
-
cachedExpressionFunction(expr
|
|
98
|
+
cachedExpressionFunction(expr) {
|
|
98
99
|
if (!this.expressionCache.has(expr)) {
|
|
99
|
-
this.expressionCache.set(expr, makeEvalFunction(expr,
|
|
100
|
+
this.expressionCache.set(expr, makeEvalFunction(expr, this.evalkeys));
|
|
100
101
|
}
|
|
101
|
-
return this.expressionCache.get(expr)
|
|
102
|
+
return this.expressionCache.get(expr);
|
|
102
103
|
}
|
|
103
104
|
async eval(expr, args = {}) {
|
|
105
|
+
const fn = this.cachedExpressionFunction(expr);
|
|
106
|
+
const vals = this.evalkeys.map((key) => args[key]);
|
|
107
|
+
if (Object.keys(args).some((key) => !this.evalkeys.includes(key))) {
|
|
108
|
+
throw new Error(`Invalid argument key, must be one of: ${this.evalkeys.join(", ")}`);
|
|
109
|
+
}
|
|
104
110
|
const [result, dependencies] = await this.trace(async function () {
|
|
105
|
-
return
|
|
111
|
+
return fn.call(this, ...vals);
|
|
106
112
|
});
|
|
107
113
|
this.log(`eval \`${expr}\` => `, result, `[ ${dependencies.join(", ")} ]`);
|
|
108
114
|
return [result, dependencies];
|
|
109
115
|
}
|
|
110
|
-
|
|
116
|
+
watchExpr(expr, args, callback) {
|
|
111
117
|
// Early exit: this eval has already been registered, we just need to add our callback.
|
|
112
118
|
if (this.evalCallbacks.has(expr)) {
|
|
113
119
|
this.evalCallbacks.get(expr)?.push(callback);
|
package/dist/mancha.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{"use strict";var t={885:(t,e)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.attributeNameToCamelCase=void 0,e.attributeNameToCamelCase=function(t){return t.replace(/-./g,(t=>t[1].toUpperCase()))}},283:(t,e,s)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.IRenderer=e.safeEval=e.makeEvalFunction=e.isRelativePath=e.dirname=e.traverse=void 0;const r=s(63),i=s(150),a=s(230);function*n(t,e=new Set){const s=new Set,r=Array.from(t.childNodes).filter((t=>!e.has(t)));for(yield t;r.length;){const t=r.pop();s.has(t)||(s.add(t),yield t),t.childNodes&&Array.from(t.childNodes).filter((t=>!e.has(t))).forEach((t=>r.push(t)))}}function o(t){return t.includes("/")?t.split("/").slice(0,-1).join("/"):""}function c(t,e={}){return new Function(...Object.keys(e),`with (this) { return (${t}); }`)}e.traverse=n,e.dirname=o,e.isRelativePath=function(t){return!(t.includes("://")||t.startsWith("/")||t.startsWith("#")||t.startsWith("data:"))},e.makeEvalFunction=c,e.safeEval=function(t,e,s={}){const r=`with (this) { return (async () => (${t}))(); }`;return new Function(...Object.keys(s),r).call(e,...Object.values(s))};class l extends r.ReactiveProxyStore{debugging=!1;dirpath="";expressionCache=new Map;evalCallbacks=new Map;skipNodes=new Set;debug(t){return this.debugging=t,this}async fetchRemote(t,e){return fetch(t,{cache:e?.cache??"default"}).then((t=>t.text()))}async fetchLocal(t,e){return this.fetchRemote(t,e)}async preprocessString(t,e){this.log("Preprocessing string content with params:\n",e);const s=this.parseHTML(t,e);return await this.preprocessNode(s,e),s}async preprocessLocal(t,e){const s=await this.fetchLocal(t,e);return this.preprocessString(s,{...e,dirpath:o(t),root:e?.root??!t.endsWith(".tpl.html")})}async preprocessRemote(t,e){const s=e?.cache||"default",r=await fetch(t,{cache:s}).then((t=>t.text()));return this.preprocessString(r,{...e,dirpath:o(t),root:e?.root??!t.endsWith(".tpl.html")})}clone(){return new this.constructor(Object.fromEntries(this.store.entries()))}log(...t){this.debugging&&console.debug(...t)}cachedExpressionFunction(t,e){return this.expressionCache.has(t)||this.expressionCache.set(t,c(t,e)),this.expressionCache.get(t)?.call(this,...Object.values(e))}async eval(t,e={}){const[s,r]=await this.trace((async function(){return this.cachedExpressionFunction(t,e)}));return this.log(`eval \`${t}\` => `,s,`[ ${r.join(", ")} ]`),[s,r]}async watchExpr(t,e,s){if(this.evalCallbacks.has(t))return this.evalCallbacks.get(t)?.push(s),this.eval(t,e).then((([t,e])=>s(t,e)));this.evalCallbacks.set(t,[s]);const r=[],i=async()=>{const[s,a]=await this.eval(t,e),n=this.evalCallbacks.get(t)||[];await Promise.all(n.map((t=>t(s,a)))),r.length>0&&this.unwatch(r,i),r.splice(0,r.length,...a),this.watch(a,i)};return i()}async preprocessNode(t,e){e=Object.assign({dirpath:this.dirpath,maxdepth:10},e);const s=new i.Iterator(n(t,this.skipNodes)).map((async t=>{this.log("Preprocessing node:\n",t),await a.RendererPlugins.resolveIncludes.call(this,t,e),await a.RendererPlugins.rebaseRelativePaths.call(this,t,e)}));await Promise.all(s.generator())}async renderNode(t,e){for(const s of n(t,this.skipNodes))this.log("Rendering node:\n",s),await a.RendererPlugins.resolveDataAttribute.call(this,s,e),await a.RendererPlugins.resolveForAttribute.call(this,s,e),await a.RendererPlugins.resolveHtmlAttribute.call(this,s,e),await a.RendererPlugins.resolveShowAttribute.call(this,s,e),await a.RendererPlugins.resolveWatchAttribute.call(this,s,e),await a.RendererPlugins.resolveBindAttribute.call(this,s,e),await a.RendererPlugins.resolvePropAttributes.call(this,s,e),await a.RendererPlugins.resolveAttrAttributes.call(this,s,e),await a.RendererPlugins.resolveEventAttributes.call(this,s,e),await a.RendererPlugins.resolveTextNodeExpressions.call(this,s,e)}async mount(t,e){await this.preprocessNode(t,e),await this.renderNode(t,e)}}e.IRenderer=l},150:(t,e)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.Iterator=void 0;class s{iterable;constructor(t){this.iterable=t}filter(t){return new s(s.filterGenerator(t,this.iterable))}map(t){return new s(s.mapGenerator(t,this.iterable))}array(){return Array.from(this.iterable)}*generator(){for(const t of this.iterable)yield t}static*filterGenerator(t,e){for(const s of e)t(s)&&(yield s)}static*mapGenerator(t,e){for(const s of e)yield t(s)}static equals(t,e){const s=t[Symbol.iterator](),r=e[Symbol.iterator]();let i=s.next(),a=r.next();for(;!i.done&&!a.done;){if(i.value!==a.value)return!1;i=s.next(),a=r.next()}return i.done===a.done}}e.Iterator=s},230:(t,e,s)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.RendererPlugins=void 0;const r=s(885),i=s(283),a=new Set([":bind",":bind-events",":data",":for",":show","@watch","$html"]),n={$text:"$text-content"};var o;!function(t){t.resolveIncludes=async function(t,e){const s=t;if("include"!==s.tagName?.toLocaleLowerCase())return;this.log("<include> tag found in:\n",t),this.log("<include> params:",e);const r=s.getAttribute?.("src");if(!r)throw new Error(`"src" attribute missing from ${t}.`);const i=e=>{t.replaceWith(...Array.from(e.childNodes))},a={...e,root:!1,maxdepth:e?.maxdepth-1};if(0===a.maxdepth)throw new Error("Maximum recursion depth reached.");if(r.includes("://")||r.startsWith("//"))this.log("Including remote file from absolute path:",r),await this.preprocessRemote(r,a).then(i);else if(e?.dirpath?.includes("://")||e?.dirpath?.startsWith("//")){const t=e.dirpath&&"."!==e.dirpath?`${e.dirpath}/${r}`:r;this.log("Including remote file from relative path:",t),await this.preprocessRemote(t,a).then(i)}else if("/"===r.charAt(0))this.log("Including local file from absolute path:",r),await this.preprocessLocal(r,a).then(i);else{const t=e?.dirpath&&"."!==e?.dirpath?`${e?.dirpath}/${r}`:r;this.log("Including local file from relative path:",t),await this.preprocessLocal(t,a).then(i)}},t.rebaseRelativePaths=async function(t,e){const s=t,r=s.tagName?.toLowerCase();if(!e?.dirpath)return;const a=t.getAttribute?.("src"),n=t.getAttribute?.("href"),o=t.getAttribute?.("data"),c=a||n||o;c&&(c&&(0,i.isRelativePath)(c)&&this.log("Rebasing relative path as:",e.dirpath,"/",c),"img"===r&&a&&(0,i.isRelativePath)(a)?s.src=`${e.dirpath}/${a}`:"a"===r&&n&&(0,i.isRelativePath)(n)||"link"===r&&n&&(0,i.isRelativePath)(n)?s.href=`${e.dirpath}/${n}`:"script"===r&&a&&(0,i.isRelativePath)(a)||"source"===r&&a&&(0,i.isRelativePath)(a)||"audio"===r&&a&&(0,i.isRelativePath)(a)||"video"===r&&a&&(0,i.isRelativePath)(a)||"track"===r&&a&&(0,i.isRelativePath)(a)||"iframe"===r&&a&&(0,i.isRelativePath)(a)?s.src=`${e.dirpath}/${a}`:"object"===r&&o&&(0,i.isRelativePath)(o)?s.data=`${e.dirpath}/${o}`:"input"===r&&a&&(0,i.isRelativePath)(a)?s.src=`${e.dirpath}/${a}`:("area"===r&&n&&(0,i.isRelativePath)(n)||"base"===r&&n&&(0,i.isRelativePath)(n))&&(s.href=`${e.dirpath}/${n}`))},t.resolveTextNodeExpressions=async function(t,e){if(3!==t.nodeType)return;const s=t.nodeValue||"";this.log("Processing node content value:\n",s);const r=new RegExp(/{{ ([^}]+) }}/gm),i=Array.from(s.matchAll(r)).map((t=>t[1])),a=async()=>{let e=s;for(const s of i){const[r]=await this.eval(s,{$elem:t});e=e.replace(`{{ ${s} }}`,String(r))}t.nodeValue=e};await Promise.all(i.map((e=>this.watchExpr(e,{$elem:t},a))))},t.resolveDataAttribute=async function(t,e){if(this.skipNodes.has(t))return;const s=t,r=s.getAttribute?.(":data");if(r){this.log(":data attribute found in:\n",t),s.removeAttribute(":data");const[e]=await this.eval(r,{$elem:t});await this.update(e)}},t.resolveWatchAttribute=async function(t,e){if(this.skipNodes.has(t))return;const s=t,r=s.getAttribute?.("@watch");r&&(this.log("@watch attribute found in:\n",t),s.removeAttribute("@watch"),await this.watchExpr(r,{$elem:t},(()=>{})))},t.resolveHtmlAttribute=async function(t,e){if(this.skipNodes.has(t))return;const s=t,r=s.getAttribute?.("$html");if(r){this.log("$html attribute found in:\n",t),s.removeAttribute("$html");const i=this.clone();await this.watchExpr(r,{$elem:t},(async t=>{const r=await i.preprocessString(t,e);await i.renderNode(r,e),s.replaceChildren(r)}))}},t.resolvePropAttributes=async function(t,e){if(this.skipNodes.has(t))return;const s=t;for(const e of Array.from(s.attributes||[]))if(e.name.startsWith("$")&&!a.has(e.name)){this.log(e.name,"attribute found in:\n",t),s.removeAttribute(e.name);const i=(n[e.name]||e.name).slice(1),a=(0,r.attributeNameToCamelCase)(i);await this.watchExpr(e.value,{$elem:t},(e=>t[a]=e))}},t.resolveAttrAttributes=async function(t,e){if(this.skipNodes.has(t))return;const s=t;for(const e of Array.from(s.attributes||[]))if(e.name.startsWith(":")&&!a.has(e.name)){this.log(e.name,"attribute found in:\n",t),s.removeAttribute(e.name);const r=(n[e.name]||e.name).slice(1);await this.watchExpr(e.value,{$elem:t},(t=>s.setAttribute(r,t)))}},t.resolveEventAttributes=async function(t,e){if(this.skipNodes.has(t))return;const s=t;for(const e of Array.from(s.attributes||[]))e.name.startsWith("@")&&!a.has(e.name)&&(this.log(e.name,"attribute found in:\n",t),s.removeAttribute(e.name),t.addEventListener?.(e.name.substring(1),(s=>{this.eval(e.value,{$elem:t,$event:s})})))},t.resolveForAttribute=async function(t,e){if(this.skipNodes.has(t))return;const s=t,r=s.getAttribute?.(":for")?.trim();if(r){this.log(":for attribute found in:\n",t),s.removeAttribute(":for");for(const e of(0,i.traverse)(t,this.skipNodes))this.skipNodes.add(e);const a=t.parentNode,n=t.ownerDocument.createElement("template");a.insertBefore(n,t),n.append(t),this.log(":for template:\n",n);const o=r.split(" in ",2);if(2!==o.length)throw new Error(`Invalid :for format: \`${r}\`. Expected "{key} in {expression}".`);const c=[],[l,h]=o;await this.watchExpr(h,{$elem:t},(s=>(this.log(":for list items:",s),this.lock=this.lock.then((()=>new Promise((async r=>{if(c.splice(0,c.length).forEach((t=>{a.removeChild(t),this.skipNodes.delete(t)})),!Array.isArray(s))return console.error(`Expression did not yield a list: \`${h}\` => \`${s}\``),r();for(const r of s.slice(0).reverse()){const s=this.clone();await s.set(l,r);const i=t.cloneNode(!0);a.insertBefore(i,n.nextSibling),c.push(i),this.skipNodes.add(i),await s.mount(i,e),this.log("Rendered list child:\n",i,i.outerHTML)}r()})))),this.lock)))}},t.resolveBindAttribute=async function(t,e){if(this.skipNodes.has(t))return;const s=t,r=s.getAttribute?.(":bind");if(r){this.log(":bind attribute found in:\n",t);const e=["change","input"],i=s.getAttribute?.(":bind-events")?.split(",")||e;s.removeAttribute(":bind"),s.removeAttribute(":bind-events");const a="checkbox"===s.getAttribute("type")?"checked":"value";this.has(r)||await this.set(r,s[a]),s[a]=this.get(r);for(const e of i)t.addEventListener(e,(()=>this.set(r,s[a])));this.watch([r],(()=>s[a]=this.get(r)))}},t.resolveShowAttribute=async function(t,e){if(this.skipNodes.has(t))return;const s=t,r=s.getAttribute?.(":show");if(r){this.log(":show attribute found in:\n",t),s.removeAttribute(":show");const e="none"===s.style.display?"":s.style.display;await this.watchExpr(r,{$elem:t},(t=>{s.style.display=t?e:"none"}))}}}(o||(e.RendererPlugins=o={}))},63:(t,e)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.proxifyStore=e.ReactiveProxyStore=e.InertProxy=e.ReactiveProxy=e.proxifyObject=e.REACTIVE_DEBOUNCE_MILLIS=void 0;class s{timeouts=new Map;debounce(t,e){return new Promise(((s,r)=>{const i=this.timeouts.get(e);i&&clearTimeout(i),this.timeouts.set(e,setTimeout((()=>{try{s(e()),this.timeouts.delete(e)}catch(t){r(t)}}),t))}))}}function r(t,e,s=!0){if(null==t||function(t){return t instanceof i||t.__is_proxy__}(t))return t;if(s)for(const s in t)t.hasOwnProperty(s)&&"object"==typeof t[s]&&null!=t[s]&&(t[s]=r(t[s],e));return new Proxy(t,{deleteProperty:(t,s)=>s in t&&(delete t[s],e(),!0),set:(t,i,a,n)=>{s&&"object"==typeof a&&(a=r(a,e));const o=Reflect.set(t,i,a,n);return e(),o},get:(t,e,s)=>"__is_proxy__"===e||Reflect.get(t,e,s)})}e.REACTIVE_DEBOUNCE_MILLIS=25,e.proxifyObject=r;class i extends s{value=null;listeners=[];constructor(t=null,...e){super(),this.value=this.wrapObjValue(t),e.forEach((t=>this.watch(t)))}static from(t,...e){return t instanceof i?(e.forEach(t.watch),t):new i(t,...e)}wrapObjValue(t){return null===t||"object"!=typeof t?t:r(t,(()=>this.trigger()))}get(){return this.value}async set(t){if(this.value!==t){const e=this.value;this.value=this.wrapObjValue(t),await this.trigger(e)}}watch(t){this.listeners.push(t)}unwatch(t){this.listeners=this.listeners.filter((e=>e!==t))}trigger(t=null){const s=this.listeners.slice();return this.debounce(e.REACTIVE_DEBOUNCE_MILLIS,(()=>Promise.all(s.map((e=>e(this.value,t)))).then((()=>{}))))}}e.ReactiveProxy=i;class a extends i{static from(t,...e){return t instanceof i?t:new a(t,...e)}watch(t){}trigger(t){return Promise.resolve()}}function n(t,e=(()=>{})){const s=Array.from(t.entries()).map((([t])=>t)),r=Object.fromEntries(s.map((t=>[t,void 0])));return new Proxy(Object.assign({},t,r),{get:(s,r,i)=>"string"==typeof r&&t.has(r)?(e("get",r),t.get(r)):"get"===r?s=>(e("get",s),t.get(s)):Reflect.get(t,r,i),set:(s,r,i,a)=>("string"!=typeof r||r in t?Reflect.set(t,r,i,a):(e("set",r,i),t.set(r,i)),!0)})}e.InertProxy=a,e.ReactiveProxyStore=class extends s{store=new Map;debouncedListeners=new Map;lock=Promise.resolve();constructor(t){super();for(const[e,s]of Object.entries(t||{}))this.store.set(e,i.from(this.wrapFnValue(s)))}wrapFnValue(t){return t&&"function"==typeof t?(...e)=>t.call(n(this),...e):t}get $(){return n(this)}entries(){return this.store.entries()}get(t){return this.store.get(t)?.get()}async set(t,e){this.store.has(t)?await this.store.get(t).set(this.wrapFnValue(e)):this.store.set(t,i.from(this.wrapFnValue(e)))}del(t){return this.store.delete(t)}has(t){return this.store.has(t)}async update(t){await Promise.all(Object.entries(t).map((([t,e])=>this.set(t,e))))}watch(t,s){t=Array.isArray(t)?t:[t];const r=()=>s(...t.map((t=>this.store.get(t).get()))),i=()=>this.debounce(e.REACTIVE_DEBOUNCE_MILLIS,r);t.forEach((t=>this.store.get(t).watch(i))),this.debouncedListeners.set(s,i)}unwatch(t,e){(t=Array.isArray(t)?t:[t]).forEach((t=>this.store.get(t).unwatch(this.debouncedListeners.get(e)))),this.debouncedListeners.delete(e)}async trigger(t){t=Array.isArray(t)?t:[t],await Promise.all(t.map((t=>this.store.get(t).trigger())))}async trace(t){const e=new Set,s=n(this,((t,s)=>{"get"===t&&e.add(s)}));return[await t.call(s),Array.from(e)]}async computed(t,e){const[s,r]=await this.trace(e);this.watch(r,(async()=>this.set(t,await e.call(n(this))))),this.set(t,s)}},e.proxifyStore=n}},e={};function s(r){var i=e[r];if(void 0!==i)return i.exports;var a=e[r]={exports:{}};return t[r](a,a.exports,s),a.exports}(()=>{const t=s(283);class e extends t.IRenderer{dirpath=(0,t.dirname)(self.location.href);parseHTML(t,e={root:!1}){if(e.root)return(new DOMParser).parseFromString(t,"text/html");{const e=document.createRange();return e.selectNodeContents(document.body),e.createContextualFragment(t)}}serializeHTML(t){return(new XMLSerializer).serializeToString(t).replace(/\s?xmlns="[^"]+"/gm,"")}preprocessLocal(t,e){return this.preprocessRemote(t,e)}}const r=new e;self.Mancha=r;const i=self.document?.currentScript;if(self.document?.currentScript?.hasAttribute("init")){r.update({...i?.dataset});const t=i?.hasAttribute("debug"),e=i?.getAttribute("cache");(i?.getAttribute("target")?.split(",")||["body"]).map((async s=>{const i=self.document.querySelector(s);await r.debug(t).mount(i,{cache:e})}))}})()})();
|
|
1
|
+
(()=>{"use strict";var e={885:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.attributeNameToCamelCase=void 0,t.attributeNameToCamelCase=function(e){return e.replace(/-./g,(e=>e[1].toUpperCase()))}},283:(e,t,s)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.IRenderer=t.safeEval=t.makeEvalFunction=t.isRelativePath=t.dirname=t.traverse=void 0;const r=s(63),i=s(150),a=s(230);function*n(e,t=new Set){const s=new Set,r=Array.from(e.childNodes).filter((e=>!t.has(e)));for(yield e;r.length;){const e=r.pop();s.has(e)||(s.add(e),yield e),e.childNodes&&Array.from(e.childNodes).filter((e=>!t.has(e))).forEach((e=>r.push(e)))}}function o(e){return e.includes("/")?e.split("/").slice(0,-1).join("/"):""}function c(e,t=[]){return new Function(...t,`with (this) { return (async () => (${e}))(); }`)}t.traverse=n,t.dirname=o,t.isRelativePath=function(e){return!(e.includes("://")||e.startsWith("/")||e.startsWith("#")||e.startsWith("data:"))},t.makeEvalFunction=c,t.safeEval=function(e,t,s={}){const r=`with (this) { return (async () => (${t}))(); }`;return new Function(...Object.keys(s),r).call(e,...Object.values(s))};class l extends r.ReactiveProxyStore{debugging=!1;dirpath="";evalkeys=["$elem","$event"];expressionCache=new Map;evalCallbacks=new Map;skipNodes=new Set;debug(e){return this.debugging=e,this}async fetchRemote(e,t){return fetch(e,{cache:t?.cache??"default"}).then((e=>e.text()))}async fetchLocal(e,t){return this.fetchRemote(e,t)}async preprocessString(e,t){this.log("Preprocessing string content with params:\n",t);const s=this.parseHTML(e,t);return await this.preprocessNode(s,t),s}async preprocessLocal(e,t){const s=await this.fetchLocal(e,t);return this.preprocessString(s,{...t,dirpath:o(e),root:t?.root??!e.endsWith(".tpl.html")})}async preprocessRemote(e,t){const s=t?.cache||"default",r=await fetch(e,{cache:s}).then((e=>e.text()));return this.preprocessString(r,{...t,dirpath:o(e),root:t?.root??!e.endsWith(".tpl.html")})}clone(){return new this.constructor(Object.fromEntries(this.store.entries()))}log(...e){this.debugging&&console.debug(...e)}cachedExpressionFunction(e){return this.expressionCache.has(e)||this.expressionCache.set(e,c(e,this.evalkeys)),this.expressionCache.get(e)}async eval(e,t={}){const s=this.cachedExpressionFunction(e),r=this.evalkeys.map((e=>t[e]));if(Object.keys(t).some((e=>!this.evalkeys.includes(e))))throw new Error(`Invalid argument key, must be one of: ${this.evalkeys.join(", ")}`);const[i,a]=await this.trace((async function(){return s.call(this,...r)}));return this.log(`eval \`${e}\` => `,i,`[ ${a.join(", ")} ]`),[i,a]}watchExpr(e,t,s){if(this.evalCallbacks.has(e))return this.evalCallbacks.get(e)?.push(s),this.eval(e,t).then((([e,t])=>s(e,t)));this.evalCallbacks.set(e,[s]);const r=[],i=async()=>{const[s,a]=await this.eval(e,t),n=this.evalCallbacks.get(e)||[];await Promise.all(n.map((e=>e(s,a)))),r.length>0&&this.unwatch(r,i),r.splice(0,r.length,...a),this.watch(a,i)};return i()}async preprocessNode(e,t){t=Object.assign({dirpath:this.dirpath,maxdepth:10},t);const s=new i.Iterator(n(e,this.skipNodes)).map((async e=>{this.log("Preprocessing node:\n",e),await a.RendererPlugins.resolveIncludes.call(this,e,t),await a.RendererPlugins.rebaseRelativePaths.call(this,e,t)}));await Promise.all(s.generator())}async renderNode(e,t){for(const s of n(e,this.skipNodes))this.log("Rendering node:\n",s),await a.RendererPlugins.resolveDataAttribute.call(this,s,t),await a.RendererPlugins.resolveForAttribute.call(this,s,t),await a.RendererPlugins.resolveHtmlAttribute.call(this,s,t),await a.RendererPlugins.resolveShowAttribute.call(this,s,t),await a.RendererPlugins.resolveWatchAttribute.call(this,s,t),await a.RendererPlugins.resolveBindAttribute.call(this,s,t),await a.RendererPlugins.resolvePropAttributes.call(this,s,t),await a.RendererPlugins.resolveAttrAttributes.call(this,s,t),await a.RendererPlugins.resolveEventAttributes.call(this,s,t),await a.RendererPlugins.resolveTextNodeExpressions.call(this,s,t)}async mount(e,t){await this.preprocessNode(e,t),await this.renderNode(e,t)}}t.IRenderer=l},150:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Iterator=void 0;class s{iterable;constructor(e){this.iterable=e}filter(e){return new s(s.filterGenerator(e,this.iterable))}map(e){return new s(s.mapGenerator(e,this.iterable))}array(){return Array.from(this.iterable)}*generator(){for(const e of this.iterable)yield e}static*filterGenerator(e,t){for(const s of t)e(s)&&(yield s)}static*mapGenerator(e,t){for(const s of t)yield e(s)}static equals(e,t){const s=e[Symbol.iterator](),r=t[Symbol.iterator]();let i=s.next(),a=r.next();for(;!i.done&&!a.done;){if(i.value!==a.value)return!1;i=s.next(),a=r.next()}return i.done===a.done}}t.Iterator=s},230:(e,t,s)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.RendererPlugins=void 0;const r=s(885),i=s(283),a=new Set([":bind",":bind-events",":data",":for",":show","@watch","$html"]),n={$text:"$text-content"};var o;!function(e){e.resolveIncludes=async function(e,t){const s=e;if("include"!==s.tagName?.toLocaleLowerCase())return;this.log("<include> tag found in:\n",e),this.log("<include> params:",t);const r=s.getAttribute?.("src");if(!r)throw new Error(`"src" attribute missing from ${e}.`);const i=t=>{e.replaceWith(...Array.from(t.childNodes))},a={...t,root:!1,maxdepth:t?.maxdepth-1};if(0===a.maxdepth)throw new Error("Maximum recursion depth reached.");if(r.includes("://")||r.startsWith("//"))this.log("Including remote file from absolute path:",r),await this.preprocessRemote(r,a).then(i);else if(t?.dirpath?.includes("://")||t?.dirpath?.startsWith("//")){const e=t.dirpath&&"."!==t.dirpath?`${t.dirpath}/${r}`:r;this.log("Including remote file from relative path:",e),await this.preprocessRemote(e,a).then(i)}else if("/"===r.charAt(0))this.log("Including local file from absolute path:",r),await this.preprocessLocal(r,a).then(i);else{const e=t?.dirpath&&"."!==t?.dirpath?`${t?.dirpath}/${r}`:r;this.log("Including local file from relative path:",e),await this.preprocessLocal(e,a).then(i)}},e.rebaseRelativePaths=async function(e,t){const s=e,r=s.tagName?.toLowerCase();if(!t?.dirpath)return;const a=e.getAttribute?.("src"),n=e.getAttribute?.("href"),o=e.getAttribute?.("data"),c=a||n||o;c&&(c&&(0,i.isRelativePath)(c)&&this.log("Rebasing relative path as:",t.dirpath,"/",c),"img"===r&&a&&(0,i.isRelativePath)(a)?s.src=`${t.dirpath}/${a}`:"a"===r&&n&&(0,i.isRelativePath)(n)||"link"===r&&n&&(0,i.isRelativePath)(n)?s.href=`${t.dirpath}/${n}`:"script"===r&&a&&(0,i.isRelativePath)(a)||"source"===r&&a&&(0,i.isRelativePath)(a)||"audio"===r&&a&&(0,i.isRelativePath)(a)||"video"===r&&a&&(0,i.isRelativePath)(a)||"track"===r&&a&&(0,i.isRelativePath)(a)||"iframe"===r&&a&&(0,i.isRelativePath)(a)?s.src=`${t.dirpath}/${a}`:"object"===r&&o&&(0,i.isRelativePath)(o)?s.data=`${t.dirpath}/${o}`:"input"===r&&a&&(0,i.isRelativePath)(a)?s.src=`${t.dirpath}/${a}`:("area"===r&&n&&(0,i.isRelativePath)(n)||"base"===r&&n&&(0,i.isRelativePath)(n))&&(s.href=`${t.dirpath}/${n}`))},e.resolveTextNodeExpressions=async function(e,t){if(3!==e.nodeType)return;const s=e.nodeValue||"";this.log("Processing node content value:\n",s);const r=new RegExp(/{{ ([^}]+) }}/gm),i=Array.from(s.matchAll(r)).map((e=>e[1])),a=async()=>{let t=s;for(const s of i){const[r]=await this.eval(s,{$elem:e});t=t.replace(`{{ ${s} }}`,String(r))}e.nodeValue=t};await Promise.all(i.map((t=>this.watchExpr(t,{$elem:e},a))))},e.resolveDataAttribute=async function(e,t){if(this.skipNodes.has(e))return;const s=e,r=s.getAttribute?.(":data");if(r){this.log(":data attribute found in:\n",e),s.removeAttribute(":data");const[t]=await this.eval(r,{$elem:e});await this.update(t)}},e.resolveWatchAttribute=async function(e,t){if(this.skipNodes.has(e))return;const s=e,r=s.getAttribute?.("@watch");r&&(this.log("@watch attribute found in:\n",e),s.removeAttribute("@watch"),await this.watchExpr(r,{$elem:e},(()=>{})))},e.resolveHtmlAttribute=async function(e,t){if(this.skipNodes.has(e))return;const s=e,r=s.getAttribute?.("$html");if(r){this.log("$html attribute found in:\n",e),s.removeAttribute("$html");const i=this.clone();await this.watchExpr(r,{$elem:e},(async e=>{const r=await i.preprocessString(e,t);await i.renderNode(r,t),s.replaceChildren(r)}))}},e.resolvePropAttributes=async function(e,t){if(this.skipNodes.has(e))return;const s=e;for(const t of Array.from(s.attributes||[]))if(t.name.startsWith("$")&&!a.has(t.name)){this.log(t.name,"attribute found in:\n",e),s.removeAttribute(t.name);const i=(n[t.name]||t.name).slice(1),a=(0,r.attributeNameToCamelCase)(i);await this.watchExpr(t.value,{$elem:e},(t=>e[a]=t))}},e.resolveAttrAttributes=async function(e,t){if(this.skipNodes.has(e))return;const s=e;for(const t of Array.from(s.attributes||[]))if(t.name.startsWith(":")&&!a.has(t.name)){this.log(t.name,"attribute found in:\n",e),s.removeAttribute(t.name);const r=(n[t.name]||t.name).slice(1);await this.watchExpr(t.value,{$elem:e},(e=>s.setAttribute(r,e)))}},e.resolveEventAttributes=async function(e,t){if(this.skipNodes.has(e))return;const s=e;for(const t of Array.from(s.attributes||[]))t.name.startsWith("@")&&!a.has(t.name)&&(this.log(t.name,"attribute found in:\n",e),s.removeAttribute(t.name),e.addEventListener?.(t.name.substring(1),(s=>{this.eval(t.value,{$elem:e,$event:s})})))},e.resolveForAttribute=async function(e,t){if(this.skipNodes.has(e))return;const s=e,r=s.getAttribute?.(":for")?.trim();if(r){this.log(":for attribute found in:\n",e),s.removeAttribute(":for");for(const t of(0,i.traverse)(e,this.skipNodes))this.skipNodes.add(t);const a=e.parentNode,n=e.ownerDocument.createElement("template");a.insertBefore(n,e),n.append(e),this.log(":for template:\n",n);const o=r.split(" in ",2);if(2!==o.length)throw new Error(`Invalid :for format: \`${r}\`. Expected "{key} in {expression}".`);const c=[],[l,h]=o;await this.watchExpr(h,{$elem:e},(s=>(this.log(":for list items:",s),this.lock=this.lock.then((()=>new Promise((async r=>{if(c.splice(0,c.length).forEach((e=>{a.removeChild(e),this.skipNodes.delete(e)})),!Array.isArray(s))return console.error(`Expression did not yield a list: \`${h}\` => \`${s}\``),r();for(const r of s.slice(0).reverse()){const s=this.clone();await s.set(l,r);const i=e.cloneNode(!0);a.insertBefore(i,n.nextSibling),c.push(i),this.skipNodes.add(i),await s.mount(i,t),this.log("Rendered list child:\n",i,i.outerHTML)}r()})))),this.lock)))}},e.resolveBindAttribute=async function(e,t){if(this.skipNodes.has(e))return;const s=e,r=s.getAttribute?.(":bind");if(r){this.log(":bind attribute found in:\n",e);const t=["change","input"],i=s.getAttribute?.(":bind-events")?.split(",")||t;s.removeAttribute(":bind"),s.removeAttribute(":bind-events");const a="checkbox"===s.getAttribute("type")?"checked":"value",n=`$elem.${a} = ${r}`;await this.watchExpr(n,{$elem:e},(e=>s[a]=e));const o=`${r} = $elem.${a}`;for(const t of i)e.addEventListener(t,(()=>this.eval(o,{$elem:e})))}},e.resolveShowAttribute=async function(e,t){if(this.skipNodes.has(e))return;const s=e,r=s.getAttribute?.(":show");if(r){this.log(":show attribute found in:\n",e),s.removeAttribute(":show");const t="none"===s.style.display?"":s.style.display;await this.watchExpr(r,{$elem:e},(e=>{s.style.display=e?t:"none"}))}}}(o||(t.RendererPlugins=o={}))},63:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.proxifyStore=t.ReactiveProxyStore=t.InertProxy=t.ReactiveProxy=t.proxifyObject=t.REACTIVE_DEBOUNCE_MILLIS=void 0;class s{timeouts=new Map;debounce(e,t){return new Promise(((s,r)=>{const i=this.timeouts.get(t);i&&clearTimeout(i),this.timeouts.set(t,setTimeout((()=>{try{s(t()),this.timeouts.delete(t)}catch(e){r(e)}}),e))}))}}function r(e,t,s=!0){if(null==e||function(e){return e instanceof i||e.__is_proxy__}(e))return e;if(s)for(const s in e)e.hasOwnProperty(s)&&"object"==typeof e[s]&&null!=e[s]&&(e[s]=r(e[s],t));return new Proxy(e,{deleteProperty:(e,s)=>s in e&&(delete e[s],t(),!0),set:(e,i,a,n)=>{s&&"object"==typeof a&&(a=r(a,t));const o=Reflect.set(e,i,a,n);return t(),o},get:(e,t,s)=>"__is_proxy__"===t||Reflect.get(e,t,s)})}t.REACTIVE_DEBOUNCE_MILLIS=10,t.proxifyObject=r;class i extends s{value=null;listeners=[];constructor(e=null,...t){super(),this.value=this.wrapObjValue(e),t.forEach((e=>this.watch(e)))}static from(e,...t){return e instanceof i?(t.forEach(e.watch),e):new i(e,...t)}wrapObjValue(e){return null===e||"object"!=typeof e?e:r(e,(()=>this.trigger()))}get(){return this.value}async set(e){if(this.value!==e){const t=this.value;this.value=this.wrapObjValue(e),await this.trigger(t)}}watch(e){this.listeners.push(e)}unwatch(e){this.listeners=this.listeners.filter((t=>t!==e))}trigger(e=null){const s=this.listeners.slice();return this.debounce(t.REACTIVE_DEBOUNCE_MILLIS,(()=>Promise.all(s.map((t=>t(this.value,e)))).then((()=>{}))))}}t.ReactiveProxy=i;class a extends i{static from(e,...t){return e instanceof i?e:new a(e,...t)}watch(e){}trigger(e){return Promise.resolve()}}function n(e,t=(()=>{})){const s=Array.from(e.entries()).map((([e])=>e)),r=Object.fromEntries(s.map((e=>[e,void 0])));return new Proxy(Object.assign({},e,r),{get:(s,r,i)=>"string"==typeof r&&e.has(r)?(t("get",r),e.get(r)):"get"===r?s=>(t("get",s),e.get(s)):Reflect.get(e,r,i),set:(s,r,i,a)=>("string"!=typeof r||r in e?Reflect.set(e,r,i,a):(t("set",r,i),e.set(r,i)),!0)})}t.InertProxy=a,t.ReactiveProxyStore=class extends s{store=new Map;debouncedListeners=new Map;lock=Promise.resolve();constructor(e){super();for(const[t,s]of Object.entries(e||{}))this.store.set(t,i.from(this.wrapFnValue(s)))}wrapFnValue(e){return e&&"function"==typeof e?(...t)=>e.call(n(this),...t):e}get $(){return n(this)}entries(){return this.store.entries()}get(e){return this.store.get(e)?.get()}async set(e,t){this.store.has(e)?await this.store.get(e).set(this.wrapFnValue(t)):this.store.set(e,i.from(this.wrapFnValue(t)))}del(e){return this.store.delete(e)}has(e){return this.store.has(e)}async update(e){await Promise.all(Object.entries(e).map((([e,t])=>this.set(e,t))))}watch(e,s){e=Array.isArray(e)?e:[e];const r=()=>s(...e.map((e=>this.store.get(e).get()))),i=()=>this.debounce(t.REACTIVE_DEBOUNCE_MILLIS,r);e.forEach((e=>this.store.get(e).watch(i))),this.debouncedListeners.set(s,i)}unwatch(e,t){(e=Array.isArray(e)?e:[e]).forEach((e=>this.store.get(e).unwatch(this.debouncedListeners.get(t)))),this.debouncedListeners.delete(t)}async trigger(e){e=Array.isArray(e)?e:[e],await Promise.all(e.map((e=>this.store.get(e).trigger())))}async trace(e){const t=new Set,s=n(this,((e,s)=>{"get"===e&&t.add(s)}));return[await e.call(s),Array.from(t)]}async computed(e,t){const[s,r]=await this.trace(t);this.watch(r,(async()=>this.set(e,await t.call(n(this))))),this.set(e,s)}},t.proxifyStore=n}},t={};function s(r){var i=t[r];if(void 0!==i)return i.exports;var a=t[r]={exports:{}};return e[r](a,a.exports,s),a.exports}(()=>{const e=s(283);class t extends e.IRenderer{dirpath=(0,e.dirname)(self.location.href);parseHTML(e,t={root:!1}){if(t.root)return(new DOMParser).parseFromString(e,"text/html");{const t=document.createRange();return t.selectNodeContents(document.body),t.createContextualFragment(e)}}serializeHTML(e){return(new XMLSerializer).serializeToString(e).replace(/\s?xmlns="[^"]+"/gm,"")}preprocessLocal(e,t){return this.preprocessRemote(e,t)}}const r=new t;self.Mancha=r;const i=self.document?.currentScript;if(self.document?.currentScript?.hasAttribute("init")){r.update({...i?.dataset});const e=i?.hasAttribute("debug"),t=i?.getAttribute("cache");(i?.getAttribute("target")?.split(",")||["body"]).map((async s=>{const i=self.document.querySelector(s);await r.debug(e).mount(i,{cache:t})}))}})()})();
|
package/dist/plugins.js
CHANGED
|
@@ -302,8 +302,8 @@ var RendererPlugins;
|
|
|
302
302
|
if (this.skipNodes.has(node))
|
|
303
303
|
return;
|
|
304
304
|
const elem = node;
|
|
305
|
-
const
|
|
306
|
-
if (
|
|
305
|
+
const bindExpr = elem.getAttribute?.(":bind");
|
|
306
|
+
if (bindExpr) {
|
|
307
307
|
this.log(":bind attribute found in:\n", node);
|
|
308
308
|
// The change events we listen for can be overriden by user.
|
|
309
309
|
const defaultEvents = ["change", "input"];
|
|
@@ -313,17 +313,15 @@ var RendererPlugins;
|
|
|
313
313
|
elem.removeAttribute(":bind-events");
|
|
314
314
|
// If the element is of type checkbox, we bind to the "checked" property.
|
|
315
315
|
const prop = elem.getAttribute("type") === "checkbox" ? "checked" : "value";
|
|
316
|
-
//
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
//
|
|
320
|
-
|
|
316
|
+
// Watch for updates in the store and bind our property ==> node value.
|
|
317
|
+
const propExpr = `$elem.${prop} = ${bindExpr}`;
|
|
318
|
+
await this.watchExpr(propExpr, { $elem: node }, (result) => (elem[prop] = result));
|
|
319
|
+
// Bind node value ==> our property.
|
|
320
|
+
const nodeExpr = `${bindExpr} = $elem.${prop}`;
|
|
321
321
|
// Watch for updates in the node's value.
|
|
322
322
|
for (const event of updateEvents) {
|
|
323
|
-
node.addEventListener(event, () => this.
|
|
323
|
+
node.addEventListener(event, () => this.eval(nodeExpr, { $elem: node }));
|
|
324
324
|
}
|
|
325
|
-
// Watch for updates in the store.
|
|
326
|
-
this.watch([bindKey], () => (elem[prop] = this.get(bindKey)));
|
|
327
325
|
}
|
|
328
326
|
};
|
|
329
327
|
RendererPlugins.resolveShowAttribute = async function (node, params) {
|
|
@@ -335,8 +333,8 @@ var RendererPlugins;
|
|
|
335
333
|
this.log(":show attribute found in:\n", node);
|
|
336
334
|
// Remove the processed attributes from node.
|
|
337
335
|
elem.removeAttribute(":show");
|
|
338
|
-
// TODO: Instead of using element display, insert a dummy <template> to track position of
|
|
339
|
-
// then replace it with the original child when needed.
|
|
336
|
+
// TODO: Instead of using element display, insert a dummy <template> to track position of
|
|
337
|
+
// child, then replace it with the original child when needed.
|
|
340
338
|
// Store the original display value to reset it later if needed.
|
|
341
339
|
const display = elem.style.display === "none" ? "" : elem.style.display;
|
|
342
340
|
// Compute the function's result and track dependencies.
|
package/dist/reactive.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ declare abstract class IDebouncer {
|
|
|
5
5
|
debounce<T>(millis: number, callback: () => T | Promise<T>): Promise<T>;
|
|
6
6
|
}
|
|
7
7
|
/** Default debouncer time in millis. */
|
|
8
|
-
export declare const REACTIVE_DEBOUNCE_MILLIS =
|
|
8
|
+
export declare const REACTIVE_DEBOUNCE_MILLIS = 10;
|
|
9
9
|
export declare function proxifyObject<T extends object>(object: T, callback: () => void, deep?: boolean): T;
|
|
10
10
|
export declare class ReactiveProxy<T> extends IDebouncer {
|
|
11
11
|
private value;
|
package/dist/reactive.js
CHANGED
|
@@ -24,7 +24,7 @@ function isProxified(object) {
|
|
|
24
24
|
return object instanceof ReactiveProxy || object["__is_proxy__"];
|
|
25
25
|
}
|
|
26
26
|
/** Default debouncer time in millis. */
|
|
27
|
-
exports.REACTIVE_DEBOUNCE_MILLIS =
|
|
27
|
+
exports.REACTIVE_DEBOUNCE_MILLIS = 10;
|
|
28
28
|
function proxifyObject(object, callback, deep = true) {
|
|
29
29
|
// If this object is already a proxy, return it as-is.
|
|
30
30
|
if (object == null || isProxified(object))
|