From f568dfe688e34798d7665c0677fefbc62e658070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Sat, 28 Dec 2013 02:15:22 +0800 Subject: [PATCH] v2.0.3 --- README.md | 76 +- demo/basic.html | 2 +- demo/compile.html | 2 +- demo/debug.html | 2 +- demo/helper.html | 2 +- demo/include.html | 2 +- demo/index.html | 4 +- demo/no-escape.html | 2 +- demo/print.html | 2 +- demo/simple-syntax/basic.html | 34 + demo/simple-syntax/compile.html | 27 + demo/simple-syntax/debug.html | 29 + demo/simple-syntax/helper.html | 38 + demo/simple-syntax/include.html | 32 + demo/simple-syntax/index.html | 20 + demo/simple-syntax/no-escape.html | 25 + demo/tag.html | 2 +- demo/tmpl.html | 2 +- doc/build.md | 11 + doc/template-syntax.md | 90 ++ extensions/README.md | 105 -- extensions/index.html | 401 ------ extensions/template-syntax.js | 121 -- index.html | 532 -------- package.json | 14 +- seajs/README.md | 77 -- seajs/transport-extensions.js | 15 - seajs/transport.js | 13 - src/template-syntax.js | 92 ++ template.js => src/template.js | 1164 ++++++++--------- template.min.js | 1 - test/js/baidu.template.js | 205 --- .../test/test.html => test/test-simple.html | 44 +- test/test-speed.html | 82 +- tools/README.md | 11 +- tools/combine.html | 131 +- 36 files changed, 1179 insertions(+), 2233 deletions(-) create mode 100644 demo/simple-syntax/basic.html create mode 100644 demo/simple-syntax/compile.html create mode 100644 demo/simple-syntax/debug.html create mode 100644 demo/simple-syntax/helper.html create mode 100644 demo/simple-syntax/include.html create mode 100644 demo/simple-syntax/index.html create mode 100644 demo/simple-syntax/no-escape.html create mode 100644 doc/build.md create mode 100644 doc/template-syntax.md delete mode 100644 extensions/README.md delete mode 100644 extensions/index.html delete mode 100644 extensions/template-syntax.js delete mode 100644 index.html delete mode 100644 seajs/README.md delete mode 100644 seajs/transport-extensions.js delete mode 100644 seajs/transport.js create mode 100644 src/template-syntax.js rename template.js => src/template.js (54%) delete mode 100644 template.min.js delete mode 100644 test/js/baidu.template.js rename extensions/test/test.html => test/test-simple.html (75%) diff --git a/README.md b/README.md index 47bc4a45..ca0044ff 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,20 @@ ###### 新一代 javascript 模板引擎 ================= -artTemplate 是新一代 javascript 模板引擎,它在 v8 中的渲染效率可接近 javascript 性能极限,在 chrome 下渲染效率测试中分别是知名引擎 Mustache 与 micro tmpl 的 25 、 32 倍。 +artTemplate 是新一代 javascript 模板引擎,它在 v8 中的渲染效率可接近 javascript 性能极限,在 chrome 下渲染效率测试中分别是知名引擎 Mustache 与 micro tmpl 的 25 、 32 倍([性能测试](http://aui.github.com/artTemplate/test/test-speed.html))。 -引擎支持调试。若渲染中遇到错误,调试器可精确定位到产生异常的模板语句,解决前端模板难以调试的问题。 +引擎支持调试。若渲染中遇到错误,调试器可精确定位到产生异常的模板语句,解决前端模板难以调试的问题([详情](http://aui.github.io/artTemplate/demo/debug.html))。 -这一切都在 2kb(gzip) 中实现! +另外,artTemplate 的模板还支持使用自动化工具预编译,支持将模板转换为 js 文件。 ## 快速上手 +### 引用引擎 + + + +直接下载 [template.js](https://raw.github.com/aui/artTemplate/master/dist/template.js) + ### 编写模板 使用一个``type="text/html"``的``script``标签存放模板: @@ -61,7 +67,7 @@ artTemplate 是新一代 javascript 模板引擎,它在 v8 中的渲染效率 ## 不转义HTML -模板引擎默认数据包含的 HTML 字符进行转义以避免 XSS 漏洞,若不需要转义的地方可使用两个``=``号。 +模板引擎默认数据包含的 HTML 字符进行转义以避免 XSS 漏洞,若不需要转义的地方可使用``==``。 +
diff --git a/demo/compile.html b/demo/compile.html index d084d902..87cb6c6d 100644 --- a/demo/compile.html +++ b/demo/compile.html @@ -3,7 +3,7 @@只需要把 extensions/template-syntax.js 合并到 template.js 即完成自定义语法扩展安装。
- -{
与 }
符号包裹起来的语句则为模板的逻辑表达式。
对内容编码输出:
- -{content}
-
-
-编码可以防止数据中含有 HTML 字符串,避免引起 XSS 攻击。
- -不编码输出:
- -{echo content}
-
-
-{if admin}
- {content}
-{/if}
-
-{if user === 'admin'}
- {content}
-{else if user === '007'}
- <strong>hello world</strong>
-{/if}
-
-
-无论数组或者对象都可以用 each 进行遍历。
- -{each list}
- <li>{$index}. {$value.user}</li>
-{/each}
-
-
-其中 list 为要遍历的数据名称, $value
与 $index
是系统变量, $value
表示数据单条内容, $index
表示索引值,这两个变量也可以自定义:
{each list as value index}
- <li>{index}. {value.user}</li>
-{/each}
-
-
-用于嵌入子模板。
- -{include 'templateID' data}
-
-
-其中 'templateID' 是外部模板的 ID, data 为传递给 'templateID' 模板的数据。 data 参数若省略则默认传入当前模板的数据。
- -{include 'templateID'}
-
-
-使用template.helper(name, callback)
注册公用辅助方法,例如一个基本的 UBB 替换方法:
template.helper('$ubb2html', function (content) {
-
- return content
- .replace(/\[b\]([^\[]*?)\[\/b\]/igm, '<b>$1</b>')
- .replace(/\[i\]([^\[]*?)\[\/i\]/igm, '<i>$1</i>')
- .replace(/\[u\]([^\[]*?)\[\/u\]/igm, '<u>$1</u>')
- .replace(/\[url=([^\]]*)\]([^\[]*?)\[\/url\]/igm, '<a href="$1">$2</a>')
- .replace(/\[img\]([^\[]*?)\[\/img\]/igm, '<img src="$1" />');
-
-});
-
-
-模板中使用的方式:
- -{$ubb2html content}
-
-
-若辅助方法有多个参数使用一个空格分隔即可:
- -{helperName args1 args2 args3}
-
-
-本文档针对 artTemplate v1.1+ 编写
- -© cdc.tencent.com
- - \ No newline at end of file diff --git a/extensions/template-syntax.js b/extensions/template-syntax.js deleted file mode 100644 index 98a2b535..00000000 --- a/extensions/template-syntax.js +++ /dev/null @@ -1,121 +0,0 @@ -/*! - * artTemplate - Syntax Extensions - * https://github.com/aui/artTemplate - * Released under the MIT, BSD, and GPL Licenses - */ - -(function (exports) { - -exports.openTag = '{'; -exports.closeTag = '}'; - -exports.parser = function (code) { - code = code.replace(/^\s/, ''); - - var args = code.split(' '); - var key = args.shift(); - var keywords = exports.keywords; - var fuc = keywords[key]; - - if (fuc && keywords.hasOwnProperty(key)) { - - args = args.join(' '); - code = fuc.call(code, args); - - } else if (exports.prototype.hasOwnProperty(key)) { - - args = args.join(','); - code = '==' + key + '(' + args + ');'; - - } else { - - code = code.replace(/[\s;]*$/, ''); - code = '=' + code; - } - - return code; -}; - - -exports.keywords = { - - 'if': function (code) { - return 'if(' + code + '){'; - }, - - 'else': function (code) { - code = code.split(' '); - - if (code.shift() === 'if') { - code = ' if(' + code.join(' ') + ')'; - } else { - code = ''; - } - - return '}else' + code + '{'; - }, - - '/if': function () { - return '}'; - }, - - 'each': function (code) { - - code = code.split(' '); - - var object = code[0] || '$data'; - var as = code[1] || 'as'; - var value = code[2] || '$value'; - var index = code[3] || '$index'; - - var args = value + ',' + index; - - if (as !== 'as') { - object = '[]'; - } - - return '$each(' + object + ',function(' + args + '){'; - }, - - '/each': function () { - return '});'; - }, - - 'echo': function (code) { - return 'print(' + code + ');'; - }, - - 'include': function (code) { - code = code.split(' '); - - var id = code[0]; - var data = code[1]; - var args = id + (data ? (',' + data) : ''); - - return 'include(' + args + ');'; - } - -}; - - -exports.helper('$each', function (data, callback) { - - var isArray = Array.isArray || function (obj) { - return Object.prototype.toString.call(obj) === '[object Array]'; - }; - - if (isArray(data)) { - for (var i = 0, len = data.length; i < len; i++) { - callback.call(data, data[i], i, data); - } - } else { - for (i in data) { - callback.call(data, data[i], i); - } - } - -}); - - - -})(template); diff --git a/index.html b/index.html deleted file mode 100644 index 88718a6d..00000000 --- a/index.html +++ /dev/null @@ -1,532 +0,0 @@ - - - - - - -artTemplate 是新一代 javascript 模板引擎,它在 v8 中的渲染效率可接近 javascript 性能极限,在 chrome 下渲染效率测试中分别是知名引擎 Mustache 与 micro tmpl 的 25 、 32 倍。
- -引擎支持调试。若渲染中遇到错误,调试器可精确定位到产生异常的模板语句,解决前端模板难以调试的问题。
- -独有模板编译工具,它能把前端模板编译成不依赖模板引擎运行的JS文件,让前端模板可以突破浏览器的限制,实现像后端模板一样按文件与目录的方式组织、按需加载、include
嵌套等。
这一切都在 2kb(gzip) 中实现!
- -使用一个type="text/html"
的script
标签存放模板:
<script id="test" type="text/html">
-<h1><%=title%></h1>
-<ul>
- <%for(i = 0; i < list.length; i ++) {%>
- <li>条目内容 <%=i + 1%> :<%=list[i]%></li>
- <%}%>
-</ul>
-</script>
-
-
-模板逻辑语法开始与结束的界定符号为<%
与%>
,若<%
后面紧跟=
号则输出变量内容。
template.render(id, data)
var data = {
- title: '标签',
- list: ['文艺', '博客', '摄影', '电影', '民谣', '旅行', '吉他']
-};
-var html = template.render('test', data);
-document.getElementById('content').innerHTML = html;
-
-
-
-
-<%include(id, [data])%>
语句可以嵌入子模板,其中第二个参数是可选的,它默认传入当前的数据。
<script id="test" type="text/html">
-<h1><%=title%></h1>
-<%include('list')%>
-</script>
-
-<script id="list" type="text/html">
-<ul>
- <%for(i = 0; i < list.length; i ++) {%>
- <li>条目内容 <%=i + 1%> :<%=list[i]%></li>
- <%}%>
-</ul>
-</script>
-
-
-
-
-模板引擎默认数据包含的 HTML 字符进行转义以避免 XSS 漏洞,若不需要转义的地方可使用两个=
号。
<script id="test" type="text/html">
-<%==value%>
-</script>
-
-
-若需要关闭默认转义,可以设置template.isEscape = false
。
template.compile([id], source)
将返回一个渲染函数。其中 id 参数是可选的,如果使用了 id 参数,可以使用template.render(id, data)
渲染模板。
var source =
- '<ul>'
-+ '<% for (var i = 0; i < list.length; i ++) { %>'
-+ '<li>索引 <%= i + 1 %> :<%= list[i] %></li>'
-+ '<% } %>'
-+ '</ul>';
-
-var data = {
- list: ['文艺', '博客', '摄影', '电影', '民谣', '旅行', '吉他']
-};
-
-var render = template.compile(source);
-var html = render(data);
-document.getElementById('content').innerHTML = html;
-
-
-
-
-template.helper(name, callback)
辅助方法一般用来进行字符串替换,如 XSS 过滤、UBB 替换、脏话替换等。
例如扩展一个UBB替换方法:
- -template.helper('$ubb2html', function (content) {
- return content
- .replace(/\[b\]([^\[]*?)\[\/b\]/igm, '<b>$1</b>')
- .replace(/\[i\]([^\[]*?)\[\/i\]/igm, '<i>$1</i>')
- .replace(/\[u\]([^\[]*?)\[\/u\]/igm, '<u>$1</u>')
- .replace(/\[url=([^\]]*)\]([^\[]*?)\[\/url\]/igm, '<a href="$1">$2</a>')
- .replace(/\[img\]([^\[]*?)\[\/img\]/igm, '<img src="$1" />');
-});
-
-
-在模板中的使用方式:
- -<%=$ubb2html(content) %>
-
-
-注意:引擎不会对辅助方法输出的 HTML 字符进行转义。
- - - -若前端模板语法与后端语法产生冲突,可以修改模板引擎界定符,例如:
- -template.openTag = "<!--[";
-template.closeTag = "]-->";
-
-
-
-
-artTemplate 提供一个语法扩展用来简化模板逻辑语法。语法示例:
- -{if admin}
- <h3>{title}</h3>
- <ul>
- {each list}
- <li>{$index + 1}: {$value}</li>
- {/each}
- </ul>
-{/if}
-
-
-
-
-./tools/compile
可把模版编译成不依赖模版引擎的js文件,这样前端模版也可以向后端模版一样按文件放置,并且支持include等语句。
- -编译后的模板可以通过RequireJS、SeaJS等加载器进行异步加载,亦能利用它们成熟的打包合并工具进行上线前优化。
- -./tools/combine/
可以把 HTML 中的模板提取出来以便把模板嵌入到 js 文件中。
- -与编译工具不同的是,抽取后的模板仍然依赖引擎运行。
- -1、不能使用 javascript 关键字作为模板变量(包括 ECMA5 严格模式下新增的关键字):
- -- -break, case, catch, continue, debugger, default, delete, do, else, false, finally, for, function, if, in, instanceof, new, null, return, switch, this, throw, true, try, typeof, var, void, while, with, abstract, boolean, byte, char, class, const, double, enum, export, extends, final, float, goto, implements, import, int, interface, long, native, package, private, protected, public, short, static, super, synchronized, throws, transient, volatile, arguments, let, yield
2、模板禁止读写全局变量,除非给模板定义辅助方法。例如:
- -template.helper('Math', Math)
-
-
-- -artTemplate编译后的模板将运行在沙箱内,模板语句无法读写外部对象。
- -在使用原生语法的引擎中,模板中若引用外部对象,随着项目复杂度增加,那时候谁都不能确定模板中的变量到底是数据还是全局对象,这种复杂的依赖关系将为会项目带来巨大的维护成本。
RequireJS
加载编译后的模板的示例include
内部实现可能产生上下文不一致的问题。感谢 @warmhugcompile.cmd
图标上进行单独编译include
支持支持,可以支持不同目录之间模板嵌套<%==value%>
,也可以关闭默认的转义功能template.isEscape = false
。Released under the MIT, BSD, and GPL Licenses
- -© cdc.tencent.com
- - \ No newline at end of file diff --git a/package.json b/package.json index b5e0b9cc..49b5295a 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,11 @@ { - "name" : "artTemplate", + "name" : "art-template", "description" : "JavaScript Template Engine", "homepage" : "http://aui.github.com/artTemplate/", "keywords" : ["util", "functional", "template"], - "author" : "tangbin <1987.tangbin@gmail.com>", + "author" : "tangbin<%= users[i].name %>:<%= users[i].blog %>
' + - '<% } %>'; - - var render = template(tpl); - - var html = render({ - users: [ - { - name: '糖饼', - blog: 'http://www.planeart.cn/' - }, - { - name: 'wǒ_is神仙', - blog: 'http://MrZhang.me/' - } - ] - }); - - document.body.innerHTML = html; -}); -``` - -```javascript -seajs.use(['template', 'template-extensions'], function(template) { - var tpl = '{ each users as user }' + - '{ user.name }:{ user.weibo }
' + - '{ /each }'; - - var render = template(tpl); - - var html = render({ - users: [ - { - name: '糖饼', - weibo: 'http://weibo.com/planeart' - }, - { - name: 'wǒ_is神仙', - weibo: 'http://weibo.com/jsw0528' - } - ] - }); - - document.body.innerHTML = html; -}); -``` diff --git a/seajs/transport-extensions.js b/seajs/transport-extensions.js deleted file mode 100644 index c65d1a54..00000000 --- a/seajs/transport-extensions.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @package https://raw.github.com/aui/artTemplate/master/package.json - * - * @filename template-extensions - * @src https://raw.github.com/aui/artTemplate/master/extensions/template-syntax.js - * @min https://raw.github.com/aui/artTemplate/master/extensions/template-syntax.min.js - */ - -define('#{{id}}', ['./template'], function(require, exports, module) { -var template = require('./template'); - -/*{{code}}*/ - -return template; -}); diff --git a/seajs/transport.js b/seajs/transport.js deleted file mode 100644 index e536ff09..00000000 --- a/seajs/transport.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @package https://raw.github.com/aui/artTemplate/master/package.json - * - * @filename template - * @src https://raw.github.com/aui/artTemplate/master/template.js - * @min https://raw.github.com/aui/artTemplate/master/template.min.js - */ - -define('#{{id}}', [], function(require, exports, module) { - -/*{{code}}*/ - -}); diff --git a/src/template-syntax.js b/src/template-syntax.js new file mode 100644 index 00000000..bb50c1de --- /dev/null +++ b/src/template-syntax.js @@ -0,0 +1,92 @@ +/*! + * artTemplate - Syntax Extensions + * https://github.com/aui/artTemplate + * Released under the MIT, BSD, and GPL Licenses + */ + +(function (exports) { + + exports.openTag = '{{'; + exports.closeTag = '}}'; + + + exports.parser = function (code) { + code = code.replace(/^\s/, ''); + + var split = code.split(' '); + var key = split.shift(); + var args = split.join(' '); + + switch (key) { + + case 'if': + + code = 'if(' + args + '){'; + break; + + case 'else': + + if (split.shift() === 'if') { + split = ' if(' + split.join(' ') + ')'; + } else { + split = ''; + } + + code = '}else' + split + '{'; + break; + + case '/if': + + code = '}'; + break; + + case 'each': + + var object = split[0] || '$data'; + var as = split[1] || 'as'; + var value = split[2] || '$value'; + var index = split[3] || '$index'; + + var param = value + ',' + index; + + if (as !== 'as') { + object = '[]'; + } + + code = '$each(' + object + ',function(' + param + '){'; + break; + + case '/each': + + code = '});'; + break; + + case 'echo': + + code = 'print(' + args + ');' + break; + + case 'include': + + code = 'include(' + split.join(',') + ');'; + break; + + default: + + if (exports.helpers.hasOwnProperty(key)) { + + code = '==' + key + '(' + split.join(',') + ');'; + + } else { + + code = code.replace(/[\s;]*$/, ''); + code = '=' + code; + } + + break; + } + + + return code; + }; +})(template); diff --git a/template.js b/src/template.js similarity index 54% rename from template.js rename to src/template.js index 3b5817f7..84e362e2 100644 --- a/template.js +++ b/src/template.js @@ -1,582 +1,582 @@ -/*! - * artTemplate - Template Engine - * https://github.com/aui/artTemplate - * Released under the MIT, BSD, and GPL Licenses - */ - - -/** - * 模板引擎路由函数 - * 若第二个参数类型为 Object 则执行 render 方法, 否则 compile 方法 - * @name template - * @param {String} 模板ID (可选) - * @param {Object, String} 数据或者模板字符串 - * @return {String, Function} 渲染好的HTML字符串或者渲染方法 - */ -var template = function (id, content) { - return template[ - typeof content === 'object' ? 'render' : 'compile' - ].apply(template, arguments); -}; - - - - -(function (exports, global) { - - -'use strict'; -exports.version = '2.0.1'; -exports.openTag = '<%'; // 设置逻辑语法开始标签 -exports.closeTag = '%>'; // 设置逻辑语法结束标签 -exports.isEscape = true; // HTML字符编码输出开关 -exports.isCompress = false; // 剔除渲染后HTML多余的空白开关 -exports.parser = null; // 自定义语法插件接口 - - - -/** - * 渲染模板 - * @name template.render - * @param {String} 模板ID - * @param {Object} 数据 - * @return {String} 渲染好的HTML字符串 - */ -exports.render = function (id, data) { - - var cache = _getCache(id); - - if (cache === undefined) { - - return _debug({ - id: id, - name: 'Render Error', - message: 'No Template' - }); - - } - - return cache(data); -}; - - - -/** - * 编译模板 - * 2012-6-6: - * define 方法名改为 compile, - * 与 Node Express 保持一致, - * 感谢 TooBug 提供帮助! - * @name template.compile - * @param {String} 模板ID (可选) - * @param {String} 模板字符串 - * @return {Function} 渲染方法 - */ -exports.compile = function (id, source) { - - var params = arguments; - var isDebug = params[2]; - var anonymous = 'anonymous'; - - if (typeof source !== 'string') { - isDebug = params[1]; - source = params[0]; - id = anonymous; - } - - - try { - - var Render = _compile(source, isDebug); - - } catch (e) { - - e.id = id || source; - e.name = 'Syntax Error'; - - return _debug(e); - - } - - - function render (data) { - - try { - - return new Render(data) + ''; - - } catch (e) { - - if (!isDebug) { - return exports.compile(id, source, true)(data); - } - - e.id = id || source; - e.name = 'Render Error'; - e.source = source; - - return _debug(e); - - } - - } - - - render.prototype = Render.prototype; - render.toString = function () { - return Render.toString(); - }; - - - if (id !== anonymous) { - _cache[id] = render; - } - - - return render; - -}; - - - - -/** - * 添加模板辅助方法 - * @name template.helper - * @param {String} 名称 - * @param {Function} 方法 - */ -exports.helper = function (name, helper) { - exports.prototype[name] = helper; -}; - - - - -/** - * 模板错误事件 - * @name template.onerror - * @event - */ -exports.onerror = function (e) { - var content = '[template]:\n' - + e.id - + '\n\n[name]:\n' - + e.name; - - if (e.message) { - content += '\n\n[message]:\n' - + e.message; - } - - if (e.line) { - content += '\n\n[line]:\n' - + e.line; - content += '\n\n[source]:\n' - + e.source.split(/\n/)[e.line - 1].replace(/^[\s\t]+/, ''); - } - - if (e.temp) { - content += '\n\n[temp]:\n' - + e.temp; - } - - if (global.console) { - console.error(content); - } -}; - - - -// 编译好的函数缓存 -var _cache = {}; - - - -// 获取模板缓存 -var _getCache = function (id) { - - var cache = _cache[id]; - - if (cache === undefined && 'document' in global) { - var elem = document.getElementById(id); - - if (elem) { - var source = elem.value || elem.innerHTML; - return exports.compile(id, source.replace(/^\s*|\s*$/g, '')); - } - - } else if (_cache.hasOwnProperty(id)) { - - return cache; - } -}; - - - -// 模板调试器 -var _debug = function (e) { - - exports.onerror(e); - - function error () { - return error + ''; - } - - error.toString = function () { - return '{Template Error}'; - }; - - return error; -}; - - - -// 模板编译器 -var _compile = (function () { - - - // 辅助方法集合 - exports.prototype = { - $render: exports.render, - $escape: function (content) { - - return typeof content === 'string' - ? content.replace(/&(?![\w#]+;)|[<>"']/g, function (s) { - return { - "<": "<", - ">": ">", - '"': """, - "'": "'", - "&": "&" - }[s]; - }) - : content; - }, - $string: function (value) { - - if (typeof value === 'string' || typeof value === 'number') { - - return value; - - } else if (typeof value === 'function') { - - return value(); - - } else { - - return ''; - - } - - } - }; - - - var arrayforEach = Array.prototype.forEach || function (block, thisObject) { - var len = this.length >>> 0; - - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - - }; - - - // 数组迭代 - var forEach = function (array, callback) { - arrayforEach.call(array, callback); - }; - - - // 静态分析模板变量 - var KEYWORDS = - // 关键字 - 'break,case,catch,continue,debugger,default,delete,do,else,false' - + ',finally,for,function,if,in,instanceof,new,null,return,switch,this' - + ',throw,true,try,typeof,var,void,while,with' - - // 保留字 - + ',abstract,boolean,byte,char,class,const,double,enum,export,extends' - + ',final,float,goto,implements,import,int,interface,long,native' - + ',package,private,protected,public,short,static,super,synchronized' - + ',throws,transient,volatile' - - // ECMA 5 - use strict - + ',arguments,let,yield' - - + ',undefined'; - var REMOVE_RE = /\/\*(?:.|\n)*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|'[^']*'|"[^"]*"|[\s\t\n]*\.[\s\t\n]*[$\w\.]+/g; - var SPLIT_RE = /[^\w$]+/g; - var KEYWORDS_RE = new RegExp(["\\b" + KEYWORDS.replace(/,/g, '\\b|\\b') + "\\b"].join('|'), 'g'); - var NUMBER_RE = /\b\d[^,]*/g; - var BOUNDARY_RE = /^,+|,+$/g; - var getVariable = function (code) { - - code = code - .replace(REMOVE_RE, '') - .replace(SPLIT_RE, ',') - .replace(KEYWORDS_RE, '') - .replace(NUMBER_RE, '') - .replace(BOUNDARY_RE, ''); - - code = code ? code.split(/,+/) : []; - - return code; - }; - - - return function (source, isDebug) { - - var openTag = exports.openTag; - var closeTag = exports.closeTag; - var parser = exports.parser; - - - var code = source; - var tempCode = ''; - var line = 1; - var uniq = {$data:true,$helpers:true,$out:true,$line:true}; - var helpers = exports.prototype; - var prototype = {}; - - - var variables = "var $helpers=this," - + (isDebug ? "$line=0," : ""); - - var isNewEngine = ''.trim;// '__proto__' in {} - var replaces = isNewEngine - ? ["$out='';", "$out+=", ";", "$out"] - : ["$out=[];", "$out.push(", ");", "$out.join('')"]; - - var concat = isNewEngine - ? "if(content!==undefined){$out+=content;return content}" - : "$out.push(content);"; - - var print = "function(content){" + concat + "}"; - - var include = "function(id,data){" - + "if(data===undefined){data=$data}" - + "var content=$helpers.$render(id,data);" - + concat - + "}"; - - - // html与逻辑语法分离 - forEach(code.split(openTag), function (code, i) { - code = code.split(closeTag); - - var $0 = code[0]; - var $1 = code[1]; - - // code: [html] - if (code.length === 1) { - - tempCode += html($0); - - // code: [logic, html] - } else { - - tempCode += logic($0); - - if ($1) { - tempCode += html($1); - } - } - - - }); - - - - code = tempCode; - - - // 调试语句 - if (isDebug) { - code = 'try{' + code + '}catch(e){' - + 'e.line=$line;' - + 'throw e' - + '}'; - } - - - code = "'use strict';" - + variables + replaces[0] + code - + 'return new String(' + replaces[3] + ')'; - - - try { - - var Render = new Function('$data', code); - Render.prototype = prototype; - - return Render; - - } catch (e) { - e.temp = 'function anonymous($data) {' + code + '}'; - throw e; - } - - - - - // 处理 HTML 语句 - function html (code) { - - // 记录行号 - line += code.split(/\n/).length - 1; - - if (exports.isCompress) { - code = code.replace(/[\n\r\t\s]+/g, ' '); - } - - code = code - // 单引号与反斜杠转义(因为编译后的函数默认使用单引号,因此双引号无需转义) - .replace(/('|\\)/g, '\\$1') - // 换行符转义(windows + linux) - .replace(/\r/g, '\\r') - .replace(/\n/g, '\\n'); - - code = replaces[1] + "'" + code + "'" + replaces[2]; - - return code + '\n'; - } - - - // 处理逻辑语句 - function logic (code) { - - var thisLine = line; - - if (parser) { - - // 语法转换插件钩子 - code = parser(code); - - } else if (isDebug) { - - // 记录行号 - code = code.replace(/\n/g, function () { - line ++; - return '$line=' + line + ';'; - }); - - } - - - // 输出语句. 转义: <%=value%> 不转义:<%==value%> - if (code.indexOf('=') === 0) { - - var isEscape = code.indexOf('==') !== 0; - - code = code.replace(/^=*|[\s;]*$/g, ''); - - if (isEscape && exports.isEscape) { - - // 转义处理,但排除辅助方法 - var name = code.replace(/\s*\([^\)]+\)/, ''); - if ( - !helpers.hasOwnProperty(name) - && !/^(include|print)$/.test(name) - ) { - code = '$escape($string(' + code + '))'; - } - - } else { - code = '$string(' + code + ')'; - } - - - code = replaces[1] + code + replaces[2]; - - } - - if (isDebug) { - code = '$line=' + thisLine + ';' + code; - } - - getKey(code); - - return code + '\n'; - } - - - // 提取模板中的变量名 - function getKey (code) { - - code = getVariable(code); - - // 分词 - forEach(code, function (name) { - - // 除重 - if (!uniq.hasOwnProperty(name)) { - setValue(name); - uniq[name] = true; - } - - }); - - } - - - // 声明模板变量 - // 赋值优先级: - // 内置特权方法(include, print) > 私有模板辅助方法 > 数据 > 公用模板辅助方法 - function setValue (name) { - - var value; - - if (name === 'print') { - - value = print; - - } else if (name === 'include') { - - prototype['$render'] = helpers['$render']; - value = include; - - } else { - - value = '$data.' + name; - - if (helpers.hasOwnProperty(name)) { - - prototype[name] = helpers[name]; - - if (name.indexOf('$') === 0) { - value = '$helpers.' + name; - } else { - value = value - + '===undefined?$helpers.' + name + ':' + value; - } - } - - - } - - variables += name + '=' + value + ','; - } - - - }; -})(); - - - - -})(template, this); - - -// RequireJS || SeaJS -if (typeof define === 'function') { - define(function(require, exports, module) { - module.exports = template; - }); -// NodeJS -} else if (typeof exports !== 'undefined') { - module.exports = template; -} +/*! + * artTemplate - Template Engine + * https://github.com/aui/artTemplate + * Released under the MIT, BSD, and GPL Licenses + */ + +(function (global) { + +'use strict'; + +/** + * 模板引擎 + * 若第二个参数类型为 String 则执行 compile 方法, 否则执行 render 方法 + * @name template + * @param {String} 模板ID + * @param {Object, String} 数据或者模板字符串 + * @return {String, Function} 渲染好的HTML字符串或者渲染方法 + */ +var template = function (id, content) { + return template[ + typeof content === 'string' ? 'compile' : 'render' + ].apply(template, arguments); +}; + + +template.version = '2.0.3'; +template.openTag = '<%'; // 设置逻辑语法开始标签 +template.closeTag = '%>'; // 设置逻辑语法结束标签 +template.isEscape = true; // HTML字符编码输出开关 +template.isCompress = false; // 剔除渲染后HTML多余的空白开关 +template.parser = null; // 自定义语法插件接口 + + + +/** + * 渲染模板 + * @name template.render + * @param {String} 模板ID + * @param {Object} 数据 + * @return {String} 渲染好的HTML字符串 + */ +template.render = function (id, data) { + + var cache = template.get(id) || _debug({ + id: id, + name: 'Render Error', + message: 'No Template' + }); + + return cache(data); +}; + + + +/** + * 编译模板 + * 2012-6-6 @TooBug: define 方法名改为 compile,与 Node Express 保持一致 + * @name template.compile + * @param {String} 模板ID (可选,用作缓存索引) + * @param {String} 模板字符串 + * @return {Function} 渲染方法 + */ +template.compile = function (id, source) { + + var params = arguments; + var isDebug = params[2]; + var anonymous = 'anonymous'; + + if (typeof source !== 'string') { + isDebug = params[1]; + source = params[0]; + id = anonymous; + } + + + try { + + var Render = _compile(id, source, isDebug); + + } catch (e) { + + e.id = id || source; + e.name = 'Syntax Error'; + + return _debug(e); + + } + + + function render (data) { + + try { + + return new Render(data, id) + ''; + + } catch (e) { + + if (!isDebug) { + return template.compile(id, source, true)(data); + } + + return _debug(e)(); + + } + + } + + + render.prototype = Render.prototype; + render.toString = function () { + return Render.toString(); + }; + + + if (id !== anonymous) { + _cache[id] = render; + } + + + return render; + +}; + + + +var _cache = template.cache = {}; + + + + +// 辅助方法集合 +var _helpers = template.helpers = (function () { + + var toString = function (value, type) { + + if (typeof value !== 'string') { + + type = typeof value; + if (type === 'number') { + value += ''; + } else if (type === 'function') { + value = toString(value.call(value)); + } else { + value = ''; + } + } + + return value; + + }; + + + var escapeMap = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "&": "&" + }; + + + var escapeHTML = function (content) { + return toString(content) + .replace(/&(?![\w#]+;)|[<>"']/g, function (s) { + return escapeMap[s]; + }); + }; + + + var isArray = Array.isArray || function (obj) { + return ({}).toString.call(obj) === '[object Array]'; + }; + + + var each = function (data, callback) { + if (isArray(data)) { + for (var i = 0, len = data.length; i < len; i++) { + callback.call(data, data[i], i, data); + } + } else { + for (i in data) { + callback.call(data, data[i], i); + } + } + }; + + + return { + + $include: template.render, + + $string: toString, + + $escape: escapeHTML, + + $each: each + + }; +})(); + + + + +/** + * 添加模板辅助方法 + * @name template.helper + * @param {String} 名称 + * @param {Function} 方法 + */ +template.helper = function (name, helper) { + _helpers[name] = helper; +}; + + + + +/** + * 模板错误事件 + * @name template.onerror + * @event + */ +template.onerror = function (e) { + var message = 'Template Error\n\n'; + for (var name in e) { + message += '<' + name + '>\n' + e[name] + '\n\n'; + } + + if (global.console) { + console.error(message); + } +}; + + + + + + + +// 获取模板缓存 +template.get = function (id) { + + var cache; + + if (_cache.hasOwnProperty(id)) { + cache = _cache[id]; + } else if ('document' in global) { + var elem = document.getElementById(id); + + if (elem) { + var source = elem.value || elem.innerHTML; + cache = template.compile(id, source.replace(/^\s*|\s*$/g, '')); + } + } + + return cache; +}; + + + +// 模板调试器 +var _debug = function (e) { + + template.onerror(e); + + return function () { + return '{Template Error}'; + }; +}; + + + +// 模板编译器 +var _compile = (function () { + + + // 数组迭代 + var forEach = _helpers.$each; + + + // 静态分析模板变量 + var KEYWORDS = + // 关键字 + 'break,case,catch,continue,debugger,default,delete,do,else,false' + + ',finally,for,function,if,in,instanceof,new,null,return,switch,this' + + ',throw,true,try,typeof,var,void,while,with' + + // 保留字 + + ',abstract,boolean,byte,char,class,const,double,enum,export,extends' + + ',final,float,goto,implements,import,int,interface,long,native' + + ',package,private,protected,public,short,static,super,synchronized' + + ',throws,transient,volatile' + + // ECMA 5 - use strict + + ',arguments,let,yield' + + + ',undefined'; + + var REMOVE_RE = /\/\*[\w\W]*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|"(?:[^"\\]|\\[\w\W])*"|'(?:[^'\\]|\\[\w\W])*'|[\s\t\n]*\.[\s\t\n]*[$\w\.]+/g; + var SPLIT_RE = /[^\w$]+/g; + var KEYWORDS_RE = new RegExp(["\\b" + KEYWORDS.replace(/,/g, '\\b|\\b') + "\\b"].join('|'), 'g'); + var NUMBER_RE = /^\d[^,]*|,\d[^,]*/g; + var BOUNDARY_RE = /^,+|,+$/g; + + var getVariable = function (code) { + return code + .replace(REMOVE_RE, '') + .replace(SPLIT_RE, ',') + .replace(KEYWORDS_RE, '') + .replace(NUMBER_RE, '') + .replace(BOUNDARY_RE, '') + .split(/^$|,+/); + }; + + + return function (id, source, isDebug) { + + var openTag = template.openTag; + var closeTag = template.closeTag; + var parser = template.parser; + + + var code = source; + var tempCode = ''; + var line = 1; + var uniq = {$data:1,$id:1,$helpers:1,$out:1,$line:1}; + var prototype = {}; + + + var variables = "var $helpers=this," + + (isDebug ? "$line=0," : ""); + + var isNewEngine = ''.trim;// '__proto__' in {} + var replaces = isNewEngine + ? ["$out='';", "$out+=", ";", "$out"] + : ["$out=[];", "$out.push(", ");", "$out.join('')"]; + + var concat = isNewEngine + ? "if(content!==undefined){$out+=content;return content;}" + : "$out.push(content);"; + + var print = "function(content){" + concat + "}"; + + var include = "function(id,data){" + + "data=data||$data;" + + "var content=$helpers.$include(id,data,$id);" + + concat + + "}"; + + + // html与逻辑语法分离 + forEach(code.split(openTag), function (code) { + code = code.split(closeTag); + + var $0 = code[0]; + var $1 = code[1]; + + // code: [html] + if (code.length === 1) { + + tempCode += html($0); + + // code: [logic, html] + } else { + + tempCode += logic($0); + + if ($1) { + tempCode += html($1); + } + } + + + }); + + + + code = tempCode; + + + // 调试语句 + if (isDebug) { + code = "try{" + code + "}catch(e){" + + "throw {" + + "id:$id," + + "name:'Render Error'," + + "message:e.message," + + "line:$line," + + "source:" + stringify(source) + + ".split(/\\n/)[$line-1].replace(/^[\\s\\t]+/,'')" + + "};" + + "}"; + } + + + code = variables + replaces[0] + code + + "return new String(" + replaces[3] + ");"; + + + try { + + var Render = new Function("$data", "$id", code); + Render.prototype = prototype; + + return Render; + + } catch (e) { + e.temp = "function anonymous($data,$id) {" + code + "}"; + throw e; + } + + + + + // 处理 HTML 语句 + function html (code) { + + // 记录行号 + line += code.split(/\n/).length - 1; + + // 压缩多余空白与注释 + if (template.isCompress) { + code = code + .replace(/[\n\r\t\s]+/g, ' ') + .replace(//g, ''); + } + + if (code) { + code = replaces[1] + stringify(code) + replaces[2] + "\n"; + } + + return code; + } + + + // 处理逻辑语句 + function logic (code) { + + var thisLine = line; + + if (parser) { + + // 语法转换插件钩子 + code = parser(code); + + } else if (isDebug) { + + // 记录行号 + code = code.replace(/\n/g, function () { + line ++; + return "$line=" + line + ";"; + }); + + } + + + // 输出语句. 转义: <%=value%> 不转义:<%==value%> + if (code.indexOf('=') === 0) { + + var isEscape = code.indexOf('==') !== 0; + + code = code.replace(/^=*|[\s;]*$/g, ''); + + if (isEscape && template.isEscape) { + + // 转义处理,但排除辅助方法 + var name = code.replace(/\s*\([^\)]+\)/, ''); + if ( + !_helpers.hasOwnProperty(name) + && !/^(include|print)$/.test(name) + ) { + code = "$escape(" + code + ")"; + } + + } else { + code = "$string(" + code + ")"; + } + + + code = replaces[1] + code + replaces[2]; + + } + + if (isDebug) { + code = "$line=" + thisLine + ";" + code; + } + + getKey(code); + + return code + "\n"; + } + + + // 提取模板中的变量名 + function getKey (code) { + + code = getVariable(code); + + // 分词 + forEach(code, function (name) { + + // 除重 + if (!uniq.hasOwnProperty(name)) { + setValue(name); + uniq[name] = true; + } + + }); + + } + + + // 声明模板变量 + // 赋值优先级: + // 内置特权方法(include, print) > 私有模板辅助方法 > 数据 > 公用模板辅助方法 + function setValue (name) { + + var value; + + if (name === 'print') { + + value = print; + + } else if (name === 'include') { + + prototype["$include"] = _helpers['$include']; + value = include; + + } else { + + value = "$data." + name; + + if (_helpers.hasOwnProperty(name)) { + + prototype[name] = _helpers[name]; + + if (name.indexOf('$') === 0) { + value = "$helpers." + name; + } else { + value = value + + "===undefined?$helpers." + name + ":" + value; + } + } + + + } + + variables += name + "=" + value + ","; + } + + + // 字符串转义 + function stringify (code) { + return "'" + code + // 单引号与反斜杠转义 + .replace(/('|\\)/g, '\\$1') + // 换行符转义(windows + linux) + .replace(/\r/g, '\\r') + .replace(/\n/g, '\\n') + "'"; + } + + + }; +})(); + + +// RequireJS && SeaJS +if (typeof define === 'function') { + define(function() { + return template; + }); + +// NodeJS +} else if (typeof exports !== 'undefined') { + module.exports = template; +} + +global.template = template; + + +})(this); + + diff --git a/template.min.js b/template.min.js deleted file mode 100644 index f1793abf..00000000 --- a/template.min.js +++ /dev/null @@ -1 +0,0 @@ -/*!artTemplate - Template Engine*/var template=function(a,b){return template["object"==typeof b?"render":"compile"].apply(template,arguments)};(function(a,b){"use strict";a.version="2.0.1",a.openTag="<%",a.closeTag="%>",a.isEscape=!0,a.isCompress=!1,a.parser=null,a.render=function(a,b){var c=d(a);return void 0===c?e({id:a,name:"Render Error",message:"No Template"}):c(b)},a.compile=function(b,d){function l(c){try{return new j(c)+""}catch(f){return h?(f.id=b||d,f.name="Render Error",f.source=d,e(f)):a.compile(b,d,!0)(c)}}var g=arguments,h=g[2],i="anonymous";"string"!=typeof d&&(h=g[1],d=g[0],b=i);try{var j=f(d,h)}catch(k){return k.id=b||d,k.name="Syntax Error",e(k)}return l.prototype=j.prototype,l.toString=function(){return""+j},b!==i&&(c[b]=l),l},a.helper=function(b,c){a.prototype[b]=c},a.onerror=function(a){var c="[template]:\n"+a.id+"\n\n[name]:\n"+a.name;a.message&&(c+="\n\n[message]:\n"+a.message),a.line&&(c+="\n\n[line]:\n"+a.line,c+="\n\n[source]:\n"+a.source.split(/\n/)[a.line-1].replace(/^[\s\t]+/,"")),a.temp&&(c+="\n\n[temp]:\n"+a.temp),b.console&&console.error(c)};var c={},d=function(d){var e=c[d];if(void 0===e&&"document"in b){var f=document.getElementById(d);if(f){var g=f.value||f.innerHTML;return a.compile(d,g.replace(/^\s*|\s*$/g,""))}}else if(c.hasOwnProperty(d))return e},e=function(b){function c(){return c+""}return a.onerror(b),c.toString=function(){return"{Template Error}"},c},f=function(){a.prototype={$render:a.render,$escape:function(a){return"string"==typeof a?a.replace(/&(?![\w#]+;)|[<>"']/g,function(a){return{"<":"<",">":">",'"':""","'":"'","&":"&"}[a]}):a},$string:function(a){return"string"==typeof a||"number"==typeof a?a:"function"==typeof a?a():""}};var b=Array.prototype.forEach||function(a,b){for(var c=this.length>>>0,d=0;c>d;d++)d in this&&a.call(b,this[d],d,this)},c=function(a,c){b.call(a,c)},d="break,case,catch,continue,debugger,default,delete,do,else,false,finally,for,function,if,in,instanceof,new,null,return,switch,this,throw,true,try,typeof,var,void,while,with,abstract,boolean,byte,char,class,const,double,enum,export,extends,final,float,goto,implements,import,int,interface,long,native,package,private,protected,public,short,static,super,synchronized,throws,transient,volatile,arguments,let,yield,undefined",e=/\/\*(?:.|\n)*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|'[^']*'|"[^"]*"|[\s\t\n]*\.[\s\t\n]*[$\w\.]+/g,f=/[^\w$]+/g,g=RegExp(["\\b"+d.replace(/,/g,"\\b|\\b")+"\\b"].join("|"),"g"),h=/\b\d[^,]*/g,i=/^,+|,+$/g,j=function(a){return a=a.replace(e,"").replace(f,",").replace(g,"").replace(h,"").replace(i,""),a=a?a.split(/,+/):[]};return function(b,d){function w(b){return k+=b.split(/\n/).length-1,a.isCompress&&(b=b.replace(/[\n\r\t\s]+/g," ")),b=b.replace(/('|\\)/g,"\\$1").replace(/\r/g,"\\r").replace(/\n/g,"\\n"),b=q[1]+"'"+b+"'"+q[2],b+"\n"}function x(b){var c=k;if(g?b=g(b):d&&(b=b.replace(/\n/g,function(){return k++,"$line="+k+";"})),0===b.indexOf("=")){var e=0!==b.indexOf("==");if(b=b.replace(/^=*|[\s;]*$/g,""),e&&a.isEscape){var f=b.replace(/\s*\([^\)]+\)/,"");m.hasOwnProperty(f)||/^(include|print)$/.test(f)||(b="$escape($string("+b+"))")}else b="$string("+b+")";b=q[1]+b+q[2]}return d&&(b="$line="+c+";"+b),y(b),b+"\n"}function y(a){a=j(a),c(a,function(a){l.hasOwnProperty(a)||(z(a),l[a]=!0)})}function z(a){var b;"print"===a?b=s:"include"===a?(n.$render=m.$render,b=t):(b="$data."+a,m.hasOwnProperty(a)&&(n[a]=m[a],b=0===a.indexOf("$")?"$helpers."+a:b+"===undefined?$helpers."+a+":"+b)),o+=a+"="+b+","}var e=a.openTag,f=a.closeTag,g=a.parser,h=b,i="",k=1,l={$data:!0,$helpers:!0,$out:!0,$line:!0},m=a.prototype,n={},o="var $helpers=this,"+(d?"$line=0,":""),p="".trim,q=p?["$out='';","$out+=",";","$out"]:["$out=[];","$out.push(",");","$out.join('')"],r=p?"if(content!==undefined){$out+=content;return content}":"$out.push(content);",s="function(content){"+r+"}",t="function(id,data){if(data===undefined){data=$data}var content=$helpers.$render(id,data);"+r+"}";c(h.split(e),function(a){a=a.split(f);var c=a[0],d=a[1];1===a.length?i+=w(c):(i+=x(c),d&&(i+=w(d)))}),h=i,d&&(h="try{"+h+"}catch(e){"+"e.line=$line;"+"throw e"+"}"),h="'use strict';"+o+q[0]+h+"return new String("+q[3]+")";try{var u=Function("$data",h);return u.prototype=n,u}catch(v){throw v.temp="function anonymous($data) {"+h+"}",v}}}()})(template,this),"function"==typeof define?define(function(a,b,c){c.exports=template}):"undefined"!=typeof exports&&(module.exports=template); \ No newline at end of file diff --git a/test/js/baidu.template.js b/test/js/baidu.template.js deleted file mode 100644 index b248e736..00000000 --- a/test/js/baidu.template.js +++ /dev/null @@ -1,205 +0,0 @@ -/** - * baiduTemplate简单好用的Javascript模板引擎 1.0.4 版本 - * 开源协议:BSD License - * 浏览器环境占用命名空间 baidu.template ,nodejs环境直接安装 npm install baiduTemplate - * @param str{String|HtmlElement} dom结点ID,dom,或者模板string - * @param data{Object} 需要渲染的json对象,可以为空。当data为{}时,仍然返回html。 - * @return 如果无data,直接返回编译后的函数;如果有data,返回html。 - * @author wangxiao - * @email 1988wangxiao@gmail.com -*/ - -;(function(){ - - //取得浏览器环境的baidu命名空间,非浏览器环境符合commonjs规范exports出去 - var baidu = typeof module === 'undefined' ? (this.baidu = this.baidu || {}) : module.exports; - - //模板函数 - var bt = function(str, data){ - - //检查是否有该id的元素存在,如果有元素则获取元素的innerHTML/value,否则认为字符串为模板 - var fn = (function(){ - - //判断如果没有document,则为非浏览器环境 - if(!this.document){ - return compile(str); - }; - - //HTML5规定ID可以由任何不包含空格字符的字符串组成 - var element = document.getElementById(str); - if (element) { - - //取到对应id的dom,缓存其编译后的HTML模板函数 - if (cache[str]) { - return cache[str]; - }; - - //textarea或input则取value,其它情况取innerHTML - var html = /^(textarea|input)$/i.test(element.nodeName) ? element.value : element.innerHTML; - return compile(html); - - }else{ - - //是模板字符串,则生成一个函数 - //如果直接传入字符串作为模板,则可能变化过多,因此不考虑缓存 - return compile(str); - }; - - })(); - - //有数据则返回HTML字符串,没有数据则返回函数 支持data={}的情况 - return isObject(data) ? fn( data ) : fn; - }; - - //取得命名空间 baidu.template - baidu.template = bt; - - //缓存 将对应id模板生成的函数缓存下来。 - var 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,'''); - }; - - //转义影响正则的字符 - 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(/\n/g,'\\n') - .replace(/\r/g,'\\r'); - }; - - //将字符串拼接生成函数,即编译过程(compile) - var compile = function(str){ - var funBody = "var _template_fun_array=[];\n(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('"+analysisStr(str)+"');\n})(_template_object);\nreturn _template_fun_array.join('');\n"; - return new Function("_template_object",funBody); - }; - - //判断是否是Object类型 - var isObject = function (source) { - return 'function' === typeof source || !!(source && 'object' === typeof source); - }; - - //解析模板字符串 - var 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 ; - }) - - //对变量后面的分号做容错(包括转义模式 如<%:h=value%>) <%=value;%> 排除掉函数的情况 <%fun1();%> 排除定义变量情况 <%var val='test';%> - .replace(new RegExp("("+_left+":?[hvu]?[\\s]*?=[\\s]*?[^;|"+_right+"]*?);[\\s]*?"+_right,"g"),"$1"+_right_) - - //定义变量,如果没有分号,需要容错 <%var val='test'%> - .replace(new RegExp("("+_left+"[\\s]*?var[\\s]*?.*?[\\s]*?[^;])[\\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){%>- + \ No newline at end of file diff --git a/tools/README.md b/tools/README.md index 66762dcd..f04f06bc 100644 --- a/tools/README.md +++ b/tools/README.md @@ -1,8 +1,9 @@ -# 抽取工具 -============ +# 工具 + +## 抽取工具 [combine.html](http://aui.github.com/artTemplate/tools/combine.html) 可以把页面中内嵌的模板抽取出来,以便部署在外部js文件中。 - -============ -© cdc.tencent.com +## 预编译工具 + +项目主页:
-
- - + +