rasti 3.0.0-alpha.2 → 3.0.0-alpha.3
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/rasti.js +22 -7
- package/dist/rasti.min.js +1 -1
- package/es/Component.js +22 -7
- package/lib/Component.cjs +22 -7
- package/package.json +1 -1
- package/src/Component.js +22 -7
package/dist/rasti.js
CHANGED
|
@@ -734,10 +734,10 @@
|
|
|
734
734
|
// so all the components are added as children by the parent component.
|
|
735
735
|
while ((match = regExp.exec(main)) !== null) {
|
|
736
736
|
const before = main.slice(lastIndex, match.index);
|
|
737
|
-
out.push(
|
|
737
|
+
out.push(Component.markAsSafeHTML(before), expressions[match[1]]);
|
|
738
738
|
lastIndex = match.index + match[0].length;
|
|
739
739
|
}
|
|
740
|
-
out.push(
|
|
740
|
+
out.push(Component.markAsSafeHTML(main.slice(lastIndex)));
|
|
741
741
|
|
|
742
742
|
return out;
|
|
743
743
|
};
|
|
@@ -1031,7 +1031,7 @@
|
|
|
1031
1031
|
if (value === null || typeof value === 'undefined') value = '';
|
|
1032
1032
|
|
|
1033
1033
|
add[key] = value;
|
|
1034
|
-
html.push(`${
|
|
1034
|
+
html.push(`${Component.sanitize(key)}="${Component.sanitize(value)}"`);
|
|
1035
1035
|
}
|
|
1036
1036
|
});
|
|
1037
1037
|
// Remove attributes that were in previousAttributes but not in current attributes.
|
|
@@ -1135,8 +1135,9 @@
|
|
|
1135
1135
|
|
|
1136
1136
|
/**
|
|
1137
1137
|
* Tagged template helper method.
|
|
1138
|
-
* Used to create a partial template.
|
|
1139
|
-
* It will return a one-dimensional array with strings and expressions.
|
|
1138
|
+
* Used to create a partial template.
|
|
1139
|
+
* It will return a one-dimensional array with strings and expressions.
|
|
1140
|
+
* Components will be added as children by the parent component. Template strings will be marked as safe HTML to be rendered.
|
|
1140
1141
|
* @param {TemplateStringsArray} strings - Template strings.
|
|
1141
1142
|
* @param {...any} expressions - Template expressions.
|
|
1142
1143
|
* @return {Array} Array containing strings and expressions.
|
|
@@ -1305,6 +1306,20 @@
|
|
|
1305
1306
|
return this;
|
|
1306
1307
|
}
|
|
1307
1308
|
|
|
1309
|
+
/**
|
|
1310
|
+
* Mark a string as safe HTML to be rendered.
|
|
1311
|
+
* Normally you don't need to use this method, as Rasti will automatically mark strings
|
|
1312
|
+
* as safe HTML when the component is @link{#module_component_create created} and when
|
|
1313
|
+
* using the @link{#module_component__partial Component.partial} method.
|
|
1314
|
+
* Be sure that the string is safe to be rendered, as it will be inserted into the DOM without any sanitization.
|
|
1315
|
+
* @static
|
|
1316
|
+
* @param {string} value
|
|
1317
|
+
* @return {Rasti.SafeHTML} A safe HTML object.
|
|
1318
|
+
*/
|
|
1319
|
+
static markAsSafeHTML(value) {
|
|
1320
|
+
return new SafeHTML(value);
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1308
1323
|
/**
|
|
1309
1324
|
* Helper method used to extend a `Component`, creating a subclass.
|
|
1310
1325
|
* @static
|
|
@@ -1465,7 +1480,7 @@
|
|
|
1465
1480
|
const { tag : tagExpression, attributes : attributesAndEvents, inner, close } = parseMatch(match, expressions);
|
|
1466
1481
|
// Get tag, attributes.
|
|
1467
1482
|
tag = function() {
|
|
1468
|
-
return
|
|
1483
|
+
return Component.sanitize(getExpressionResult(tagExpression, this));
|
|
1469
1484
|
};
|
|
1470
1485
|
// Get attributes.
|
|
1471
1486
|
attributes = function() {
|
|
@@ -1493,7 +1508,7 @@
|
|
|
1493
1508
|
if (typeof item !== 'undefined' && item !== null && item !== false && item !== true) {
|
|
1494
1509
|
if (item instanceof SafeHTML) return item;
|
|
1495
1510
|
if (item instanceof Component) return addChild(item);
|
|
1496
|
-
return
|
|
1511
|
+
return Component.sanitize(`${item}`);
|
|
1497
1512
|
}
|
|
1498
1513
|
return '';
|
|
1499
1514
|
}).join('');
|
package/dist/rasti.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).Rasti={})}(this,(function(t){"use strict";class e{on(t,e){if("function"!=typeof e)throw new TypeError("Listener must be a function");return this.listeners||(this.listeners={}),this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(e),()=>this.off(t,e)}once(t,e){if("function"==typeof e){const s=this,i=e;e=function(...n){i(...n),s.off(t,e)}}return this.on(t,e)}off(t,e){this.listeners&&(t?this.listeners[t]&&(e?(this.listeners[t]=this.listeners[t].filter((t=>t!==e)),this.listeners[t].length||delete this.listeners[t]):delete this.listeners[t],Object.keys(this.listeners).length||delete this.listeners):delete this.listeners)}emit(t,...e){this.listeners&&this.listeners[t]&&this.listeners[t].slice().forEach((function(t){t(...e)}))}}var s=(t,e,...s)=>"function"!=typeof t?t:t.apply(e,s);const i={el:!0,tag:!0,attributes:!0,events:!0,model:!0,template:!0,onDestroy:!0};class n extends e{constructor(t={}){super(),this.preinitialize.apply(this,arguments),this.uid="uid"+ ++n.uid,this.delegatedEventListeners=[],this.children=[],this.destroyQueue=[],Object.keys(t).forEach((e=>{i[e]&&(this[e]=t[e])})),this.ensureElement()}preinitialize(){}$(t){return this.el.querySelector(t)}$$(t){return this.el.querySelectorAll(t)}destroy(){return this.destroyChildren(),this.undelegateEvents(),this.off(),this.destroyQueue.forEach((t=>t())),this.destroyQueue=[],this.onDestroy.apply(this,arguments),this}onDestroy(){}addChild(t){return this.children.push(t),t}destroyChildren(){this.children.forEach((t=>t.destroy())),this.children=[]}ensureElement(){if(this.el)this.el=s(this.el,this);else{const t=s(this.tag,this),e=s(this.attributes,this);this.el=this.createElement(t,e)}this.delegateEvents()}createElement(t="div",e={}){let s=document.createElement(t);return Object.keys(e).forEach((t=>s.setAttribute(t,e[t]))),s}removeElement(){return this.el.parentNode.removeChild(this.el),this}delegateEvents(t){if(t||(t=s(this.events,this)),!t)return this;this.delegatedEventListeners.length&&this.undelegateEvents();let e={};return Object.keys(t).forEach((s=>{const i=s.split(" "),n=i.shift(),r=i.join(" ");let h=t[s];h=("string"==typeof h?this[h]:h).bind(this),e[n]||(e[n]=[]),e[n].push({selector:r,listener:h})})),Object.keys(e).forEach((t=>{const s=s=>{e[t].forEach((({selector:t,listener:e})=>{t&&!s.target.closest(t)||e(s,this)}))};this.delegatedEventListeners.push({type:t,listener:s}),this.el.addEventListener(t,s)})),this}undelegateEvents(){return this.delegatedEventListeners.forEach((({type:t,listener:e})=>{this.el.removeEventListener(t,e)})),this.delegatedEventListeners=[],this}render(){return this.template&&(this.el.innerHTML=this.template(this.model)),this}static sanitize(t){return`${t}`.replace(/[&<>"']/g,(t=>({"&":"&","<":"<",">":">",'"':""","'":"'"}[t])))}}n.uid=0;const r=t=>t.reduce(((t,e)=>(Array.isArray(e)?t.push(...r(e)):t.push(e),t)),[]);class h{constructor(t){this.value=t}toString(){return this.value}}const o=(t,e)=>s(t,e,e),l=(t,e)=>t.reduce(((t,s,i)=>(t.push(s),void 0!==e[i]&&t.push(f.PLACEHOLDER_EXPRESSION(i)),t)),[]).join(""),a=(t,e)=>{const s=f.PLACEHOLDER_EXPRESSION("(\\d+)"),i=new RegExp(`${s}`,"g"),n=[];let r,o=0;for(;null!==(r=i.exec(t));){const s=t.slice(o,r.index);n.push(new h(s),e[r[1]]),o=r.index+r[0].length}return n.push(new h(t.slice(o))),n},c=(t,e)=>{const s=t.reduce(((t,s)=>{const i=e(s[0]);if(1===s.length)"object"==typeof i?t.all=Object.assign(t.all,i):"string"==typeof i&&(t.all[i]=!0);else{const n=e(s[1]);t.all[i]=n}return t}),{all:{},events:{},attributes:{}});return Object.keys(s.all).forEach((t=>{const e=t.match(/on(([A-Z]{1}[a-z]+)+)/);e&&e[1]?s.events[e[1].toLowerCase()]=s.all[t]:s.attributes[t]=s.all[t]})),s},u=(t,e)=>{const s=f.PLACEHOLDER_EXPRESSION("(\\d+)");return t.replace(new RegExp(`<(${s})([^>]*)>([\\s\\S]*?)</(${s})>|<(${s})([^>]*)/>`,"g"),(function(){const{tag:t,attributes:s,inner:i,close:n,raw:h}=d(arguments,e);if(!(t.prototype instanceof f))return h;let l;if(n){if(t!==n)return h;const s=a(u(i,e),e);l=function(){return r(s.map((t=>o(t,this))))}}return e.push((function(){const e=c(s,(t=>o(t,this))).all;return l&&(e.renderChildren=l.bind(this)),t.mount(e)})),f.PLACEHOLDER_EXPRESSION(e.length-1)}))},d=(t,e)=>{const s=f.PLACEHOLDER_EXPRESSION("(\\d+)"),[i,n,r,h,o,l,a,c,u,d]=t,E={raw:i,attributes:[]};let p;n?(E.tag=void 0!==r?e[r]:n,E.inner=o,E.close=void 0!==a?e[a]:l,p=h):(E.tag=void 0!==u?e[u]:c,p=d);const y=new RegExp(`(${s}|[\\w-]+)(?:=(["']?)(?:${s}|((?:.?(?!["']?\\s+(?:\\S+)=|\\s*/?[>"']))+.))\\3)?`,"g");let g;for(;null!==(g=y.exec(p));){const[,t,s,,i,n]=g,r=void 0!==s?e[s]:t,h=void 0!==i?e[i]:n;void 0!==h?E.attributes.push([r,h]):E.attributes.push([r])}return E},E={area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,link:!0,meta:!0,source:!0,track:!0,wbr:!0},p={key:!0,state:!0,onCreate:!0,onChange:!0,onRender:!0};class f extends n{constructor(t={}){super(...arguments),Object.keys(t).forEach((e=>{p[e]&&(this[e]=t[e])})),this.options=t,this.onCreate.apply(this,arguments)}subscribe(t){if(!t.on)return;const e=this.onChange.bind(this),s=t.on("change",e);return this.destroyQueue.push("function"==typeof s?s:()=>t.off("change",e)),this}isContainer(){return!(this.tag||!this.template)}ensureElement(){this.el&&(this.el=s(this.el,this),this.delegateEvents())}findElement(t){return(t||document).querySelector(`[${f.DATA_ATTRIBUTE_UID}="${this.uid}"]`)}getAttributes(){const t={},e={},i=[],r={[f.DATA_ATTRIBUTE_UID]:this.uid};this.attributes&&Object.assign(r,s(this.attributes,this));const h=this.previousAttributes||{};return this.previousAttributes=r,Object.keys(r).forEach((s=>{let h=r[s];!1===h?e[s]=!0:!0===h?(t[s]="",i.push(s)):(null==h&&(h=""),t[s]=h,i.push(`${n.sanitize(s)}="${n.sanitize(h)}"`))})),Object.keys(h).forEach((t=>{t in r||(e[t]=!0)})),{add:t,remove:e,html:i.join(" ")}}hydrate(t){return this.model&&this.subscribe(this.model),this.state&&this.subscribe(this.state),this.isContainer()?(this.children[0].hydrate(t),this.el=this.children[0].el):(this.el=this.findElement(t),this.delegateEvents(),this.children.forEach((t=>t.hydrate(this.el)))),this.onRender.call(this,f.RENDER_TYPE_HYDRATE),this}recycle(t){if(this.isContainer())return this.children[0].recycle(t);return this.findElement(t).replaceWith(this.el),this.onRender.call(this,f.RENDER_TYPE_RECYCLE),this}destroy(){return super.destroy.apply(this,arguments),this.destroyed=!0,this}onCreate(){}onChange(){this.render()}onRender(){}onDestroy(){}partial(t,...e){return r(a(u(l(t,e),e),e).map((t=>o(t,this))))}getRecyclePlaceholder(){if(this.isContainer())return this.children[0].getRecyclePlaceholder();const t=s(this.tag,this)||"div",e=`${f.DATA_ATTRIBUTE_UID}="${this.uid}"`;return this.template||!E[t]?`<${t} ${e}></${t}>`:`<${t} ${e} />`}toString(){if(this.destroyChildren(),this.isContainer())return this.template.call(this,this.addChild.bind(this));const t=s(this.tag,this)||"div",e=this.getAttributes().html,i=this.template?this.template.call(this,this.addChild.bind(this)):"";return this.template||!E[t]?`<${t} ${e}>${i}</${t}>`:`<${t} ${e} />`}render(){if(this.destroyed)return this;if(!this.isContainer()){if(!this.el){const t=this.createElement("template");return t.innerHTML=this,this.hydrate(t.content),this}const t=this.getAttributes();Object.keys(t.remove).forEach((t=>{this.el.removeAttribute(t)})),Object.keys(t.add).forEach((e=>{this.el.setAttribute(e,t.add[e])}))}if(this.template){const t=document.activeElement,e=[],s=[],i=this.children;this.children=[];const n=this.template.call(this,(t=>{let n=t;const r=t.key&&i.find((e=>e.key===t.key));return r?(n=r.getRecyclePlaceholder(),s.push(r),t.destroy()):e.push(t),n}));if(this.isContainer())if(e[0]){const t=this.createElement("template");t.innerHTML=n,this.addChild(e[0]).hydrate(t.content);const s=t.content.children[0];this.el&&this.el.replaceWith(s),this.el=s}else{if(!s[0])throw new Error("Container component must have a child component");this.addChild(s[0])}else this.el.innerHTML=n,e.forEach((t=>{this.addChild(t).hydrate(this.el)})),s.forEach((t=>{this.addChild(t).recycle(this.el)}));i.forEach((t=>{s.indexOf(t)>-1||t.destroy()})),this.el.contains(t)&&t.focus()}return this.onRender.call(this,f.RENDER_TYPE_RENDER),this}static extend(t){const e=this;class s extends e{}return Object.assign(s.prototype,"function"==typeof t?t(e.prototype):t),s}static mount(t,e,s){const i=new this(t);return e&&(s?(i.toString(),i.hydrate(e)):e.appendChild(i.render().el)),i}static create(t,...e){const s=f.PLACEHOLDER_EXPRESSION("(\\d+)");let i,E,p,y;"function"==typeof t&&(e=[t],t=["",""]);const g=u(l(t,e),e);let b=g.match(new RegExp(`^\\s*<([a-z]+[1-6]?|${s})([^>]*)>([\\s\\S]*?)</(\\1|${s})>\\s*$|^\\s*<([a-z]+[1-6]?|${s})([^>]*)/>\\s*$`));if(b){const{tag:t,attributes:s,inner:l,close:u}=d(b,e);if(i=function(){return n.sanitize(o(t,this))},E=function(){return c(s,(t=>o(t,this))).attributes},p=function(){const t=c(s,(t=>o(t,this))).events;return Object.keys(t).reduce(((e,s)=>{const i=o(t[s],this);return Object.keys(i).forEach((t=>{e[`${s}${"&"===t?"":` ${t}`}`]=i[t]})),e}),{})},u){const t=l?a(l,e):[];y=function(e){return r(t.map((t=>o(t,this)))).map((t=>null!=t&&!1!==t&&!0!==t?t instanceof h?t:t instanceof f?e(t):n.sanitize(`${t}`):"")).join("")}}}else{if(b=g.match(new RegExp(`^\\s*${s}\\s*$`)),!b)throw new SyntaxError("Invalid component");y=function(t){return t(o(e[b[1]],this)).toString()}}return this.extend({tag:i,attributes:E,events:p,template:y})}}f.PLACEHOLDER_EXPRESSION=t=>`__RASTI_{${t}}__`,f.DATA_ATTRIBUTE_UID="data-rasti-uid",f.RENDER_TYPE_HYDRATE="hydrate",f.RENDER_TYPE_RECYCLE="recycle",f.RENDER_TYPE_RENDER="render",t.Component=f,t.Emitter=e,t.Model=class extends e{constructor(t={}){super(),this.preinitialize.apply(this,arguments);const e=s(this.defaults,this)||{};this.attributes=Object.assign({},e,t),this.previous={},Object.keys(this.attributes).forEach(this.defineAttribute.bind(this))}preinitialize(){}defineAttribute(t){Object.defineProperty(this,t,{get:()=>this.get(t),set:e=>{this.set(t,e)}})}get(t){return this.attributes[t]}set(t,e,...s){let i,n;"object"==typeof t?(i=t,n=[e,...s]):(i={[t]:e},n=s);const r=this._changing;this._changing=!0;const h={};r||(this.previous=Object.assign({},this.attributes)),Object.keys(i).forEach((t=>{i[t]!==this.attributes[t]&&(h[t]=i[t],this.attributes[t]=i[t])}));const o=Object.keys(h);if(o.length&&(this._pending=["change",this,h,...n]),o.forEach((t=>{this.emit(`change:${t}`,this,i[t],...n)})),r)return this;for(;this._pending;){const t=this._pending;this._pending=null,this.emit.apply(this,t)}return this._pending=null,this._changing=!1,this}toJSON(){return Object.assign({},this.attributes)}},t.View=n}));
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).Rasti={})}(this,(function(t){"use strict";class e{on(t,e){if("function"!=typeof e)throw new TypeError("Listener must be a function");return this.listeners||(this.listeners={}),this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(e),()=>this.off(t,e)}once(t,e){if("function"==typeof e){const s=this,i=e;e=function(...n){i(...n),s.off(t,e)}}return this.on(t,e)}off(t,e){this.listeners&&(t?this.listeners[t]&&(e?(this.listeners[t]=this.listeners[t].filter((t=>t!==e)),this.listeners[t].length||delete this.listeners[t]):delete this.listeners[t],Object.keys(this.listeners).length||delete this.listeners):delete this.listeners)}emit(t,...e){this.listeners&&this.listeners[t]&&this.listeners[t].slice().forEach((function(t){t(...e)}))}}var s=(t,e,...s)=>"function"!=typeof t?t:t.apply(e,s);const i={el:!0,tag:!0,attributes:!0,events:!0,model:!0,template:!0,onDestroy:!0};class n extends e{constructor(t={}){super(),this.preinitialize.apply(this,arguments),this.uid="uid"+ ++n.uid,this.delegatedEventListeners=[],this.children=[],this.destroyQueue=[],Object.keys(t).forEach((e=>{i[e]&&(this[e]=t[e])})),this.ensureElement()}preinitialize(){}$(t){return this.el.querySelector(t)}$$(t){return this.el.querySelectorAll(t)}destroy(){return this.destroyChildren(),this.undelegateEvents(),this.off(),this.destroyQueue.forEach((t=>t())),this.destroyQueue=[],this.onDestroy.apply(this,arguments),this}onDestroy(){}addChild(t){return this.children.push(t),t}destroyChildren(){this.children.forEach((t=>t.destroy())),this.children=[]}ensureElement(){if(this.el)this.el=s(this.el,this);else{const t=s(this.tag,this),e=s(this.attributes,this);this.el=this.createElement(t,e)}this.delegateEvents()}createElement(t="div",e={}){let s=document.createElement(t);return Object.keys(e).forEach((t=>s.setAttribute(t,e[t]))),s}removeElement(){return this.el.parentNode.removeChild(this.el),this}delegateEvents(t){if(t||(t=s(this.events,this)),!t)return this;this.delegatedEventListeners.length&&this.undelegateEvents();let e={};return Object.keys(t).forEach((s=>{const i=s.split(" "),n=i.shift(),r=i.join(" ");let h=t[s];h=("string"==typeof h?this[h]:h).bind(this),e[n]||(e[n]=[]),e[n].push({selector:r,listener:h})})),Object.keys(e).forEach((t=>{const s=s=>{e[t].forEach((({selector:t,listener:e})=>{t&&!s.target.closest(t)||e(s,this)}))};this.delegatedEventListeners.push({type:t,listener:s}),this.el.addEventListener(t,s)})),this}undelegateEvents(){return this.delegatedEventListeners.forEach((({type:t,listener:e})=>{this.el.removeEventListener(t,e)})),this.delegatedEventListeners=[],this}render(){return this.template&&(this.el.innerHTML=this.template(this.model)),this}static sanitize(t){return`${t}`.replace(/[&<>"']/g,(t=>({"&":"&","<":"<",">":">",'"':""","'":"'"}[t])))}}n.uid=0;const r=t=>t.reduce(((t,e)=>(Array.isArray(e)?t.push(...r(e)):t.push(e),t)),[]);class h{constructor(t){this.value=t}toString(){return this.value}}const o=(t,e)=>s(t,e,e),l=(t,e)=>t.reduce(((t,s,i)=>(t.push(s),void 0!==e[i]&&t.push(p.PLACEHOLDER_EXPRESSION(i)),t)),[]).join(""),a=(t,e)=>{const s=p.PLACEHOLDER_EXPRESSION("(\\d+)"),i=new RegExp(`${s}`,"g"),n=[];let r,h=0;for(;null!==(r=i.exec(t));){const s=t.slice(h,r.index);n.push(p.markAsSafeHTML(s),e[r[1]]),h=r.index+r[0].length}return n.push(p.markAsSafeHTML(t.slice(h))),n},c=(t,e)=>{const s=t.reduce(((t,s)=>{const i=e(s[0]);if(1===s.length)"object"==typeof i?t.all=Object.assign(t.all,i):"string"==typeof i&&(t.all[i]=!0);else{const n=e(s[1]);t.all[i]=n}return t}),{all:{},events:{},attributes:{}});return Object.keys(s.all).forEach((t=>{const e=t.match(/on(([A-Z]{1}[a-z]+)+)/);e&&e[1]?s.events[e[1].toLowerCase()]=s.all[t]:s.attributes[t]=s.all[t]})),s},u=(t,e)=>{const s=p.PLACEHOLDER_EXPRESSION("(\\d+)");return t.replace(new RegExp(`<(${s})([^>]*)>([\\s\\S]*?)</(${s})>|<(${s})([^>]*)/>`,"g"),(function(){const{tag:t,attributes:s,inner:i,close:n,raw:h}=d(arguments,e);if(!(t.prototype instanceof p))return h;let l;if(n){if(t!==n)return h;const s=a(u(i,e),e);l=function(){return r(s.map((t=>o(t,this))))}}return e.push((function(){const e=c(s,(t=>o(t,this))).all;return l&&(e.renderChildren=l.bind(this)),t.mount(e)})),p.PLACEHOLDER_EXPRESSION(e.length-1)}))},d=(t,e)=>{const s=p.PLACEHOLDER_EXPRESSION("(\\d+)"),[i,n,r,h,o,l,a,c,u,d]=t,E={raw:i,attributes:[]};let f;n?(E.tag=void 0!==r?e[r]:n,E.inner=o,E.close=void 0!==a?e[a]:l,f=h):(E.tag=void 0!==u?e[u]:c,f=d);const y=new RegExp(`(${s}|[\\w-]+)(?:=(["']?)(?:${s}|((?:.?(?!["']?\\s+(?:\\S+)=|\\s*/?[>"']))+.))\\3)?`,"g");let g;for(;null!==(g=y.exec(f));){const[,t,s,,i,n]=g,r=void 0!==s?e[s]:t,h=void 0!==i?e[i]:n;void 0!==h?E.attributes.push([r,h]):E.attributes.push([r])}return E},E={area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,link:!0,meta:!0,source:!0,track:!0,wbr:!0},f={key:!0,state:!0,onCreate:!0,onChange:!0,onRender:!0};class p extends n{constructor(t={}){super(...arguments),Object.keys(t).forEach((e=>{f[e]&&(this[e]=t[e])})),this.options=t,this.onCreate.apply(this,arguments)}subscribe(t){if(!t.on)return;const e=this.onChange.bind(this),s=t.on("change",e);return this.destroyQueue.push("function"==typeof s?s:()=>t.off("change",e)),this}isContainer(){return!(this.tag||!this.template)}ensureElement(){this.el&&(this.el=s(this.el,this),this.delegateEvents())}findElement(t){return(t||document).querySelector(`[${p.DATA_ATTRIBUTE_UID}="${this.uid}"]`)}getAttributes(){const t={},e={},i=[],n={[p.DATA_ATTRIBUTE_UID]:this.uid};this.attributes&&Object.assign(n,s(this.attributes,this));const r=this.previousAttributes||{};return this.previousAttributes=n,Object.keys(n).forEach((s=>{let r=n[s];!1===r?e[s]=!0:!0===r?(t[s]="",i.push(s)):(null==r&&(r=""),t[s]=r,i.push(`${p.sanitize(s)}="${p.sanitize(r)}"`))})),Object.keys(r).forEach((t=>{t in n||(e[t]=!0)})),{add:t,remove:e,html:i.join(" ")}}hydrate(t){return this.model&&this.subscribe(this.model),this.state&&this.subscribe(this.state),this.isContainer()?(this.children[0].hydrate(t),this.el=this.children[0].el):(this.el=this.findElement(t),this.delegateEvents(),this.children.forEach((t=>t.hydrate(this.el)))),this.onRender.call(this,p.RENDER_TYPE_HYDRATE),this}recycle(t){if(this.isContainer())return this.children[0].recycle(t);return this.findElement(t).replaceWith(this.el),this.onRender.call(this,p.RENDER_TYPE_RECYCLE),this}destroy(){return super.destroy.apply(this,arguments),this.destroyed=!0,this}onCreate(){}onChange(){this.render()}onRender(){}onDestroy(){}partial(t,...e){return r(a(u(l(t,e),e),e).map((t=>o(t,this))))}getRecyclePlaceholder(){if(this.isContainer())return this.children[0].getRecyclePlaceholder();const t=s(this.tag,this)||"div",e=`${p.DATA_ATTRIBUTE_UID}="${this.uid}"`;return this.template||!E[t]?`<${t} ${e}></${t}>`:`<${t} ${e} />`}toString(){if(this.destroyChildren(),this.isContainer())return this.template.call(this,this.addChild.bind(this));const t=s(this.tag,this)||"div",e=this.getAttributes().html,i=this.template?this.template.call(this,this.addChild.bind(this)):"";return this.template||!E[t]?`<${t} ${e}>${i}</${t}>`:`<${t} ${e} />`}render(){if(this.destroyed)return this;if(!this.isContainer()){if(!this.el){const t=this.createElement("template");return t.innerHTML=this,this.hydrate(t.content),this}const t=this.getAttributes();Object.keys(t.remove).forEach((t=>{this.el.removeAttribute(t)})),Object.keys(t.add).forEach((e=>{this.el.setAttribute(e,t.add[e])}))}if(this.template){const t=document.activeElement,e=[],s=[],i=this.children;this.children=[];const n=this.template.call(this,(t=>{let n=t;const r=t.key&&i.find((e=>e.key===t.key));return r?(n=r.getRecyclePlaceholder(),s.push(r),t.destroy()):e.push(t),n}));if(this.isContainer())if(e[0]){const t=this.createElement("template");t.innerHTML=n,this.addChild(e[0]).hydrate(t.content);const s=t.content.children[0];this.el&&this.el.replaceWith(s),this.el=s}else{if(!s[0])throw new Error("Container component must have a child component");this.addChild(s[0])}else this.el.innerHTML=n,e.forEach((t=>{this.addChild(t).hydrate(this.el)})),s.forEach((t=>{this.addChild(t).recycle(this.el)}));i.forEach((t=>{s.indexOf(t)>-1||t.destroy()})),this.el.contains(t)&&t.focus()}return this.onRender.call(this,p.RENDER_TYPE_RENDER),this}static markAsSafeHTML(t){return new h(t)}static extend(t){const e=this;class s extends e{}return Object.assign(s.prototype,"function"==typeof t?t(e.prototype):t),s}static mount(t,e,s){const i=new this(t);return e&&(s?(i.toString(),i.hydrate(e)):e.appendChild(i.render().el)),i}static create(t,...e){const s=p.PLACEHOLDER_EXPRESSION("(\\d+)");let i,n,E,f;"function"==typeof t&&(e=[t],t=["",""]);const y=u(l(t,e),e);let g=y.match(new RegExp(`^\\s*<([a-z]+[1-6]?|${s})([^>]*)>([\\s\\S]*?)</(\\1|${s})>\\s*$|^\\s*<([a-z]+[1-6]?|${s})([^>]*)/>\\s*$`));if(g){const{tag:t,attributes:s,inner:l,close:u}=d(g,e);if(i=function(){return p.sanitize(o(t,this))},n=function(){return c(s,(t=>o(t,this))).attributes},E=function(){const t=c(s,(t=>o(t,this))).events;return Object.keys(t).reduce(((e,s)=>{const i=o(t[s],this);return Object.keys(i).forEach((t=>{e[`${s}${"&"===t?"":` ${t}`}`]=i[t]})),e}),{})},u){const t=l?a(l,e):[];f=function(e){return r(t.map((t=>o(t,this)))).map((t=>null!=t&&!1!==t&&!0!==t?t instanceof h?t:t instanceof p?e(t):p.sanitize(`${t}`):"")).join("")}}}else{if(g=y.match(new RegExp(`^\\s*${s}\\s*$`)),!g)throw new SyntaxError("Invalid component");f=function(t){return t(o(e[g[1]],this)).toString()}}return this.extend({tag:i,attributes:n,events:E,template:f})}}p.PLACEHOLDER_EXPRESSION=t=>`__RASTI_{${t}}__`,p.DATA_ATTRIBUTE_UID="data-rasti-uid",p.RENDER_TYPE_HYDRATE="hydrate",p.RENDER_TYPE_RECYCLE="recycle",p.RENDER_TYPE_RENDER="render",t.Component=p,t.Emitter=e,t.Model=class extends e{constructor(t={}){super(),this.preinitialize.apply(this,arguments);const e=s(this.defaults,this)||{};this.attributes=Object.assign({},e,t),this.previous={},Object.keys(this.attributes).forEach(this.defineAttribute.bind(this))}preinitialize(){}defineAttribute(t){Object.defineProperty(this,t,{get:()=>this.get(t),set:e=>{this.set(t,e)}})}get(t){return this.attributes[t]}set(t,e,...s){let i,n;"object"==typeof t?(i=t,n=[e,...s]):(i={[t]:e},n=s);const r=this._changing;this._changing=!0;const h={};r||(this.previous=Object.assign({},this.attributes)),Object.keys(i).forEach((t=>{i[t]!==this.attributes[t]&&(h[t]=i[t],this.attributes[t]=i[t])}));const o=Object.keys(h);if(o.length&&(this._pending=["change",this,h,...n]),o.forEach((t=>{this.emit(`change:${t}`,this,i[t],...n)})),r)return this;for(;this._pending;){const t=this._pending;this._pending=null,this.emit.apply(this,t)}return this._pending=null,this._changing=!1,this}toJSON(){return Object.assign({},this.attributes)}},t.View=n}));
|
package/es/Component.js
CHANGED
|
@@ -58,10 +58,10 @@ const splitPlaceholders = (main, expressions) => {
|
|
|
58
58
|
// so all the components are added as children by the parent component.
|
|
59
59
|
while ((match = regExp.exec(main)) !== null) {
|
|
60
60
|
const before = main.slice(lastIndex, match.index);
|
|
61
|
-
out.push(
|
|
61
|
+
out.push(Component.markAsSafeHTML(before), expressions[match[1]]);
|
|
62
62
|
lastIndex = match.index + match[0].length;
|
|
63
63
|
}
|
|
64
|
-
out.push(
|
|
64
|
+
out.push(Component.markAsSafeHTML(main.slice(lastIndex)));
|
|
65
65
|
|
|
66
66
|
return out;
|
|
67
67
|
};
|
|
@@ -355,7 +355,7 @@ class Component extends View {
|
|
|
355
355
|
if (value === null || typeof value === 'undefined') value = '';
|
|
356
356
|
|
|
357
357
|
add[key] = value;
|
|
358
|
-
html.push(`${
|
|
358
|
+
html.push(`${Component.sanitize(key)}="${Component.sanitize(value)}"`);
|
|
359
359
|
}
|
|
360
360
|
});
|
|
361
361
|
// Remove attributes that were in previousAttributes but not in current attributes.
|
|
@@ -459,8 +459,9 @@ class Component extends View {
|
|
|
459
459
|
|
|
460
460
|
/**
|
|
461
461
|
* Tagged template helper method.
|
|
462
|
-
* Used to create a partial template.
|
|
463
|
-
* It will return a one-dimensional array with strings and expressions.
|
|
462
|
+
* Used to create a partial template.
|
|
463
|
+
* It will return a one-dimensional array with strings and expressions.
|
|
464
|
+
* Components will be added as children by the parent component. Template strings will be marked as safe HTML to be rendered.
|
|
464
465
|
* @param {TemplateStringsArray} strings - Template strings.
|
|
465
466
|
* @param {...any} expressions - Template expressions.
|
|
466
467
|
* @return {Array} Array containing strings and expressions.
|
|
@@ -629,6 +630,20 @@ class Component extends View {
|
|
|
629
630
|
return this;
|
|
630
631
|
}
|
|
631
632
|
|
|
633
|
+
/**
|
|
634
|
+
* Mark a string as safe HTML to be rendered.
|
|
635
|
+
* Normally you don't need to use this method, as Rasti will automatically mark strings
|
|
636
|
+
* as safe HTML when the component is @link{#module_component_create created} and when
|
|
637
|
+
* using the @link{#module_component__partial Component.partial} method.
|
|
638
|
+
* Be sure that the string is safe to be rendered, as it will be inserted into the DOM without any sanitization.
|
|
639
|
+
* @static
|
|
640
|
+
* @param {string} value
|
|
641
|
+
* @return {Rasti.SafeHTML} A safe HTML object.
|
|
642
|
+
*/
|
|
643
|
+
static markAsSafeHTML(value) {
|
|
644
|
+
return new SafeHTML(value);
|
|
645
|
+
}
|
|
646
|
+
|
|
632
647
|
/**
|
|
633
648
|
* Helper method used to extend a `Component`, creating a subclass.
|
|
634
649
|
* @static
|
|
@@ -789,7 +804,7 @@ class Component extends View {
|
|
|
789
804
|
const { tag : tagExpression, attributes : attributesAndEvents, inner, close } = parseMatch(match, expressions);
|
|
790
805
|
// Get tag, attributes.
|
|
791
806
|
tag = function() {
|
|
792
|
-
return
|
|
807
|
+
return Component.sanitize(getExpressionResult(tagExpression, this));
|
|
793
808
|
};
|
|
794
809
|
// Get attributes.
|
|
795
810
|
attributes = function() {
|
|
@@ -817,7 +832,7 @@ class Component extends View {
|
|
|
817
832
|
if (typeof item !== 'undefined' && item !== null && item !== false && item !== true) {
|
|
818
833
|
if (item instanceof SafeHTML) return item;
|
|
819
834
|
if (item instanceof Component) return addChild(item);
|
|
820
|
-
return
|
|
835
|
+
return Component.sanitize(`${item}`);
|
|
821
836
|
}
|
|
822
837
|
return '';
|
|
823
838
|
}).join('');
|
package/lib/Component.cjs
CHANGED
|
@@ -60,10 +60,10 @@ const splitPlaceholders = (main, expressions) => {
|
|
|
60
60
|
// so all the components are added as children by the parent component.
|
|
61
61
|
while ((match = regExp.exec(main)) !== null) {
|
|
62
62
|
const before = main.slice(lastIndex, match.index);
|
|
63
|
-
out.push(
|
|
63
|
+
out.push(Component.markAsSafeHTML(before), expressions[match[1]]);
|
|
64
64
|
lastIndex = match.index + match[0].length;
|
|
65
65
|
}
|
|
66
|
-
out.push(
|
|
66
|
+
out.push(Component.markAsSafeHTML(main.slice(lastIndex)));
|
|
67
67
|
|
|
68
68
|
return out;
|
|
69
69
|
};
|
|
@@ -357,7 +357,7 @@ class Component extends View {
|
|
|
357
357
|
if (value === null || typeof value === 'undefined') value = '';
|
|
358
358
|
|
|
359
359
|
add[key] = value;
|
|
360
|
-
html.push(`${
|
|
360
|
+
html.push(`${Component.sanitize(key)}="${Component.sanitize(value)}"`);
|
|
361
361
|
}
|
|
362
362
|
});
|
|
363
363
|
// Remove attributes that were in previousAttributes but not in current attributes.
|
|
@@ -461,8 +461,9 @@ class Component extends View {
|
|
|
461
461
|
|
|
462
462
|
/**
|
|
463
463
|
* Tagged template helper method.
|
|
464
|
-
* Used to create a partial template.
|
|
465
|
-
* It will return a one-dimensional array with strings and expressions.
|
|
464
|
+
* Used to create a partial template.
|
|
465
|
+
* It will return a one-dimensional array with strings and expressions.
|
|
466
|
+
* Components will be added as children by the parent component. Template strings will be marked as safe HTML to be rendered.
|
|
466
467
|
* @param {TemplateStringsArray} strings - Template strings.
|
|
467
468
|
* @param {...any} expressions - Template expressions.
|
|
468
469
|
* @return {Array} Array containing strings and expressions.
|
|
@@ -631,6 +632,20 @@ class Component extends View {
|
|
|
631
632
|
return this;
|
|
632
633
|
}
|
|
633
634
|
|
|
635
|
+
/**
|
|
636
|
+
* Mark a string as safe HTML to be rendered.
|
|
637
|
+
* Normally you don't need to use this method, as Rasti will automatically mark strings
|
|
638
|
+
* as safe HTML when the component is @link{#module_component_create created} and when
|
|
639
|
+
* using the @link{#module_component__partial Component.partial} method.
|
|
640
|
+
* Be sure that the string is safe to be rendered, as it will be inserted into the DOM without any sanitization.
|
|
641
|
+
* @static
|
|
642
|
+
* @param {string} value
|
|
643
|
+
* @return {Rasti.SafeHTML} A safe HTML object.
|
|
644
|
+
*/
|
|
645
|
+
static markAsSafeHTML(value) {
|
|
646
|
+
return new SafeHTML(value);
|
|
647
|
+
}
|
|
648
|
+
|
|
634
649
|
/**
|
|
635
650
|
* Helper method used to extend a `Component`, creating a subclass.
|
|
636
651
|
* @static
|
|
@@ -791,7 +806,7 @@ class Component extends View {
|
|
|
791
806
|
const { tag : tagExpression, attributes : attributesAndEvents, inner, close } = parseMatch(match, expressions);
|
|
792
807
|
// Get tag, attributes.
|
|
793
808
|
tag = function() {
|
|
794
|
-
return
|
|
809
|
+
return Component.sanitize(getExpressionResult(tagExpression, this));
|
|
795
810
|
};
|
|
796
811
|
// Get attributes.
|
|
797
812
|
attributes = function() {
|
|
@@ -819,7 +834,7 @@ class Component extends View {
|
|
|
819
834
|
if (typeof item !== 'undefined' && item !== null && item !== false && item !== true) {
|
|
820
835
|
if (item instanceof SafeHTML) return item;
|
|
821
836
|
if (item instanceof Component) return addChild(item);
|
|
822
|
-
return
|
|
837
|
+
return Component.sanitize(`${item}`);
|
|
823
838
|
}
|
|
824
839
|
return '';
|
|
825
840
|
}).join('');
|
package/package.json
CHANGED
package/src/Component.js
CHANGED
|
@@ -57,10 +57,10 @@ const splitPlaceholders = (main, expressions) => {
|
|
|
57
57
|
// so all the components are added as children by the parent component.
|
|
58
58
|
while ((match = regExp.exec(main)) !== null) {
|
|
59
59
|
const before = main.slice(lastIndex, match.index);
|
|
60
|
-
out.push(
|
|
60
|
+
out.push(Component.markAsSafeHTML(before), expressions[match[1]]);
|
|
61
61
|
lastIndex = match.index + match[0].length;
|
|
62
62
|
}
|
|
63
|
-
out.push(
|
|
63
|
+
out.push(Component.markAsSafeHTML(main.slice(lastIndex)));
|
|
64
64
|
|
|
65
65
|
return out;
|
|
66
66
|
};
|
|
@@ -354,7 +354,7 @@ export default class Component extends View {
|
|
|
354
354
|
if (value === null || typeof value === 'undefined') value = '';
|
|
355
355
|
|
|
356
356
|
add[key] = value;
|
|
357
|
-
html.push(`${
|
|
357
|
+
html.push(`${Component.sanitize(key)}="${Component.sanitize(value)}"`);
|
|
358
358
|
}
|
|
359
359
|
});
|
|
360
360
|
// Remove attributes that were in previousAttributes but not in current attributes.
|
|
@@ -458,8 +458,9 @@ export default class Component extends View {
|
|
|
458
458
|
|
|
459
459
|
/**
|
|
460
460
|
* Tagged template helper method.
|
|
461
|
-
* Used to create a partial template.
|
|
462
|
-
* It will return a one-dimensional array with strings and expressions.
|
|
461
|
+
* Used to create a partial template.
|
|
462
|
+
* It will return a one-dimensional array with strings and expressions.
|
|
463
|
+
* Components will be added as children by the parent component. Template strings will be marked as safe HTML to be rendered.
|
|
463
464
|
* @param {TemplateStringsArray} strings - Template strings.
|
|
464
465
|
* @param {...any} expressions - Template expressions.
|
|
465
466
|
* @return {Array} Array containing strings and expressions.
|
|
@@ -628,6 +629,20 @@ export default class Component extends View {
|
|
|
628
629
|
return this;
|
|
629
630
|
}
|
|
630
631
|
|
|
632
|
+
/**
|
|
633
|
+
* Mark a string as safe HTML to be rendered.
|
|
634
|
+
* Normally you don't need to use this method, as Rasti will automatically mark strings
|
|
635
|
+
* as safe HTML when the component is @link{#module_component_create created} and when
|
|
636
|
+
* using the @link{#module_component__partial Component.partial} method.
|
|
637
|
+
* Be sure that the string is safe to be rendered, as it will be inserted into the DOM without any sanitization.
|
|
638
|
+
* @static
|
|
639
|
+
* @param {string} value
|
|
640
|
+
* @return {Rasti.SafeHTML} A safe HTML object.
|
|
641
|
+
*/
|
|
642
|
+
static markAsSafeHTML(value) {
|
|
643
|
+
return new SafeHTML(value);
|
|
644
|
+
}
|
|
645
|
+
|
|
631
646
|
/**
|
|
632
647
|
* Helper method used to extend a `Component`, creating a subclass.
|
|
633
648
|
* @static
|
|
@@ -788,7 +803,7 @@ export default class Component extends View {
|
|
|
788
803
|
const { tag : tagExpression, attributes : attributesAndEvents, inner, close } = parseMatch(match, expressions);
|
|
789
804
|
// Get tag, attributes.
|
|
790
805
|
tag = function() {
|
|
791
|
-
return
|
|
806
|
+
return Component.sanitize(getExpressionResult(tagExpression, this));
|
|
792
807
|
};
|
|
793
808
|
// Get attributes.
|
|
794
809
|
attributes = function() {
|
|
@@ -816,7 +831,7 @@ export default class Component extends View {
|
|
|
816
831
|
if (typeof item !== 'undefined' && item !== null && item !== false && item !== true) {
|
|
817
832
|
if (item instanceof SafeHTML) return item;
|
|
818
833
|
if (item instanceof Component) return addChild(item);
|
|
819
|
-
return
|
|
834
|
+
return Component.sanitize(`${item}`);
|
|
820
835
|
}
|
|
821
836
|
return '';
|
|
822
837
|
}).join('');
|