commit 0cf6dc0b36d7965bfdae171e0e2cabb8ec06179b
parent dec3c863fa6c34a57dc26c282180e041aaa38fbb
Author: finwo <finwo@pm.me>
Date: Sun, 14 Feb 2021 22:39:14 +0100
Restructured a bit
Diffstat:
| M | fwebc.js | | | 81 | ++++++++++++++++++++++++++----------------------------------------------------- |
| M | fwebc.min.js | | | 2 | +- |
2 files changed, 27 insertions(+), 56 deletions(-)
diff --git a/fwebc.js b/fwebc.js
@@ -17,12 +17,6 @@
};
const util = fwebc.util = {
- isObject(obj) {
- if (null === obj) return false;
- if ('object' !== typeof obj) return false;
- if (Array.isArray(obj)) return false;
- return true;
- },
unescape(input) {
const el = document.createElement('textarea');
el.innerHTML = input;
@@ -80,85 +74,62 @@
if (window.customElements.get(name)) return;
// Parse remplate
- let template = document.createElement('template');
- template.innerHTML = source;
-
- // Remove template wrapper
- while(
- (template.content.children.length == 1) &&
- (template.content.firstChild instanceof HTMLTemplateElement)
- ) {
- template = template.content.firstChild;
+ const wrapper = document.createElement('template');
+ wrapper.innerHTML = source;
+
+ // Separate style, script & template
+ let template = null;
+ let scripts = [];
+ let styles = [];
+ for(const node of [...wrapper.content.children]) {
+ if (node instanceof HTMLTemplateElement) template = node;
+ if (node instanceof HTMLScriptElement ) {scripts.push(node);wrapper.content.removeChild(node);}
+ if (node instanceof HTMLStyleElement ) {styles.push(node);wrapper.content.removeChild(node);}
}
-
- // Remove template wrapper around html section
- if (template.content.firstChild instanceof HTMLTemplateElement) {
- const wrapper = template.content.firstChild;
- template.content.prepend(...wrapper.content.children);
- template.content.removeChild(wrapper);
+ if (!template) {
+ template = wrapper;
}
- // Extract scripts
+ // Convert template and code into a string
+ template = util.unescape(template.innerHTML);
let code = '';
- for(const child of [...template.content.children]) {
- if (!(child instanceof HTMLScriptElement)) continue;
- if (child.getAttribute('src')) continue;
- code += child.innerHTML;
- template.content.removeChild(child);
+ for(const script of scripts) {
+ if (script.getAttribute('src')) continue;
+ code += script.innerHTML;
}
- // Template should be a string
- template = util.unescape(template.innerHTML);
-
// Register the actual element
window.customElements.define(name, class extends HTMLElement {
constructor() {
super();
-
- // Initialize shadow root
- this.root = this.attachShadow({ mode: 'open' });
-
- // Initial state
+ this.root = this.attachShadow({ mode: 'open' });
this.state = {};
-
- // Run plugins
- for (const plugin of plugins) {
- plugin(this);
- }
-
- // Run component code
+ for(const plugin of plugins) plugin(this);
(new Function(code)).call(this);
-
- // Load dependencies
- if (this.dependencies) {
- this.dependencies.forEach(fwebc.load);
- }
-
- // Start observing state & initial rendering
+ if (this.dependencies) this.dependencies.forEach(fwebc.load);
this.state = util.observable(this.state, () => this.emit('update'));
this.on('update', this.render.bind(this));
this.render();
}
-
render() {
const fn = new Function(...Object.keys(this.state), 'return `'+template+'`;');
- const styles = Array.from(this.root.ownerDocument.styleSheets).map(stylesheet => stylesheet.ownerNode.outerHTML);
+ const stylez = Array
+ .from(this.root.ownerDocument.styleSheets)
+ .map(stylesheet => stylesheet.ownerNode.outerHTML)
+ .concat(styles.map(stylesheet => stylesheet.outerHTML));
try {
- this.root.innerHTML = styles.join('') + fn(...Object.values(this.state));
+ this.root.innerHTML = stylez.join('') + fn(...Object.values(this.state));
} catch(e) {
console.error(e);
}
}
-
emit(event, data = {}) {
const ev = new CustomEvent(event, data);
this.dispatchEvent(ev);
}
-
on(event, handler) {
this.addEventListener(event, handler);
}
-
});
};
diff --git a/fwebc.min.js b/fwebc.min.js
@@ -1 +1 @@
-(e=>{"object"==typeof module&&"exports"in module?module.exports=e({fetch:require("node-fetch")}):"object"==typeof window&&(window.fwebc=e({fetch:window.fetch}))})(({fetch:e})=>{const t={},n=[],o={ext:"tag",base:"/partial"},r=t.util={isObject:e=>null!==e&&("object"==typeof e&&!Array.isArray(e)),unescape(e){const t=document.createElement("textarea");return t.innerHTML=e,0===t.childNodes.length?"":t.childNodes[0].nodeValue},observable(e,t,n=""){if(Object(e)!==e)throw new Error(`Object is not an object, got: ${e}`);if("function"!=typeof t)throw new Error(`Callback is not a function, got: ${t}`);for(const o of Object.keys(e))Object(e[o])===e[o]&&(e[o]=r.observable(e[o],t,`${n}${o}.`));return new Proxy(e,{set(e,o,s,i){if(e[o]===s)return;let c=o in e?"update":"add";const l={name:o,type:c,object:e};return"update"==c&&(l.oldValue=e[o]),e[o]=Object(s)===s?r.observable(s,t,`${n}${o}.`):s,t([l]),!0},deleteProperty(e,n,o){if(!(n in e))return;const r={name:n,type:"delete",object:e,oldValue:e[n]};return delete e[n],t([r]),!0}})}};return t.cfg=(e=>{Object.assign(o,e)}),t.install=(e=>{"function"==typeof e&&n.push(e)}),t.uninstall=(e=>{const t=n.indexOf(e);~t&&n.splice(t,1)}),t.register=((e,o)=>{if(window.customElements.get(e))return;let s=document.createElement("template");for(s.innerHTML=o;1==s.content.children.length&&s.content.firstChild instanceof HTMLTemplateElement;)s=s.content.firstChild;if(s.content.firstChild instanceof HTMLTemplateElement){const e=s.content.firstChild;s.content.prepend(...e.content.children),s.content.removeChild(e)}let i="";for(const e of[...s.content.children])e instanceof HTMLScriptElement&&(e.getAttribute("src")||(i+=e.innerHTML,s.content.removeChild(e)));s=r.unescape(s.innerHTML),window.customElements.define(e,class extends HTMLElement{constructor(){super(),this.root=this.attachShadow({mode:"open"}),this.state={};for(const e of n)e(this);new Function(i).call(this),this.dependencies&&this.dependencies.forEach(t.load),this.state=r.observable(this.state,()=>this.emit("update")),this.on("update",this.render.bind(this)),this.render()}render(){const e=new Function(...Object.keys(this.state),"return `"+s+"`;"),t=Array.from(this.root.ownerDocument.styleSheets).map(e=>e.ownerNode.outerHTML);try{this.root.innerHTML=t.join("")+e(...Object.values(this.state))}catch(e){console.error(e)}}emit(e,t={}){const n=new CustomEvent(e,t);this.dispatchEvent(n)}on(e,t){this.addEventListener(e,t)}})}),t.load=(n=>{e(`${o.base}/${n.replace(/-/g,"/")}.${o.ext}`).then(e=>e.text()).then(e=>{t.register(n,e)})}),t});
+(e=>{"object"==typeof module&&"exports"in module?module.exports=e({fetch:require("node-fetch")}):"object"==typeof window&&(window.fwebc=e({fetch:window.fetch}))})(({fetch:e})=>{const t={},n=[],o={ext:"tag",base:"/partial"},r=t.util={unescape(e){const t=document.createElement("textarea");return t.innerHTML=e,0===t.childNodes.length?"":t.childNodes[0].nodeValue},observable(e,t,n=""){if(Object(e)!==e)throw new Error(`Object is not an object, got: ${e}`);if("function"!=typeof t)throw new Error(`Callback is not a function, got: ${t}`);for(const o of Object.keys(e))Object(e[o])===e[o]&&(e[o]=r.observable(e[o],t,`${n}${o}.`));return new Proxy(e,{set(e,o,s,c){if(e[o]===s)return;let i=o in e?"update":"add";const a={name:o,type:i,object:e};return"update"==i&&(a.oldValue=e[o]),e[o]=Object(s)===s?r.observable(s,t,`${n}${o}.`):s,t([a]),!0},deleteProperty(e,n,o){if(!(n in e))return;const r={name:n,type:"delete",object:e,oldValue:e[n]};return delete e[n],t([r]),!0}})}};return t.cfg=(e=>{Object.assign(o,e)}),t.install=(e=>{"function"==typeof e&&n.push(e)}),t.uninstall=(e=>{const t=n.indexOf(e);~t&&n.splice(t,1)}),t.register=((e,o)=>{if(window.customElements.get(e))return;const s=document.createElement("template");s.innerHTML=o;let c=null,i=[],a=[];for(const e of[...s.content.children])e instanceof HTMLTemplateElement&&(c=e),e instanceof HTMLScriptElement&&(i.push(e),s.content.removeChild(e)),e instanceof HTMLStyleElement&&(a.push(e),s.content.removeChild(e));c||(c=s),c=r.unescape(c.innerHTML);let l="";for(const e of i)e.getAttribute("src")||(l+=e.innerHTML);window.customElements.define(e,class extends HTMLElement{constructor(){super(),this.root=this.attachShadow({mode:"open"}),this.state={};for(const e of n)e(this);new Function(l).call(this),this.dependencies&&this.dependencies.forEach(t.load),this.state=r.observable(this.state,()=>this.emit("update")),this.on("update",this.render.bind(this)),this.render()}render(){const e=new Function(...Object.keys(this.state),"return `"+c+"`;"),t=Array.from(this.root.ownerDocument.styleSheets).map(e=>e.ownerNode.outerHTML).concat(a.map(e=>e.outerHTML));try{this.root.innerHTML=t.join("")+e(...Object.values(this.state))}catch(e){console.error(e)}}emit(e,t={}){const n=new CustomEvent(e,t);this.dispatchEvent(n)}on(e,t){this.addEventListener(e,t)}})}),t.load=(n=>{e(`${o.base}/${n.replace(/-/g,"/")}.${o.ext}`).then(e=>e.text()).then(e=>{t.register(n,e)})}),t});