diff --git a/Ext.js b/Ext.js index a490cbf..db2ec22 100644 --- a/Ext.js +++ b/Ext.js @@ -218,7 +218,7 @@ var Ext = { //based on 4.2.1.833 only rewritten to fit node * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create * http://stackoverflow.com/questions/15192722/javascript-extending-class */ - extend: function(Parent) { + extend: function(Parent, data) { //define the new class that extends the parent function F() { // Call the parent constructor diff --git a/Template.js b/Template.js index f48506b..2268790 100644 --- a/Template.js +++ b/Template.js @@ -11,242 +11,6 @@ module.exports = function(Ext) { * @param {Object} config (optional) Config object */ var Template = function(html) { //Ext.Template - - /* Begin Definitions */ - - //this.requires = ['Ext.dom.Helper', 'Ext.util.Format'], - this.inheritableStatics = { - /** - * Creates a template from the passed element's value (_display:none_ textarea, preferred) or innerHTML. - * @param {String/HTMLElement} el A DOM element or its id - * @param {Object} config (optional) Config object - * @return {Ext.Template} The created template - * @static - * @inheritable - */ - from: function(el, config) { - el = Ext.getDom(el); - return new this(el.value || el.innerHTML, config || ''); - } - }, - - /* End Definitions */ - - /** - * @property {Boolean} isTemplate - * `true` in this class to identify an object as an instantiated Template, or subclass thereof. - */ - this.isTemplate = true, - - /** - * @cfg {Boolean} compiled - * True to immediately compile the template. Defaults to false. - */ - - /** - * @cfg {Boolean} disableFormats - * True to disable format functions in the template. If the template doesn't contain - * format functions, setting disableFormats to true will reduce apply time. Defaults to false. - */ - this.disableFormats = false, - - this.re = /\{([\w\-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g, - - /** - * Returns an HTML fragment of this template with the specified values applied. - * - * @param {Object/Array} values The template values. Can be an array if your params are numeric: - * - * var tpl = new Ext.Template('Name: {0}, Age: {1}'); - * tpl.apply(['John', 25]); - * - * or an object: - * - * var tpl = new Ext.Template('Name: {name}, Age: {age}'); - * tpl.apply({name: 'John', age: 25}); - * - * @return {String} The HTML fragment - */ - this.apply = function(values) { - var me = this, - useFormat = me.disableFormats !== true, - fm = Ext.util.Format, - tpl = me, - ret; - - if (me.compiled) { - return me.compiled(values).join(''); - } - - function fn(m, name, format, args) { - if (format && useFormat) { - if (args) { - args = [values[name]].concat(Ext.functionFactory('return ['+ args +'];')()); - } else { - args = [values[name]]; - } - if (format.substr(0, 5) == "this.") { - return tpl[format.substr(5)].apply(tpl, args); - } - else { - return fm[format].apply(fm, args); - } - } - else { - return values[name] !== undefined ? values[name] : ""; - } - } - - ret = me.html.replace(me.re, fn); - return ret; - }, - - /** - * Appends the result of this template to the provided output array. - * @param {Object/Array} values The template values. See {@link #apply}. - * @param {Array} out The array to which output is pushed. - * @return {Array} The given out array. - */ - this.applyOut = function(values, out) { - var me = this; - - if (me.compiled) { - out.push.apply(out, me.compiled(values)); - } else { - out.push(me.apply(values)); - } - - return out; - }, - - /** - * @method applyTemplate - * @member Ext.Template - * Alias for {@link #apply}. - * @inheritdoc Ext.Template#apply - */ - this.applyTemplate = function () { - return this.apply.apply(this, arguments); - }, - - /** - * Sets the HTML used as the template and optionally compiles it. - * @param {String} html - * @param {Boolean} compile (optional) True to compile the template. - * @return {Ext.Template} this - */ - this.set = function(html, compile) { - var me = this; - me.html = html; - me.compiled = null; - return compile ? me.compile() : me; - }, - - this.compileARe = /\\/g, - this.compileBRe = /(\r\n|\n)/g, - this.compileCRe = /'/g, - - /** - * Compiles the template into an internal function, eliminating the RegEx overhead. - * @return {Ext.Template} this - */ - this.compile = function() { - var me = this, - fm = Ext.util.Format, - useFormat = me.disableFormats !== true, - body, bodyReturn; - - function fn(m, name, format, args) { - if (format && useFormat) { - args = args ? ',' + args: ""; - if (format.substr(0, 5) != "this.") { - format = "fm." + format + '('; - } - else { - format = 'this.' + format.substr(5) + '('; - } - } - else { - args = ''; - format = "(values['" + name + "'] == undefined ? '' : "; - } - return "'," + format + "values['" + name + "']" + args + ") ,'"; - } - - bodyReturn = me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn); - body = "this.compiled = function(values){ return ['" + bodyReturn + "'];};"; - eval(body); - return me; - }, - - /** - * Applies the supplied values to the template and inserts the new node(s) as the first child of el. - * - * @param {String/HTMLElement/Ext.Element} el The context element - * @param {Object/Array} values The template values. See {@link #applyTemplate} for details. - * @param {Boolean} returnElement (optional) true to return a Ext.Element. - * @return {HTMLElement/Ext.Element} The new node or Element - */ - this.insertFirst = function(el, values, returnElement) { - return this.doInsert('afterBegin', el, values, returnElement); - }, - - /** - * Applies the supplied values to the template and inserts the new node(s) before el. - * - * @param {String/HTMLElement/Ext.Element} el The context element - * @param {Object/Array} values The template values. See {@link #applyTemplate} for details. - * @param {Boolean} returnElement (optional) true to return a Ext.Element. - * @return {HTMLElement/Ext.Element} The new node or Element - */ - this.insertBefore = function(el, values, returnElement) { - return this.doInsert('beforeBegin', el, values, returnElement); - }, - - /** - * Applies the supplied values to the template and inserts the new node(s) after el. - * - * @param {String/HTMLElement/Ext.Element} el The context element - * @param {Object/Array} values The template values. See {@link #applyTemplate} for details. - * @param {Boolean} returnElement (optional) true to return a Ext.Element. - * @return {HTMLElement/Ext.Element} The new node or Element - */ - this.insertAfter = function(el, values, returnElement) { - return this.doInsert('afterEnd', el, values, returnElement); - }, - - /** - * Applies the supplied `values` to the template and appends the new node(s) to the specified `el`. - * - * For example usage see {@link Ext.Template Ext.Template class docs}. - * - * @param {String/HTMLElement/Ext.Element} el The context element - * @param {Object/Array} values The template values. See {@link #applyTemplate} for details. - * @param {Boolean} returnElement (optional) true to return an Ext.Element. - * @return {HTMLElement/Ext.Element} The new node or Element - */ - this.append = function(el, values, returnElement) { - return this.doInsert('beforeEnd', el, values, returnElement); - }, - - this.doInsert = function(where, el, values, returnElement) { - var newNode = Ext.DomHelper.insertHtml(where, Ext.getDom(el), this.apply(values)); - return returnElement ? Ext.get(newNode) : newNode; - }, - - /** - * Applies the supplied values to the template and overwrites the content of el with the new node(s). - * - * @param {String/HTMLElement/Ext.Element} el The context element - * @param {Object/Array} values The template values. See {@link #applyTemplate} for details. - * @param {Boolean} returnElement (optional) true to return a Ext.Element. - * @return {HTMLElement/Ext.Element} The new node or Element - */ - this.overwrite = function(el, values, returnElement) { - var newNode = Ext.DomHelper.overwrite(Ext.getDom(el), this.apply(values)); - return returnElement ? Ext.get(newNode) : newNode; - } - //this.constructor = function(html) { var me = this, args = arguments, @@ -285,10 +49,244 @@ module.exports = function(Ext) { if (me.compiled) { me.compile(); } - //}, }; Template.prototype.className = 'Ext.Template'; + /* Begin Definitions */ + + //this.requires = ['Ext.dom.Helper', 'Ext.util.Format'], + Template.prototype.inheritableStatics = { + /** + * Creates a template from the passed element's value (_display:none_ textarea, preferred) or innerHTML. + * @param {String/HTMLElement} el A DOM element or its id + * @param {Object} config (optional) Config object + * @return {Ext.Template} The created template + * @static + * @inheritable + */ + from: function(el, config) { + el = Ext.getDom(el); + return new this(el.value || el.innerHTML, config || ''); + } + }, + + /* End Definitions */ + + /** + * @property {Boolean} isTemplate + * `true` in this class to identify an object as an instantiated Template, or subclass thereof. + */ + Template.prototype.isTemplate = true, + + /** + * @cfg {Boolean} compiled + * True to immediately compile the template. Defaults to false. + */ + + /** + * @cfg {Boolean} disableFormats + * True to disable format functions in the template. If the template doesn't contain + * format functions, setting disableFormats to true will reduce apply time. Defaults to false. + */ + Template.prototype.disableFormats = false, + + Template.prototype.re = /\{([\w\-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g, + + /** + * Returns an HTML fragment of this template with the specified values applied. + * + * @param {Object/Array} values The template values. Can be an array if your params are numeric: + * + * var tpl = new Ext.Template('Name: {0}, Age: {1}'); + * tpl.apply(['John', 25]); + * + * or an object: + * + * var tpl = new Ext.Template('Name: {name}, Age: {age}'); + * tpl.apply({name: 'John', age: 25}); + * + * @return {String} The HTML fragment + */ + Template.prototype.apply = function(values) { + var me = this, + useFormat = me.disableFormats !== true, + fm = Ext.util.Format, + tpl = me, + ret; + + if (me.compiled) { + return me.compiled(values).join(''); + } + + function fn(m, name, format, args) { + if (format && useFormat) { + if (args) { + args = [values[name]].concat(Ext.functionFactory('return ['+ args +'];')()); + } else { + args = [values[name]]; + } + if (format.substr(0, 5) == "this.") { + return tpl[format.substr(5)].apply(tpl, args); + } + else { + return fm[format].apply(fm, args); + } + } + else { + return values[name] !== undefined ? values[name] : ""; + } + } + + ret = me.html.replace(me.re, fn); + return ret; + }, + + /** + * Appends the result of this template to the provided output array. + * @param {Object/Array} values The template values. See {@link #apply}. + * @param {Array} out The array to which output is pushed. + * @return {Array} The given out array. + */ + Template.prototype.applyOut = function(values, out) { + var me = this; + + if (me.compiled) { + out.push.apply(out, me.compiled(values)); + } else { + out.push(me.apply(values)); + } + + return out; + }, + + /** + * @method applyTemplate + * @member Ext.Template + * Alias for {@link #apply}. + * @inheritdoc Ext.Template#apply + */ + Template.prototype.applyTemplate = function () { + return this.apply.apply(this, arguments); + }, + + /** + * Sets the HTML used as the template and optionally compiles it. + * @param {String} html + * @param {Boolean} compile (optional) True to compile the template. + * @return {Ext.Template} this + */ + Template.prototype.set = function(html, compile) { + var me = this; + me.html = html; + me.compiled = null; + return compile ? me.compile() : me; + }, + + Template.prototype.compileARe = /\\/g, + Template.prototype.compileBRe = /(\r\n|\n)/g, + Template.prototype.compileCRe = /'/g, + + /** + * Compiles the template into an internal function, eliminating the RegEx overhead. + * @return {Ext.Template} this + */ + Template.prototype.compile = function() { + var me = this, + fm = Ext.util.Format, + useFormat = me.disableFormats !== true, + body, bodyReturn; + + function fn(m, name, format, args) { + if (format && useFormat) { + args = args ? ',' + args: ""; + if (format.substr(0, 5) != "this.") { + format = "fm." + format + '('; + } + else { + format = 'this.' + format.substr(5) + '('; + } + } + else { + args = ''; + format = "(values['" + name + "'] == undefined ? '' : "; + } + return "'," + format + "values['" + name + "']" + args + ") ,'"; + } + + bodyReturn = me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn); + body = "this.compiled = function(values){ return ['" + bodyReturn + "'];};"; + eval(body); + return me; + }, + + /** + * Applies the supplied values to the template and inserts the new node(s) as the first child of el. + * + * @param {String/HTMLElement/Ext.Element} el The context element + * @param {Object/Array} values The template values. See {@link #applyTemplate} for details. + * @param {Boolean} returnElement (optional) true to return a Ext.Element. + * @return {HTMLElement/Ext.Element} The new node or Element + */ + Template.prototype.insertFirst = function(el, values, returnElement) { + return this.doInsert('afterBegin', el, values, returnElement); + }, + + /** + * Applies the supplied values to the template and inserts the new node(s) before el. + * + * @param {String/HTMLElement/Ext.Element} el The context element + * @param {Object/Array} values The template values. See {@link #applyTemplate} for details. + * @param {Boolean} returnElement (optional) true to return a Ext.Element. + * @return {HTMLElement/Ext.Element} The new node or Element + */ + Template.prototype.insertBefore = function(el, values, returnElement) { + return this.doInsert('beforeBegin', el, values, returnElement); + }, + + /** + * Applies the supplied values to the template and inserts the new node(s) after el. + * + * @param {String/HTMLElement/Ext.Element} el The context element + * @param {Object/Array} values The template values. See {@link #applyTemplate} for details. + * @param {Boolean} returnElement (optional) true to return a Ext.Element. + * @return {HTMLElement/Ext.Element} The new node or Element + */ + Template.prototype.insertAfter = function(el, values, returnElement) { + return this.doInsert('afterEnd', el, values, returnElement); + }, + + /** + * Applies the supplied `values` to the template and appends the new node(s) to the specified `el`. + * + * For example usage see {@link Ext.Template Ext.Template class docs}. + * + * @param {String/HTMLElement/Ext.Element} el The context element + * @param {Object/Array} values The template values. See {@link #applyTemplate} for details. + * @param {Boolean} returnElement (optional) true to return an Ext.Element. + * @return {HTMLElement/Ext.Element} The new node or Element + */ + Template.prototype.append = function(el, values, returnElement) { + return this.doInsert('beforeEnd', el, values, returnElement); + }, + + Template.prototype.doInsert = function(where, el, values, returnElement) { + var newNode = Ext.DomHelper.insertHtml(where, Ext.getDom(el), this.apply(values)); + return returnElement ? Ext.get(newNode) : newNode; + }, + + /** + * Applies the supplied values to the template and overwrites the content of el with the new node(s). + * + * @param {String/HTMLElement/Ext.Element} el The context element + * @param {Object/Array} values The template values. See {@link #applyTemplate} for details. + * @param {Boolean} returnElement (optional) true to return a Ext.Element. + * @return {HTMLElement/Ext.Element} The new node or Element + */ + Template.prototype.overwrite = function(el, values, returnElement) { + var newNode = Ext.DomHelper.overwrite(Ext.getDom(el), this.apply(values)); + return returnElement ? Ext.get(newNode) : newNode; + }; + return Template; }; \ No newline at end of file diff --git a/XTemplateCompiler.js b/XTemplateCompiler.js index 8548471..500ca13 100644 --- a/XTemplateCompiler.js +++ b/XTemplateCompiler.js @@ -5,6 +5,8 @@ module.exports = function(Ext) { var XTemplateCompiler = Ext.extend(Ext.XTemplateParser); + XTemplateCompiler.prototype.className = 'Ext.XTemplateCompiler'; + // @tag core /** * This class compiles the XTemplate syntax into a function object. The function is used @@ -541,5 +543,8 @@ module.exports = function(Ext) { XTemplateCompiler.prototype.intRe = /^\s*(\d+)\s*$/; XTemplateCompiler.prototype.tagRe = /^([\w-\.\#\$]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\/]\s?[\d\.\+\-\*\/\(\)]+)?$/; + XTemplateCompiler.prototype.fnArgs = 'out,values,parent,xindex,xcount,xkey'; + XTemplateCompiler.prototype.callFn = '.call(this,' + XTemplateCompiler.prototype.fnArgs + ')'; + return XTemplateCompiler; }; \ No newline at end of file diff --git a/XTemplateParser.js b/XTemplateParser.js index 1727d7e..f36f99a 100644 --- a/XTemplateParser.js +++ b/XTemplateParser.js @@ -8,264 +8,269 @@ module.exports = function(Ext) { * This class parses the XTemplate syntax and calls abstract methods to process the parts. * @private */ - return function(config) { //Ext.XTemplateParser - /** - * @property {Number} level The 'for' or 'foreach' loop context level. This is adjusted - * up by one prior to calling {@link #doFor} or {@link #doForEach} and down by one after - * calling the corresponding {@link #doEnd} that closes the loop. This will be 1 on the - * first {@link #doFor} or {@link #doForEach} call. - */ + var XTemplateParser = function(config) { + }; - /** - * This method is called to process a piece of raw text from the tpl. - * @param {String} text - * @method doText - */ - // doText: function (text) + XTemplateParser.prototype.className = 'Ext.XTemplateParser'; + + /** + * @property {Number} level The 'for' or 'foreach' loop context level. This is adjusted + * up by one prior to calling {@link #doFor} or {@link #doForEach} and down by one after + * calling the corresponding {@link #doEnd} that closes the loop. This will be 1 on the + * first {@link #doFor} or {@link #doForEach} call. + */ - /** - * This method is called to process expressions (like `{[expr]}`). - * @param {String} expr The body of the expression (inside "{[" and "]}"). - * @method doExpr - */ - // doExpr: function (expr) + /** + * This method is called to process a piece of raw text from the tpl. + * @param {String} text + * @method doText + */ + // doText: function (text) - /** - * This method is called to process simple tags (like `{tag}`). - * @method doTag - */ - // doTag: function (tag) + /** + * This method is called to process expressions (like `{[expr]}`). + * @param {String} expr The body of the expression (inside "{[" and "]}"). + * @method doExpr + */ + // doExpr: function (expr) - /** - * This method is called to process ``. - * @method doElse - */ - // doElse: function () + /** + * This method is called to process simple tags (like `{tag}`). + * @method doTag + */ + // doTag: function (tag) - /** - * This method is called to process `{% text %}`. - * @param {String} text - * @method doEval - */ - // doEval: function (text) + /** + * This method is called to process ``. + * @method doElse + */ + // doElse: function () - /** - * This method is called to process ``. If there are other attributes, - * these are passed in the actions object. - * @param {String} action - * @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). - * @method doIf - */ - // doIf: function (action, actions) + /** + * This method is called to process `{% text %}`. + * @param {String} text + * @method doEval + */ + // doEval: function (text) - /** - * This method is called to process ``. If there are other attributes, - * these are passed in the actions object. - * @param {String} action - * @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). - * @method doElseIf - */ - // doElseIf: function (action, actions) + /** + * This method is called to process ``. If there are other attributes, + * these are passed in the actions object. + * @param {String} action + * @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). + * @method doIf + */ + // doIf: function (action, actions) - /** - * This method is called to process ``. If there are other attributes, - * these are passed in the actions object. - * @param {String} action - * @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). - * @method doSwitch - */ - // doSwitch: function (action, actions) + /** + * This method is called to process ``. If there are other attributes, + * these are passed in the actions object. + * @param {String} action + * @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). + * @method doElseIf + */ + // doElseIf: function (action, actions) - /** - * This method is called to process ``. If there are other attributes, - * these are passed in the actions object. - * @param {String} action - * @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). - * @method doCase - */ - // doCase: function (action, actions) + /** + * This method is called to process ``. If there are other attributes, + * these are passed in the actions object. + * @param {String} action + * @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). + * @method doSwitch + */ + // doSwitch: function (action, actions) - /** - * This method is called to process ``. - * @method doDefault - */ - // doDefault: function () + /** + * This method is called to process ``. If there are other attributes, + * these are passed in the actions object. + * @param {String} action + * @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). + * @method doCase + */ + // doCase: function (action, actions) - /** - * This method is called to process ``. It is given the action type that started - * the tpl and the set of additional actions. - * @param {String} type The type of action that is being ended. - * @param {Object} actions The other actions keyed by the attribute name (such as 'exec'). - * @method doEnd - */ - // doEnd: function (type, actions) + /** + * This method is called to process ``. + * @method doDefault + */ + // doDefault: function () - /** - * This method is called to process ``. If there are other attributes, - * these are passed in the actions object. - * @param {String} action - * @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). - * @method doFor - */ - // doFor: function (action, actions) + /** + * This method is called to process ``. It is given the action type that started + * the tpl and the set of additional actions. + * @param {String} type The type of action that is being ended. + * @param {Object} actions The other actions keyed by the attribute name (such as 'exec'). + * @method doEnd + */ + // doEnd: function (type, actions) - /** - * This method is called to process ``. If there are other - * attributes, these are passed in the actions object. - * @param {String} action - * @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). - * @method doForEach - */ - // doForEach: function (action, actions) + /** + * This method is called to process ``. If there are other attributes, + * these are passed in the actions object. + * @param {String} action + * @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). + * @method doFor + */ + // doFor: function (action, actions) - /** - * This method is called to process ``. If there are other attributes, - * these are passed in the actions object. - * @param {String} action - * @param {Object} actions Other actions keyed by the attribute name. - * @method doExec - */ - // doExec: function (action, actions) + /** + * This method is called to process ``. If there are other + * attributes, these are passed in the actions object. + * @param {String} action + * @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). + * @method doForEach + */ + // doForEach: function (action, actions) - /** - * This method is called to process an empty ``. This is unlikely to need to be - * implemented, so a default (do nothing) version is provided. - * @method - */ - this.doTpl = function () {}, //Ext.emptyFn, + /** + * This method is called to process ``. If there are other attributes, + * these are passed in the actions object. + * @param {String} action + * @param {Object} actions Other actions keyed by the attribute name. + * @method doExec + */ + // doExec: function (action, actions) - this.parse = function (str) { - var me = this, - len = str.length, - aliases = { elseif: 'elif' }, - topRe = me.topRe, - actionsRe = me.actionsRe, - index, stack, s, m, t, prev, frame, subMatch, begin, end, actions, - prop; + /** + * This method is called to process an empty ``. This is unlikely to need to be + * implemented, so a default (do nothing) version is provided. + * @method + */ + XTemplateParser.prototype.doTpl = function () {}, //Ext.emptyFn, - me.level = 0; - me.stack = stack = []; + XTemplateParser.prototype.parse = function (str) { + var me = this, + len = str.length, + aliases = { elseif: 'elif' }, + topRe = me.topRe, + actionsRe = me.actionsRe, + index, stack, s, m, t, prev, frame, subMatch, begin, end, actions, + prop; - for (index = 0; index < len; index = end) { - topRe.lastIndex = index; - m = topRe.exec(str); + me.level = 0; + me.stack = stack = []; - if (!m) { - me.doText(str.substring(index, len)); - break; - } + for (index = 0; index < len; index = end) { + topRe.lastIndex = index; + m = topRe.exec(str); - begin = m.index; - end = topRe.lastIndex; + if (!m) { + me.doText(str.substring(index, len)); + break; + } - if (index < begin) { - me.doText(str.substring(index, begin)); - } + begin = m.index; + end = topRe.lastIndex; - if (m[1]) { - end = str.indexOf('%}', begin+2); - me.doEval(str.substring(begin+2, end)); - end += 2; - } else if (m[2]) { - end = str.indexOf(']}', begin+2); - me.doExpr(str.substring(begin+2, end)); - end += 2; - } else if (m[3]) { // if ('{' token) - me.doTag(m[3]); - } else if (m[4]) { // content of a tag - actions = null; - while ((subMatch = actionsRe.exec(m[4])) !== null) { - s = subMatch[2] || subMatch[3]; - if (s) { - s = Ext.String.htmlDecode(s); // decode attr value - t = subMatch[1]; - t = aliases[t] || t; - actions = actions || {}; - prev = actions[t]; + if (index < begin) { + me.doText(str.substring(index, begin)); + } - if (typeof prev == 'string') { - actions[t] = [prev, s]; - } else if (prev) { - actions[t].push(s); - } else { - actions[t] = s; - } - } - } + if (m[1]) { + end = str.indexOf('%}', begin+2); + me.doEval(str.substring(begin+2, end)); + end += 2; + } else if (m[2]) { + end = str.indexOf(']}', begin+2); + me.doExpr(str.substring(begin+2, end)); + end += 2; + } else if (m[3]) { // if ('{' token) + me.doTag(m[3]); + } else if (m[4]) { // content of a tag + actions = null; + while ((subMatch = actionsRe.exec(m[4])) !== null) { + s = subMatch[2] || subMatch[3]; + if (s) { + s = Ext.String.htmlDecode(s); // decode attr value + t = subMatch[1]; + t = aliases[t] || t; + actions = actions || {}; + prev = actions[t]; - if (!actions) { - if (me.elseRe.test(m[4])) { - me.doElse(); - } else if (me.defaultRe.test(m[4])) { - me.doDefault(); + if (typeof prev == 'string') { + actions[t] = [prev, s]; + } else if (prev) { + actions[t].push(s); } else { - me.doTpl(); - stack.push({ type: 'tpl' }); + actions[t] = s; } } - else if (actions['if']) { - me.doIf(actions['if'], actions); - stack.push({ type: 'if' }); - } - else if (actions['switch']) { - me.doSwitch(actions['switch'], actions); - stack.push({ type: 'switch' }); - } - else if (actions['case']) { - me.doCase(actions['case'], actions); - } - else if (actions['elif']) { - me.doElseIf(actions['elif'], actions); - } - else if (actions['for']) { - ++me.level; + } - // Extract property name to use from indexed item - if (prop = me.propRe.exec(m[4])) { - actions.propName = prop[1] || prop[2]; - } - me.doFor(actions['for'], actions); - stack.push({ type: 'for', actions: actions }); + if (!actions) { + if (me.elseRe.test(m[4])) { + me.doElse(); + } else if (me.defaultRe.test(m[4])) { + me.doDefault(); + } else { + me.doTpl(); + stack.push({ type: 'tpl' }); } - else if (actions['foreach']) { - ++me.level; + } + else if (actions['if']) { + me.doIf(actions['if'], actions); + stack.push({ type: 'if' }); + } + else if (actions['switch']) { + me.doSwitch(actions['switch'], actions); + stack.push({ type: 'switch' }); + } + else if (actions['case']) { + me.doCase(actions['case'], actions); + } + else if (actions['elif']) { + me.doElseIf(actions['elif'], actions); + } + else if (actions['for']) { + ++me.level; - // Extract property name to use from indexed item - if (prop = me.propRe.exec(m[4])) { - actions.propName = prop[1] || prop[2]; - } - me.doForEach(actions['foreach'], actions); - stack.push({ type: 'foreach', actions: actions }); - } - else if (actions.exec) { - me.doExec(actions.exec, actions); - stack.push({ type: 'exec', actions: actions }); - } - /* - else { - // todo - error + // Extract property name to use from indexed item + if (prop = me.propRe.exec(m[4])) { + actions.propName = prop[1] || prop[2]; } - */ - } else if (m[0].length === 5) { - // if the length of m[0] is 5, assume that we're dealing with an opening tpl tag with no attributes (e.g. ...) - // in this case no action is needed other than pushing it on to the stack - stack.push({ type: 'tpl' }); - } else { - frame = stack.pop(); - me.doEnd(frame.type, frame.actions); - if (frame.type == 'for' || frame.type == 'foreach') { - --me.level; + me.doFor(actions['for'], actions); + stack.push({ type: 'for', actions: actions }); + } + else if (actions['foreach']) { + ++me.level; + + // Extract property name to use from indexed item + if (prop = me.propRe.exec(m[4])) { + actions.propName = prop[1] || prop[2]; } + me.doForEach(actions['foreach'], actions); + stack.push({ type: 'foreach', actions: actions }); + } + else if (actions.exec) { + me.doExec(actions.exec, actions); + stack.push({ type: 'exec', actions: actions }); + } + /* + else { + // todo - error + } + */ + } else if (m[0].length === 5) { + // if the length of m[0] is 5, assume that we're dealing with an opening tpl tag with no attributes (e.g. ...) + // in this case no action is needed other than pushing it on to the stack + stack.push({ type: 'tpl' }); + } else { + frame = stack.pop(); + me.doEnd(frame.type, frame.actions); + if (frame.type == 'for' || frame.type == 'foreach') { + --me.level; } } - }, + } + }, - // Internal regexes - - this.topRe = /(?:(\{\%)|(\{\[)|\{([^{}]+)\})|(?:]*)\>)|(?:<\/tpl>)/g, - this.actionsRe = /\s*(elif|elseif|if|for|foreach|exec|switch|case|eval|between)\s*\=\s*(?:(?:"([^"]*)")|(?:'([^']*)'))\s*/g, - this.propRe = /prop=(?:(?:"([^"]*)")|(?:'([^']*)'))/, - this.defaultRe = /^\s*default\s*$/, - this.elseRe = /^\s*else\s*$/ - }; + // Internal regexes + + XTemplateParser.prototype.topRe = /(?:(\{\%)|(\{\[)|\{([^{}]+)\})|(?:]*)\>)|(?:<\/tpl>)/g, + XTemplateParser.prototype.actionsRe = /\s*(elif|elseif|if|for|foreach|exec|switch|case|eval|between)\s*\=\s*(?:(?:"([^"]*)")|(?:'([^']*)'))\s*/g, + XTemplateParser.prototype.propRe = /prop=(?:(?:"([^"]*)")|(?:'([^']*)'))/, + XTemplateParser.prototype.defaultRe = /^\s*default\s*$/, + XTemplateParser.prototype.elseRe = /^\s*else\s*$/ + + return XTemplateParser; }; \ No newline at end of file diff --git a/package.json b/package.json index cf01144..970ea75 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "extjs", "template" ], - "version": "0.2.4", + "version": "0.2.5", "repository": { "type" : "git", "url": "https://github.com/devotis/node-extjs-custom" diff --git a/test/test.js b/test/test.js index 187e7a8..508df06 100644 --- a/test/test.js +++ b/test/test.js @@ -17,6 +17,20 @@ var data = { }; describe('Class instantiation and inheritance', function() { + it('Ext.XTemplateParser instantiation should work', function() { + var x = new Ext.XTemplateParser(); + + (x instanceof Ext.XTemplateParser).should.be.equal(true); + (x instanceof Ext.XTemplateCompiler).should.be.equal(false); + x.className.should.be.a.String.and.be.equal('Ext.XTemplateParser'); + }); + it('Ext.XTemplateCompiler instantiation should work', function() { + var x = new Ext.XTemplateCompiler(); + + (x instanceof Ext.XTemplateParser).should.be.equal(true); + (x instanceof Ext.XTemplateCompiler).should.be.equal(true); + x.className.should.be.a.String.and.be.equal('Ext.XTemplateCompiler'); + }); it('Ext.Template instantiation should work', function() { var tpl = new Ext.Template(''); @@ -38,7 +52,7 @@ describe('Template', function() { var tpl = new Ext.Template('Name: {0}, Age: {1}'); var html = tpl.apply(['John', 25]); - html.should.be.a.String.and.be.equal('Name: John, Age: 25') + html.should.be.a.String.and.be.equal('Name: John, Age: 25'); }); }); @@ -55,7 +69,9 @@ describe('XTemplate', function() { ); var html = tpl.apply(data); - html.should.be.a.String.and.be.equal('

Name: Don Griffin

Title: Senior Technomage

Company: Sencha Inc.

Kids:

Don Griffin

') + //The test below is to see that it's Actually XTemplate at work an not Template + html.should.be.a.String.and.not.be.equal('

Name: Don Griffin

Title: Senior Technomage

Company: Sencha Inc.

Kids:

Don Griffin

') + html.should.be.a.String.and.be.equal('

Name: Don Griffin

Title: Senior Technomage

Company: Sencha Inc.

Kids:

Aubrey

Joshua

Cale

Nikol

Solomon

'); }); });