smarty4js
Version:
A JavaScript Template Engine Most Like Smarty
336 lines (311 loc) • 12 kB
JavaScript
/**
* @file render of php_function and smarty_tag_function
* @author johnson [zoumiaojiang@gmail.com]
*/
var utils = require('../utils');
module.exports = function (Renderer) {
utils.mixin(Renderer.prototype, {
/**
* enter of all functions
* @param {Object} node ast node
* @param {number|boolean} f is literal(2) or echo result(1)?
* @return {string} render result
*/
_getFunction: function (node, f) {
// exclued 'config_load, php, include_php'
var me = this;
var name = node.name;
var ret;
var fobj = me.engine.phpfunc;
if (fobj[name] && node.params) {
if (f === true) {
ret = utils.p(me._getPhpFunc(node));
}
else {
ret = me._getPhpFunc(node);
}
}
else {
switch (name) {
case 'function':
return me._getFunc(node);
case 'strip':
return me._getStrip(node);
case 'call':
return me._getCallFunc(node);
case 'assign':
return me._assignFunc(node);
case 'foreach':
return me._getFuncFor(node);
case 'section':
return me._getSection(node);
case 'include':
return me._getInclude(node);
case 'insert':
return me._getInclude(node);
case 'nocache':
return me._getFuncLiteral(node);
case 'ldelim':
return utils.p('"' + me.engine.conf.left_delimiter + '"');
case 'rdelim':
return utils.p('"' + me.engine.conf.right_delimiter + '"');
case 'block':
return me._getMasterBlock(node);
case 'append':
return me._getAppend(node);
case 'capture':
return me._getCapture(node);
case 'break':
return 'break;';
default:
return me._callFunc(node, f);
}
}
return ret;
},
/**
* function define tag
* {%function name=example ... %}
* ...
* {%/function%}
* @param {Object} node ast node
* @return {string} render result
*/
_getFunc: function (node) {
var me = this;
var func = '__func' + utils.getGUID();
var funcName;
var tmparr = [];
node.attrs.forEach(function (nod, index) {
if (nod.value) {
if (nod.key.value === 'name' && nod.value.type === 'STR') {
funcName = nod.value.value;
}
else {
tmparr.push(nod);
}
}
else if (!nod.value && index === 0) {
funcName = nod.key.value;
}
else {
tmparr.push(nod);
}
});
/**
* get default params of smarty-tag function
* @param {string} func function context scope
* @param {Array<Object>} arr array of ast node(params)
* @return {string} render result
*/
function getDefaultParams(func, arr) {
var tmps = '';
arr.forEach(function (node) {
if (node.value) {
var p = node.key.value;
var vara = me._getExpr(node.value);
tmps += '\n' + func + '.' + p + '=__v(__p.' + p + ',' + vara + ');';
}
});
return tmps;
}
var ret = '\n__func["__fn__' + funcName + '"]=function(__p){';
var fc = utils.getGUID();
me.ctxScope.push(func);
ret += 'for(var __ in __p){if(__p.hasOwnProperty(__)&&__da[__]==undefined){__da[__]=__p[__];}}\n'
+ 'var ' + func + ' = __p,__' + fc + 'h="";'
+ getDefaultParams(func, tmparr)
+ me._init(node.block).replace(/__h/g, '__' + fc + 'h')
+ 'return __' + fc + 'h;\n};';
me.ctxScope.pop();
return ret;
},
/**
* call function
* just like ({%a_function_name attrK=attrV ...%})
* @param {Object} node ast node
* @param {number} flag is literal or echo?
* @return {string} render result
*/
_callFunc: function (node, flag) {
var me = this;
var funcName = node.name;
var attrs = node.attrs;
var ps = [];
var ret = '';
var begin = (flag === 2 ? '' : '__h+=');
var end = (flag === 2 ? '' : ';');
if (funcName) {
if (attrs) {
if (attrs.length === 0) {
ret = begin + '__func["__fn__' + funcName + '"]({})' + end;
}
else {
node.attrs.forEach(function (nod) {
ps.push(nod.key.value + ':' + (nod.value ? me._getExpr(nod.value) : ''));
});
ret = begin + '__func["__fn__' + funcName + '"]({' + ps.join(',') + '})' + end;
}
}
}
else {
throw new Error('No Function Error! No function `' + funcName + '`!');
}
return ret;
},
/**
* render strip function
* {%strip%} ... {%/strip%}
* @param {Object} node ast node
* @return {string} render result
*/
_getStrip: function (node) {
var block = node.block;
return this._init(block)
.replace(/\"\s+/g, '" ')
.replace(/\s+\"/g, ' "')
.replace(/\s+</g, '<')
.replace(/>\s+/g, '>');
},
/**
* render literal function
* {%literal%} ... {%/literal%}
* don't suggest to use single '{' and '}' to be limiter
* so this function is smarty adapter
* @param {Object} node ast node
* @return {string} render result
*/
_getFuncLiteral: function (node) {
var block = node.block;
return this._init(block);
},
/**
* render call function..
* {%call k1=v1 k2=v2 ...%}
* @param {Object} node ast node
* @return {string} render result
*/
_getCallFunc: function (node) {
var me = this;
var ret = '';
var attrs = node.attrs;
var name = '';
var params = '{';
var assign;
if (attrs) {
attrs.forEach(function (attr) {
if ((attr.key.value === 'name' || !attr.value)) {
name = (attr.value ? me._getExpr(attr.value).replace(/"/g, '') : attr.key.value);
}
else if (attr.key.value === 'assign') {
assign = me._getExpr(attr.value).replace(/"/g, '');
}
else {
params += attr.key.value + ':' + me._getExpr(attr.value) + ',';
}
});
if (name) {
ret += '\n'
+ (assign ? ('__da.' + assign + '=') : '__h+=')
+ '__func["__fn__' + name + '"]('
+ (params.indexOf(',') > -1 ? params : ',')
+ (assign ? '__s:1' : '') + '});';
}
}
return ret;
},
/**
* append function
* {%append var=xxx index=xxx value=xxx%}
* @param {Object} node ast node
* @return {string} render result
*/
_getAppend: function (node) {
var me = this;
var ret = '';
var attrs = node.attrs;
var vara;
var ind = '';
var value;
if (attrs) {
attrs.forEach(function (attr, index) {
var key = attr.key.value;
var valObj = attr.value;
if ((key === 'var' || (!valObj && index === 0))) {
var varaNode = valObj ? valObj : attr.key;
if (varaNode.type === 'STR') {
vara = me._getStr(varaNode);
}
}
if ((key === 'value' || (!valObj && index === 1))) {
value = me._getExpr(valObj ? valObj : attr.key);
}
if (key === 'index') {
var tmp = valObj.value;
ind = typeof tmp === 'string' ? ('"' + tmp + '"') : me._getExpr(tmp);
}
});
if (vara && value) {
ret += '__ti_=' + (ind ? ind : '__f["count"]((__da[' + vara + ']||{}))') + ';'
+ '__t_=__da[' + vara + '];'
+ '__da[' + vara + ']=__t_?__t_:{};'
+ '__da[' + vara + '][__ti_]=' + value + ';';
}
}
return ret;
},
/**
* capture function
* {%capture name=xxx assign=xxx append=xxx%}
* @param {Object} node ast node
* @return {string} render result
*/
_getCapture: function (node) {
var me = this;
var ret = '';
var attrs = node.attrs;
var block = node.block;
var name = '';
var assign = '';
var append = '';
var ca = '__ca' + utils.getGUID();
if (attrs) {
ret += 'var ' + ca + 'a=function(){var ' + ca + '="";'
+ me._init(block).replace(/__h/g, ca)
+ 'return ' + ca + ';};';
attrs.forEach(function (attr) {
var key = attr.key.value;
var valObj = attr.value;
if (key === 'name' || !valObj) {
name = valObj ? valObj.value : key;
}
if (key === 'assign' && valObj.type === 'STR') {
assign = valObj.value;
}
if (key === 'append' && valObj.type === 'STR') {
append = valObj.value;
}
});
}
if (name) {
ret += 'smarty.capture["' + name + '"]=' + ca + 'a();';
}
if (assign) {
me.capAssign[assign] = true;
ret += '__cap["' + assign + '"]=function(){return ' + ca + 'a();}';
}
if (append) {
var f = me.capAssign[append];
if (f) {
ret += '__da.' + append + '["__a' + f + '"]=' + ca + 'a;';
me.capAssign[append]++;
}
else {
ret += '__da.' + append + '={"__a0":' + ca + 'a};';
me.capAssign[append] = 1;
}
}
return ret;
}
});
};