/**
baiduTemplate簡單好用的Javascript模板引擎 1.0.6 版本
開源協(xié)議:BSD License
瀏覽器環(huán)境占用命名空間 baidu.template ,nodejs環(huán)境直接安裝 npm install baidutemplate
@param str{String} dom結(jié)點(diǎn)ID滔岳,或者模板string
@param data{Object} 需要渲染的json對象卓研,可以為空。當(dāng)data為{}時磨淌,仍然返回html埃脏。
@return 如果無data堵幽,直接返回編譯后的函數(shù);如果有data,返回html竿屹。
@author wangxiao
-
@email 1988wangxiao@gmail.com
*/
;(function(window){//取得瀏覽器環(huán)境的baidu命名空間厨钻,非瀏覽器環(huán)境符合commonjs規(guī)范exports出去
//修正在nodejs環(huán)境下,采用baidu.template變量名
var baidu = typeof module === 'undefined' ? (window.baidu = window.baidu || {}) : module.exports;//模板函數(shù)(放置于baidu.template命名空間下)
baidu.template = function(str, data){//檢查是否有該id的元素存在,如果有元素則獲取元素的innerHTML/value格仲,否則認(rèn)為字符串為模板 var fn = (function(){ //判斷如果沒有document侮东,則為非瀏覽器環(huán)境 if(!window.document){ return bt._compile(str); }; //HTML5規(guī)定ID可以由任何不包含空格字符的字符串組成 var element = document.getElementById(str); if (element) { //取到對應(yīng)id的dom,緩存其編譯后的HTML模板函數(shù) 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{ //是模板字符串,則生成一個函數(shù) //如果直接傳入字符串作為模板,則可能變化過多,因此不考慮緩存 return bt._compile(str); }; })(); //有數(shù)據(jù)則返回HTML字符串,沒有數(shù)據(jù)則返回函數(shù) 支持data={}的情況 var result = bt._isObject(data) ? fn( data ) : fn; fn = null; return result;
};
//取得命名空間 baidu.template
var bt = baidu.template;//標(biāo)記當(dāng)前版本
bt.versions = bt.versions || [];
bt.versions.push('1.0.6');//緩存 將對應(yīng)id模板生成的函數(shù)緩存下來。
bt.cache = {};//自定義分隔符涕俗,可以含有正則中的字符元镀,可以是HTML注釋開頭 <! !>
bt.LEFT_DELIMITER = bt.LEFT_DELIMITER||'<%';
bt.RIGHT_DELIMITER = bt.RIGHT_DELIMITER||'%>';//自定義默認(rèn)是否轉(zhuǎn)義蔽挠,默認(rèn)為默認(rèn)自動轉(zhuǎn)義
bt.ESCAPE = true;//HTML轉(zhuǎn)義
bt._encodeHTML = function (source) {
return String(source)
.replace(/&/g,'&')
.replace(/</g,'<')
.replace(/>/g,'>')
.replace(/\/g,'\')
.replace(/"/g,'"')
.replace(/'/g,''');
};
//轉(zhuǎn)義影響正則的字符
bt._encodeReg = function (source) {
return String(source).replace(/([.*+?^=!{}()|[]/\])/g,'\$1');
};//轉(zhuǎn)義UI UI變量使用在HTML頁面標(biāo)簽onclick等事件函數(shù)參數(shù)中
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');
};//將字符串拼接生成函數(shù)量窘,即編譯過程(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; //對分隔符進(jìn)行轉(zhuǎn)義囚痴,支持正則中的元字符深滚,可以是HTML注釋 <! !> var _left = bt._encodeReg(_left_); var _right = bt._encodeReg(_right_); str = String(str) //去掉分隔符中js注釋 .replace(new RegExp("("+_left+"[^"+_right+"]*)//.*\n","g"), "$1") //去掉注釋內(nèi)容 <%* 這里可以任意的注釋 *%> //默認(rèn)支持HTML注釋,將HTML注釋匹配掉的原因是用戶有可能用 <! !>來做分割符 .replace(new RegExp("<!--.*?-->", "g"),"") .replace(new RegExp(_left+"\\*.*?\\*"+_right, "g"),"") //把所有換行去掉 \r回車符 \t制表符 \n換行符 .replace(new RegExp("[\\r\\t\\n]","g"), "") //用來處理非分隔符內(nèi)部的內(nèi)容中含有 斜杠 \ 單引號 ‘ 明刷,處理辦法為HTML轉(zhuǎn)義 .replace(new RegExp(_left+"(??!"+_right+")[\\s\\S])*"+_right+"|((??!"+_left+")[\\s\\S])+)","g"),function (item, $1) { var str = ''; if($1){ //將 斜杠 單引 HTML轉(zhuǎn)義 str = $1.replace(/\\/g,"\").replace(/'/g,'''); while(/<[^<]*?'[^<]*?>/g.test(str)){ //將標(biāo)簽內(nèi)的單引號轉(zhuǎn)義為\r 結(jié)合最后一步轰枝,替換為\' 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_) //對變量后面的分號做容錯(包括轉(zhuǎn)義模式 如<%:h=value%>) <%=value;%> 排除掉函數(shù)的情況 <%fun1();%> 排除定義變量情況 <%var val='test';%> .replace(new RegExp("("+_left+":?[hvu]?[\\s]*?=[\\s]*?[^;|"+_right+"]*?);[\\s]*?"+_right,"g"),"$1"+_right_) //按照 <% 分割為一個個數(shù)組,再用 \t 和在一起莹汤,相當(dāng)于將 <% 替換為 \t //將模板按照<%分為一段一段的钞楼,再在每段的結(jié)尾加入 \t,即用 \t 將每個模板片段前面分隔開 .split(_left_).join("\t"); //支持用戶配置默認(rèn)是否自動轉(zhuǎn)義 if(bt.ESCAPE){ str = str //找到 \t=任意一個字符%> 替換為 ‘嘿辟,任意字符,' //即替換簡單變量 \t=data%> 替換為 ',data,' //默認(rèn)HTML轉(zhuǎn)義 也支持HTML轉(zhuǎn)義寫法<%:h=value%> .replace(new RegExp("\\t=(.*?)"+_right,"g"),"',typeof($1) === 'undefined'?'':baidu.template._encodeHTML($1),'"); }else{ str = str //默認(rèn)不轉(zhuǎn)義HTML轉(zhuǎn)義 .replace(new RegExp("\\t=(.*?)"+_right,"g"),"',typeof($1) === 'undefined'?''1,'"); }; str = str //支持HTML轉(zhuǎn)義寫法<%:h=value%> .replace(new RegExp("\\t:h=(.*?)"+_right,"g"),"',typeof($1) === 'undefined'?'':baidu.template._encodeHTML($1),'") //支持不轉(zhuǎn)義寫法 <%:=value%>和<%-value%> .replace(new RegExp("\\t(?::=|-)(.*?)"+_right,"g"),"',typeof($1)==='undefined'?''1,'") //支持url轉(zhuǎn)義 <%:u=value%> .replace(new RegExp("\\t:u=(.*?)"+_right,"g"),"',typeof($1)==='undefined'?'':encodeURIComponent($1),'") //支持UI 變量使用在HTML頁面標(biāo)簽onclick等事件函數(shù)參數(shù)中 <%:v=value%> .replace(new RegExp("\\t:v=(.*?)"+_right,"g"),"',typeof($1)==='undefined'?'':baidu.template._encodeEventHTML($1),'") //將字符串按照 \t 分成為數(shù)組玻墅,在用'); 將其合并线得,即替換掉結(jié)尾的 \t 為 '); //在if,for等語句前面加上 '); 揭措,形成 ');if ');for 的形式 .split("\t").join("');") //將 %> 替換為_template_fun_array.push(' //即去掉結(jié)尾符充甚,生成函數(shù)中的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);