hmpl-js
Version:
🐜 Server-oriented customizable templating for JavaScript
1 lines • 15.4 kB
JavaScript
!function(e,t){"object"==typeof module&&module.exports?module.exports=t():"function"==typeof define&&define.amd?define([],t):e.hmpl=e.hmpl||t()}("undefined"!=typeof self?self:this,(function(){return function(){"use strict";const e="src",t="method",o="initId",n="after",r="repeat",s="memo",i="indicators",l="autoBody",c="hmpl",d="formData",a="disallowedTags",p="sanitize",f="allowedContentTypes",u="get",h="interval",m="BadResponseError",y="RequestInitError",$="RenderError",g="RequestComponentError",b="CompileOptionsError",v="ParseError",w="CompileError",T={formData:!0},N={formData:!1},O=[e,t,o,n,r,i,s,l,f,a,p,h],P=["get","post","put","delete","patch","trace","options"],E=[100,101,102,103,300,301,302,303,304,305,306,307,308,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,421,422,423,424,425,426,428,429,431,451,500,501,502,503,504,505,506,507,508,510,511],j=["script","style","iframe"],x=["text/html"],k=[],I=e=>"object"==typeof e&&!Array.isArray(e)&&null!==e,C=e=>"[object Function]"===Object.prototype.toString.call(e),A=e=>{throw new Error(e)},q=e=>{console.warn(e)},D=(e,t=!1)=>{let o=e;t&&(o=DOMPurify.sanitize(e));return(new DOMParser).parseFromString(`<template>${o}</template>`,"text/html").childNodes[0].childNodes[0].firstChild},R=(e,t,o,n,r,s,i,l,c={},d,a,p,f,u,h,$)=>{const{mode:g,cache:b,redirect:v,get:w,referrerPolicy:T,signal:N,credentials:O,timeout:P,referrer:E,headers:j,body:x,window:k,integrity:C}=c,R={method:n.toUpperCase()};void 0!==O&&(R.credentials=O),void 0!==x&&(R.body=x),void 0!==g&&(R.mode=g),void 0!==b&&(R.cache=b),void 0!==v&&(R.redirect=v),void 0!==T&&(R.referrerPolicy=T),void 0!==C&&(R.integrity=C),void 0!==E&&(R.referrer=E);const F=void 0!==N;if(F&&(R.signal=N),void 0!==k&&(R.window=k),void 0!==c.keepalive&&q(`${y}: The "keepalive" property is not yet supported`),j)if(I(j)){const e=new Headers;for(const t in j){const o=j[t],n=typeof o;"string"===n?e.set(t,o):A(`${y}: Expected type string, but received type ${n}`)}R.headers=e}else A(`${y}: The "headers" property must contain a value object`);P&&(F?q(`${y}: The "signal" property overwrote the AbortSignal from "timeout"`):R.signal=AbortSignal.timeout(P));const M=l&&!s&&o?.memo,B=e=>"rejected"===e||"number"==typeof e&&(e<200||e>299),L=S(void 0,$),z=e=>{i&&(u.response=e,w?.("response",e,L,u)),w?.("response",t,L)},W=(t,n=!0,r=!1)=>{if(s)d.response=t.cloneNode(!0),w?.("response",t,L);else{let s=[];const i=[...(n?t.cloneNode(!0):t).content.childNodes];if(o.nodes){const e=o.parentNode,t=[],n=o.nodes.length;for(let r=0;r<n;r++){const s=o.nodes[r];if(r===n-1)for(let o=0;o<i.length;o++){const n=i[o],r=e.insertBefore(n,s);t.push(r)}e.removeChild(s)}s=t.slice(),o.nodes=t}else{const t=e.parentNode,n=[],r=i.length;for(let o=0;o<r;o++){const r=i[o],s=t.insertBefore(r,e);n.push(s)}t.removeChild(e),s=n.slice(),o.nodes=n,o.parentNode=t}M&&r&&(o.memo.nodes=o.nodes,o.memo.isPending&&(o.memo.isPending=!1)),z(s)}};let U=!1;const H=()=>{if(s)d.response=void 0,w?.("response",void 0,L);else if(o?.nodes){const e=o.parentNode,n=o.nodes.length;for(let t=0;t<n;t++){const r=o.nodes[t];t===n-1&&e.insertBefore(o.comment,r),e.removeChild(r)}o.nodes=null,o.parentNode=null,i&&(u.response=void 0,w?.("response",void 0,L,u)),w?.("response",t,L)}M&&null!==o.memo.response&&(o.memo.response=null,delete o.memo.isPending,delete o.memo.nodes)},_=e=>{i?u.status!==e&&(u.status=e,w?.("status",e,L,u)):d.status!==e&&(d.status=e,w?.("status",e,L)),M&&B(e)&&(o.memo.response=null,delete o.memo.nodes),(e=>{if(h)if(M&&"pending"!==e&&B(e)&&o.memo.isPending&&(o.memo.isPending=!1),"pending"===e){const e=h.pending;void 0!==e&&(M&&(o.memo.isPending=!0),W(e))}else if("rejected"===e){const e=h.rejected;if(void 0!==e)W(e);else{const e=h.error;void 0!==e?W(e):H()}}else{const t=h[`${e}`];if(e>399)if(void 0!==t)W(t);else{const e=h.error;void 0!==e?W(e):H()}else(e<200||e>299)&&(U=!0,void 0!==t?W(t):H())}})(e)};let G=200;_("pending");let V=!0,J=!0;fetch(r,R).then((e=>{if(V=!1,G=e.status,_(G),e.ok||(h&&(J=!1),A(`${m}: Response with status code ${G}`)),Array.isArray(a)&&0!==a.length){const t=e.headers.get("Content-Type");((e,t)=>{if(!e)return!0;let o=!1;for(let n=0;n<t.length;n++){const r=t[n];if(e.includes(r)){o=!0;break}}return!o})(t,a)&&A(`${m}: Expected ${a.map((e=>`"${e}"`)).join(", ")}, but received "${t}"`)}return e.text()})).then((n=>{if(!U){if(M){const{response:e}=o.memo;if(null===e)o.memo.response=n;else{if(e===n)return void(()=>{if(o.memo.isPending){const e=o.parentNode,t=o.memo.nodes,n=o.nodes,r=n.length,s=[];for(let o=0;o<r;o++){const i=n[o];if(o===r-1)for(let o=0;o<t.length;o++){const n=t[o],r=e.insertBefore(n,i);s.push(r)}e.removeChild(i)}o.nodes=s.slice(),o.memo.isPending=!1,o.memo.nodes=s.slice()}const e=o.nodes.slice();z(e)})();o.memo.response=n,delete o.memo.nodes}}const r=((e,t=[],o)=>{const n=D(e,o),r=n.content;for(let e=0;e<t.length;e++){const o=t[e],n=r.querySelectorAll(o);for(let e=0;e<n.length;e++)r.removeChild(n[e])}return n})(n,p,f);if(s)d.response=r,w?.("response",r,L);else{const n=[],s=[...r.content.childNodes];if(o)W(r,!1,!0);else{const o=e.parentNode;for(let t=0;t<s.length;t++){const r=s[t],l=o.insertBefore(r,e);i&&n.push(l)}o.removeChild(e),i&&(u.response=n,w?.("response",n,L,u)),w?.("response",t,L)}}}})).catch((e=>{throw V?(_("rejected"),h||H()):J&&H(),e}))},S=(e,t)=>{const o={};return void 0!==e&&(o.event=e),t&&(o.clearInterval=t),{request:o}},F=(i,d,u,m,b,w,O,j,q,F=!1)=>{const W=(i,c)=>{const d=i[e];if(d){const e=(i[t]||"GET").toLowerCase();if((e=>P.includes(e.toLowerCase()))(e)){const t=i[n];t&&F&&A(`${$}: EventTarget is undefined`);const u=!i.hasOwnProperty(r),v="all"===(!!u||i[r]?"all":"one"),P=i[h],W=!i.hasOwnProperty(s),H=!i.hasOwnProperty(h);let _=!b&&m[s];W?_&&(t&&v||(_=!1)):t?i[s]?v?_=!0:A(`${g}: Memoization works in the enabled repetition mode`):_=!1:A(`${g}: Memoization works in the enabled repetition mode`),H||v&&t&&A(`${g}: The "${h}" property does not work with repetiton mode yet`);const G=!i.hasOwnProperty(l);let V=!w&&m[l];if(G)!0===V&&(V=T),t||(V=!1);else if(t){let e=i[l];if(B(e),!0===V&&(V=T),!0===e&&(e=T),!1===e)V=!1;else{const t={...!1===V?N:V,...e};V=t}}else V=!1,A(`${g}: The "${l}" property does not work without the "${n}" property`);const J=!i.hasOwnProperty(f);let X=O?x:m[f];if(!J){const e=i[f];M(e),X=e}const Z=!i.hasOwnProperty(a);let K=j?k:m[a];if(!Z){const e=i[a];L(e),K=e}const Q=!i.hasOwnProperty(p);let Y=!q&&m[p];if(!Q){const e=i[p];z(e),Y=e}const ee=i[o],te=i.nodeId;let oe=i.indicators;if(oe){const e=e=>{const{trigger:t,content:o}=e;t||A(`${g}: Failed to activate or detect the indicator`),o||A(`${g}: Failed to activate or detect the indicator`),-1===E.indexOf(t)&&"pending"!==t&&"rejected"!==t&&"error"!==t&&A(`${g}: Failed to activate or detect the indicator`);const n=D(o);return{...e,content:n}},t={},o=[];for(let n=0;n<oe.length;n++){const r=e(oe[n]),{trigger:s}=r;-1===o.indexOf(s)?o.push(s):A(`${g}: Indicator trigger must be unique`),t[`${s}`]=r.content}oe=t}const ne=(e,t=!1)=>{if(t){if(ee){let t;for(let o=0;o<e.length;o++){const n=e[o];if(n.id===ee){t=n.value;break}}return t||A(`${g}: ID referenced by request not found`),t}return{}}return ee&&A(`${g}: ID referenced by request not found`),e},re=void 0!==P,se=v&&t||re,ie=(t,o,n,r,s,i=!1,l,a=!1,p,f,u)=>{const h=r.currentId;if(F)t||(t=c);else if(!t){let e;const{els:o}=r;for(let t=0;t<o.length;t++){const n=o[t];if(n.id===te){p=n,e=n.el;break}}t=e}let m;F||(se||oe)&&(m=p.objNode,m||(m={id:h,nodes:null,parentNode:null,comment:t},_&&(m.memo={response:null},oe&&(m.memo.isPending=!1)),re&&u&&(m.interval={value:u,clearInterval:()=>clearInterval(u)}),p.objNode=m,r.dataObjects.push(m),r.currentId++));let $=ne(o,i);const g=C($);if(!g&&$&&($={...$}),V&&V.formData&&f&&!g){const{type:e,target:t}=f;"submit"===e&&t&&t instanceof HTMLFormElement&&"FORM"===t.nodeName&&($.body=new FormData(t,f.submitter))}let b=u?()=>clearInterval(u):void 0;b=F?b:m?.interval?.clearInterval;const v=g?((e,t,o)=>e(S(t,o)))($,f,b):$;I(v)||void 0===v||A(`${y}: Expected an object with initialization options`),R(t,s,m,e,d,F,a,_,v,n,X,K,Y,l,oe,b)};let le=ie;if(P){U(P);const e=Number(P);le=(t,o,n,r,s,i=!1,l,c=!1,d,a)=>{let p=null;p=setInterval((()=>{ie(t,o,n,r,s,i,l,c,d,a,p)}),e)}}let ce=le;if(t){const e=(e,t,o,n,r,s,i,l,c,d,a)=>{const p=c.querySelectorAll(o);0===p.length&&A(`${$}: Selectors nodes not found`);const f=v?t=>{le(e,n,r,s,c,i,d,l,a,t)}:o=>{le(e,n,r,s,c,i,d,l,a,o);for(let e=0;e<p.length;e++){p[e].removeEventListener(t,f)}};for(let e=0;e<p.length;e++){p[e].addEventListener(t,f)}};if(t.indexOf(":")>0){const o=t.split(":"),n=o[0],r=o.slice(1).join(":");ce=(t,o,s,i,l,c=!1,d,a=!1,p)=>{e(t,n,r,o,s,i,c,a,l,d,p)}}else A(`${g}: The "${n}" property doesn't work without EventTargets`)}else u||A(`${g}: The "${r}" property doesn't work without "${n}" property`);return ce}A(`${g}: The "${t}" property has only GET, POST, PUT, PATCH, TRACE, OPTIONS or DELETE values`)}else A(`${g}: The "${e}" property are not found or empty`)};let H;if(F)u[0].el=i,H=W(u[0]);else{let e=-2;const t=o=>{if(e++,8==o.nodeType){let t=o.nodeValue;if(t&&t.startsWith(c)){t=t.slice(4);const n=Number(t),r=u[n];(Number.isNaN(n)||void 0===r)&&A(`${v}: Request object with id "${n}" not found`),r.el=o,r.nodeId=e}}if(o.hasChildNodes()){const e=o.childNodes;for(let o=0;o<e.length;o++)t(e[o])}};if(t(i),u.length>1){const e=[];for(let t=0;t<u.length;t++){const o=u[t];e.push(W(o,i))}H=(t,o,n,r,s,i=!1)=>{t||(t=s);const l=[],c=r.els;for(let s=0;s<c.length;s++){const d=c[s],a=d.el,p={response:void 0};(0,e[s])(a,o,n,r,t,i,p,!0,d),l.push(p)}n.requests=l}}else{H=W(u[0],i)}}return d(H)},M=(e,t=!1)=>{const o=t?b:g;"*"===e||((e,t)=>{if(!Array.isArray(e))return!1;let o=!0;for(let n=0;n<e.length;n++)if("string"!=typeof e[n]){A(`${t}: In the array, the element with index ${n} is not a string`),o=!1;break}return o})(e,o)||A(`${o}: Expected "*" or string array, but got neither`)},B=(e,t=!1)=>{const o=I(e),n=t?b:g;if("boolean"==typeof e||o||A(`${n}: Expected a boolean or object, but got neither`),o)for(const t in e)if(t===d)"boolean"!=typeof e[d]&&A(`${n}: The "${d}" property should be a boolean`);else A(`${n}: Unexpected property "${t}"`)},L=(e,t=!1)=>{const o=t?b:g;Array.isArray(e)||A(`${o}: The value of the property "${a}" must be an array`);for(let t=0;t<e.length;t++){const n=e[t];j.includes(n)||A(`${o}: The value "${n}" is not processed`)}},z=(e,t=!1)=>{"boolean"!=typeof e&&A(`${t?b:g}: The value of the property "${p}" must be a boolean`)},W=e=>{e.hasOwnProperty("id")&&e.hasOwnProperty("value")||A(`${y}: Missing "id" or "value" property`)},U=e=>{"number"!=typeof e&&A(`${g}: The "${h}" value must be number`)};return{compile:(e,t={})=>{"string"!=typeof e&&A(`${w}: Template was not found or the type of the passed value is not string`),e||A(`${w}: Template must not be a falsey value`),I(t)||A(`${b}: Options must be an object`);const n=!t.hasOwnProperty(s);n||"boolean"==typeof t[s]||A(`${b}: The value of the property ${s} must be a boolean`);const d=!t.hasOwnProperty(l);d||B(t[l],!0);const m=!t.hasOwnProperty(f);m||M(t[f],!0);const T=!t.hasOwnProperty(a);T||L(t[a],!0);const N=!t.hasOwnProperty(p);N||z(t[p],!0);const P=[],E=[],j=e=>{let t="";const o=/\s*([a-zA-Z0-9_-]+)\s*=\s*(("[^"]*"|'[^']*'|`[^`]*`|\[[^\]]*\]|\{[^}]*\}|true|false|\d+)(?=\s|,|$))\s*/g;let n;for(;null!==(n=o.exec(e));){t+=`${n[1].trim()}:${n[2].trim()},`}return t.replace(/,$/,"").trim()},x=(e=>{const t=[];let o=0;const n=[{open:"{{#request",close:"{{/request}}"},{open:"{{#r",close:"{{/r}}"}];for(;o<e.length;){const r=n.map((t=>({...t,index:e.indexOf(t.open,o)}))).filter((e=>-1!==e.index));if(0===r.length){t.push(e.slice(o));break}const s=r.sort(((e,t)=>e.index-t.index))[0];t.push(e.slice(o,s.index));const i=s.index+s.open.length,l=e.indexOf("}}",i);-1===l&&A(`${v}: Unclosed block (no ending '}}') for ${s.open}`);const c=e.slice(i,l).trim(),d=e.indexOf(s.close,l);-1===d&&A(`${v}: No closing '${s.close}' found for ${s.open}}}`);const a=e.slice(l+2,d);a.includes(s.open)&&A(`${v}: Nested ${s.open}}} blocks are not supported`);const p=j(c);if(s.open.startsWith(n[0].open)||s.open.startsWith(n[1].open)){const e=[],o=/{{#indicator\s+([^}]*)}}([\s\S]*?){{\/indicator}}/g;let n;for(;null!==(n=o.exec(a));){const t=n[1].trim();let o=n[2].trim();const r="{{#indicator";o.includes(r)&&A(`${v}: Nested ${r}}} blocks are not supported`);const s=j(t);o=o.replace(/\n/g," ").replace(/\s+/g," ").trim();const i=o.replace(/\\/g,"\\\\").replace(/`/g,"\\`").replace(/\$/g,"\\$");e.push(`{${s}, content:'${i}'}`)}const r=e.length>0?`,indicators:[${e.join(",")}]`:"";t.push(`{${p}${r}}`)}E.push(t.length-1),o=d+s.close.length}return t})(e);0===E.length&&A(`${v}: Request object not found`);const k=e=>{const t=JSON5.parse(e);for(const e in t){const n=t[e];switch(O.includes(e)||A(`${g}: Property "${e}" is not processed`),e){case i:Array.isArray(n)||A(`${g}: The value of the property "${e}" must be an array`);break;case o:"string"!=typeof n&&"number"!=typeof n&&A(`${g}: The value of the property "${e}" must be a string`);break;case s:case r:"boolean"!=typeof n&&A(`${g}: The value of the property "${e}" must be a boolean value`);break;case l:B(n);break;case f:M(n);break;case a:L(n);break;case p:z(n);break;case h:U(n);break;default:"string"!=typeof n&&A(`${g}: The value of the property "${e}" must be a string`)}}const n={...t};P.push(n)};for(let e=0;e<E.length;e++){const t=E[e];k(x[t]);const o=`\x3c!--hmpl${e}--\x3e`;x[t]=o}e=x.join("");let q=!1;const R=(e=>{const t=D(e.trim());(t.content.childNodes.length>1||1!==t.content.children.length&&8!==t.content.childNodes[0].nodeType)&&A(`${$}: Template includes only one node of the Element type or one response object`);const o=e=>{switch(e.nodeType){case Node.ELEMENT_NODE:if("PRE"===e.tagName)return;break;case Node.TEXT_NODE:if(!/\S/.test(e.textContent))return void e.remove()}for(let t=0;t<e.childNodes.length;t++)o(e.childNodes.item(t))};o(t.content.childNodes[0]);let n=t.content.firstElementChild;if(!n){const e=t.content.firstChild;8===e?.nodeType&&(q=!0,n=e)}return n})(e);return F(R,(e=>(t={})=>{const o=R.cloneNode(!0),n={response:q?void 0:o},r={dataObjects:[],els:[],currentId:0};if(!q){let e=-2;const t=o=>{if(e++,8==o.nodeType){const t=o.nodeValue;if(t&&t.startsWith(c)){const t={el:o,id:e};r.els.push(t)}}if(o.hasChildNodes()){const e=o.childNodes;for(let o=0;o<e.length;o++)t(e[o])}};t(o)}var s;return I(t)||C(t)?(I(s=t)&&s.hasOwnProperty(`${u}`)&&(C(s[u])||A(`${y}: The "${u}" property has a function value`)),e(void 0,t,n,r,o)):Array.isArray(t)?((e=>{const t=[];for(let o=0;o<e.length;o++){const n=e[o];I(n)||A(`${y}: IdentificationRequestInit is of type object`),W(n);const{id:r}=n,s="string"==typeof n.id;s||"number"==typeof n.id||A(`${y}: ID must be a string or a number`),t.indexOf(r)>-1?A(`${y}: ID with value ${s?`"${r}"`:r} already exists`):t.push(r)}})(t),e(void 0,t,n,r,o,!0)):A(`${y}: The type of the value being passed does not match the supported types for RequestInit`),n}),P,t,n,d,m,T,N,q)},stringify:e=>{const t=e=>"string"==typeof e?`"${e}"`:"number"==typeof e||"boolean"==typeof e?`${e}`:Array.isArray(e)?`[${e.map((e=>t(e))).join(",")}]`:"object"==typeof e&&null!==e?`{${Object.entries(e).map((([e,o])=>`${e}:${t(o)}`)).join(",")}}`:"";let o=Object.entries(e).map((([e,o])=>`${e}=${t(o)}`)).join(" ");return o.endsWith("}")&&(o+=" "),`{{#request ${o}}}{{/request}}`}}}()}));