baiduTemplate.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /**
  2. * baiduTemplate简单好用的Javascript模板引擎 1.0.6 版本
  3. * http://baidufe.github.com/BaiduTemplate
  4. * 开源协议:BSD License
  5. * 浏览器环境占用命名空间 baidu.template ,nodejs环境直接安装 npm install baidutemplate
  6. * @param str{String} dom结点ID,或者模板string
  7. * @param data{Object} 需要渲染的json对象,可以为空。当data为{}时,仍然返回html。
  8. * @return 如果无data,直接返回编译后的函数;如果有data,返回html。
  9. * @author wangxiao
  10. * @email 1988wangxiao@gmail.com
  11. */
  12. ;(function(window){
  13. //取得浏览器环境的baidu命名空间,非浏览器环境符合commonjs规范exports出去
  14. //修正在nodejs环境下,采用baidu.template变量名
  15. var baidu = typeof module === 'undefined' ? (window.baidu = window.baidu || {}) : module.exports;
  16. //模板函数(放置于baidu.template命名空间下)
  17. baidu.template = function(str, data){
  18. //检查是否有该id的元素存在,如果有元素则获取元素的innerHTML/value,否则认为字符串为模板
  19. var fn = (function(){
  20. //判断如果没有document,则为非浏览器环境
  21. if(!window.document){
  22. return bt._compile(str);
  23. };
  24. //HTML5规定ID可以由任何不包含空格字符的字符串组成
  25. var element = document.getElementById(str);
  26. if (element) {
  27. //取到对应id的dom,缓存其编译后的HTML模板函数
  28. if (bt.cache[str]) {
  29. return bt.cache[str];
  30. };
  31. //textarea或input则取value,其它情况取innerHTML
  32. var html = /^(textarea|input)$/i.test(element.nodeName) ? element.value : element.innerHTML;
  33. return bt._compile(html);
  34. }else{
  35. //是模板字符串,则生成一个函数
  36. //如果直接传入字符串作为模板,则可能变化过多,因此不考虑缓存
  37. return bt._compile(str);
  38. };
  39. })();
  40. //有数据则返回HTML字符串,没有数据则返回函数 支持data={}的情况
  41. var result = bt._isObject(data) ? fn( data ) : fn;
  42. fn = null;
  43. return result;
  44. };
  45. //取得命名空间 baidu.template
  46. var bt = baidu.template;
  47. //标记当前版本
  48. bt.versions = bt.versions || [];
  49. bt.versions.push('1.0.6');
  50. //缓存 将对应id模板生成的函数缓存下来。
  51. bt.cache = {};
  52. //自定义分隔符,可以含有正则中的字符,可以是HTML注释开头 <! !>
  53. bt.LEFT_DELIMITER = bt.LEFT_DELIMITER||'<%';
  54. bt.RIGHT_DELIMITER = bt.RIGHT_DELIMITER||'%>';
  55. //自定义默认是否转义,默认为默认自动转义
  56. bt.ESCAPE = true;
  57. //HTML转义
  58. bt._encodeHTML = function (source) {
  59. return String(source)
  60. .replace(/&/g,'&amp;')
  61. .replace(/</g,'&lt;')
  62. .replace(/>/g,'&gt;')
  63. .replace(/\\/g,'&#92;')
  64. .replace(/"/g,'&quot;')
  65. .replace(/'/g,'&#39;');
  66. };
  67. //转义影响正则的字符
  68. bt._encodeReg = function (source) {
  69. return String(source).replace(/([.*+?^=!:${}()|[\]/\\])/g,'\\$1');
  70. };
  71. //转义UI UI变量使用在HTML页面标签onclick等事件函数参数中
  72. bt._encodeEventHTML = function (source) {
  73. return String(source)
  74. .replace(/&/g,'&amp;')
  75. .replace(/</g,'&lt;')
  76. .replace(/>/g,'&gt;')
  77. .replace(/"/g,'&quot;')
  78. .replace(/'/g,'&#39;')
  79. .replace(/\\\\/g,'\\')
  80. .replace(/\\\//g,'\/')
  81. .replace(/\\n/g,'\n')
  82. .replace(/\\r/g,'\r');
  83. };
  84. //将字符串拼接生成函数,即编译过程(compile)
  85. bt._compile = function(str){
  86. 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";
  87. return new Function("_template_object",funBody);
  88. };
  89. //判断是否是Object类型
  90. bt._isObject = function (source) {
  91. return 'function' === typeof source || !!(source && 'object' === typeof source);
  92. };
  93. //解析模板字符串
  94. bt._analysisStr = function(str){
  95. //取得分隔符
  96. var _left_ = bt.LEFT_DELIMITER;
  97. var _right_ = bt.RIGHT_DELIMITER;
  98. //对分隔符进行转义,支持正则中的元字符,可以是HTML注释 <! !>
  99. var _left = bt._encodeReg(_left_);
  100. var _right = bt._encodeReg(_right_);
  101. str = String(str)
  102. //去掉分隔符中js注释
  103. .replace(new RegExp("("+_left+"[^"+_right+"]*)//.*\n","g"), "$1")
  104. //去掉注释内容 <%* 这里可以任意的注释 *%>
  105. //默认支持HTML注释,将HTML注释匹配掉的原因是用户有可能用 <! !>来做分割符
  106. .replace(new RegExp("<!--.*?-->", "g"),"")
  107. .replace(new RegExp(_left+"\\*.*?\\*"+_right, "g"),"")
  108. //把所有换行去掉 \r回车符 \t制表符 \n换行符
  109. .replace(new RegExp("[\\r\\t\\n]","g"), "")
  110. //用来处理非分隔符内部的内容中含有 斜杠 \ 单引号 ‘ ,处理办法为HTML转义
  111. .replace(new RegExp(_left+"(?:(?!"+_right+")[\\s\\S])*"+_right+"|((?:(?!"+_left+")[\\s\\S])+)","g"),function (item, $1) {
  112. var str = '';
  113. if($1){
  114. //将 斜杠 单引 HTML转义
  115. str = $1.replace(/\\/g,"&#92;").replace(/'/g,'&#39;');
  116. while(/<[^<]*?&#39;[^<]*?>/g.test(str)){
  117. //将标签内的单引号转义为\r 结合最后一步,替换为\'
  118. str = str.replace(/(<[^<]*?)&#39;([^<]*?>)/g,'$1\r$2')
  119. };
  120. }else{
  121. str = item;
  122. }
  123. return str ;
  124. });
  125. str = str
  126. //定义变量,如果没有分号,需要容错 <%var val='test'%>
  127. .replace(new RegExp("("+_left+"[\\s]*?var[\\s]*?.*?[\\s]*?[^;])[\\s]*?"+_right,"g"),"$1;"+_right_)
  128. //对变量后面的分号做容错(包括转义模式 如<%:h=value%>) <%=value;%> 排除掉函数的情况 <%fun1();%> 排除定义变量情况 <%var val='test';%>
  129. .replace(new RegExp("("+_left+":?[hvu]?[\\s]*?=[\\s]*?[^;|"+_right+"]*?);[\\s]*?"+_right,"g"),"$1"+_right_)
  130. //按照 <% 分割为一个个数组,再用 \t 和在一起,相当于将 <% 替换为 \t
  131. //将模板按照<%分为一段一段的,再在每段的结尾加入 \t,即用 \t 将每个模板片段前面分隔开
  132. .split(_left_).join("\t");
  133. //支持用户配置默认是否自动转义
  134. if(bt.ESCAPE){
  135. str = str
  136. //找到 \t=任意一个字符%> 替换为 ‘,任意字符,'
  137. //即替换简单变量 \t=data%> 替换为 ',data,'
  138. //默认HTML转义 也支持HTML转义写法<%:h=value%>
  139. .replace(new RegExp("\\t=(.*?)"+_right,"g"),"',typeof($1) === 'undefined'?'':baidu.template._encodeHTML($1),'");
  140. }else{
  141. str = str
  142. //默认不转义HTML转义
  143. .replace(new RegExp("\\t=(.*?)"+_right,"g"),"',typeof($1) === 'undefined'?'':$1,'");
  144. };
  145. str = str
  146. //支持HTML转义写法<%:h=value%>
  147. .replace(new RegExp("\\t:h=(.*?)"+_right,"g"),"',typeof($1) === 'undefined'?'':baidu.template._encodeHTML($1),'")
  148. //支持不转义写法 <%:=value%>和<%-value%>
  149. .replace(new RegExp("\\t(?::=|-)(.*?)"+_right,"g"),"',typeof($1)==='undefined'?'':$1,'")
  150. //支持url转义 <%:u=value%>
  151. .replace(new RegExp("\\t:u=(.*?)"+_right,"g"),"',typeof($1)==='undefined'?'':encodeURIComponent($1),'")
  152. //支持UI 变量使用在HTML页面标签onclick等事件函数参数中 <%:v=value%>
  153. .replace(new RegExp("\\t:v=(.*?)"+_right,"g"),"',typeof($1)==='undefined'?'':baidu.template._encodeEventHTML($1),'")
  154. //将字符串按照 \t 分成为数组,在用'); 将其合并,即替换掉结尾的 \t 为 ');
  155. //在if,for等语句前面加上 '); ,形成 ');if ');for 的形式
  156. .split("\t").join("');")
  157. //将 %> 替换为_template_fun_array.push('
  158. //即去掉结尾符,生成函数中的push方法
  159. //如:if(list.length=5){%><h2>',list[4],'</h2>');}
  160. //会被替换为 if(list.length=5){_template_fun_array.push('<h2>',list[4],'</h2>');}
  161. .split(_right_).join("_template_fun_array.push('")
  162. //将 \r 替换为 \
  163. .split("\r").join("\\'");
  164. return str;
  165. };
  166. })(window);