/**
baiduTemplate简单好用的Javascript模板引擎 1.0.6 版本
开源协议:BSD License
浏览器环境占用命名空间 baidu.template ,nodejs环境直接安装 npm install baidutemplate
@param str{String} dom结点ID,或者模板string
@param data{Object} 需要渲染的json对象,可以为空。当data为{}时,仍然返回html。
@return 如果无data,直接返回编译后的函数;如果有data,返回html。
@author wangxiao
-
@email 1988wangxiao@gmail.com
*/
;(function(window){//取得浏览器环境的baidu命名空间,非浏览器环境符合commonjs规范exports出去
//修正在nodejs环境下,采用baidu.template变量名
var baidu = typeof module === 'undefined' ? (window.baidu = window.baidu || {}) : module.exports;//模板函数(放置于baidu.template命名空间下)
baidu.template = function(str, data){//检查是否有该id的元素存在,如果有元素则获取元素的innerHTML/value,否则认为字符串为模板 var fn = (function(){ //判断如果没有document,则为非浏览器环境 if(!window.document){ return bt._compile(str); }; //HTML5规定ID可以由任何不包含空格字符的字符串组成 var element = document.getElementById(str); if (element) { //取到对应id的dom,缓存其编译后的HTML模板函数 if (bt.cache[str]) { return bt.cache[str]; }; //textarea或input则取value,其它情况取innerHTML var html = /^(textarea|input)$/i.test(element.nodeName) ? element.value : element.innerHTML; return bt._compile(html); }else{ //是模板字符串,则生成一个函数 //如果直接传入字符串作为模板,则可能变化过多,因此不考虑缓存 return bt._compile(str); }; })(); //有数据则返回HTML字符串,没有数据则返回函数 支持data={}的情况 var result = bt._isObject(data) ? fn( data ) : fn; fn = null; return result;
};
//取得命名空间 baidu.template
var bt = baidu.template;//标记当前版本
bt.versions = bt.versions || [];
bt.versions.push('1.0.6');//缓存 将对应id模板生成的函数缓存下来。
bt.cache = {};//自定义分隔符,可以含有正则中的字符,可以是HTML注释开头 <! !>
bt.LEFT_DELIMITER = bt.LEFT_DELIMITER||'<%';
bt.RIGHT_DELIMITER = bt.RIGHT_DELIMITER||'%>';//自定义默认是否转义,默认为默认自动转义
bt.ESCAPE = true;//HTML转义
bt._encodeHTML = function (source) {
return String(source)
.replace(/&/g,'&')
.replace(/</g,'<')
.replace(/>/g,'>')
.replace(/\/g,'\')
.replace(/"/g,'"')
.replace(/'/g,''');
};
//转义影响正则的字符
bt._encodeReg = function (source) {
return String(source).replace(/([.*+?^=!{}()|[]/\])/g,'\$1');
};//转义UI UI变量使用在HTML页面标签onclick等事件函数参数中
bt._encodeEventHTML = function (source) {
return String(source)
.replace(/&/g,'&')
.replace(/</g,'<')
.replace(/>/g,'>')
.replace(/"/g,'"')
.replace(/'/g,''')
.replace(/\\/g,'\')
.replace(/\//g,'/')
.replace(/\n/g,'\n')
.replace(/\r/g,'\r');
};//将字符串拼接生成函数,即编译过程(compile)
bt._compile = function(str){
var funBody = "var _template_fun_array=[];\nvar fn=(function(data){\nvar _template_varName='';\nfor(name in data){\n_template_varName+=('var '+name+'=data["'+name+'"];');\n};\neval(_template_varName);\n_template_fun_array.push('"+bt._analysisStr(str)+"');\n_template_varName=null;\n})(_template_object);\nfn = null;\nreturn _template_fun_array.join('');\n";
return new Function("_template_object",funBody);
};//判断是否是Object类型
bt._isObject = function (source) {
return 'function' === typeof source || !!(source && 'object' === typeof source);
};//解析模板字符串
bt._analysisStr = function(str){//取得分隔符 var _left_ = bt.LEFT_DELIMITER; var _right_ = bt.RIGHT_DELIMITER; //对分隔符进行转义,支持正则中的元字符,可以是HTML注释 <! !> var _left = bt._encodeReg(_left_); var _right = bt._encodeReg(_right_); str = String(str) //去掉分隔符中js注释 .replace(new RegExp("("+_left+"[^"+_right+"]*)//.*\n","g"), "$1") //去掉注释内容 <%* 这里可以任意的注释 *%> //默认支持HTML注释,将HTML注释匹配掉的原因是用户有可能用 <! !>来做分割符 .replace(new RegExp("<!--.*?-->", "g"),"") .replace(new RegExp(_left+"\\*.*?\\*"+_right, "g"),"") //把所有换行去掉 \r回车符 \t制表符 \n换行符 .replace(new RegExp("[\\r\\t\\n]","g"), "") //用来处理非分隔符内部的内容中含有 斜杠 \ 单引号 ‘ ,处理办法为HTML转义 .replace(new RegExp(_left+"(??!"+_right+")[\\s\\S])*"+_right+"|((??!"+_left+")[\\s\\S])+)","g"),function (item, $1) { var str = ''; if($1){ //将 斜杠 单引 HTML转义 str = $1.replace(/\\/g,"\").replace(/'/g,'''); while(/<[^<]*?'[^<]*?>/g.test(str)){ //将标签内的单引号转义为\r 结合最后一步,替换为\' str = str.replace(/(<[^<]*?)'([^<]*?>)/g,'$1\r$2') }; }else{ str = item; } return str ; }); str = str //定义变量,如果没有分号,需要容错 <%var val='test'%> .replace(new RegExp("("+_left+"[\\s]*?var[\\s]*?.*?[\\s]*?[^;])[\\s]*?"+_right,"g"),"$1;"+_right_) //对变量后面的分号做容错(包括转义模式 如<%:h=value%>) <%=value;%> 排除掉函数的情况 <%fun1();%> 排除定义变量情况 <%var val='test';%> .replace(new RegExp("("+_left+":?[hvu]?[\\s]*?=[\\s]*?[^;|"+_right+"]*?);[\\s]*?"+_right,"g"),"$1"+_right_) //按照 <% 分割为一个个数组,再用 \t 和在一起,相当于将 <% 替换为 \t //将模板按照<%分为一段一段的,再在每段的结尾加入 \t,即用 \t 将每个模板片段前面分隔开 .split(_left_).join("\t"); //支持用户配置默认是否自动转义 if(bt.ESCAPE){ str = str //找到 \t=任意一个字符%> 替换为 ‘,任意字符,' //即替换简单变量 \t=data%> 替换为 ',data,' //默认HTML转义 也支持HTML转义写法<%:h=value%> .replace(new RegExp("\\t=(.*?)"+_right,"g"),"',typeof($1) === 'undefined'?'':baidu.template._encodeHTML($1),'"); }else{ str = str //默认不转义HTML转义 .replace(new RegExp("\\t=(.*?)"+_right,"g"),"',typeof($1) === 'undefined'?''1,'"); }; str = str //支持HTML转义写法<%:h=value%> .replace(new RegExp("\\t:h=(.*?)"+_right,"g"),"',typeof($1) === 'undefined'?'':baidu.template._encodeHTML($1),'") //支持不转义写法 <%:=value%>和<%-value%> .replace(new RegExp("\\t(?::=|-)(.*?)"+_right,"g"),"',typeof($1)==='undefined'?''1,'") //支持url转义 <%:u=value%> .replace(new RegExp("\\t:u=(.*?)"+_right,"g"),"',typeof($1)==='undefined'?'':encodeURIComponent($1),'") //支持UI 变量使用在HTML页面标签onclick等事件函数参数中 <%:v=value%> .replace(new RegExp("\\t:v=(.*?)"+_right,"g"),"',typeof($1)==='undefined'?'':baidu.template._encodeEventHTML($1),'") //将字符串按照 \t 分成为数组,在用'); 将其合并,即替换掉结尾的 \t 为 '); //在if,for等语句前面加上 '); ,形成 ');if ');for 的形式 .split("\t").join("');") //将 %> 替换为_template_fun_array.push(' //即去掉结尾符,生成函数中的push方法 //如:if(list.length=5){%><h2>',list[4],'</h2>');} //会被替换为 if(list.length=5){_template_fun_array.push('<h2>',list[4],'</h2>');} .split(_right_).join("_template_fun_array.push('") //将 \r 替换为 \ .split("\r").join("\\'"); return str;
};
})(window);