nuijs
Version:
nui框架
303 lines (274 loc) • 9.88 kB
JavaScript
/**
* @author Aniu[2016-11-11 16:54]
* @update Aniu[2016-01-12 04:33]
* @version 1.0.2
* @description 模版引擎
*/
Nui.define(['./util'], function(util){
var template = function(tplid, data, opts){
if(this.tplid = tplid){
if(caches[tplid]){
return render.call(this, caches[tplid], data, opts)
}
var ele = document.getElementById(tplid);
if(ele && ele.nodeName==='SCRIPT' && ele.type === 'text/html'){
return render.call(this, caches[tplid] = ele.innerHTML, data, opts)
}
}
return ''
}
var caches = {};
var options = {
openTag:'<%',
closeTag:'%>'
}
var methods = {
trim:Nui.trim,
formatDate:util.formatDate,
formatNumber:util.formatNumber,
setParam:util.setParam,
toFixed:util.toFixed,
numberToCN:util.numberToCN
}
var isstr = !!''.trim;
var snippet = ';$that.out = function(){return $that.code';
//低版本IE用push拼接字符串效率更高
snippet = (isstr ? '""'+snippet : '[]'+snippet+'.join("")')+'}';
var join = function(iscode){
if(isstr){
if(iscode){
return function(code){
return '$that.code += '+code+';'
}
}
return function(code, snippet){
return code += snippet
}
}
if(iscode){
return function(code){
return '$that.code.push('+code+');'
}
}
return function(code, snippet){
code.push(snippet);
return code
}
}
var joinCode = join(true);
var joinSnippet = join();
var replaceInclude = function(tpl, openTag, closeTag, opts){
var that = this;
var regs = openTag.replace(/([^\s])/g, '\\$1');
var rege = closeTag.replace(/([^\s])/g, '\\$1');
return tpl.replace(new RegExp(regs+'\\s*include\\s+[\'\"]([^\'\"]*)[\'\"]\\s*'+rege, 'g'), function(str, tid){
if(tid){
var tmp;
Nui.each(tid.split('.'), function(attr){
tmp = (tmp||that)[attr];
if(tmp === undefined){
return false
}
})
if(typeof tmp === 'function'){
tmp = tmp()
}
if(typeof tmp === 'string'){
return render.call(that, tmp, null, opts)
}
else{
return template(tid, null, opts)
}
}
return ''
})
}
//部分浏览器中表单对象name属性如果和模版中需要使用的变量重名,而这个变量又不存在,返回值就会变成该dom....
var isNode = typeof HTMLElement === 'object' ?
function(obj){
return obj instanceof HTMLElement;
} :
function(obj){
return obj.nodeType === 1 && typeof obj.nodeName === 'string';
};
var isDom = function(obj){
if(obj && typeof obj === 'object'){
//元素集合
var ele = obj[0];
if(ele){
return isNode(ele)
}
//元素
return isNode(obj)
}
}
var render = function(tpl, data, opts){
var that = this;
if(typeof tpl === 'string' && tpl){
opts = opts || {};
var openTag = opts.openTag || options.openTag, closeTag = opts.closeTag || options.closeTag;
tpl = replaceInclude.call(that, tpl, openTag, closeTag);
if(data && typeof data === 'object'){
if(Nui.isArray(data)){
data = {
$list:data
}
}
var code = isstr ? '' : [];
tpl = tpl.replace(/\s+/g, ' ');
Nui.each(tpl.split(openTag), function(val, key){
val = val.split(closeTag);
if(key >= 1){
code = joinSnippet(code, compile(Nui.trim(val[0]), true))
}
else{
val[1] = val[0];
}
code = joinSnippet(code, compile(val[1].replace(/'/g, "\\'").replace(/"/g, '\\"')))
});
var variables = isstr ? '' : [];
for(var k in data){
variables = joinSnippet(variables, k+'=$data.'+k+',')
}
if(!isstr){
code = code.join('');
variables = variables.join('');
}
code = 'var '+ variables +'$that=this,$method=$that.methods; $that.line=4; $that.code='+ snippet +';\ntry{\n' + code + ';}\ncatch(e){\n$that.error(e, $that.line)\n};';
try{
var Rander = new Function('$data', code);
Rander.prototype.methods = methods;
Rander.prototype.error = error(code, data, that.tplid);
Rander.prototype.dom = isDom;
tpl = new Rander(data).out();
Rander = null
}
catch(e){
error(code, data, that.tplid)(e)
}
}
return tpl
}
return ''
}
var error = function(code, data, tplid){
return function(e, line){
var msg = '\n';
var codes = [];
code = 'function anonymous($data){\n'+code+'\n}';
code = code.split('\n');
Nui.each(code, function(v, k){
codes.push((k+1)+ ' ' +v.replace('$that.line++;', ''))
})
msg += 'code\n';
msg += codes.join('\n')+'\n\n';
if(typeof JSON !== undefined){
msg += 'data\n';
msg += JSON.stringify(data)+'\n\n'
}
if(tplid){
msg += 'templateid\n';
msg += tplid+'\n\n'
}
if(line){
msg += 'line\n';
msg += line+'\n\n'
}
msg += 'message\n';
msg += e.message;
console.error(msg)
}
}
var compile = function(tpl, logic){
if(!tpl){
return ''
}
var code,res;
if(logic){
if((res = match(tpl, 'if')) !== undefined){
code = 'if('+exists(res)+'){'
}
else if((res = match(tpl, 'elseif')) !== undefined){
code = '\n}\nelse if('+exists(res)+'){'
}
else if(tpl === 'else'){
code = '\n}\nelse{'
}
else if(tpl === '/if'){
code = '}'
}
else if((res = match(tpl, 'each ', /\s+/)) !== undefined){
code = 'Nui.each('+ res[0] +', function('+(res[1]||'$value')+','+(res[2]||'$index')+'){'
}
else if(tpl === '/each'){
code = '});'
}
else if((res = match(tpl, ' | ', /\s*,\s*/)) !== undefined){
var str = res[0];
var i = str.lastIndexOf('(');
var _call = '(' +exists(res.slice(1).toString()) +')';
//赋值操作必须要用括号包裹起来
if(i !== -1){
var start = str.substr(0, i);
var end = Nui.trimLeft(str.substr(i+1));
code = joinCode(start+'($that.methods.' + end + _call)
}
else{
code = joinCode('$that.methods.'+ str + _call)
}
}
else if(/^(var|let|const|return|delete)\s+/.test(tpl)){
code = exists(tpl)+';'
}
else{
code = joinCode(exists(tpl, true))
}
}
else{
code = joinCode('\''+tpl+'\'')
}
return code + '\n' + '$that.line++;'
}
//判断变量是否存在
//a.b?? a[b]?? a['b']?? a[b['c']]??
var exists = function(code, isVal){
return code.replace(/([\.\$\w]+\s*(\[[\'\"\[\]\w\.\$\s]+\])?)\?\?/g, function(a, b){
var rep = '(typeof '+ b + '!=="undefined"&&'+ b +'!==null&&'+ b +'!==undefined&&!$that.dom('+ b +')';
if(isVal){
rep += '?' + b + ':' + '""';
}
return rep + ')'
})
}
var match = function(str, syntax, regexp){
var replace;
if(str.indexOf(syntax) === 0){
replace = ''
}
else if(syntax === ' | ' && str.indexOf(syntax) > 0){
replace = ','
}
if(replace !== undefined){
str = Nui.trimLeft(str.replace(syntax, replace));
return regexp ? str.split(regexp) : str
}
}
template.method = function(name, callback){
if(!methods[name]){
methods[name] = callback
}
}
template.config = function(){
var args = arguments;
if(Nui.type(args[0], 'Object')){
Nui.each(args[0], function(v, k){
options[k] = v
})
}
else if(args.length > 1 && typeof args[0] === 'string'){
options[args[0]] = args[1]
}
}
template.render = render;
return template
})