UNPKG

nuijs

Version:

nui框架

303 lines (274 loc) 9.88 kB
/** * @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 })