hybridstart
Version:
A hybrid application development framework
72 lines (71 loc) • 19.3 kB
JavaScript
/*
* name: etpl.js
* version: v0.1.1
* update: dec过滤器bug
* date: 2017-01-24
*/
define('etpl', function(require, exports, module) {
function extend(target,source){for(var key in source){if(source.hasOwnProperty(key)){target[key]=source[key]}}return target}function Stack(){this.raw=[];this.length=0}Stack.prototype={push:function(elem){this.raw[this.length++]=elem},pop:function(){if(this.length>0){var elem=this.raw[--this.length];this.raw.length=this.length;return elem}},top:function(){return this.raw[this.length-1]},bottom:function(){return this.raw[0]},find:function(condition){var index=this.length;while(index--){var item=this.raw[index];if(condition(item)){return item}}}};var guidIndex=0x2B845;function generateGUID(){return'___'+(guidIndex++)}function inherits(subClass,superClass){var F=new Function();F.prototype=superClass.prototype;subClass.prototype=new F();subClass.prototype.constructor=subClass}var HTML_ENTITY={'&':'&','<':'<','>':'>','"':'"',"'":'''};function htmlFilterReplacer(c){return HTML_ENTITY[c]}var DEFAULT_FILTERS={html:function(source){return source.replace(/[&<>"']/g,htmlFilterReplacer)},url:encodeURIComponent,raw:function(source){return source}};function stringLiteralize(source){return'"'+source.replace(/\x5C/g,'\\\\').replace(/"/g,'\\"').replace(/\x0A/g,'\\n').replace(/\x09/g,'\\t').replace(/\x0D/g,'\\r')+'"'}function regexpLiteral(source){return source.replace(/[\^\[\]\$\(\)\{\}\?\*\.\+]/g,function(c){return'\\'+c})}function stringFormat(source){var args=arguments;return source.replace(/\{([0-9]+)\}/g,function(match,index){return args[index-0+1]})}var RENDER_STRING_DECLATION='var r="";';var RENDER_STRING_ADD_START='r+=';var RENDER_STRING_ADD_END=';';var RENDER_STRING_RETURN='return r;';if(typeof navigator!=='undefined'&&/msie\s*([0-9]+)/i.test(navigator.userAgent)&&RegExp.$1-0<8){RENDER_STRING_DECLATION='var r=[],ri=0;';RENDER_STRING_ADD_START='r[ri++]=';RENDER_STRING_RETURN='return r.join("");'}function toGetVariableLiteral(name){name=name.replace(/^\s*\*/,'');return stringFormat('gv({0},["{1}"])',stringLiteralize(name),name.replace(/\[['"]?([^'"]+)['"]?\]/g,function(match,name){return'.'+name}).split('.').join('","'))}function parseTextBlock(source,open,close,greedy,onInBlock,onOutBlock){var closeLen=close.length;var texts=source.split(open);var level=0;var buf=[];for(var i=0,len=texts.length;i<len;i++){var text=texts[i];if(i){var openBegin=1;level++;while(1){var closeIndex=text.indexOf(close);if(closeIndex<0){buf.push(level>1&&openBegin?open:'',text);break}level=greedy?level-1:0;buf.push(level>0&&openBegin?open:'',text.slice(0,closeIndex),level>0?close:'');text=text.slice(closeIndex+closeLen);openBegin=0;if(level===0){break}}if(level===0){onInBlock(buf.join(''));onOutBlock(text);buf=[]}}else{text&&onOutBlock(text)}}if(level>0&&buf.length>0){onOutBlock(open);onOutBlock(buf.join(''))}}function compileVariable(source,engine,forText){var code=[];var options=engine.options;var toStringHead='';var toStringFoot='';var wrapHead='';var wrapFoot='';var defaultFilter;if(forText){toStringHead='ts(';toStringFoot=')';wrapHead=RENDER_STRING_ADD_START;wrapFoot=RENDER_STRING_ADD_END;defaultFilter=options.defaultFilter}parseTextBlock(source,options.variableOpen,options.variableClose,1,function(text){if(forText&&text.indexOf('|')<0&&defaultFilter){text+='|'+defaultFilter}var filterCharIndex=text.indexOf('|');var variableName=(filterCharIndex>0?text.slice(0,filterCharIndex):text).replace(/^\s+/,'').replace(/\s+$/,'');var filterSource=filterCharIndex>0?text.slice(filterCharIndex+1):'';var variableRawValue=variableName.indexOf('*')===0;var variableCode=[variableRawValue?'':toStringHead,toGetVariableLiteral(variableName),variableRawValue?'':toStringFoot];if(filterSource){filterSource=compileVariable(filterSource,engine);var filterSegs=filterSource.split('|');for(var i=0,len=filterSegs.length;i<len;i++){var seg=filterSegs[i];if(/^\s*([a-z0-9_-]+)(\((.*)\))?\s*$/i.test(seg)){variableCode.unshift('fs["'+RegExp.$1+'"](');if(RegExp.$3){variableCode.push(',',RegExp.$3)}variableCode.push(')')}}}code.push(wrapHead,variableCode.join(''),wrapFoot)},function(text){code.push(wrapHead,forText?stringLiteralize(text):text,wrapFoot)});return code.join('')}function TextNode(value,engine){this.value=value;this.engine=engine}TextNode.prototype={getRendererBody:function(){var value=this.value;var options=this.engine.options;if(!value||(options.strip&&/^\s*$/.test(value))){return''}return compileVariable(value,this.engine,1)},clone:function(){return this}};function Command(value,engine){this.value=value;this.engine=engine;this.children=[];this.cloneProps=[]}Command.prototype={addChild:function(node){this.children.push(node)},open:function(context){var parent=context.stack.top();parent&&parent.addChild(this);context.stack.push(this)},close:function(context){if(context.stack.top()===this){context.stack.pop()}},getRendererBody:function(){var buf=[];var children=this.children;for(var i=0;i<children.length;i++){buf.push(children[i].getRendererBody())}return buf.join('')},clone:function(){var Clazz=this.constructor;var node=new Clazz(this.value,this.engine);for(var i=0,l=this.children.length;i<l;i++){node.addChild(this.children[i].clone())}for(var i=0,l=this.cloneProps.length;i<l;i++){var prop=this.cloneProps[i];node[prop]=this[prop]}return node}};function autoCloseCommand(context,CommandType){var stack=context.stack;var closeEnd=CommandType?stack.find(function(item){return item instanceof CommandType}):stack.bottom();if(closeEnd){var node;while((node=stack.top())!==closeEnd){if(!node.autoClose){throw new Error(node.type+' must be closed manually: '+node.value);}node.autoClose(context)}closeEnd.close(context)}return closeEnd}var RENDERER_BODY_START=''+'data=data||{};'+'var v={},fs=engine.filters,hg=typeof data.get=="function",'+'gv=function(n,ps){'+'var p=ps[0],d=v[p];'+'if(d==null){'+'if(hg){return data.get(n);}'+'d=data[p];'+'}'+'for(var i=1,l=ps.length;i<l;i++)if(d!=null)d = d[ps[i]];'+'return d;'+'},'+'ts=function(s){'+'if(typeof s==="string"){return s;}'+'if(s==null){s="";}'+'return ""+s;'+'};';function TargetCommand(value,engine){if(!/^\s*([a-z0-9\/_-]+)\s*(\(\s*master\s*=\s*([a-z0-9\/_-]+)\s*\))?\s*/i.test(value)){throw new Error('Invalid '+this.type+' syntax: '+value);}this.master=RegExp.$3;this.name=RegExp.$1;Command.call(this,value,engine);this.blocks={}}inherits(TargetCommand,Command);function BlockCommand(value,engine){if(!/^\s*([a-z0-9\/_-]+)\s*$/i.test(value)){throw new Error('Invalid '+this.type+' syntax: '+value);}this.name=RegExp.$1;Command.call(this,value,engine);this.cloneProps=['name']}inherits(BlockCommand,Command);function ImportCommand(value,engine){if(!/^\s*([a-z0-9\/_-]+)\s*$/i.test(value)){throw new Error('Invalid '+this.type+' syntax: '+value);}this.name=RegExp.$1;Command.call(this,value,engine);this.cloneProps=['name','state','blocks'];this.blocks={}}inherits(ImportCommand,Command);function VarCommand(value,engine){if(!/^\s*([a-z0-9_]+)\s*=([\s\S]*)$/i.test(value)){throw new Error('Invalid '+this.type+' syntax: '+value);}this.name=RegExp.$1;this.expr=RegExp.$2;Command.call(this,value,engine);this.cloneProps=['name','expr']}inherits(VarCommand,Command);function FilterCommand(value,engine){if(!/^\s*([a-z0-9_-]+)\s*(\(([\s\S]*)\))?\s*$/i.test(value)){throw new Error('Invalid '+this.type+' syntax: '+value);}this.name=RegExp.$1;this.args=RegExp.$3;Command.call(this,value,engine);this.cloneProps=['name','args']}inherits(FilterCommand,Command);function UseCommand(value,engine){if(!/^\s*([a-z0-9\/_-]+)\s*(\(([\s\S]*)\))?\s*$/i.test(value)){throw new Error('Invalid '+this.type+' syntax: '+value);}this.name=RegExp.$1;this.args=RegExp.$3;Command.call(this,value,engine);this.cloneProps=['name','args']}inherits(UseCommand,Command);function ForCommand(value,engine){var rule=new RegExp(stringFormat('^\\s*({0}[\\s\\S]+{1})\\s+as\\s+{0}([0-9a-z_]+){1}\\s*(,\\s*{0}([0-9a-z_]+){1})?\\s*$',regexpLiteral(engine.options.variableOpen),regexpLiteral(engine.options.variableClose)),'i');if(!rule.test(value)){throw new Error('Invalid '+this.type+' syntax: '+value);}this.list=RegExp.$1;this.item=RegExp.$2;this.index=RegExp.$4;Command.call(this,value,engine);this.cloneProps=['list','item','index']}inherits(ForCommand,Command);function IfCommand(value,engine){Command.call(this,value,engine)}inherits(IfCommand,Command);function ElifCommand(value,engine){IfCommand.call(this,value,engine)}inherits(ElifCommand,IfCommand);function ElseCommand(value,engine){Command.call(this,value,engine)}inherits(ElseCommand,IfCommand);var TargetState={READING:1,READED:2,APPLIED:3,READY:4};ImportCommand.prototype.applyMaster=TargetCommand.prototype.applyMaster=function(masterName){if(this.state>=TargetState.APPLIED){return 1}var blocks=this.blocks;function replaceBlock(node){var children=node.children;if(children instanceof Array){for(var i=0,len=children.length;i<len;i++){var child=children[i];if(child instanceof BlockCommand&&blocks[child.name]){child=children[i]=blocks[child.name]}replaceBlock(child)}}}var master=this.engine.targets[masterName];if(master&&master.applyMaster(master.master)){this.children=master.clone().children;replaceBlock(this);this.state=TargetState.APPLIED;return 1}};TargetCommand.prototype.isReady=function(){if(this.state>=TargetState.READY){return 1}var engine=this.engine;var readyState=1;function checkReadyState(node){for(var i=0,len=node.children.length;i<len;i++){var child=node.children[i];if(child instanceof ImportCommand){var target=engine.targets[child.name];readyState=readyState&&target&&target.isReady(engine)}else if(child instanceof Command){checkReadyState(child)}}}if(this.applyMaster(this.master)){checkReadyState(this);readyState&&(this.state=TargetState.READY);return readyState}};TargetCommand.prototype.getRenderer=function(){if(this.renderer){return this.renderer}if(this.isReady()){var realRenderer=new Function('data','engine',[RENDERER_BODY_START,RENDER_STRING_DECLATION,this.getRendererBody(),RENDER_STRING_RETURN].join('\n'));var engine=this.engine;this.renderer=function(data){return realRenderer(data,engine)};return this.renderer}return null};function addTargetToContext(target,context){context.target=target;var engine=context.engine;var name=target.name;if(engine.targets[name]){switch(engine.options.namingConflict){case'override':engine.targets[name]=target;context.targets.push(name);case'ignore':break;default:throw new Error('Target exists: '+name);}}else{engine.targets[name]=target;context.targets.push(name)}}TargetCommand.prototype.open=function(context){autoCloseCommand(context);Command.prototype.open.call(this,context);this.state=TargetState.READING;addTargetToContext(this,context)};VarCommand.prototype.open=UseCommand.prototype.open=function(context){context.stack.top().addChild(this)};BlockCommand.prototype.open=function(context){Command.prototype.open.call(this,context);context.stack.find(function(node){return node.blocks}).blocks[this.name]=this};ElifCommand.prototype.open=function(context){var elseCommand=new ElseCommand();elseCommand.open(context);var ifCommand=autoCloseCommand(context,IfCommand);ifCommand.addChild(this);context.stack.push(this)};ElseCommand.prototype.open=function(context){var ifCommand=autoCloseCommand(context,IfCommand);ifCommand.addChild(this);context.stack.push(this)};ImportCommand.prototype.open=function(context){this.parent=context.stack.top();this.target=context.target;Command.prototype.open.call(this,context);this.state=TargetState.READING};UseCommand.prototype.close=VarCommand.prototype.close=function(){};ImportCommand.prototype.close=function(context){Command.prototype.close.call(this,context);this.state=TargetState.READED};TargetCommand.prototype.close=function(context){Command.prototype.close.call(this,context);this.state=this.master?TargetState.READED:TargetState.APPLIED;context.target=null};ImportCommand.prototype.autoClose=function(context){var parentChildren=this.parent.children;parentChildren.push.apply(parentChildren,this.children);this.children.length=0;for(var key in this.blocks){this.target.blocks[key]=this.blocks[key]}this.blocks={};this.close(context)};UseCommand.prototype.beforeOpen=ImportCommand.prototype.beforeOpen=VarCommand.prototype.beforeOpen=ForCommand.prototype.beforeOpen=FilterCommand.prototype.beforeOpen=BlockCommand.prototype.beforeOpen=IfCommand.prototype.beforeOpen=TextNode.prototype.beforeAdd=function(context){if(context.stack.bottom()){return}var target=new TargetCommand(generateGUID(),context.engine);target.open(context)};ImportCommand.prototype.getRendererBody=function(){this.applyMaster(this.name);return Command.prototype.getRendererBody.call(this)};UseCommand.prototype.getRendererBody=function(){return stringFormat('{0}engine.render({2},{{3}}){1}',RENDER_STRING_ADD_START,RENDER_STRING_ADD_END,stringLiteralize(this.name),compileVariable(this.args,this.engine).replace(/(^|,)\s*([a-z0-9_]+)\s*=/ig,function(match,start,argName){return(start||'')+stringLiteralize(argName)+':'}))};VarCommand.prototype.getRendererBody=function(){if(this.expr){return stringFormat('v[{0}]={1};',stringLiteralize(this.name),compileVariable(this.expr,this.engine))}return''};IfCommand.prototype.getRendererBody=function(){return stringFormat('if({0}){{1}}',compileVariable(this.value,this.engine),Command.prototype.getRendererBody.call(this))};ElseCommand.prototype.getRendererBody=function(){return stringFormat('}else{{0}',Command.prototype.getRendererBody.call(this))};ForCommand.prototype.getRendererBody=function(){return stringFormat(''+'var {0}={1};'+'if({0} instanceof Array)'+'for (var {4}=0,{5}={0}.length;{4}<{5};{4}++){v[{2}]={4};v[{3}]={0}[{4}];{6}}'+'else if(typeof {0}==="object")'+'for(var {4} in {0}){v[{2}]={4};v[{3}]={0}[{4}];{6}}',generateGUID(),compileVariable(this.list,this.engine),stringLiteralize(this.index||generateGUID()),stringLiteralize(this.item),generateGUID(),generateGUID(),Command.prototype.getRendererBody.call(this))};FilterCommand.prototype.getRendererBody=function(){var args=this.args;return stringFormat('{2}fs[{5}]((function(){{0}{4}{1}})(){6}){3}',RENDER_STRING_DECLATION,RENDER_STRING_RETURN,RENDER_STRING_ADD_START,RENDER_STRING_ADD_END,Command.prototype.getRendererBody.call(this),stringLiteralize(this.name),args?','+compileVariable(args,this.engine):'')};var commandTypes={};function addCommandType(name,Type){commandTypes[name]=Type;Type.prototype.type=name}addCommandType('target',TargetCommand);addCommandType('block',BlockCommand);addCommandType('import',ImportCommand);addCommandType('use',UseCommand);addCommandType('var',VarCommand);addCommandType('for',ForCommand);addCommandType('if',IfCommand);addCommandType('elif',ElifCommand);addCommandType('else',ElseCommand);addCommandType('filter',FilterCommand);function Engine(options){this.options={commandOpen:'<!--',commandClose:'-->',commandSyntax:/^\s*(\/)?([a-z]+)\s*(?::([\s\S]*))?$/,variableOpen:'${',variableClose:'}',defaultFilter:'html'};this.config(options);this.targets={};this.filters=extend({},DEFAULT_FILTERS)}Engine.prototype.config=function(options){extend(this.options,options)};Engine.prototype.compile=Engine.prototype.parse=function(source){if(source){var targetNames=parseSource(source,this);if(targetNames.length){return this.targets[targetNames[0]].getRenderer()}}return new Function('return ""')};Engine.prototype.getRenderer=function(name){var target=this.targets[name];if(target){return target.getRenderer()}};Engine.prototype.render=function(name,data){var renderer=this.getRenderer(name);if(renderer){return renderer(data)}return''};Engine.prototype.addFilter=function(name,filter){if(typeof filter==='function'){this.filters[name]=filter}};function parseSource(source,engine){var commandOpen=engine.options.commandOpen;var commandClose=engine.options.commandClose;var commandSyntax=engine.options.commandSyntax;var stack=new Stack();var analyseContext={engine:engine,targets:[],stack:stack,target:null};var textBuf=[];function flushTextBuf(){var text;if(textBuf.length>0&&(text=textBuf.join(''))){var textNode=new TextNode(text,engine);textNode.beforeAdd(analyseContext);stack.top().addChild(textNode);textBuf=[];if(engine.options.strip&&analyseContext.current instanceof Command){textNode.value=text.replace(/^[\x20\t\r]*\n/,'')}analyseContext.current=textNode}}var NodeType;parseTextBlock(source,commandOpen,commandClose,0,function(text){var match=commandSyntax.exec(text);if(match&&(NodeType=commandTypes[match[2].toLowerCase()])&&typeof NodeType==='function'){flushTextBuf();var currentNode=analyseContext.current;if(engine.options.strip&¤tNode instanceof TextNode){currentNode.value=currentNode.value.replace(/\r?\n[\x20\t]*$/,'\n')}if(match[1]){currentNode=autoCloseCommand(analyseContext,NodeType)}else{currentNode=new NodeType(match[3],engine);if(typeof currentNode.beforeOpen==='function'){currentNode.beforeOpen(analyseContext)}currentNode.open(analyseContext)}analyseContext.current=currentNode}else if(!/^\s*\/\//.test(text)){textBuf.push(commandOpen,text,commandClose)}NodeType=null},function(text){textBuf.push(text)});flushTextBuf();autoCloseCommand(analyseContext);return analyseContext.targets}var etpl=new Engine();etpl.Engine=Engine;
//日期过滤器
etpl.addFilter('date', function ( source, outFormat, inFormat ) {
if(!inFormat){
inFormat = 'YYYY-MM-DD';
}
if(!outFormat){
outFormat = 'YYYY年MM月DD日';
}
source = source.replace(/^\s*|\s*$/,'');
var yearIndex = inFormat.indexOf('YYYY'),
monthIndex = inFormat.indexOf('MM'),
dayIndex = inFormat.indexOf('DD'),
year = source.slice(yearIndex, yearIndex + 4),
month = source.slice(monthIndex, monthIndex + 2),
day = source.slice(dayIndex, dayIndex + 2),
result = outFormat.replace('YYYY',year).replace('MM',month).replace('DD',day);
return result;
});
//数字过滤
etpl.addFilter('dec', function ( source, format) {
if(!format){
format = '#,###.00';
}
if(isNaN(Number(source))){
console.warn('etpl[dec]过滤器输入源不是数字,无法格式化!');
return source;
}
source = source.replace(/^\s*|\s*$/,'');
var fixedlength = 0,
seplength = 0,
intFormat = format,
intPart = source.split('.')[0],
floatPart = source.split('.').pop();
if(format.indexOf('.') > 0){
fixedlength = format.split('.').pop().length;
intFormat = format.split('.')[0];
}
if(intFormat.indexOf(',') > 0){
seplength = intFormat.split(',').pop().length;
}
if(seplength && seplength < intPart.length){
var result = [];
var joinsep = function(string, num){
var last = string.slice(-num);
var head = string.slice(0, string.length-num);
result.unshift(last);
if(head.length>num){
joinsep(head, num);
}else if(head.length){
result.unshift(head);
}
}
joinsep(intPart,seplength);
intPart = result.join(',');
}
if(fixedlength){
var numFixed = Number(source).toFixed(fixedlength);
floatPart = numFixed.toString().split('.').pop();
}
return intPart + '.' + floatPart;
});
module.exports = etpl;
});