solarite 0.2.2 → 0.2.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/Solarite-debug.js +357 -39
- package/dist/Solarite.js +357 -39
- package/dist/Solarite.min.js +2 -2
- package/package.json +3 -2
- package/src/solarite/ExprPath.js +84 -17
- package/src/solarite/Globals.js +3 -2
- package/src/solarite/NodeGroup.js +37 -0
- package/src/solarite/Solarite.js +1 -1
- package/src/solarite/Template.js +5 -3
- package/src/solarite/getArg.js +18 -8
- package/src/solarite/watch3.js +119 -28
- package/src/util/MultiValueMap.js +35 -2
- package/src/util/Util.js +26 -1
- package/src/unused/FastLookupArray.js +0 -54
- package/src/unused/Hashes.js +0 -339
- package/src/unused/InUse.test.js +0 -92
- package/src/unused/InUseMap.js +0 -98
- package/src/unused/LinkedList.js +0 -117
- package/src/unused/LinkedList.test.js +0 -115
- package/src/unused/NodeGroupManager.js +0 -444
- package/src/unused/Perf.js +0 -47
- package/src/unused/Template.js +0 -108
- package/src/unused/onConnect.js +0 -79
- package/src/unused/watch.js +0 -302
- package/src/unused/watch2.js +0 -439
package/dist/Solarite.min.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Version 2024.10.
|
|
1
|
+
// Version 2024.10.10.0426
|
|
2
2
|
// License: MIT
|
|
3
3
|
// http://vorticode.github.io/solarite
|
|
4
|
-
var Util$1={callback(...t){var e=!1;let l=async(...t)=>{let s=[];if(!e)for(let e=0;l.length>e;e++)s.push(l[e](...t));return await Promise.all(s)};l[Symbol.iterator]=()=>{let t=0;return{next(){return l.length>t?{value:l[t++],done:!1}:{done:!0}}}};for(let t of Object.getOwnPropertyNames(Array.prototype))"length"!==t&&"constructor"!==t&&(l[t]=Array.prototype[t]);l.l=0,Object.defineProperty(l,"length",{get:()=>l.l,set(t){l.l=t}}),l.remove=t=>{let e=l.findIndex((e=>e===t));-1!==e&&l.splice(e,1)},l.pause=()=>e=!0,l.resume=()=>e=!1;for(let e of t)l.push(e);return l}};function delve(t,e,l=delveDontCreate){let s=l!==delveDontCreate,i=e.length;if(!t&&!s&&i)return;let o=0;for(let r of e){if(void 0===t[r]){if(!s)return;i-1>o&&(t[r]=(e[o+1]+"").match(/^\d+$/)?[]:{})}s&&o===i-1&&(t[r]=l),t=t[r],o++}return t}let delveDontCreate={};function getArg(el,name,val=null,type=ArgType.String){let attrVal=el.getAttribute(name);if(null!==attrVal&&(val=attrVal),Array.isArray(type))return type.includes(val)?val:void 0;if("function"==typeof type)return type(val);if(type===ArgType.Bool){let t="string"==typeof val?val.toLowerCase():val;return!["false","0",!1,0,null,void 0].includes(t)}switch(type){case ArgType.Int:return parseInt(val);case ArgType.Float:return parseFloat(val);case ArgType.String:return[void 0,null,!1].includes(val)?"":val+"";case ArgType.JSON:case ArgType.Eval:if("string"!=typeof val||!val.length)return val;try{return type===ArgType.JSON?JSON.parse(val):eval(`(${val})`)}catch(t){return val}default:return val}}var ArgType={Bool:"Bool",Int:"Int",Float:"Float",String:"String",JSON:"JSON",Eval:"Eval"};let lastObjectId=1,objectIds=new WeakMap;function getObjectId(t){let e=objectIds.get(t);return e||(e=lastObjectId++,objectIds.set(t,e)),e}let isHashing=!0;function toJSON(){return isHashing?getObjectId(this):this}function getObjectHash(t){let e;isHashing=!0;try{e=JSON.stringify(t)}catch(l){e=getObjectHashCircular(t)}return isHashing=!1,e}function getObjectHashCircular(t){const e=new Set;return JSON.stringify(t,((t,l)=>{if("object"==typeof l&&null!==l){if(e.has(l))return getObjectId(l);e.add(l)}return l}))}Node.prototype.toJSON!==toJSON&&(Node.prototype.toJSON=toJSON,(()=>{}).toJSON!==toJSON&&(Function.prototype.toJSON=toJSON));var Globals={componentHash:new WeakMap,connected:new WeakSet,rendered:new WeakSet,currentExprPath:[],elementClasses:{},nodeEvents:new WeakMap,nodeGroups:new WeakMap,objToEl:new WeakMap,pendingChildren:[],rendering:new WeakSet,shells:new WeakMap};let Util={bindStyles(t,e){let l=e.getAttribute("data-style");l||(e.constructor.styleId||(e.constructor.styleId=1),l=e.constructor.styleId++,e.setAttribute("data-style",l));let s=e.tagName.toLowerCase();for(let e of t.childNodes)if(3===e.nodeType){let t=e.textContent,i=t.replace(/:host(?=[^a-z0-9_])/gi,s+'[data-style="'+l+'"]');t!==i&&(e.textContent=i)}},*flatten(t){if(Array.isArray(t))for(const e of t)yield*Util.flatten(e);else if("function"==typeof t){const e=t();yield*Util.flatten(e)}else yield t},getInputValue:t=>"checkbox"===t.type||"radio"===t.type?t.checked:"file"===t.type?[...t.files]:"number"===t.type||"range"===t.type?t.valueAsNumber:"date"===t.type||"time"===t.type||"datetime-local"===t.type?t.valueAsDate:"select-multiple"===t.type?[...t.selectedOptions].map((t=>t.value)):t.value,isPath:t=>Array.isArray(t)&&"object"==typeof t[0]&&!t.slice(1).find((t=>"string"!=typeof t&&"number"!=typeof t)),saveOrphans(t,e){let l=new Map;for(let e of t)l.set(e.startNode,e);for(let t,s=0;t=e[s];s++){let i;if(!t.parentNode&&(i=l.get(t))){let l=document.createDocumentFragment(),o=i.endNode;for(;t!==o;)l.append(t),s++,t=e[s];l.append(o)}}},trimEmptyNodes(t){const e=t=>t.nodeType!==Node.ELEMENT_NODE&&(t.nodeType!==Node.TEXT_NODE||""===t.textContent.trim()),l=[...t];for(;l.length>0&&e(l[0]);)l.shift();for(;l.length>0&&e(l[l.length-1]);)l.pop();return l}},div=document.createElement("div"),isEvent=t=>t.startsWith("on")&&t in div;function camelToDashes(t){return(t=(t=(t=t.replace(/([a-z0-9])([A-Z])/g,"$1-$2")).replace(/([A-Z])([A-Z][a-z])/g,"$1-$2")).replace(/([a-zA-Z])([0-9])/g,"$1-$2")).toLowerCase()}function arraySame(t,e){let l=t.length;if(l!==e.length)return!1;for(let s=0;l>s;s++)if(t[s]!==e[s])return!1;return!0}function htmlContext(t){if(null===t)return state={...defaultState},state.context;for(let e=0;t.length>e;e++){const l=t[e];switch(state.context){case htmlContext.Text:"<"===l&&t[e+1].match(/[a-z!]/i)&&(state.context=htmlContext.Tag,state.buffer="");break;case htmlContext.Tag:if(">"===l)state.context=htmlContext.Text,state.quote=null,state.buffer="";else{if(" "===l&&!state.buffer)continue;" "===l||"/"===l||"?"===l?state.buffer="":'"'===l||"'"===l||"="===l?(state.context=htmlContext.Attribute,state.quote="="===l?null:l,state.buffer=""):state.buffer+=l}break;case htmlContext.Attribute:state.quote||state.buffer.length||'"'!==l&&"'"!==l?l===state.quote||!state.quote&&state.buffer.length?(state.context=htmlContext.Tag,state.quote=null,state.buffer=""):state.quote||">"!==l?" "!==l&&(state.buffer+=l):(state.context=htmlContext.Text,state.quote=null,state.buffer=""):state.quote=l}}return state.context}htmlContext.Attribute="Attribute",htmlContext.Text="Text",htmlContext.Tag="Tag";let defaultState={context:htmlContext.Text,quote:null,buffer:"",lastChar:null},state={...defaultState};class MultiValueMap{data={};add(t,e){let l=this.data,s=l[t];s||(s=new Set,l[t]=s),s.add(e)}isEmpty(){for(let t in this.data)return!0;return!1}getAll(t){return this.data[t]||[]}delete(t,e=void 0){let l,s=this.data,i=s[t];if(i)return void 0===e?([l]=i,i.delete(l)):(i.delete(e),l=e),0===i.size&&delete s[t],l}hasValue(t){let e=this.data,l=[];for(let s in e)e[s].has(t)&&l.push(s);return l}}const udomdiff=(t,e,l,s)=>{const i=l.length;let o=e.length,r=i,n=0,a=0,h=null;for(;o>n||r>a;)if(o===n){const e=i>r?a?l[a-1].nextSibling:l[r-a]:s;for(;r>a;){let s=l[a++];t.insertBefore(s,e)}}else if(r===a)for(;o>n;){let l=e[n];h&&h.has(l)||t.removeChild(l),n++}else if(e[n]===l[a])n++,a++;else if(e[o-1]===l[r-1])o--,r--;else if(e[n]===l[r-1]&&l[a]===e[o-1]){const s=e[--o].nextSibling;let i=l[a++],h=e[n++];t.insertBefore(i,h.nextSibling);let f=l[--r];t.insertBefore(f,s),e[o]=l[r]}else{if(!h){h=new Map;let t=a;for(;r>t;)h.set(l[t],t++)}if(h.has(e[n])){const s=h.get(e[n]);if(s>a&&r>s){let i=n,f=1;for(;++i<o&&r>i&&h.get(e[i])===s+f;)f++;if(f>s-a){const i=e[n];for(;s>a;){let e=l[a++];t.insertBefore(e,i)}}else{let s=e[n++],i=l[a++];t.replaceChild(i,s)}}else n++}else{let l=e[n++];t.removeChild(l)}}return l};class ExprPath{type;attrName;attrValue;attrNames;nodeBefore;nodeMarker;parentNg;nodeGroups=[];nodesCache;nodeBeforeIndex;nodeMarkerPath;constructor(t,e,l=PathType.Content,s=null,i=null){this.nodeBefore=t,this.nodeMarker=e,this.type=l,this.attrName=s,this.attrValue=i,l===PathType.Multiple&&(this.attrNames=new Set)}apply(t,e=null,l=0,s={}){switch(this.type){case 1:this.applyNodes(t);break;case 2:this.applyMultipleAttribs(this.nodeMarker,t);break;case 5:break;case 6:this.applyEventAttrib(this.nodeMarker,t,this.parentNg.rootNg.root);break;default:4===this.type&&this.nodeMarker!==this.parentNg.rootNg.root?s[this.attrName]=t:l=this.applyValueAttrib(this.nodeMarker,e||[t],l)}return l}applyNodes(t){let e=this,l=[],s=e.nodeGroups,i=[];e.nodeGroups=[],e.applyExact(t,l,i),this.existingTextNodes=null;let o=!1;if(i.length){for(let[t,s]of i){let i=e.getNodeGroup(l[t],!1),r=i.getNodes();1===r.length?l[t]=r[0]:(l[t]=r,o=!0),e.nodeGroups[s]=i}o&&(l=l.flat())}let r=e.getNodes();arraySame(r,l)||(e.nodesCache=l,this.parentNg.parentPath&&this.parentNg.parentPath.clearNodesCache(),r.length&&!l.length&&e.fastClear()||udomdiff(e.nodeMarker.parentNode,r,l,e.nodeMarker),Util.saveOrphans(s,r)),e.freeNodeGroups()}applyExact(t,e,l){if(t instanceof Template){let s=this.getNodeGroup(t,!0);s?(e.push(...s.getNodes()),this.nodeGroups.push(s)):(l.push([e.length,this.nodeGroups.length]),e.push(t),this.nodeGroups.push(null))}else if(t instanceof Node)t instanceof DocumentFragment?e.push(...t.childNodes):e.push(t);else if(Array.isArray(t))for(let s of t)this.applyExact(s,e,l);else if("function"==typeof t){Globals.currentExprPath=[this,t];let s=t();Globals.currentExprPath=null,this.applyExact(s,e,l)}else{let l=void 0===t||!1===t||null===t?"":t+"",s=this.nodeBefore.nextSibling;if(3!==s.nodeType||s.nextSibling!==this.nodeMarker||e.includes(s)){this.existingTextNodes||(this.existingTextNodes=this.getNodes().filter((t=>3===t.nodeType)));let t=this.existingTextNodes.findIndex((t=>t.textContent===l));-1!==t?e.push(...this.existingTextNodes.splice(t,1)):e.push(this.nodeMarker.ownerDocument.createTextNode(l))}else s.textContent!==l&&(s.textContent=l),e.push(s)}}applyMultipleAttribs(t,e){Array.isArray(e)&&(e=e.flat().join(" "));let l=this.attrNames;if(this.attrNames=new Set,e){let l=(e+"").split(/([\w-]+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s]+))/g).map((t=>t.trim())).filter((t=>t.length));for(let e of l){let[l,s]=e.split(/\s*=\s*/);s=(s||"").replace(/^(['"])(.*)\1$/,"$2"),t.setAttribute(l,s),this.attrNames.add(l)}}for(let e of l)this.attrNames.has(e)||t.removeAttribute(e)}applyEventAttrib(t,e,l){let s,i=this.attrName.slice(2),o=[];Array.isArray(e)?"function"==typeof e[0]?(s=e[0],o=e.slice(1)):(s=setValue,o=[e[0],e.slice(1),t],t.value=delve(e[0],e.slice(1))):s=e;let r=Globals.nodeEvents.get(t);r||(r={[i]:[,,,]},Globals.nodeEvents.set(t,r));let n=r[i];if(n[0]!==s){let[e,o,r]=n;e&&t.removeEventListener(i,o);let a=s,h=e=>a.call(l,...n[2],e,t);n[0]=a,n[1]=h,t.addEventListener(i,h)}r[i][2]=o}applyValueAttrib(t,e,l){let s=e[l];if(this.attrValue||!1!==s&&null!=s)if(this.attrValue||!0!==s)if("value"!==this.attrName&&"data-value"!==this.attrName||!Util.isPath(s)){let i=[];if(this.attrValue){for(let t=this.attrValue.length-1;t>=0;t--)if(i.unshift(this.attrValue[t]),t>0){let t=e[l];!1!==t&&null!=t&&i.unshift(t),l--}l++}else i.unshift(s);let o=i.join("");("value"===this.attrName?t.value:t.getAttribute(this.attrName))!==o&&t.setAttribute(this.attrName,o),this.attrName in t&&(t[this.attrName]=o)}else{let[e,l]=[s[0],s.slice(1)];t.value=delve(e,l),t.addEventListener("input",(()=>{delve(e,l,Util.getInputValue(t))}),!0)}else t.setAttribute(this.attrName,"");else t.removeAttribute(this.attrName);return l}clone(t,e=0){let l,s,i=t,o=e?this.nodeMarkerPath.slice(0,-e):this.nodeMarkerPath;for(let t=o.length-1;t>0;t--)i=i.childNodes[o[t]];let r=i.childNodes;return l=o.length?r[o[0]]:t,this.nodeBefore&&(s=r[this.nodeBeforeIndex]),new ExprPath(s,l,this.type,this.attrName,this.attrValue)}clearNodesCache(){let t=this,e=this.nodeMarker.parentNode;for(;t&&t.nodeMarker.parentNode===e;)t.nodesCache=null,t=t.parentNg?.parentPath}fastClear(){let t=this.nodeBefore.parentNode;return this.nodeBefore===t.firstChild&&this.nodeMarker===t.lastChild&&(t.innerHTML="",t.append(this.nodeBefore,this.nodeMarker),!0)}getNodes(){let t;if(t=this.nodesCache,t)return t;t=[];let e=this.nodeBefore.nextSibling,l=this.nodeMarker;for(;e&&e!==l;)t.push(e),e=e.nextSibling;return this.nodesCache=t,t}getParentNode(){return this.nodeMarker.parentNode}getNodeGroup(t,e=!0){let l;if(e){if(l=this.nodeGroupsFree.delete(t.getExactKey()),!l)return null;this.nodeGroupsFree.delete(t.getCloseKey(),l)}else l=this.nodeGroupsFree.delete(t.getCloseKey()),l&&(this.nodeGroupsFree.delete(l.exactKey,l),l.applyExprs(t.exprs),l.exactKey=t.getExactKey());return l||(l=new NodeGroup(t,this)),this.nodeGroupsInUse.push(l),l}nodeGroupsInUse=[];nodeGroupsFree=new MultiValueMap;freeNodeGroups(){let t=this.nodeGroupsFree;for(let e of this.nodeGroupsInUse)t.add(e.exactKey,e),t.add(e.closeKey,e);this.nodeGroupsInUse=[]}}function setValue(t,e,l){let s=l.value;"number"===l.type&&(s=parseFloat(s)),delve(t,e,s)}const PathType={Content:1,Multiple:2,Value:3,Component:4,Comment:5,Event:6};function getNodePath(t){let e=[];for(;;){let l=t.parentNode;if(!l)break;e.push([].indexOf.call(t.parentNode.childNodes,t)),t=l}return e}function resolveNodePath(t,e){for(let l=e.length-1;l>=0;l--)t=t.childNodes[e[l]];return t}class Shell{fragment;paths=[];events=[];ids=[];scripts=[];styles=[];staticComponents=[];constructor(t=null){if(!t)return;let e=[],l="\x3c!--!✨!--\x3e",s={};htmlContext(null);for(let i=0;t.length>i;i++){let o=t[i],r=htmlContext(o);if(r===htmlContext.Attribute){let t,e;if(o.replace(/<[a-z][a-z0-9]*-[a-z0-9-]+/gi,((l,s)=>{t=s+1,e=l.slice(1)})),e){let l=e+"-solarite-placeholder";o=o.slice(0,t)+l+o.slice(t+e.length),s[e]=l}}e.push(o),t.length-1>i&&e.push(r===htmlContext.Text?l:String.fromCharCode(57344+i))}let i,o=document.createElement("template"),r=e.join("");for(let t in s)r=r.replaceAll(`</${t}>`,`</${s[t]}>`);r?o.innerHTML=r:o.content.append(document.createTextNode("")),this.fragment=o.content;let n=[];const a=document.createTreeWalker(o.content,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_COMMENT|NodeFilter.SHOW_TEXT);for(;i=a.nextNode();)if(n.map((t=>t.remove())),n=[],1===i.nodeType)for(let t of[...i.attributes]){let e=t.name.match(/^[\ue000-\uf8ff]$/);if(e)this.paths.push(new ExprPath(null,i,PathType.Multiple)),i.removeAttribute(e[0]);else{let e=t.value.split(/[\ue000-\uf8ff]/g);if(e.length>1){let l=2!==e.length||e[0].length||e[1].length?e:null,s=isEvent(t.name)?PathType.Event:PathType.Value;this.paths.push(new ExprPath(null,i,s,t.name,l)),i.setAttribute(t.name,e.join(""))}}}else if(i.nodeType===Node.COMMENT_NODE&&"!✨!"===i.nodeValue){let t,e=i.previousSibling;e||(e=document.createComment("ExprPath:"+this.paths.length),i.parentNode.insertBefore(e,i)),!i.nextSibling||8===i.nextSibling.nodeType&&"!✨!"===i.nextSibling.textContent?(t=i,t.textContent="ExprPathEnd:"+this.paths.length):(t=i.nextSibling,n.push(i));let l=new ExprPath(e,t,PathType.Content);this.paths.push(l)}else if(i.nodeType===Node.COMMENT_NODE){let t=i.textContent.split(/[\ue000-\uf8ff]/g);for(let e=0;t.length-1>e;e++){let t=new ExprPath(i.previousSibling,i);t.type=PathType.Comment,this.paths.push(t)}}else if(i.nodeType===Node.TEXT_NODE&&["SCRIPT","STYLE"].includes(i.parentNode?.nodeName)){let t=i.textContent.split(l);if(t.length>1){let e=[];for(let l=0;t.length>l;l++){let s=document.createTextNode(t[l]);i.parentNode.insertBefore(s,i),l>0&&e.push(s)}for(let t,l=0;t=e[l];l++){let e=new ExprPath(t.previousSibling,t,PathType.Content);this.paths.push(e)}n.push(i)}}n.map((t=>t.remove()));for(let t of this.fragment.querySelectorAll("[is]"))t.setAttribute("_is",t.getAttribute("is"));for(let t of this.paths)t.nodeBefore&&(t.nodeBeforeIndex=[].indexOf.call(t.nodeBefore.parentNode.childNodes,t.nodeBefore)),t.nodeMarkerPath=getNodePath(t.nodeMarker),t.type===PathType.Value&&1===t.nodeMarker.nodeType&&(t.nodeMarker.tagName.includes("-")||t.nodeMarker.hasAttribute("is"))&&(t.type=PathType.Component);this.findEmbeds()}findEmbeds(){this.scripts=[].map.call(this.fragment.querySelectorAll("scripts"),(t=>getNodePath(t))),this.styles=[].map.call(this.fragment.querySelectorAll("style"),(t=>getNodePath(t)));let t=this.fragment.querySelectorAll("[id],[data-id]");for(let e of t){let t=e.getAttribute("data-id")||e.getAttribute("id");if(div.hasOwnProperty(t))throw Error(`<${e.tagName.toLowerCase()} id="${t}"> can't override existing HTMLElement id property.`)}this.ids=[].map.call(t,(t=>getNodePath(t)));for(let t of this.fragment.querySelectorAll("*")){for(let e of t.attributes)isEvent(e.name)&&this.events.push([e.name,getNodePath(t)]);(t.tagName.includes("-")||t.hasAttribute("_is"))&&(this.paths.find((e=>e.nodeMarker===t))||this.staticComponents.push(getNodePath(t)))}}static get(t){let e=Globals.shells.get(t);return e||(e=new Shell(t),Globals.shells.set(t,e)),e}}class NodeGroup{rootNg;parentPath;startNode;endNode;paths=[];exactKey;closeKey;nodesCache;styles;currentComponentProps={};constructor(t,e=null){if(!(this instanceof RootNodeGroup)){let[l,s]=this.init(t,e);this.updatePaths(l,s.paths),this.activateEmbeds(l,s),this.applyExprs(t.exprs)}}init(t,e=null,l=null,s=null){this.exactKey=l||t.getExactKey(),this.closeKey=s||t.getCloseKey(),this.parentPath=e,this.rootNg=e?.parentNg?.rootNg||this,this.template=t,t.nodeGroup=this;let i=Shell.get(t.html),o=i.fragment.cloneNode(!0),r=o.childNodes;return this.startNode=r[0],this.endNode=r[r.length-1],[o,i]}applyExprs(t,e=null){e=e||this.paths;let l,s,i=t.length-1;for(let o of e.toReversed())l=t[i],s&&s!==this.rootNg.root&&s!==o.nodeMarker&&Object.keys(this.currentComponentProps).length&&(this.applyComponentExprs(s,this.currentComponentProps),this.currentComponentProps={}),i=o.apply(l,t,i,this.currentComponentProps),s=o.nodeMarker,i--;s&&s!==this.rootNg.root&&Object.keys(this.currentComponentProps).length&&(this.applyComponentExprs(s,this.currentComponentProps),this.currentComponentProps={}),this.updateStyles(),this.nodesCache=null}applyComponentExprs(t,e){let l=getObjectHash(e),s=t.tagName.endsWith("-SOLARITE-PLACEHOLDER"),i=t.hasAttribute("_is");s||i?t=this.createNewComponent(t,s,e):t.render&&Globals.componentHash.get(t)!==l&&t.render(e),Globals.componentHash.set(t,l)}createNewComponent(t,e=void 0,l=void 0){void 0===e&&(e=!t.hasAttribute("_is"));let s=(e?t.tagName.endsWith("-SOLARITE-PLACEHOLDER")?t.tagName.slice(0,-21):t.tagName:t.getAttribute("is")).toLowerCase(),i={...l||{}};if(t.attributes.length){l||(l={});for(let e of t.attributes)l.hasOwnProperty(e.name)||(l[e.name]=e.value)}let o=customElements.get(s);if(!o)throw Error(`The custom tag name ${s} is not registered.`);let r=[...t.childNodes];Globals.pendingChildren.push(r);let n=new o(l,r);e||n.setAttribute("is",t.getAttribute("is").toLowerCase()),t.replaceWith(n);for(let t in l){let e=l[t];if(t.startsWith("on")&&"function"==typeof e&&n.addEventListener(t.slice(2),(t=>e(t,n))),("value"===t||"data-value"===t)&&Util.isPath(e)){let[t,l]=[e[0],e.slice(1)];n.value=delve(t,l),n.addEventListener("input",(e=>{delve(t,l,Util.getInputValue(n))}),!0)}}let a=t.getAttribute("data-id")||t.getAttribute("id");a&&delve(this.getRootNode(),a.split(/\./g),n);for(let e of this.paths)e.nodeMarker===t&&(e.nodeMarker=n),e.nodeBefore===t&&(e.nodeBefore=n);this.startNode===t&&(this.startNode=n),this.endNode===t&&(this.endNode=n),n.renderFirstTime&&n.renderFirstTime();for(let e of t.attributes)"_is"!==e.name&&n.setAttribute(e.name,e.value);for(let t in i){let e=i[t];"boolean"==typeof e?!1!==e&&null!=e&&n.setAttribute(t,""):["number","bigint","string"].includes(typeof e)&&n.setAttribute(t,e)}return n}getNodes(){let t=this.nodesCache;if(t)return t;t=[];let e=this.startNode,l=this.endNode?.nextSibling;for(;e&&e!==l;)t.push(e),e=e.nextSibling;return this.nodesCache=t,t}getParentNode(){return this.startNode?.parentNode}getRootNode(){return this.rootNg.root}getRootNodeGroup(){return this.rootNg}updatePaths(t,e,l){this.paths.length=e.length;for(let s=0;e.length>s;s++){let i=e[s].clone(t,l);i.parentNg=this,this.paths[s]=i}}updateStyles(){if(this.styles)for(let[t,e]of this.styles)e!==t.textContent&&Util.bindStyles(t,this.getRootNodeGroup().root)}activateEmbeds(root,shell,pathOffset=0){for(let t of shell.staticComponents){pathOffset&&(t=t.slice(0,-pathOffset));let e=resolveNodePath(root,t);root!==e&&this.createNewComponent(e)}let rootEl=this.rootNg.root;if(rootEl){if(!1!==this.options?.ids)for(let t of shell.ids){pathOffset&&(t=t.slice(0,-pathOffset));let e=resolveNodePath(root,t),l=e.getAttribute("data-id")||e.getAttribute("id");if(l){if(rootEl[l]&&!(rootEl[l]instanceof Node))throw Error(`${rootEl.constructor.name}.${l} already has a value. Can't set it as a reference to <${e.tagName.toLowerCase()} id="${l}">`);delve(rootEl,l.split(/\./g),e)}}if(!1!==this.options?.styles){shell.styles.length&&(this.styles=new Map);for(let t of shell.styles){pathOffset&&(t=t.slice(0,-pathOffset));let e=resolveNodePath(root,t);Util.bindStyles(e,rootEl),this.styles.set(e,e.textContent)}}if(!1!==this.options?.scripts)for(let path of shell.scripts){pathOffset&&(path=path.slice(0,-pathOffset));let script=resolveNodePath(root,path);eval(script.textContent)}}}}class RootNodeGroup extends NodeGroup{root;constructor(t,e,l){super(t),this.options=l,this.rootNg=this;let[s,i]=this.init(t),o=0,r=s;if(e){let t;if(e.childNodes.length&&(t=document.createDocumentFragment(),t.append(...e.childNodes)),this.root=e,isReplaceEl(s,e)){e.append(...s.children[0].childNodes);for(let t of s.children[0].attributes)e.hasAttribute(t.name)||e.setAttribute(t.name,t.value);o=1}else 1===s.childNodes.length&&3===s.childNodes[0].nodeType&&""===s.childNodes[0].textContent||e.append(...s.childNodes);if(t){for(let l of e.querySelectorAll("slot[name]")){let e=l.getAttribute("name");if(e){let s=t.querySelectorAll(`[slot='${e}']`);l.append(...s)}}let l=e.querySelector("slot:not([name])");l?l.append(t):e.append(t)}r=e,this.startNode=e,this.endNode=e}else{let t=getSingleEl(s);this.root=t||s,t&&(r=t,o=1)}this.updatePaths(r,i.paths,o),this.activateEmbeds(r,i,o),this.applyExprs(t.exprs)}}function getSingleEl(t){let e=[];for(let l of t.childNodes)if(1===l.nodeType||3===l.nodeType&&l.textContent.trim().length){if(e.length)return null;e.push(l)}return e[0]}function isReplaceEl(t,e){return e.tagName.includes("-")&&1===t.children.length&&t.children[0].tagName.replace("-SOLARITE-PLACEHOLDER","")===e.tagName}class Template{exprs=[];html=[];hashedFields;parentPath;nodeGroup;paths=[];constructor(t,e){this.html=t,this.exprs=e}toJSON(){return this.hashedFields||(this.hashedFields=[getObjectId(this.html),this.exprs]),this.hashedFields}render(t=null,e={}){let l,s=!1;return t?(l=Globals.nodeGroups.get(t),l||(l=new RootNodeGroup(this,t,e),Globals.nodeGroups.set(t,l),s=!0)):(l=new RootNodeGroup(this,null,e),t=l.getRootNode(),Globals.nodeGroups.set(t,l),s=!0),s||(1!==this.html?.length||this.html[0]?l.applyExprs(this.exprs):t.innerHTML=""),t}getExactKey(){return this.exactKey||(this.exactKey=getObjectHash(this)),this.exactKey}getCloseKey(){return this.closeKey||(this.closeKey="@"+this.toJSON()[0]),this.closeKey}}function r(t=void 0,...e){if(!(t instanceof Node)){if(Array.isArray(t))return new Template(t,e);if("string"==typeof t||t instanceof String){t.match(/^\s^</)&&(t=t.trim());let e=document.createElement("template");e.innerHTML=t;let l=Util.trimEmptyNodes(e.content.childNodes);return 1===l.length?l[0]:e.content}if(void 0===t)return(t,...e)=>r(t,...e).render();if(t instanceof Template)return t.render();if("object"==typeof t){let e=t;if(Globals.objToEl.has(e))return((...t)=>{let l=r(...t).render();Globals.objToEl.set(e,l)}).bind(e);{Globals.objToEl.set(e,null),e.render();let t=Globals.objToEl.get(e);Globals.objToEl.delete(e);for(let l in e)t[l]="function"==typeof e[l]?e[l].bind(t):e[l];return t}}throw Error("Unsupported arguments.")}{let l=t,s=e[0];if(!(e[0]instanceof Template)){l.shadowRoot&&(l.innerHTML="");let t=e[0],s=(e,...s)=>(Globals.rendered.add(l),new Template(e,s).render(l,t));return s}if(!(e[0]instanceof Template))throw Error("unsupported");s.render(l,e[1]),!l.childNodes.length&&this&&l.append(this.rootNg.getParentNode())}}function defineClass(t,e,l){if(!customElements.getName(t)){(e=e||camelToDashes(t.name)).includes("-")||(e+="-element");let s=null;l&&(s={extends:l}),customElements.define(e,t,s)}}function createSolarite(t=null){let e=HTMLElement;t&&!t.includes("-")&&(t=t.toLowerCase(),e=Globals.elementClasses[t],e||(e=document.createElement(t).constructor,Globals.elementClasses[t]=e));let l=new Proxy(e,{construct:(e,l,s)=>(defineClass(s,null,t),Reflect.construct(e,l,s))});return class extends l{onConnect=Util$1.callback();onFirstConnect=Util$1.callback();onDisconnect=Util$1.callback();constructor(t={}){super(),!0===t.render?this.render():!1===t.render&&Globals.rendered.add(this);let e=Globals.pendingChildren.pop();e&&(this.querySelector("slot")||this).append(...e),Object.defineProperty(this,"html",{set(e){Globals.rendered.add(this),"string"==typeof e?(console.warn("Assigning to this.html without the r template prefix."),this.innerHTML=e):this.modifications=r(this,e,t)}})}renderFirstTime(){!Globals.rendered.has(this)&&this.render&&this.render()}connectedCallback(){this.renderFirstTime(),Globals.connected.has(this)||(Globals.connected.add(this),this.onFirstConnect()),this.onConnect()}disconnectedCallback(){this.onDisconnect()}static define(e=null){defineClass(this,e,t)}}}let Solarite=new Proxy(createSolarite(),{apply:(t,e,l)=>createSolarite(...l)}),getInputValue=Util.getInputValue;export{ArgType,Globals,Solarite,Template,delve,getArg,getInputValue,r};
|
|
4
|
+
var Util$1={callback(...t){var e=!1;let l=async(...t)=>{let s=[];if(!e)for(let e=0;l.length>e;e++)s.push(l[e](...t));return await Promise.all(s)};l[Symbol.iterator]=()=>{let t=0;return{next(){return l.length>t?{value:l[t++],done:!1}:{done:!0}}}};for(let t of Object.getOwnPropertyNames(Array.prototype))"length"!==t&&"constructor"!==t&&(l[t]=Array.prototype[t]);l.l=0,Object.defineProperty(l,"length",{get:()=>l.l,set(t){l.l=t}}),l.remove=t=>{let e=l.findIndex((e=>e===t));-1!==e&&l.splice(e,1)},l.pause=()=>e=!0,l.resume=()=>e=!1;for(let e of t)l.push(e);return l},mapAdd(t,e,l){let s=t instanceof Map||t instanceof WeakMap,i=s?t.get(e):t[e];i?i.push(l):(i=[l],s?t.set(e,i):t[e]=i)},weakMemoize(t,e){let l=weakMemoizeInputs.get(t);return l||(l=e(t),weakMemoizeInputs.set(t,l)),l}};let weakMemoizeInputs=new WeakMap;function delve(t,e,l=delveDontCreate){let s=l!==delveDontCreate,i=e.length;if(!t&&!s&&i)return;let o=0;for(let r of e){if(void 0===t[r]){if(!s)return;i-1>o&&(t[r]=(e[o+1]+"").match(/^\d+$/)?[]:{})}s&&o===i-1&&(t[r]=l),t=t[r],o++}return t}let delveDontCreate={};function getArg(el,name,val=void 0,type=ArgType.String,fallback=void 0){let attrVal=el.getAttribute(name),result;if(null!==attrVal&&(val=attrVal),Array.isArray(type))return type.includes(val)?val:fallback;if("function"==typeof type)return type(val);if(type===ArgType.Bool){let t="string"==typeof val?val.toLowerCase():val;return!["false","0",!1,0,null,void 0,NaN].includes(t)&&(!(!["true",!0].includes(t)&&0===parseFloat(t))||fallback)}switch(type){case ArgType.Int:return result=parseInt(val),isNaN(result)?fallback:result;case ArgType.Float:return result=parseFloat(val),isNaN(result)?fallback:result;case ArgType.String:return[void 0,null,!1].includes(val)?"":val+"";case ArgType.JSON:case ArgType.Eval:if("string"!=typeof val||!val.length)return fallback;try{return type===ArgType.JSON?JSON.parse(val):eval(`(${val})`)}catch(t){return val}default:return val}}var ArgType={Bool:"Bool",Int:"Int",Float:"Float",String:"String",JSON:"JSON",Eval:"Eval"};let lastObjectId=1,objectIds=new WeakMap;function getObjectId(t){let e=objectIds.get(t);return e||(e=lastObjectId++,objectIds.set(t,e)),e}let isHashing=!0;function toJSON(){return isHashing?getObjectId(this):this}function getObjectHash(t){let e;isHashing=!0;try{e=JSON.stringify(t)}catch(l){e=getObjectHashCircular(t)}return isHashing=!1,e}function getObjectHashCircular(t){const e=new Set;return JSON.stringify(t,((t,l)=>{if("object"==typeof l&&null!==l){if(e.has(l))return getObjectId(l);e.add(l)}return l}))}Node.prototype.toJSON!==toJSON&&(Node.prototype.toJSON=toJSON,(()=>{}).toJSON!==toJSON&&(Function.prototype.toJSON=toJSON));var Globals={componentHash:new WeakMap,connected:new WeakSet,rendered:new WeakSet,currentExprPath:null,elementClasses:{},nodeEvents:new WeakMap,nodeGroups:new WeakMap,objToEl:new WeakMap,pendingChildren:[],rendering:new WeakSet,shells:new WeakMap};let Util={bindStyles(t,e){let l=e.getAttribute("data-style");l||(e.constructor.styleId||(e.constructor.styleId=1),l=e.constructor.styleId++,e.setAttribute("data-style",l));let s=e.tagName.toLowerCase();for(let e of t.childNodes)if(3===e.nodeType){let t=e.textContent,i=t.replace(/:host(?=[^a-z0-9_])/gi,s+'[data-style="'+l+'"]');t!==i&&(e.textContent=i)}},*flatten(t){if(Array.isArray(t))for(const e of t)yield*Util.flatten(e);else if("function"==typeof t){const e=t();yield*Util.flatten(e)}else yield t},getInputValue:t=>"checkbox"===t.type||"radio"===t.type?t.checked:"file"===t.type?[...t.files]:"number"===t.type||"range"===t.type?t.valueAsNumber:"date"===t.type||"time"===t.type||"datetime-local"===t.type?t.valueAsDate:"select-multiple"===t.type?[...t.selectedOptions].map((t=>t.value)):t.value,isPath:t=>Array.isArray(t)&&"object"==typeof t[0]&&!t.slice(1).find((t=>"string"!=typeof t&&"number"!=typeof t)),saveOrphans(t,e){let l=new Map;for(let e of t)l.set(e.startNode,e);for(let t,s=0;t=e[s];s++){let i;if(!t.parentNode&&(i=l.get(t))){let l=document.createDocumentFragment(),o=i.endNode;for(;t!==o;)l.append(t),s++,t=e[s];l.append(o)}}},trimEmptyNodes(t){const e=t=>t.nodeType!==Node.ELEMENT_NODE&&(t.nodeType!==Node.TEXT_NODE||""===t.textContent.trim()),l=[...t];for(;l.length>0&&e(l[0]);)l.shift();for(;l.length>0&&e(l[l.length-1]);)l.pop();return l}},div=document.createElement("div"),isEvent=t=>t.startsWith("on")&&t in div;function camelToDashes(t){return(t=(t=(t=t.replace(/([a-z0-9])([A-Z])/g,"$1-$2")).replace(/([A-Z])([A-Z][a-z])/g,"$1-$2")).replace(/([a-zA-Z])([0-9])/g,"$1-$2")).toLowerCase()}function arraySame(t,e){let l=t.length;if(l!==e.length)return!1;for(let s=0;l>s;s++)if(t[s]!==e[s])return!1;return!0}function htmlContext(t){if(null===t)return state={...defaultState},state.context;for(let e=0;t.length>e;e++){const l=t[e];switch(state.context){case htmlContext.Text:"<"===l&&t[e+1].match(/[a-z!]/i)&&(state.context=htmlContext.Tag,state.buffer="");break;case htmlContext.Tag:if(">"===l)state.context=htmlContext.Text,state.quote=null,state.buffer="";else{if(" "===l&&!state.buffer)continue;" "===l||"/"===l||"?"===l?state.buffer="":'"'===l||"'"===l||"="===l?(state.context=htmlContext.Attribute,state.quote="="===l?null:l,state.buffer=""):state.buffer+=l}break;case htmlContext.Attribute:state.quote||state.buffer.length||'"'!==l&&"'"!==l?l===state.quote||!state.quote&&state.buffer.length?(state.context=htmlContext.Tag,state.quote=null,state.buffer=""):state.quote||">"!==l?" "!==l&&(state.buffer+=l):(state.context=htmlContext.Text,state.quote=null,state.buffer=""):state.quote=l}}return state.context}htmlContext.Attribute="Attribute",htmlContext.Text="Text",htmlContext.Tag="Tag";let defaultState={context:htmlContext.Text,quote:null,buffer:"",lastChar:null},state={...defaultState};class MultiValueMap{data={};add(t,e){let l=this.data,s=l[t];s||(s=new Set,l[t]=s),s.add(e)}isEmpty(){for(let t in this.data)return!0;return!1}getAll(t){return this.data[t]||[]}delete(t,e=void 0){let l,s=this.data,i=s[t];if(i)return void 0===e?([l]=i,i.delete(l)):(i.delete(e),l=e),0===i.size&&delete s[t],l}deletePreferred(t,e){let l,s=this.data,i=s[t];if(i){for(let t of i)if(e(t)){i.delete(t),l=t;break}return l||([l]=i,i.delete(l)),0===i.size&&delete s[t],l}}hasValue(t){let e=this.data,l=[];for(let s in e)e[s].has(t)&&l.push(s);return l}}const udomdiff=(t,e,l,s)=>{const i=l.length;let o=e.length,r=i,a=0,n=0,h=null;for(;o>a||r>n;)if(o===a){const e=i>r?n?l[n-1].nextSibling:l[r-n]:s;for(;r>n;){let s=l[n++];t.insertBefore(s,e)}}else if(r===n)for(;o>a;){let l=e[a];h&&h.has(l)||t.removeChild(l),a++}else if(e[a]===l[n])a++,n++;else if(e[o-1]===l[r-1])o--,r--;else if(e[a]===l[r-1]&&l[n]===e[o-1]){const s=e[--o].nextSibling;let i=l[n++],h=e[a++];t.insertBefore(i,h.nextSibling);let f=l[--r];t.insertBefore(f,s),e[o]=l[r]}else{if(!h){h=new Map;let t=n;for(;r>t;)h.set(l[t],t++)}if(h.has(e[a])){const s=h.get(e[a]);if(s>n&&r>s){let i=a,f=1;for(;++i<o&&r>i&&h.get(e[i])===s+f;)f++;if(f>s-n){const i=e[a];for(;s>n;){let e=l[n++];t.insertBefore(e,i)}}else{let s=e[a++],i=l[n++];t.replaceChild(i,s)}}else a++}else{let l=e[a++];t.removeChild(l)}}return l};class ExprPath{type;attrName;attrValue;attrNames;nodeBefore;nodeMarker;parentNg;nodeGroups=[];nodesCache;nodeBeforeIndex;nodeMarkerPath;watchFunction;constructor(t,e,l=PathType.Content,s=null,i=null){this.nodeBefore=t,this.nodeMarker=e,this.type=l,this.attrName=s,this.attrValue=i,l===PathType.Multiple&&(this.attrNames=new Set)}apply(t,e=null,l=0,s={}){switch(this.type){case 1:this.applyNodes(t);break;case 2:this.applyMultipleAttribs(this.nodeMarker,t);break;case 5:break;case 6:this.applyEventAttrib(this.nodeMarker,t,this.parentNg.rootNg.root);break;default:4===this.type&&this.nodeMarker!==this.parentNg.rootNg.root?s[this.attrName]=t:l=this.applyValueAttrib(this.nodeMarker,e||[t],l)}return l}applyNodes(t){let e=this;e.freeNodeGroups();let l=[],s=e.nodeGroups,i=[];e.nodeGroups=[],e.applyExact(t,l,i),this.existingTextNodes=null;let o=!1;if(i.length){for(let[t,s]of i){let i=e.getNodeGroup(l[t],!1),r=i.getNodes();1===r.length?l[t]=r[0]:(l[t]=r,o=!0),e.nodeGroups[s]=i}o&&(l=l.flat())}let r=e.getNodes();if(!arraySame(r,l)){e.nodesCache=l,this.parentNg.parentPath&&this.parentNg.parentPath.clearNodesCache(),r.length&&!l.length&&e.fastClear()||udomdiff(e.nodeMarker.parentNode,r,l,e.nodeMarker);for(let t of s)t.startNode.parentNode||t.saveOrphans()}}applyLoopItemUpdate(t,e){let l=this.nodeGroups[t];this.nodeGroupsFree.add(l.exactKey,l),this.nodeGroupsFree.add(l.closeKey,l);let s=this.getNodeGroup(e,!0);if(!s){s=this.getNodeGroup(e,!1),this.nodeGroups[t]=s;for(let t of s.getNodes())l.startNode.parentNode.insertBefore(t,l.startNode);if(l!==s){for(let t of l.getNodes())t.remove();l.saveOrphans()}this.nodesCache=null}}applyExact(t,e,l){if(t instanceof Template){let s=this.getNodeGroup(t,!0);s?(e.push(...s.getNodes()),this.nodeGroups.push(s)):(l.push([e.length,this.nodeGroups.length]),e.push(t),this.nodeGroups.push(null))}else if(t instanceof Node)t instanceof DocumentFragment?e.push(...t.childNodes):e.push(t);else if(Array.isArray(t))for(let s of t)this.applyExact(s,e,l);else if("function"==typeof t){Globals.currentExprPath=[this,t],this.watchFunction=t;let s=t();Globals.currentExprPath=null,this.applyExact(s,e,l)}else{let l=void 0===t||!1===t||null===t?"":t+"",s=this.nodeBefore.nextSibling;if(3!==s.nodeType||s.nextSibling!==this.nodeMarker||e.includes(s)){this.existingTextNodes||(this.existingTextNodes=this.getNodes().filter((t=>3===t.nodeType)));let t=this.existingTextNodes.findIndex((t=>t.textContent===l));-1!==t?e.push(...this.existingTextNodes.splice(t,1)):e.push(this.nodeMarker.ownerDocument.createTextNode(l))}else s.textContent!==l&&(s.textContent=l),e.push(s)}}applyMultipleAttribs(t,e){Array.isArray(e)&&(e=e.flat().join(" "));let l=this.attrNames;if(this.attrNames=new Set,e){let l=(e+"").split(/([\w-]+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s]+))/g).map((t=>t.trim())).filter((t=>t.length));for(let e of l){let[l,s]=e.split(/\s*=\s*/);s=(s||"").replace(/^(['"])(.*)\1$/,"$2"),t.setAttribute(l,s),this.attrNames.add(l)}}for(let e of l)this.attrNames.has(e)||t.removeAttribute(e)}applyEventAttrib(t,e,l){let s,i=this.attrName.slice(2),o=[];Array.isArray(e)?"function"==typeof e[0]?(s=e[0],o=e.slice(1)):(s=setValue,o=[e[0],e.slice(1),t],t.value=delve(e[0],e.slice(1))):s=e,this.bindEvent(t,l,i,i,s,o)}bindEvent(t,e,l,s,i,o,r=!1){let a=Globals.nodeEvents.get(t);a||(a={[l]:[,,,]},Globals.nodeEvents.set(t,a));let n=a[l];if(n||(a[l]=n=[,,,]),n[0]!==i){let[l,o,a]=n;l&&t.removeEventListener(s,o,r);let h=i,f=l=>h.call(e,...n[2],l,t);n[0]=h,n[1]=f,t.addEventListener(s,f,r)}a[l][2]=o}applyValueAttrib(t,e,l){let s=e[l];if(this.attrValue||!1!==s&&null!=s)if(this.attrValue||!0!==s)if("value"!==this.attrName&&"data-value"!==this.attrName||!Util.isPath(s)){let i=[];if(this.attrValue){for(let t=this.attrValue.length-1;t>=0;t--)if(i.unshift(this.attrValue[t]),t>0){let t=e[l];!1!==t&&null!=t&&i.unshift(t),l--}l++}else i.unshift(s);let o=i.join("");("value"===this.attrName?t.value:t.getAttribute(this.attrName))!==o&&t.setAttribute(this.attrName,o),this.attrName in t&&(t[this.attrName]=o)}else{let[e,l]=[s[0],s.slice(1)];t.value=delve(e,l),this.bindEvent(t,l[0],"value","input",(()=>{delve(e,l,Util.getInputValue(t))}),[],!0)}else t.setAttribute(this.attrName,"");else t.removeAttribute(this.attrName);return l}clone(t,e=0){let l,s,i=t,o=e?this.nodeMarkerPath.slice(0,-e):this.nodeMarkerPath;for(let t=o.length-1;t>0;t--)i=i.childNodes[o[t]];let r=i.childNodes;return l=o.length?r[o[0]]:t,this.nodeBefore&&(s=r[this.nodeBeforeIndex]),new ExprPath(s,l,this.type,this.attrName,this.attrValue)}clearNodesCache(){let t=this,e=this.nodeMarker.parentNode;for(;t&&t.nodeMarker.parentNode===e;)t.nodesCache=null,t=t.parentNg?.parentPath}fastClear(){let t=this.nodeBefore.parentNode;return this.nodeBefore===t.firstChild&&this.nodeMarker===t.lastChild&&(t.innerHTML="",t.append(this.nodeBefore,this.nodeMarker),!0)}getNodes(){let t;if(t=this.nodesCache,t)return t;t=[];let e=this.nodeBefore.nextSibling,l=this.nodeMarker;for(;e&&e!==l;)t.push(e),e=e.nextSibling;return this.nodesCache=t,t}getParentNode(){return this.nodeMarker.parentNode}getNodeGroup(t,e=!0){let l;if(e){if(l=this.nodeGroupsFree.deletePreferred(t.getExactKey(),(t=>t.startNode.parentElement)),!l)return null;this.nodeGroupsFree.delete(t.getCloseKey(),l)}else l=this.nodeGroupsFree.deletePreferred(t.getCloseKey(),(t=>t.startNode.parentElement)),l&&(this.nodeGroupsFree.delete(l.exactKey,l),l.applyExprs(t.exprs),l.exactKey=t.getExactKey());return l||(l=new NodeGroup(t,this)),this.nodeGroupsInUse.push(l),l}nodeGroupsInUse=[];nodeGroupsFree=new MultiValueMap;nodeGroupsDetached=new MultiValueMap;freeNodeGroups(){let t=this.nodeGroupsFree;for(let e of this.nodeGroupsInUse)t.add(e.exactKey,e),t.add(e.closeKey,e);this.nodeGroupsInUse=[]}}function setValue(t,e,l){let s=l.value;"number"===l.type&&(s=parseFloat(s)),delve(t,e,s)}const PathType={Content:1,Multiple:2,Value:3,Component:4,Comment:5,Event:6};function getNodePath(t){let e=[];for(;;){let l=t.parentNode;if(!l)break;e.push([].indexOf.call(t.parentNode.childNodes,t)),t=l}return e}function resolveNodePath(t,e){for(let l=e.length-1;l>=0;l--)t=t.childNodes[e[l]];return t}class Shell{fragment;paths=[];events=[];ids=[];scripts=[];styles=[];staticComponents=[];constructor(t=null){if(!t)return;let e=[],l="\x3c!--!✨!--\x3e",s={};htmlContext(null);for(let i=0;t.length>i;i++){let o=t[i],r=htmlContext(o);if(r===htmlContext.Attribute){let t,e;if(o.replace(/<[a-z][a-z0-9]*-[a-z0-9-]+/gi,((l,s)=>{t=s+1,e=l.slice(1)})),e){let l=e+"-solarite-placeholder";o=o.slice(0,t)+l+o.slice(t+e.length),s[e]=l}}e.push(o),t.length-1>i&&e.push(r===htmlContext.Text?l:String.fromCharCode(57344+i))}let i,o=document.createElement("template"),r=e.join("");for(let t in s)r=r.replaceAll(`</${t}>`,`</${s[t]}>`);r?o.innerHTML=r:o.content.append(document.createTextNode("")),this.fragment=o.content;let a=[];const n=document.createTreeWalker(o.content,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_COMMENT|NodeFilter.SHOW_TEXT);for(;i=n.nextNode();)if(a.map((t=>t.remove())),a=[],1===i.nodeType)for(let t of[...i.attributes]){let e=t.name.match(/^[\ue000-\uf8ff]$/);if(e)this.paths.push(new ExprPath(null,i,PathType.Multiple)),i.removeAttribute(e[0]);else{let e=t.value.split(/[\ue000-\uf8ff]/g);if(e.length>1){let l=2!==e.length||e[0].length||e[1].length?e:null,s=isEvent(t.name)?PathType.Event:PathType.Value;this.paths.push(new ExprPath(null,i,s,t.name,l)),i.setAttribute(t.name,e.join(""))}}}else if(i.nodeType===Node.COMMENT_NODE&&"!✨!"===i.nodeValue){let t,e=i.previousSibling;e||(e=document.createComment("ExprPath:"+this.paths.length),i.parentNode.insertBefore(e,i)),!i.nextSibling||8===i.nextSibling.nodeType&&"!✨!"===i.nextSibling.textContent?(t=i,t.textContent="ExprPathEnd:"+this.paths.length):(t=i.nextSibling,a.push(i));let l=new ExprPath(e,t,PathType.Content);this.paths.push(l)}else if(i.nodeType===Node.COMMENT_NODE){let t=i.textContent.split(/[\ue000-\uf8ff]/g);for(let e=0;t.length-1>e;e++){let t=new ExprPath(i.previousSibling,i);t.type=PathType.Comment,this.paths.push(t)}}else if(i.nodeType===Node.TEXT_NODE&&["SCRIPT","STYLE"].includes(i.parentNode?.nodeName)){let t=i.textContent.split(l);if(t.length>1){let e=[];for(let l=0;t.length>l;l++){let s=document.createTextNode(t[l]);i.parentNode.insertBefore(s,i),l>0&&e.push(s)}for(let t,l=0;t=e[l];l++){let e=new ExprPath(t.previousSibling,t,PathType.Content);this.paths.push(e)}a.push(i)}}a.map((t=>t.remove()));for(let t of this.fragment.querySelectorAll("[is]"))t.setAttribute("_is",t.getAttribute("is"));for(let t of this.paths)t.nodeBefore&&(t.nodeBeforeIndex=[].indexOf.call(t.nodeBefore.parentNode.childNodes,t.nodeBefore)),t.nodeMarkerPath=getNodePath(t.nodeMarker),t.type===PathType.Value&&1===t.nodeMarker.nodeType&&(t.nodeMarker.tagName.includes("-")||t.nodeMarker.hasAttribute("is"))&&(t.type=PathType.Component);this.findEmbeds()}findEmbeds(){this.scripts=[].map.call(this.fragment.querySelectorAll("scripts"),(t=>getNodePath(t))),this.styles=[].map.call(this.fragment.querySelectorAll("style"),(t=>getNodePath(t)));let t=this.fragment.querySelectorAll("[id],[data-id]");for(let e of t){let t=e.getAttribute("data-id")||e.getAttribute("id");if(div.hasOwnProperty(t))throw Error(`<${e.tagName.toLowerCase()} id="${t}"> can't override existing HTMLElement id property.`)}this.ids=[].map.call(t,(t=>getNodePath(t)));for(let t of this.fragment.querySelectorAll("*")){for(let e of t.attributes)isEvent(e.name)&&this.events.push([e.name,getNodePath(t)]);(t.tagName.includes("-")||t.hasAttribute("_is"))&&(this.paths.find((e=>e.nodeMarker===t))||this.staticComponents.push(getNodePath(t)))}}static get(t){let e=Globals.shells.get(t);return e||(e=new Shell(t),Globals.shells.set(t,e)),e}}class NodeGroup{rootNg;parentPath;startNode;endNode;paths=[];exactKey;closeKey;nodesCache;styles;currentComponentProps={};constructor(t,e=null){if(!(this instanceof RootNodeGroup)){let[l,s]=this.init(t,e);this.updatePaths(l,s.paths),this.activateEmbeds(l,s),this.applyExprs(t.exprs)}}init(t,e=null,l=null,s=null){this.exactKey=l||t.getExactKey(),this.closeKey=s||t.getCloseKey(),this.parentPath=e,this.rootNg=e?.parentNg?.rootNg||this,this.template=t,t.nodeGroup=this;let i=Shell.get(t.html),o=i.fragment.cloneNode(!0),r=o.childNodes;return this.startNode=r[0],this.endNode=r[r.length-1],[o,i]}applyExprs(t,e=null){e=e||this.paths;let l,s,i=t.length-1;for(let o of e.toReversed())l=t[i],s&&s!==this.rootNg.root&&s!==o.nodeMarker&&Object.keys(this.currentComponentProps).length&&(this.applyComponentExprs(s,this.currentComponentProps),this.currentComponentProps={}),i=o.apply(l,t,i,this.currentComponentProps),s=o.nodeMarker,i--;s&&s!==this.rootNg.root&&Object.keys(this.currentComponentProps).length&&(this.applyComponentExprs(s,this.currentComponentProps),this.currentComponentProps={}),this.updateStyles(),this.nodesCache=null}applyComponentExprs(t,e){let l=getObjectHash(e),s=t.tagName.endsWith("-SOLARITE-PLACEHOLDER"),i=t.hasAttribute("_is");s||i?t=this.createNewComponent(t,s,e):t.render&&Globals.componentHash.get(t)!==l&&t.render(e),Globals.componentHash.set(t,l)}createNewComponent(t,e=void 0,l=void 0){void 0===e&&(e=!t.hasAttribute("_is"));let s=(e?t.tagName.endsWith("-SOLARITE-PLACEHOLDER")?t.tagName.slice(0,-21):t.tagName:t.getAttribute("is")).toLowerCase(),i={...l||{}};if(t.attributes.length){l||(l={});for(let e of t.attributes)l.hasOwnProperty(e.name)||(l[e.name]=e.value)}let o=customElements.get(s);if(!o)throw Error(`The custom tag name ${s} is not registered.`);let r=[...t.childNodes];Globals.pendingChildren.push(r);let a=new o(l,r);e||a.setAttribute("is",t.getAttribute("is").toLowerCase()),t.replaceWith(a);for(let t in l){let e=l[t];if(t.startsWith("on")&&"function"==typeof e&&a.addEventListener(t.slice(2),(t=>e(t,a))),("value"===t||"data-value"===t)&&Util.isPath(e)){let[t,l]=[e[0],e.slice(1)];a.value=delve(t,l),a.addEventListener("input",(e=>{delve(t,l,Util.getInputValue(a))}),!0)}}let n=t.getAttribute("data-id")||t.getAttribute("id");n&&delve(this.getRootNode(),n.split(/\./g),a);for(let e of this.paths)e.nodeMarker===t&&(e.nodeMarker=a),e.nodeBefore===t&&(e.nodeBefore=a);this.startNode===t&&(this.startNode=a),this.endNode===t&&(this.endNode=a),a.renderFirstTime&&a.renderFirstTime();for(let e of t.attributes)"_is"!==e.name&&a.setAttribute(e.name,e.value);for(let t in i){let e=i[t];"boolean"==typeof e?!1!==e&&null!=e&&a.setAttribute(t,""):["number","bigint","string"].includes(typeof e)&&a.setAttribute(t,e)}return a}getNodes(){let t=this.nodesCache;if(t)return t;t=[];let e=this.startNode,l=this.endNode?.nextSibling;for(;e&&e!==l;)t.push(e),e=e.nextSibling;return this.nodesCache=t,t}getParentNode(){return this.startNode?.parentNode}getRootNode(){return this.rootNg.root}getRootNodeGroup(){return this.rootNg}saveOrphans(){let t=document.createDocumentFragment();for(let e of this.getNodes())t.append(e)}updatePaths(t,e,l){this.paths.length=e.length;for(let s=0;e.length>s;s++){let i=e[s].clone(t,l);i.parentNg=this,this.paths[s]=i}}updateStyles(){if(this.styles)for(let[t,e]of this.styles)e!==t.textContent&&Util.bindStyles(t,this.getRootNodeGroup().root)}activateEmbeds(root,shell,pathOffset=0){for(let t of shell.staticComponents){pathOffset&&(t=t.slice(0,-pathOffset));let e=resolveNodePath(root,t);root!==e&&this.createNewComponent(e)}let rootEl=this.rootNg.root;if(rootEl){if(!1!==this.options?.ids)for(let t of shell.ids){pathOffset&&(t=t.slice(0,-pathOffset));let e=resolveNodePath(root,t),l=e.getAttribute("data-id")||e.getAttribute("id");if(l){if(rootEl[l]&&!(rootEl[l]instanceof Node))throw Error(`${rootEl.constructor.name}.${l} already has a value. Can't set it as a reference to <${e.tagName.toLowerCase()} id="${l}">`);delve(rootEl,l.split(/\./g),e)}}if(!1!==this.options?.styles){shell.styles.length&&(this.styles=new Map);for(let t of shell.styles){pathOffset&&(t=t.slice(0,-pathOffset));let e=resolveNodePath(root,t);Util.bindStyles(e,rootEl),this.styles.set(e,e.textContent)}}if(!1!==this.options?.scripts)for(let path of shell.scripts){pathOffset&&(path=path.slice(0,-pathOffset));let script=resolveNodePath(root,path);eval(script.textContent)}}}}class RootNodeGroup extends NodeGroup{root;watchedExprPaths={};mapCallbacks=new Map;exprsToRender=new Map;constructor(t,e,l){super(t),this.options=l,this.rootNg=this;let[s,i]=this.init(t),o=0,r=s;if(e){let t;if(Globals.nodeGroups.set(e,this),e.childNodes.length&&(t=document.createDocumentFragment(),t.append(...e.childNodes)),this.root=e,isReplaceEl(s,e)){e.append(...s.children[0].childNodes);for(let t of s.children[0].attributes)e.hasAttribute(t.name)||e.setAttribute(t.name,t.value);o=1}else 1===s.childNodes.length&&3===s.childNodes[0].nodeType&&""===s.childNodes[0].textContent||e.append(...s.childNodes);if(t){for(let l of e.querySelectorAll("slot[name]")){let e=l.getAttribute("name");if(e){let s=t.querySelectorAll(`[slot='${e}']`);l.append(...s)}}let l=e.querySelector("slot:not([name])");l?l.append(t):e.append(t)}r=e,this.startNode=e,this.endNode=e}else{let t=getSingleEl(s);this.root=t||s,Globals.nodeGroups.set(this.root,this),t&&(r=t,o=1)}this.updatePaths(r,i.paths,o),this.activateEmbeds(r,i,o),this.applyExprs(t.exprs)}clearRenderWatched(){this.watchedExprPaths={},this.mapCallbacks=new Map}}function getSingleEl(t){let e=[];for(let l of t.childNodes)if(1===l.nodeType||3===l.nodeType&&l.textContent.trim().length){if(e.length)return null;e.push(l)}return e[0]}function isReplaceEl(t,e){return e.tagName.includes("-")&&1===t.children.length&&t.children[0].tagName.replace("-SOLARITE-PLACEHOLDER","")===e.tagName}class Template{exprs=[];html=[];hashedFields;parentPath;nodeGroup;paths=[];constructor(t,e){this.html=t,this.exprs=e}toJSON(){return this.hashedFields||(this.hashedFields=[getObjectId(this.html),this.exprs]),this.hashedFields}render(t=null,e={}){let l,s=!1;return t?(l=Globals.nodeGroups.get(t),l||(l=new RootNodeGroup(this,t,e),s=!0)):(l=new RootNodeGroup(this,null,e),t=l.getRootNode(),s=!0),s||(1!==this.html?.length||this.html[0]?(l.clearRenderWatched(),l.applyExprs(this.exprs)):t.innerHTML=""),t}getExactKey(){return this.exactKey||(this.exactKey=getObjectHash(this)),this.exactKey}getCloseKey(){return this.closeKey||(this.closeKey="@"+this.toJSON()[0]),this.closeKey}}function r(t=void 0,...e){if(!(t instanceof Node)){if(Array.isArray(t))return new Template(t,e);if("string"==typeof t||t instanceof String){t.match(/^\s^</)&&(t=t.trim());let e=document.createElement("template");e.innerHTML=t;let l=Util.trimEmptyNodes(e.content.childNodes);return 1===l.length?l[0]:e.content}if(void 0===t)return(t,...e)=>r(t,...e).render();if(t instanceof Template)return t.render();if("object"==typeof t){let e=t;if(Globals.objToEl.has(e))return((...t)=>{let l=r(...t).render();Globals.objToEl.set(e,l)}).bind(e);{Globals.objToEl.set(e,null),e.render();let t=Globals.objToEl.get(e);Globals.objToEl.delete(e);for(let l in e)t[l]="function"==typeof e[l]?e[l].bind(t):e[l];return t}}throw Error("Unsupported arguments.")}{let l=t,s=e[0];if(!(e[0]instanceof Template)){l.shadowRoot&&(l.innerHTML="");let t=e[0],s=(e,...s)=>(Globals.rendered.add(l),new Template(e,s).render(l,t));return s}if(!(e[0]instanceof Template))throw Error("unsupported");s.render(l,e[1]),!l.childNodes.length&&this&&l.append(this.rootNg.getParentNode())}}function defineClass(t,e,l){if(!customElements.getName(t)){(e=e||camelToDashes(t.name)).includes("-")||(e+="-element");let s=null;l&&(s={extends:l}),customElements.define(e,t,s)}}function createSolarite(t=null){let e=HTMLElement;t&&!t.includes("-")&&(t=t.toLowerCase(),e=Globals.elementClasses[t],e||(e=document.createElement(t).constructor,Globals.elementClasses[t]=e));let l=new Proxy(e,{construct:(e,l,s)=>(defineClass(s,null,t),Reflect.construct(e,l,s))});return class extends l{onConnect=Util$1.callback();onFirstConnect=Util$1.callback();onDisconnect=Util$1.callback();constructor(t={}){super(),!0===t.render?this.render():!1===t.render&&Globals.rendered.add(this);let e=Globals.pendingChildren.pop();e&&(this.querySelector("slot")||this).append(...e),Object.defineProperty(this,"html",{set(e){Globals.rendered.add(this),"string"==typeof e?(console.warn("Assigning to this.html without the r template prefix."),this.innerHTML=e):this.modifications=r(this,e,t)}})}renderFirstTime(){!Globals.rendered.has(this)&&this.render&&this.render()}connectedCallback(){this.renderFirstTime(),Globals.connected.has(this)||(Globals.connected.add(this),this.onFirstConnect()),this.onConnect()}disconnectedCallback(){this.onDisconnect()}static define(e=null){defineClass(this,e,t)}}}let unusedArg=Symbol("unusedArg");function map(t,e){let l=[];for(let s=0;t.length>s;s++)l.push(e(t[s],s,t));return l}function watch3(t,e,l=unusedArg){l!==unusedArg?t[e]=l:l=t[e];const s={get(i,o,r){let a=i===r&&e===o?l:Reflect.get(i,o,r);if("map"===o)return e=>()=>(Globals.nodeGroups.get(t).mapCallbacks.set(i,e),map(new Proxy(i,s),e));if(Globals.currentExprPath){let[l,s]=Globals.currentExprPath,i=Globals.nodeGroups.get(t);i.watchedExprPaths[e]=i.watchedExprPaths[e]||new Set,i.watchedExprPaths[e].add(l)}return a&&"object"==typeof a?new Proxy(a,s):a},set(s,i,o,r){s===r&&e===i?l=o:Reflect.set(s,i,o,r);let a=Globals.nodeGroups.get(t);for(let t of a.watchedExprPaths[e])Array.isArray(s)&&parseInt(i)==i?!0!==a.exprsToRender.get(t)&&Util$1.mapAdd(a.exprsToRender,t,[s,i,o]):a.exprsToRender.set(t,!0);return!0}};Object.defineProperty(t,e,{get(){return s.get(t,e,t)},set(l){return s.set(t,e,l,t)}})}function renderWatched(t){let e=Globals.nodeGroups.get(t),l=[];for(let[t,s]of e.exprsToRender)if(!0===s)t.apply(t.watchFunction),t.freeNodeGroups(),l.push(...t.getNodes());else for(let i of s){let[s,o,r]=i,a=e.mapCallbacks.get(s)(r);t.applyLoopItemUpdate(o,a),l.push(...t.nodeGroups[o].getNodes())}return e.exprsToRender=new Map,l}let Solarite=new Proxy(createSolarite(),{apply:(t,e,l)=>createSolarite(...l)}),getInputValue=Util.getInputValue;export{ArgType,Globals,Solarite,Template,delve,getArg,getInputValue,r,renderWatched,watch3 as watch};
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "solarite",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Solarite is a small, fast, compilation-free JavaScript web component library that closely follows modern web standards.",
|
|
5
5
|
"main": "dist/Solarite.js",
|
|
6
6
|
"files": [
|
|
7
7
|
"readme.md",
|
|
8
8
|
"dist",
|
|
9
|
-
"src"
|
|
9
|
+
"src/solarite",
|
|
10
|
+
"src/util"
|
|
10
11
|
],
|
|
11
12
|
"scripts": {
|
|
12
13
|
"test": "echo \"Error: no test specified\" && exit 1"
|
package/src/solarite/ExprPath.js
CHANGED
|
@@ -73,6 +73,9 @@ export default class ExprPath {
|
|
|
73
73
|
nodeMarkerPath;
|
|
74
74
|
|
|
75
75
|
|
|
76
|
+
/** @type {?function} */
|
|
77
|
+
watchFunction
|
|
78
|
+
|
|
76
79
|
/**
|
|
77
80
|
* @param nodeBefore {Node}
|
|
78
81
|
* @param nodeMarker {?Node}
|
|
@@ -142,6 +145,11 @@ export default class ExprPath {
|
|
|
142
145
|
applyNodes(expr) {
|
|
143
146
|
let path = this;
|
|
144
147
|
|
|
148
|
+
// This can be done at the beginning or the end of this function.
|
|
149
|
+
// If at the end, we may get rendering done faster.
|
|
150
|
+
// But when at the beginning, it leaves all the nodes in-use so we can do a renderWatched().
|
|
151
|
+
path.freeNodeGroups();
|
|
152
|
+
|
|
145
153
|
/*#IFDEV*/path.verify();/*#ENDIF*/
|
|
146
154
|
|
|
147
155
|
/** @type {(Node|NodeGroup|Expr)[]} */
|
|
@@ -206,15 +214,52 @@ export default class ExprPath {
|
|
|
206
214
|
// Rearrange nodes.
|
|
207
215
|
udomdiff(path.nodeMarker.parentNode, oldNodes, newNodes, path.nodeMarker)
|
|
208
216
|
|
|
209
|
-
|
|
217
|
+
// TODO: Put this in a remove() function of NodeGroup.
|
|
218
|
+
// Then only run it on the old nodeGroups that were actually removed.
|
|
219
|
+
//Util.saveOrphans(oldNodeGroups, oldNodes);
|
|
220
|
+
|
|
221
|
+
for (let ng of oldNodeGroups)
|
|
222
|
+
if (!ng.startNode.parentNode)
|
|
223
|
+
ng.saveOrphans();
|
|
210
224
|
}
|
|
211
225
|
|
|
212
|
-
// Must happen after second pass.
|
|
213
|
-
path.freeNodeGroups();
|
|
214
226
|
|
|
215
227
|
/*#IFDEV*/path.verify();/*#ENDIF*/
|
|
216
228
|
}
|
|
217
229
|
|
|
230
|
+
/**
|
|
231
|
+
* Used by watch() for replacing individual loop items. */
|
|
232
|
+
applyLoopItemUpdate(index, template) {
|
|
233
|
+
// At this point none of the nodes being used will be in nodeGroupsFree.
|
|
234
|
+
let oldNg = this.nodeGroups[index];
|
|
235
|
+
this.nodeGroupsFree.add(oldNg.exactKey, oldNg);
|
|
236
|
+
this.nodeGroupsFree.add(oldNg.closeKey, oldNg);
|
|
237
|
+
|
|
238
|
+
let ng = this.getNodeGroup(template, true);
|
|
239
|
+
if (ng) {
|
|
240
|
+
return; // It's an exactl match, so replace nothing.
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
ng = this.getNodeGroup(template, false);
|
|
246
|
+
|
|
247
|
+
this.nodeGroups[index] = ng;
|
|
248
|
+
|
|
249
|
+
// Splice in the new nodes.
|
|
250
|
+
for (let node of ng.getNodes()) {
|
|
251
|
+
oldNg.startNode.parentNode.insertBefore(node, oldNg.startNode);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (oldNg !== ng) {
|
|
255
|
+
for (let node of oldNg.getNodes())
|
|
256
|
+
node.remove();
|
|
257
|
+
oldNg.saveOrphans();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// TODO: update or invalidate the nodes cache?
|
|
261
|
+
this.nodesCache = null;
|
|
262
|
+
}
|
|
218
263
|
|
|
219
264
|
|
|
220
265
|
/**
|
|
@@ -261,7 +306,11 @@ export default class ExprPath {
|
|
|
261
306
|
this.applyExact(subExpr, newNodes, secondPass);
|
|
262
307
|
|
|
263
308
|
else if (typeof expr === 'function') {
|
|
309
|
+
// TODO: One ExprPath can have multiple expr functions.
|
|
310
|
+
// But if using it as a watch, it should only have one at the top level.
|
|
311
|
+
// So maybe this is ok.
|
|
264
312
|
Globals.currentExprPath = [this, expr]; // Used by watch3()
|
|
313
|
+
this.watchFunction = expr; // TODO: Only do this if it's a top level function.
|
|
265
314
|
let result = expr();
|
|
266
315
|
Globals.currentExprPath = null;
|
|
267
316
|
|
|
@@ -354,7 +403,6 @@ export default class ExprPath {
|
|
|
354
403
|
args = expr.slice(1);
|
|
355
404
|
}
|
|
356
405
|
|
|
357
|
-
// Undocumented.
|
|
358
406
|
// oninput=${[this, 'value']}
|
|
359
407
|
else {
|
|
360
408
|
func = setValue;
|
|
@@ -366,20 +414,27 @@ export default class ExprPath {
|
|
|
366
414
|
else
|
|
367
415
|
func = expr;
|
|
368
416
|
|
|
417
|
+
this.bindEvent(node, root, eventName, eventName, func, args);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
bindEvent(node, root, key, eventName, func, args, capture=false) {
|
|
369
422
|
let nodeEvents = Globals.nodeEvents.get(node);
|
|
370
423
|
if (!nodeEvents) {
|
|
371
|
-
nodeEvents = {[
|
|
424
|
+
nodeEvents = {[key]: new Array(3)};
|
|
372
425
|
Globals.nodeEvents.set(node, nodeEvents);
|
|
373
426
|
}
|
|
374
|
-
let nodeEvent = nodeEvents[
|
|
375
|
-
|
|
427
|
+
let nodeEvent = nodeEvents[key];
|
|
428
|
+
if (!nodeEvent)
|
|
429
|
+
nodeEvents[key] = nodeEvent = new Array(3);
|
|
376
430
|
|
|
377
431
|
|
|
378
432
|
// If function has changed, remove and rebind the event.
|
|
379
433
|
if (nodeEvent[0] !== func) {
|
|
434
|
+
|
|
380
435
|
let [existing, existingBound, _] = nodeEvent;
|
|
381
436
|
if (existing)
|
|
382
|
-
node.removeEventListener(eventName, existingBound);
|
|
437
|
+
node.removeEventListener(eventName, existingBound, capture);
|
|
383
438
|
|
|
384
439
|
let originalFunc = func;
|
|
385
440
|
|
|
@@ -395,15 +450,15 @@ export default class ExprPath {
|
|
|
395
450
|
nodeEvent[0] = originalFunc;
|
|
396
451
|
nodeEvent[1] = boundFunc;
|
|
397
452
|
|
|
398
|
-
node.addEventListener(eventName, boundFunc);
|
|
453
|
+
node.addEventListener(eventName, boundFunc, capture);
|
|
399
454
|
|
|
400
455
|
// TODO: classic event attribs?
|
|
401
|
-
//el[attr.name] = e => // e.g. el.onclick = ...
|
|
402
|
-
// (new Function('event', 'el', attr.value)).bind(this.manager.rootEl)(e, el)
|
|
456
|
+
//el[attr.name] = e => // e.g. el.onclick = ... // put "event", "el", and "this" in scope for the event code.
|
|
457
|
+
// (new Function('event', 'el', attr.value)).bind(this.manager.rootEl)(e, el)
|
|
403
458
|
}
|
|
404
459
|
|
|
405
460
|
// Otherwise just update the args to the function.
|
|
406
|
-
nodeEvents[
|
|
461
|
+
nodeEvents[key][2] = args;
|
|
407
462
|
}
|
|
408
463
|
|
|
409
464
|
applyValueAttrib(node, exprs, exprIndex) {
|
|
@@ -421,9 +476,14 @@ export default class ExprPath {
|
|
|
421
476
|
else if ((this.attrName === 'value' || this.attrName === 'data-value') && Util.isPath(expr)) {
|
|
422
477
|
let [obj, path] = [expr[0], expr.slice(1)];
|
|
423
478
|
node.value = delve(obj, path);
|
|
424
|
-
|
|
479
|
+
// TODO: We need to remove any old listeners, like in bindEventAttribute
|
|
480
|
+
|
|
481
|
+
let func = () => {
|
|
425
482
|
delve(obj, path, Util.getInputValue(node));
|
|
426
|
-
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// We use capture so we update the values before other events added by the user.
|
|
486
|
+
this.bindEvent(node, path[0], 'value', 'input', func, [], true);
|
|
427
487
|
}
|
|
428
488
|
|
|
429
489
|
// Regular attribute
|
|
@@ -621,8 +681,9 @@ export default class ExprPath {
|
|
|
621
681
|
|
|
622
682
|
let result;
|
|
623
683
|
|
|
624
|
-
|
|
625
|
-
|
|
684
|
+
// TODO: Would it be faster to maintain a separate list of detached nodegroups?
|
|
685
|
+
if (exact) { // [below] parentElement will be null if the parent is a DocumentFragment
|
|
686
|
+
result = this.nodeGroupsFree.deletePreferred(template.getExactKey(), ng=>ng.startNode.parentElement);
|
|
626
687
|
if (result) // also delete the matching close key.
|
|
627
688
|
this.nodeGroupsFree.delete(template.getCloseKey(), result);
|
|
628
689
|
else
|
|
@@ -633,7 +694,7 @@ export default class ExprPath {
|
|
|
633
694
|
// This is a match that has matching html, but different expressions applied.
|
|
634
695
|
// We can then apply the expressions to make it an exact match.
|
|
635
696
|
else {
|
|
636
|
-
result = this.nodeGroupsFree.
|
|
697
|
+
result = this.nodeGroupsFree.deletePreferred(template.getCloseKey(), ng=>ng.startNode.parentElement)
|
|
637
698
|
if (result) {
|
|
638
699
|
/*#IFDEV*/assert(result.exactKey);/*#ENDIF*/
|
|
639
700
|
this.nodeGroupsFree.delete(result.exactKey, result);
|
|
@@ -676,12 +737,18 @@ export default class ExprPath {
|
|
|
676
737
|
* @type {MultiValueMap<key:string, value:NodeGroup>} */
|
|
677
738
|
nodeGroupsFree = new MultiValueMap();
|
|
678
739
|
|
|
740
|
+
nodeGroupsDetached = new MultiValueMap();
|
|
741
|
+
|
|
679
742
|
|
|
680
743
|
/**
|
|
681
744
|
* Move everything from this.nodeGroupsInUse to this.nodeGroupsFree.
|
|
682
745
|
* TODO: this could run as needed in getNodeGroup? */
|
|
683
746
|
freeNodeGroups() {
|
|
684
747
|
// old:
|
|
748
|
+
|
|
749
|
+
//this.nodeGroupsDetached = this.nodeGroupsFree;
|
|
750
|
+
//this.nodeGroupsFree = new MultiValueMap();
|
|
751
|
+
|
|
685
752
|
let ngf = this.nodeGroupsFree;
|
|
686
753
|
for (let ng of this.nodeGroupsInUse) {
|
|
687
754
|
ngf.add(ng.exactKey, ng);
|
package/src/solarite/Globals.js
CHANGED
|
@@ -16,8 +16,9 @@ var Globals = {
|
|
|
16
16
|
rendered: new WeakSet(),
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* Used by watch3 to see which expressions are being accessed.
|
|
20
|
-
|
|
19
|
+
* Used by watch3 to see which expressions are being accessed.
|
|
20
|
+
* @type {[]}*/
|
|
21
|
+
currentExprPath: null,
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* @type {Object<string, Class<Node>>} A map from built-in tag names to the constructors that create them. */
|
|
@@ -7,6 +7,7 @@ import Util, {arraySame, flattenAndIndent, isEvent, nodeToArrayTree, setIndent}
|
|
|
7
7
|
//import NodeGroupManager from "./NodeGroupManager.js";
|
|
8
8
|
import delve from "../util/delve.js";
|
|
9
9
|
import Globals from "./Globals.js";
|
|
10
|
+
import MultiValueMap from "../util/MultiValueMap.js";
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
/** @typedef {boolean|string|number|function|Object|Array|Date|Node|Template} Expr */
|
|
@@ -349,6 +350,16 @@ export default class NodeGroup {
|
|
|
349
350
|
return this.rootNg;
|
|
350
351
|
}
|
|
351
352
|
|
|
353
|
+
/**
|
|
354
|
+
* Requires the nodeCache to be present. */
|
|
355
|
+
saveOrphans() {
|
|
356
|
+
/*#IFDEV*/assert(!this.startNode.parentNode);/*#ENDIF*/
|
|
357
|
+
/*#IFDEV*/assert(this.nodesCache);/*#ENDIF*/
|
|
358
|
+
let fragment = document.createDocumentFragment();
|
|
359
|
+
for (let node of this.getNodes())
|
|
360
|
+
fragment.append(node);
|
|
361
|
+
}
|
|
362
|
+
|
|
352
363
|
|
|
353
364
|
updatePaths(fragment, paths, offset) {
|
|
354
365
|
// Update paths to point to the fragment.
|
|
@@ -510,6 +521,23 @@ export class RootNodeGroup extends NodeGroup {
|
|
|
510
521
|
* @type {HTMLElement} */
|
|
511
522
|
root;
|
|
512
523
|
|
|
524
|
+
/**
|
|
525
|
+
* Store the expressions that use this watched variable,
|
|
526
|
+
* along with the functions used to get their values.
|
|
527
|
+
* @type {Object<field:string, Set<ExprPath>>} */
|
|
528
|
+
watchedExprPaths = {};
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Map from arrays where .map is called and their callback functions.
|
|
532
|
+
* TODO: One array might be called with two different map functions in different places!
|
|
533
|
+
* @type {Map<Array, function>} */
|
|
534
|
+
mapCallbacks = new Map();
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
*
|
|
538
|
+
* @type {Map<ExprPath, boolean|Array>} */
|
|
539
|
+
exprsToRender = new Map();
|
|
540
|
+
|
|
513
541
|
/**
|
|
514
542
|
*
|
|
515
543
|
* @param template
|
|
@@ -528,6 +556,7 @@ export class RootNodeGroup extends NodeGroup {
|
|
|
528
556
|
let offset = 0;
|
|
529
557
|
let root = fragment; // TODO: Rename so it's not confused with this.root.
|
|
530
558
|
if (el) {
|
|
559
|
+
Globals.nodeGroups.set(el, this);
|
|
531
560
|
|
|
532
561
|
// Save slot children
|
|
533
562
|
let slotFragment;
|
|
@@ -573,12 +602,15 @@ export class RootNodeGroup extends NodeGroup {
|
|
|
573
602
|
}
|
|
574
603
|
|
|
575
604
|
root = el;
|
|
605
|
+
|
|
576
606
|
this.startNode = el;
|
|
577
607
|
this.endNode = el;
|
|
578
608
|
}
|
|
579
609
|
else {
|
|
580
610
|
let singleEl = getSingleEl(fragment);
|
|
581
611
|
this.root = singleEl || fragment; // We return the whole fragment when calling r() with a collection of nodes.
|
|
612
|
+
|
|
613
|
+
Globals.nodeGroups.set(this.root, this);
|
|
582
614
|
if (singleEl) {
|
|
583
615
|
root = singleEl;
|
|
584
616
|
offset = 1;
|
|
@@ -592,6 +624,11 @@ export class RootNodeGroup extends NodeGroup {
|
|
|
592
624
|
// Apply exprs
|
|
593
625
|
this.applyExprs(template.exprs);
|
|
594
626
|
}
|
|
627
|
+
|
|
628
|
+
clearRenderWatched() {
|
|
629
|
+
this.watchedExprPaths = {};
|
|
630
|
+
this.mapCallbacks = new Map();
|
|
631
|
+
}
|
|
595
632
|
}
|
|
596
633
|
|
|
597
634
|
function getSingleEl(fragment) {
|
package/src/solarite/Solarite.js
CHANGED
|
@@ -31,4 +31,4 @@ export {default as delve} from '../util/delve.js';
|
|
|
31
31
|
|
|
32
32
|
//Experimental:
|
|
33
33
|
//export {forEach, watchGet, watchSet} from './watch.js' // old, unfinished
|
|
34
|
-
|
|
34
|
+
export {default as watch, renderWatched} from './watch3.js'; // unfinished
|
package/src/solarite/Template.js
CHANGED
|
@@ -81,14 +81,14 @@ export default class Template {
|
|
|
81
81
|
if (standalone) {
|
|
82
82
|
ng = new RootNodeGroup(this, null, options);
|
|
83
83
|
el = ng.getRootNode();
|
|
84
|
-
Globals.nodeGroups.set(el, ng);
|
|
84
|
+
//Globals.nodeGroups.set(el, ng);
|
|
85
85
|
firstTime = true;
|
|
86
86
|
}
|
|
87
87
|
else {
|
|
88
88
|
ng = Globals.nodeGroups.get(el);
|
|
89
89
|
if (!ng) {
|
|
90
90
|
ng = new RootNodeGroup(this, el, options);
|
|
91
|
-
Globals.nodeGroups.set(el, ng);
|
|
91
|
+
//Globals.nodeGroups.set(el, ng);
|
|
92
92
|
firstTime = true;
|
|
93
93
|
}
|
|
94
94
|
}
|
|
@@ -98,8 +98,10 @@ export default class Template {
|
|
|
98
98
|
if (!firstTime) {
|
|
99
99
|
if (this.html?.length === 1 && !this.html[0])
|
|
100
100
|
el.innerHTML = ''; // Fast path for empty component.
|
|
101
|
-
else
|
|
101
|
+
else {
|
|
102
|
+
ng.clearRenderWatched();
|
|
102
103
|
ng.applyExprs(this.exprs);
|
|
104
|
+
}
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
return el;
|
package/src/solarite/getArg.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* There are three ways to create an instance of a Solarite Component:
|
|
6
6
|
* 1. new ComponentName(); // direct class instantiation
|
|
7
|
-
* 2. this.html = r`<div><component-name></component-name></div>; // as a child of another
|
|
7
|
+
* 2. this.html = r`<div><component-name></component-name></div>; // as a child of another RedComponent.
|
|
8
8
|
* 3. <body><component-name></component-name></body> // in the Document html.
|
|
9
9
|
*
|
|
10
10
|
* When created via #3, Solarite has no way to pass attributes as arguments to the constructor. So to make
|
|
@@ -29,14 +29,15 @@
|
|
|
29
29
|
* @param type {ArgType|function|*[]}
|
|
30
30
|
* If an array, use the value if it's in the array, otherwise return undefined.
|
|
31
31
|
* If it's a function, pass the value to the function and return the result.
|
|
32
|
-
* @
|
|
33
|
-
|
|
32
|
+
* @param fallback {*} If the type can't be parsed as the given type, use this value.
|
|
33
|
+
* @return {*} Undefined if attribute isn't set. */
|
|
34
|
+
export function getArg(el, name, val=undefined, type=ArgType.String, fallback=undefined) {
|
|
34
35
|
let attrVal = el.getAttribute(name);
|
|
35
36
|
if (attrVal !== null) // If attribute doesn't exist.
|
|
36
37
|
val = attrVal;
|
|
37
38
|
|
|
38
39
|
if (Array.isArray(type))
|
|
39
|
-
return type.includes(val) ? val :
|
|
40
|
+
return type.includes(val) ? val : fallback;
|
|
40
41
|
|
|
41
42
|
if (typeof type === 'function')
|
|
42
43
|
return type(val);
|
|
@@ -44,15 +45,22 @@ export function getArg(el, name, val=null, type=ArgType.String) {
|
|
|
44
45
|
// If bool, it's true as long as it exists and its value isn't falsey.
|
|
45
46
|
if (type===ArgType.Bool) {
|
|
46
47
|
let lAttrVal = typeof val === 'string' ? val.toLowerCase() : val;
|
|
47
|
-
|
|
48
|
+
if (['false', '0', false, 0, null, undefined, NaN].includes(lAttrVal))
|
|
49
|
+
return false;
|
|
50
|
+
if (['true', true].includes(lAttrVal) || parseFloat(lAttrVal) !== 0)
|
|
51
|
+
return true;
|
|
52
|
+
return fallback;
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
// Attribute doesn't exist
|
|
56
|
+
let result;
|
|
51
57
|
switch (type) {
|
|
52
58
|
case ArgType.Int:
|
|
53
|
-
|
|
59
|
+
result = parseInt(val);
|
|
60
|
+
return isNaN(result) ? fallback : result;
|
|
54
61
|
case ArgType.Float:
|
|
55
|
-
|
|
62
|
+
result = parseFloat(val);
|
|
63
|
+
return isNaN(result) ? fallback : result;
|
|
56
64
|
case ArgType.String:
|
|
57
65
|
return [undefined, null, false].includes(val) ? '' : val+'';
|
|
58
66
|
case ArgType.JSON:
|
|
@@ -66,7 +74,9 @@ export function getArg(el, name, val=null, type=ArgType.String) {
|
|
|
66
74
|
} catch (e) {
|
|
67
75
|
return val;
|
|
68
76
|
}
|
|
69
|
-
else return
|
|
77
|
+
else return fallback;
|
|
78
|
+
|
|
79
|
+
// type not provided
|
|
70
80
|
default:
|
|
71
81
|
return val;
|
|
72
82
|
}
|