-
-
+
+
```
@@ -62,46 +57,48 @@ Javascript part (js/Sample.js) :
```js
function init()
{
- var fooFunc = function(){return "bar"};
- var table = ["This", "must", "work"];
- var me = {"name":"Template.js", "mood":"pretty good"};
- var tpl = new Template("firstTemplate");
- tpl.assign("var1", "world");
- tpl.assign("me", me);
- tpl.assign("myTable", table);
- tpl.setFunction("fooFunc", fooFunc);
- tpl.addEventListener(TemplateEvent.RENDER_INIT, tplRenderInitHandler, false);
- tpl.addEventListener(TemplateEvent.RENDER_COMPLETE, tplRenderCompleteHandler, false);
- tpl.addEventListener(TemplateEvent.RENDER_COMPLETE_LOADED, tplRenderCompleteLoadedHandler, false);
- tpl.render("#holder");
+ var fooFunc = function(named_var){return "bar "+named_var;};
+ var table = ["This", "must", "work"];
+ var me = {"name":"Template.js", "mood":"pretty good"};
+ var tpl = new Template("firstTemplate");
+ tpl.assign("var1", "world");
+ tpl.assign("me", me);
+ tpl.assign("myTable", table);
+ tpl.setFunction("fooFunc", fooFunc);
+ tpl.addEventListener(TemplateEvent.RENDER_INIT, tplRenderInitHandler, false);
+ tpl.addEventListener(TemplateEvent.RENDER_COMPLETE, tplRenderCompleteHandler, false);
+ tpl.addEventListener(TemplateEvent.RENDER_COMPLETE_LOADED, tplRenderCompleteLoadedHandler, false);
+ tpl.render("#holder");
}
window.addEventListener("load", init, false);
function tplRenderInitHandler(e)
{
- console.log("Sample.js : init");
+ console.log("Sample.js : init");
}
function tplRenderCompleteHandler(e)
{
- //Ready to display result
- console.log("Sample.js : complete");
+ //Ready to display result
+ console.log("Sample.js : complete");
}
function tplRenderCompleteLoadedHandler(e)
{
- //Ready to display & everything is loaded (images)
- console.log("Sample.js : complete & loaded");
+ //Ready to display & everything is loaded (images)
+ console.log("Sample.js : complete & loaded");
}
```
API Reference
--------------
+---
### Template
-#### Template(pIdTemplate)
+
+#### Template(pIdTemplate, pData)
Constructor - Instanciate a Template Object
* pIdTemplate (*string*) : Script template's id within DOM
+* pData (*object*) : Default assigned data
#### assign(pName, pValue)
Variable asignation method
@@ -123,9 +120,9 @@ Function definition method
* pName (*string*) : Function's name
* pFunction (*function*) : function to execute each time template refer to it
```js
-tpl.setFunction("round", function(pValue)
+tpl.setFunction("round", function(value)
{
- return Math.round(pValue);
+ return Math.round(value);
});
```
@@ -145,6 +142,7 @@ tpl.setFunction("round", function(pValue)
(inherited)
### TemplateEvent
+
#### RENDER_INIT
Event triggered once rendering initialized
diff --git a/samples/01_basics.html b/samples/01_basics.html
index cc8194d..0530c5f 100644
--- a/samples/01_basics.html
+++ b/samples/01_basics.html
@@ -2,8 +2,7 @@
JS Templating
-
-
+
+
+
+
+
+
Template.js - Sample
+
\ No newline at end of file
diff --git a/samples/06_advance_testing.html b/samples/06_advance_testing.html
index ae6f6ca..37851d0 100644
--- a/samples/06_advance_testing.html
+++ b/samples/06_advance_testing.html
@@ -2,8 +2,17 @@
Template.js - Sample
-
+
\ No newline at end of file
diff --git a/samples/07_puppy.html b/samples/07_databinding.html
similarity index 69%
rename from samples/07_puppy.html
rename to samples/07_databinding.html
index 16c2886..44e03fb 100644
--- a/samples/07_puppy.html
+++ b/samples/07_databinding.html
@@ -3,18 +3,18 @@
Puppy
-
-
+
+
-
+
diff --git a/src/Puppy.js b/src/Puppy.js
deleted file mode 100644
index 45b6daf..0000000
--- a/src/Puppy.js
+++ /dev/null
@@ -1,78 +0,0 @@
-var Puppy = (function(){
- var sources = {};
- var events = {
- UPDATED_DATA: 'evt_updated_data'
- };
-
- function Container(pElement, pDataSource, pTemplate)
- {
- this.element = pElement;
- this.source = pDataSource;
- this.template = pTemplate;
- this.template.addEventListener(TemplateEvent.RENDER_COMPLETE, this.dispatchEvent.proxy(this));
- this.source.addEventListener(events.UPDATED_DATA, this._dataUpdated.proxy(this), false);
- this._dataUpdated();
- }
-
- Class.define(Container, [EventDispatcher], {
- _dataUpdated:function()
- {
- this.template._content = this.source.getData();
- this.element.innerHTML = '';
- this.template.render(this.element);
- }
- });
-
-
- function DataSource(pData)
- {
- this._data = pData;
- this.dispatchEvent(new Event(events.UPDATED_DATA));
- }
-
- Class.define(DataSource, [EventDispatcher], {
- setData:function(pData)
- {
- this._data = pData;
- this.dispatchEvent(new Event(events.UPDATED_DATA));
- },
- setValue:function(pName, pData)
- {
- this._data[pName] = pData;
- this.dispatchEvent(new Event(events.UPDATED_DATA));
- },
- getData:function()
- {
- return this._data;
- }
- });
-
- if(!NodeList.prototype.forEach)
- NodeList.prototype.forEach = Array.prototype.forEach;
-
-
- function defineContainer(pElement)
- {
- if(!pElement.getAttribute('data-source') || !pElement.getAttribute('data-template'))
- return false;
- var src = pElement.getAttribute('data-source');
- var tpl = pElement.getAttribute('data-template');
- if(!sources[src])
- return false;
- return new Container(pElement, sources[src], new Template(tpl));
- }
-
- function defineSource(pName, pData)
- {
- pName = 'Puppy.'+pName;
- sources[pName] = new DataSource(pData);
- return sources[pName];
- }
-
- return {
- define: {
- container:defineContainer,
- source:defineSource
- }
- }
-})();
\ No newline at end of file
diff --git a/src/Template.js b/src/Template.js
index e4233ad..43ce2b8 100644
--- a/src/Template.js
+++ b/src/Template.js
@@ -1,427 +1,465 @@
-function Template(pIdTemplate, pContent)
+"use strict";
+/**
+ * @author Arnaud NICOLAS
+ * @repo https://github.com/arno06/Template
+ */
+class EventEmitter
{
- this.removeAllEventListener();
- this._content = pContent||{};
- this._c = {};
- this._functions = Template.FUNCTIONS||{};
- this.time = null;
- this._id = pIdTemplate;
+ constructor()
+ {
+ this.delegate = document.createDocumentFragment();
+ }
+
+ addEventListener(...args){
+ this.delegate.addEventListener(...args);
+ }
+
+ dispatchEvent(event){
+ this.delegate.dispatchEvent(event);
+ }
+
+ removeEventListener(...args){
+ this.delegate.removeEventListener(...args);
+ }
}
-Class.define(Template, [EventDispatcher],
+class Template extends EventEmitter
{
- _content:{},
- assign:function(pName, pValue)
- {
- this._content[pName] = pValue;
- },
- setFunction:function(pName, pCallBack)
- {
- this._functions[pName] = pCallBack;
- },
- render:function(pParentNode)
- {
- var self = this;
- var p = pParentNode;
- if((typeof p).toLowerCase()=="string")
- p = document.querySelector(pParentNode);
- if(!p)
- return;
+ constructor(pIdTemplate, pContent = {})
+ {
+ super();
+ this._content = pContent;
+ this._c = {};
+ this._functions = Template.FUNCTIONS||{};
+ this._setReFuncs();
+ this.time = null;
+ this._id = pIdTemplate;
+ }
- this.dispatchEvent(new TemplateEvent(TemplateEvent.RENDER_INIT, 0, false));
+ assign(pName, pValue)
+ {
+ this._content[pName] = pValue;
+ }
- p.innerHTML += this.evaluate();
+ setFunction(pName, pFunction)
+ {
+ this._functions[pName] = pFunction;
+ this._setReFuncs();
+ }
- this.dispatchEvent(new TemplateEvent(TemplateEvent.RENDER_COMPLETE, this.time, false));
+ _setReFuncs(){
+ let funcs = [];
+ for(let k in this._functions){
+ funcs.push(k);
+ }
+ this.RE_FUNCS = new RegExp(Template.TAG[0]+"("+funcs.join("|")+")\\s([^"+Template.TAG[1]+"]+)"+Template.TAG[1], "gi");
+ }
+
+ render(pParent)
+ {
+ let p = pParent;
+ if((typeof p).toLowerCase()==="string")
+ p = document.querySelector(pParent);
+ if(!p)
+ return;
- var imgs = p.querySelectorAll("img");
+ this.dispatchEvent(new Event(TemplateEvent.RENDER_INIT));
- var max = imgs.length;
+ p.innerHTML += this.evaluate();
- if(!max)
- {
- this.dispatchEvent(new TemplateEvent(TemplateEvent.RENDER_COMPLETE_LOADED, this.time, false));
- return;
- }
+ this.dispatchEvent(new Event(TemplateEvent.RENDER_COMPLETE));
- var i = 0;
+ let images = p.querySelectorAll("img");
- function tick()
+ let max = images.length;
+
+ if(!max)
{
- if(++i==max)
- self.dispatchEvent(new TemplateEvent(TemplateEvent.RENDER_COMPLETE_LOADED, self.time, false));
+ this.dispatchEvent(new Event(TemplateEvent.RENDER_COMPLETE_LOADED));
+ return;
}
- imgs.forEach(function(img)
- {
- if(img.complete && (++i==max))
+ let i = 0;
+
+ let tick = _ => {
+
+ if(++i===max)
+ this.dispatchEvent(new Event(TemplateEvent.RENDER_COMPLETE_LOADED));
+ };
+
+ images.forEach(img =>
+ {
+ if(img.complete && (++i===max))
{
- self.dispatchEvent(new TemplateEvent(TemplateEvent.RENDER_COMPLETE_LOADED, self.time, false));
+ this.dispatchEvent(new Event(TemplateEvent.RENDER_COMPLETE_LOADED));
}
- img.onload = tick;
+ img.onload = tick;
img.onerror = tick;
- });
+ });
+ }
- },
- evaluate:function()
- {
- this._c = JSON.parse(JSON.stringify(this._content));
- var start = new Date().getTime();
- var t = Template.$[this._id];
- if(!t)
- return "";
-
- var t0 = Template.TAG[0];
- var t1 = Template.TAG[1];
-
- var re_blocs = new RegExp("(\\"+t0+"[a-z]+|\\"+t0+"\/[a-z]+)(\\s|\\"+t1+"){1}", "gi");
-
- var opener = [t0+"foreach", t0+"if"];
- var closer = [t0+"\/foreach", t0+"\/if"];
- var neutral= [t0+"else"];
-
- var step = 0;
-
- var result, tag, currentId;
-
- var opened = [];
-
- while (result = re_blocs.exec(t))
- {
- tag = result[1];
- if(opener.indexOf(tag)>-1)
- {
- currentId = ++step;
- opened.unshift(currentId);
- }
- else if (closer.indexOf(tag)>-1)
- {
- currentId = opened.shift();
- }
- else if (neutral.indexOf(tag)>-1)
- {
- currentId = opened[0];
- }
- else
- continue;
-
- t = t.replace(result[0], tag+"_"+currentId+result[2]);
- }
- var eval = this._parseBlock(t, this._c);
- var end = new Date().getTime();
- this.time = end - start;
- return eval;
- },
- _parseBlock:function(pString, pData)
- {
- var t_0 = Template.TAG[0];
- var t_1 = Template.TAG[1];
-
- //{opener_X}
- var opener = new RegExp('\\'+t_0+'([a-z]+)(_[0-9]+)([^\}]*)\\'+t_1, 'i');
-
- //$path.to.var
- var rea = /\$([a-z0-9\._\-]+)*/i;
-
- var o, start, neutral, n, closer, c, length, totalBlock, blc, alt, params;
-
- while(o = opener.exec(pString))
- {
- start = o.index;
-
- closer = new RegExp('\\'+t_0+'\/'+o[1]+o[2]+'\\'+t_1, 'gi');
- c = closer.exec(pString);
-
- if(!c)
- {
- console.log("no end tag");
- break;
- }
-
- blc = pString.substr((start + o[0].length), c.index - (start + o[0].length));
- alt = "";
-
- neutral = new RegExp('\\'+t_0+'else'+o[2]+'\\'+t_1, 'gi');
-
- n = neutral.exec(pString);
- if(n)
- {
- blc = pString.substr(start+o[0].length, n.index - (start + o[0].length));
- alt = pString.substr(n.index+n[0].length, c.index - (n.index+n[0].length));
- }
-
- length = (c.index + c[0].length) - start;
-
- totalBlock = pString.substr(start, length);
-
- var r = "";
- switch(o[1])
- {
- case "foreach":
- params = o[3].split(" ");//Setup [*, tablename, itemname, keyname]
- params[1] = params[1].replace("$","");
- var d = this._getVariable(params[1], pData);
- if(d)
- {
- var val = t_0+(params[2]||"$v")+t_1;
- var key = t_0+(params[3]||"$k")+t_1;
- var c_key = (params[3]||"$k").replace("$", "");
- var re = new RegExp("\\"+t_0+"\\"+(params[2]||"$v")+"([a-z0-9\.\_\-]+)*\\"+t_1, "gi");
- var empty = true;
- var v = "";
- var tmp = "";
- var vr;
- for(var j in d)
- {
+ evaluate()
+ {
+ this._c = Object.assign({}, this._content);
+ let start = new Date().getTime();
+ let t = Template.$[this._id];
+
+ if(!t)
+ return "";
+
+ let t0 = Template.TAG[0];
+ let t1 = Template.TAG[1];
+
+ let re_blocs = new RegExp("(\\"+t0+"[a-z]+|\\"+t0+"\/[a-z]+)(\\s|\\"+t1+"){1}", "gi");
+
+ let opener = [t0+"foreach", t0+"if"];
+ let closer = [t0+"\/foreach", t0+"\/if"];
+ let neutral= [t0+"else"];
+
+ let step = 0;
+
+ let result, currentId;
+
+ let opened = [];
+
+ let allblocks = [...t.matchAll(re_blocs)];
+
+ allblocks.forEach((pBlock)=>{
+ let tag = pBlock[1];
+ if(opener.indexOf(tag)>-1)
+ {
+ currentId = ++step;
+ opened.unshift(currentId);
+ }
+ else if (closer.indexOf(tag)>-1)
+ {
+ currentId = opened.shift();
+ }
+ else if (neutral.indexOf(tag)>-1)
+ {
+ currentId = opened[0];
+ }
+ else{
+ return;
+ }
+
+ t = t.replace(pBlock[0], tag+"_"+currentId+pBlock[2]);
+ });
+ let evaluation = this._parseBlock(t, this._c);
+ let end = new Date().getTime();
+ this.time = end - start;
+ return evaluation;
+ }
+
+ _parseBlock(pString, pData)
+ {
+ let t_0 = Template.TAG[0];
+ let t_1 = Template.TAG[1];
+
+ //{opener_X}
+ let opener = new RegExp('\\'+t_0+'([a-z]+)(_[0-9]+)([^\}]*)\\'+t_1, 'gi');
+
+ //$path.to.var
+ let rea = /\$([a-z0-9\-_\\.]+)+(?=\.|>|<|\!|\||=|\s|$)/gi;
+
+ let o;
+
+ let allblocks = [...pString.matchAll(opener)];
+ allblocks.forEach((pOpener)=>{
+
+ let params;
+ let start = pString.indexOf(pOpener[0]);
+ if(start===-1){
+ //Block already replaced
+ return;
+ }
+ let closer = new RegExp('\\'+t_0+'\/'+pOpener[1]+pOpener[2]+'\\'+t_1);
+ let c = closer.exec(pString);
+
+ if(!c)
+ {
+ console.log("no end tag");
+ return;
+ }
+
+ let blc = pString.substr((start + pOpener[0].length), c.index - (start + pOpener[0].length));
+ let alt = "";
+
+ let neutral = new RegExp('\\'+t_0+'else'+pOpener[2]+'\\'+t_1, 'gi');
+
+ let n = neutral.exec(pString);
+ if(n)
+ {
+ blc = pString.substr(start+pOpener[0].length, n.index - (start + pOpener[0].length));
+ alt = pString.substr(n.index+n[0].length, c.index - (n.index+n[0].length));
+ }
+
+ let length = (c.index + c[0].length) - start;
+
+ let totalBlock = pString.substr(start, length);
+
+ let r = "";
+ switch(pOpener[1])
+ {
+ case "foreach":
+
+ params = Template.parseParams(pOpener[3], {from:null, item:"item", key:"key"});
+ let d = this._getVariable(params.from, pData);
+ if(d)
+ {
+ let empty = true;
+ let c_key = params.key;
+ let re = new RegExp("\\"+t_0+"\\$"+params.item+"([a-z0-9\.\_\-]+)*\\"+t_1, "gi");
+ for(let j in d)
+ {
+ let vr;
if(!d.hasOwnProperty(j))
continue;
empty = false;
- v = blc.replace(val, d[j]);
- tmp = v;
- while(vr = re.exec(v))//Keep exec on "v" and replacing on "tmp" (loosing string index)
- {
- vr[1] = vr[1].substr(1, vr[1].length-1);
- tmp = tmp.replace(vr[0], this._getVariable(vr[1], d[j]));
- }
- v = tmp.replace(key, t_0+(params[2]||"$v")+"."+c_key+t_1);
- v = v.replace("$"+c_key, (params[2]||"$v")+"."+c_key);
- if(typeof d[j] == "string" || typeof d[j] == "number" || typeof d[j] == "boolean" || d[j] === null)
- {
- tmp = d[j];
- d[j] = {};
- d[j][(params[2]||"$v")] = tmp;
- }
- d[j][c_key] = j;
-
- var dataCloned = Object.clone(pData);
- dataCloned[(params[2]||"$v").replace("$", "")] = d[j];
- dataCloned[c_key] = j;
- v = this._parseBlock(v, dataCloned);
- r += v;
- }
+ let v = blc;
+ let dataCloned = Object.assign({}, pData);//Data cloning
+ dataCloned[params.item] = d[j];
+ dataCloned[c_key] = j;
+ v = this._parseBlock(v, dataCloned);
+ r += v;
+ }
if(empty === true)
{
r = this._parseBlock(alt, pData);
}
- }
- else
- r = this._parseBlock(alt, pData);
- break;
- case "if":
- var f = this._parseVariables(o[3], pData, rea, true);
- while(f[0]==" ")
- f = f.replace(/^\s/, '');
- if(/^\s*$/.exec(f)||/^(!|=|>|<)/.exec(f)||/(\||&)(!|=|>|<)/.exec(f))
- f = false;
- r = eval("(function(){var r = false; try { r = "+f+"; } catch(e){ r= false;} return r;})()");
- r = r?blc:(alt||"");
- r = this._parseBlock(r, pData);
- break;
- default:
- continue;
- break;
- }
-
- pString = pString.replace(totalBlock, r);
- }
-
- pString = this._parseVariables(pString, pData, Template.REGXP_VAR);
-
- var func, a, p;
- while(func = Template.REGXP_FUNC.exec(pString))
- {
- var funcName = func[1];
- if(!this._functions[funcName])
- {
- throw new Error("Call to undefined function "+funcName);
- }
- params = func[2];
- p = [];
- params = params.replace(/,\s/g, ",");
- params = params.split(",");
- for(var i = 0, max = params.length;i|<)/.exec(f)||/(\||&)(!|=|>|<)/.exec(f))
+ f = false;
+ let cond = eval("(_ => {let r = false; try { r = "+f+"; } catch(e){ r= false;} return r;})()");
+ r = cond?blc:(alt||"");
+ r = this._parseBlock(r, pData);
+ break;
+ default:
+ return;
+ }
+ pString = pString.replace(totalBlock, r);
+ });
+ let allFuncs = [...pString.matchAll(this.RE_FUNCS)];
+ allFuncs.forEach((pFunc)=>{
+ let funcName = pFunc[1];
+ let p = [];
+ if(!this._functions[funcName])
+ {
+ throw new Error("Call to undefined function "+funcName);
+ }
+
+ let params = Template.parseParams(pFunc[2], {}, false);
+ for(let k in params){
+ if(!params.hasOwnProperty(k)){
+ continue;
+ }
+
+ if(params[k]){
+ if(params[k][0]==="$"){
+ params[k] = this._getVariable(params[k].replace("$", ""), pData);
+ }
+ else
+ {
+ if(/^[0-9][0-9\.]*[0-9]*$/.exec(params[k]))
+ params[k] = Number(params[k]);
+ if(/^("|')/.exec(params[k]))
+ params[k] = params[k].substr(1, params[k].length-2);
+ }
+ }
+ }
+
+ let pa = /function\(([^\)]+)\)/.exec(this._functions[funcName].toString());
+
+ if(pa){
+ pa = pa[1].split(',').map((pName)=>pName.trim());
+ pa.forEach((pName)=>{
+ p.push(params[pName]||null);
+ });
+ }
+ p.push(params);
+ pString = pString.replace(pFunc[0], this._functions[funcName].apply(null, p));
+ });
+
+ let allVars = [...pString.matchAll(Template.REGEXP_VAR)];
+ allVars.forEach((pRes)=>{
+ let val = this._parseVariables("$"+pRes[1], pData, rea);
+ pString = pString.replace(pRes[0], val);
+ });
+
+ return pString;
+ }
+
+ _parseVariables(pString, pData, pREGEXP = Template.REGEXP_ID, pEscapeString = false)
+ {
+ let res;
+ while(res = pREGEXP.exec(pString))
+ {
+ let value = this._getVariable(res[1], pData);
+ if(pEscapeString&& (typeof value )== "string")
+ value = "'"+value.replace(/\'/g, "\\'")+"'";
+ if(pString.indexOf("()")>-1){
+ let method = pString.replace("$"+res[1]+".", "").replace("()", "");
+ res[0] = "$"+res[1]+"."+method+"()";
+ value = value[method]();
+ }
+ pString = pString.replace(res[0], value);
+ }
+ return pString;
+ }
+
+ static parseParams(pString, pDefaults, pEscape = true){
+ pString = pString.split(" ");
+ let params = {...pDefaults};
+ let index = 0;
+ pString.filter((pVal)=>pVal.length).forEach((pParam)=>{
+ let [name, val] = pParam.split('=');
+ if(!val){
+ val = name;
+ name = "param"+(index++);
+ }
+ params[name] = pEscape?val.replace(/("|'|\$)/g, ''):val;
+ });
+ return params;
+ }
+
+ _getVariable(pName, pContext)
+ {
+ let default_value = "";
+ let data = pContext||this._c;
+ let result = Template.REGEXP_ID.exec(pName);
+
+ if(!result)
+ return default_value;
+
+ let levels = result[1].split(".");
+
+ for(let i = 0, max = levels.length;i=_data.length)
+ {
+ if(_callBack)
+ _callBack();
+ return;
+ }
- if(!result)
- return default_value;
+ Request.load(_data[_currentIndex].file).onComplete(templateLoadedHandler).onError(next);
+ }
- var levels = result[1].split(".");
+ next();
- for(var i = 0, max = levels.length;i=_data.length)
- {
- if(_callBack)
- _callBack();
- return;
- }
-
- Request.load(_data[_currentIndex].file, {}, "get").onComplete(templateLoadedHandler).onError(next);
- }
-
- next();
-
- return {
- onComplete:function(pCallBack)
- {
- _callBack = pCallBack;
- return this;
- }
- };
-};
+NodeList.prototype.forEach||(NodeList.prototype.forEach = Array.prototype.forEach);
window.addEventListener("DOMContentLoaded", Template.setup, false);
-
-function TemplateEvent(pType, pTime, pBubbles)
-{
- this.time = pTime||0;
- this.super("constructor", pType, pBubbles);
-}
-
-Class.define(TemplateEvent, [Event],
-{
- clone:function(){var e = new TemplateEvent(this.type, this.time, this.bubbles);e.target = this.target;return e;},
- toString:function(){return this.formatToString("type", "time", "eventPhase", "target", "currentTarget", "bubbles");}
-});
-
-TemplateEvent.RENDER_INIT = "evt_render_start";
-TemplateEvent.RENDER_COMPLETE = "evt_render_complete";
-TemplateEvent.RENDER_COMPLETE_LOADED = "evt_render_loaded_complete";
\ No newline at end of file
diff --git a/src/TemplateDataBinding.js b/src/TemplateDataBinding.js
new file mode 100644
index 0000000..afc43d4
--- /dev/null
+++ b/src/TemplateDataBinding.js
@@ -0,0 +1,86 @@
+"use strict";
+
+Template = Template||{};
+
+Template.DataBinding = (function(){
+ let sources = {};
+ let events = {
+ UPDATED_DATA: 'evt_updated_data'
+ };
+
+ class Container extends EventEmitter
+ {
+ constructor(pElement, pDataSource, pTemplate)
+ {
+ super();
+ this.element = pElement;
+ this.source = pDataSource;
+ this.template = pTemplate;
+ this.template.addEventListener(TemplateEvent.RENDER_COMPLETE, (e)=>{
+ this.dispatchEvent(new e.constructor(e.type, e));
+ });
+ this.source.addEventListener(events.UPDATED_DATA, this._dataUpdated.bind(this), false);
+ this._dataUpdated();
+ }
+
+ _dataUpdated()
+ {
+ this.template._content = this.source.data;
+ this.element.innerHTML = '';
+ this.template.render(this.element);
+ }
+ }
+
+ class DataSource extends EventEmitter
+ {
+ constructor(pData = null)
+ {
+ super();
+ if(pData !== null)
+ this.data = pData;
+ }
+
+ setValue(pName, pData)
+ {
+ this._data[pName] = pData;
+ this.dispatchEvent(new Event(events.UPDATED_DATA));
+ }
+
+ set data(pValue)
+ {
+ this._data = pValue;
+ this.dispatchEvent(new Event(events.UPDATED_DATA));
+ }
+
+ get data()
+ {
+ return this._data;
+ }
+ }
+
+ if(!NodeList.prototype.forEach)
+ NodeList.prototype.forEach = Array.prototype.forEach;
+
+
+ function defineContainer(pElement)
+ {
+ if(!pElement.getAttribute('data-source') || !pElement.getAttribute('data-template'))
+ return false;
+ let src = pElement.getAttribute('data-source');
+ let tpl = pElement.getAttribute('data-template');
+ if(!sources[src])
+ return false;
+ return new Container(pElement, sources[src], new Template(tpl));
+ }
+
+ function defineSource(pName, pData)
+ {
+ sources[pName] = new DataSource(pData);
+ return sources[pName];
+ }
+
+ return {
+ container:defineContainer,
+ source:defineSource
+ }
+})();
\ No newline at end of file