UNPKG

smarty4js

Version:

A JavaScript Template Engine Most Like Smarty

282 lines (265 loc) 9.09 kB
/** * @file Renderer of Smarty4Js * @author johnson [zoumiaojiang@gmail.com] */ var utils = require('../utils'); /** * add php functions after generate jsCode * @param {Object} engine engine * @param {string} jsTpl js code generate by engine * @return {string} result code */ function addPhpFuncs(engine, jsTpl) { var arr = []; var obj = {}; var matches = jsTpl.match(/__f\[\".+?\"\]\(/g); if (matches) { matches.forEach( function (item) { arr.push(item.slice(5, item.length - 3)); } ); utils.excludeItem(arr).forEach( function (p) { obj[p] = engine.phpfunc[p]; } ); return 'var __f=' + utils.toFuncString(obj) + ';' + jsTpl; } return jsTpl; } /** * add smarty-tag functions after generate jsCode * @param {Object} engine engine * @param {string} jsTpl js code generate by engine * @return {string} result code */ function addUserDefinedFuncs(engine, jsTpl) { var arr = []; var obj = {}; var matches = jsTpl.match(/"__fn__.+?"/g); if (matches) { matches.forEach( function (item) { arr.push(item.slice(7, item.length - 1)); } ); utils.excludeItem(arr).forEach( function (p) { obj[p] = engine.func[p]; } ); return 'var __func=' + utils.toFuncString(obj, '__fn__') + ';' + jsTpl; } return jsTpl; } /** * add a closure after generate jsCode * @param {string} jsTpl js code generate by engine * @return {string} result code */ function addClosure(jsTpl) { var conf = this.conf; return '' + '(function(root){' + 'var __ret={' + 'render:function(__tn, __da){' + 'if(typeof __tn=="object"){__da=__tn;__tn=undefined;}' + 'if(typeof __tn=="string"){__da=__da||{};}' + jsTpl + '}' + '};\n' + (conf.isCmd ? 'if (typeof exports=="object" && typeof module=="object"){exports=module.exports=__ret;}\n' : '') + (conf.isAmd ? 'if (typeof define=="function" && define.amd){define(__ret);}\n' : '') + 'root.' + (conf.globalVar || '_smartyTpl') + '=__ret;\n' + 'return __ret;' + '})(this);'; } /** * @constructor * @param {Object} engine current engine */ function Renderer(engine) { this.ctxId = utils.getGUID(); this.engine = engine; this.eClass = require('../../index'); this.conf = engine.conf; this.includeAssign = {}; this.capAssign = {}; } /** * @prototype of Renderer */ Renderer.prototype = { constructor: Renderer, // context scope stack ctxScope: ['__da'], // extends scope stack extScope: [], /** * parser of Renderer * @param {number} flag flag of env (1,2...: addclosure, 0: just logic template) * @return {string} generate js template */ parser: function (flag) { var me = this; var ast = me.engine.ast; var jsTpl = ''; if (!flag) { jsTpl += '\nvar __h="",__cap={},__ext={},__assign,__sec={},__for={},smarty={' + 'foreach:{},capture:{},' + 'ldelim:"' + me.conf.left_delimiter + '",' + 'rdelim:"' + me.conf.right_delimiter + '"},' + '__dre=/^\\d+(\\.\\d+)?$/g,__nre=/[\\.\\(\\)\\[\\]\\{\\}\\+\\-\\*\\?\\|\\^\\$]/g,' + '\n__v=function(){' + 'var __va=Array.prototype.slice.call(arguments);' + 'for(var __vi=0,__vl=__va.length;__vi<__vl;__vi++){' + 'var __vd=__va[__vi];' + 'if(__vd!=undefined&&""+__vd!="NaN"){' + 'if(typeof __vd=="function"){return __vd();}' + 'else{return __vd;}' + '}' + '}' + '};' + me._init(ast) + '\nif(__tn){return __func["__fn__"+__tn](__da);}' + '\nreturn __h;'; jsTpl = addClosure.call(this, addPhpFuncs(this.engine, addUserDefinedFuncs(this.engine, jsTpl))); } else { jsTpl += me._init(ast, flag); } return jsTpl; }, /** * enter of render * @param {Object} root ast tree * @param {number|boolean} flag env flag(2: tag in string, other: normal) * @return {string} js template */ _init: function (root, flag) { var me = this; var ret = ''; var n0 = root[0]; var n1 = root[1]; if (flag === 2) { // smarty tags in string var tmpRet = []; root.forEach(function (node, index) { var type = node.type; switch (type) { case 'T': tmpRet.push(me._getText(node)); break; case 'VAR': tmpRet.push(me._getVar(node)); break; case 'E': tmpRet.push(me._getExpr(node)); break; case 'IF': tmpRet.push(me._getIf(node, flag)); break; case 'FUNC': tmpRet.push(me._getFunction(node, flag)); break; default: break; } }); return tmpRet.join('+'); } if (n0 && n1 && n0.type === 'T' && n0.value.trim() === '' && n1.type === 'FUNC' && n1.name === 'extends' ) { // extends var prefix = '__ext__' + utils.getGUID(); me.extScope.push(prefix); ret += me._getExtends(n1); root.forEach(function (node) { var type = node.type; switch (type) { // if extends, just deal with block stmts case 'FUNC': if (node.name === 'block') { ret += me._getSubBlock(node); } break; default: break; } }); me.extScope.pop(); // return ret; } else { // normal smarty tags root.forEach(function (node) { var type = node.type; switch (type) { case 'T': ret += utils.p(me._getText(node)); break; case 'VAR': ret += utils.p(me._getVar(node)) .replace(/__h\+=__fn__/g, '__fn__') .replace(/__h\+=__cap__/g, '__cap__'); break; case 'E': ret += utils.p(me._getExpr(node)); break; case 'ASSIGN': ret += me._assign(node); break; case 'IF': ret += me._getIf(node); break; case 'FOR': ret += me._getFor(node); break; case 'FUNC': ret += me._getFunction(node, true); break; case 'WHILE': ret += me._getWhile(node); break; case 'GLOBAL': ret += utils.p(me._getGlobal(node)); break; case 'AUTO': ret += me._getVar(node.items, node.r + node.ops); break; case 'C': break; case 'STR': ret += utils.p(me._getStr(node)); break; default: throw new Error('error in ' + node.type); } }); } var config = me.engine.conf; // finanly escape to normal JS code (replace placeholder) // __D: . // __QD: " // __QS: ' // __LD: left_delimiter // __RD: right_delimiter ret = ret .replace(/__D/g, '.') .replace(/__QD/g, '\\\"') .replace(/__QS/g, '\\\'') .replace(/__RD/g, config.right_delimiter) .replace(/__LD/g, config.left_delimiter); return ret; } }; require('./expr')(Renderer); require('./literal')(Renderer); require('./if')(Renderer); require('./loop')(Renderer); require('./assign')(Renderer); require('./function')(Renderer); require('./module')(Renderer); module.exports = Renderer;