| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739 | /*! * artTemplate - Template Engine * https://github.com/aui/artTemplate * Released under the MIT, BSD, and GPL Licenses */ !(function () {/** * 模板引擎 * @name    template * @param   {String}            模板名 * @param   {Object, String}    数据。如果为字符串则编译并缓存编译结果 * @return  {String, Function}  渲染好的HTML字符串或者渲染方法 */var template = function (filename, content) {    return typeof content === 'string'    ?   compile(content, {            filename: filename        })    :   renderFile(filename, content);};template.version = '3.0.0';/** * 设置全局配置 * @name    template.config * @param   {String}    名称 * @param   {Any}       值 */template.config = function (name, value) {    defaults[name] = value;};var defaults = template.defaults = {    openTag: '<%',    // 逻辑语法开始标签    closeTag: '%>',   // 逻辑语法结束标签    escape: true,     // 是否编码输出变量的 HTML 字符    cache: true,      // 是否开启缓存(依赖 options 的 filename 字段)    compress: false,  // 是否压缩输出    parser: null      // 自定义语法格式器 @see: template-syntax.js};var cacheStore = template.cache = {};/** * 渲染模板 * @name    template.render * @param   {String}    模板 * @param   {Object}    数据 * @return  {String}    渲染好的字符串 */template.render = function (source, options) {    return compile(source)(options);};/** * 渲染模板(根据模板名) * @name    template.render * @param   {String}    模板名 * @param   {Object}    数据 * @return  {String}    渲染好的字符串 */var renderFile = template.renderFile = function (filename, data) {    var fn = template.get(filename) || showDebugInfo({        filename: filename,        name: 'Render Error',        message: 'Template not found'    });    return data ? fn(data) : fn;};/** * 获取编译缓存(可由外部重写此方法) * @param   {String}    模板名 * @param   {Function}  编译好的函数 */template.get = function (filename) {    var cache;        if (cacheStore[filename]) {        // 使用内存缓存        cache = cacheStore[filename];    } else if (typeof document === 'object') {        // 加载模板并编译        var elem = document.getElementById(filename);                if (elem) {            var source = (elem.value || elem.innerHTML)            .replace(/^\s*|\s*$/g, '');            cache = compile(source, {                filename: filename            });        }    }    return cache;};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 escapeFn = function (s) {    return escapeMap[s];};var escapeHTML = function (content) {    return toString(content)    .replace(/&(?![\w#]+;)|[<>"']/g, escapeFn);};var isArray = Array.isArray || function (obj) {    return ({}).toString.call(obj) === '[object Array]';};var each = function (data, callback) {    var i, len;            if (isArray(data)) {        for (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);        }    }};var utils = template.utils = {	$helpers: {},    $include: renderFile,    $string: toString,    $escape: escapeHTML,    $each: each    };/** * 添加模板辅助方法 * @name    template.helper * @param   {String}    名称 * @param   {Function}  方法 */template.helper = function (name, helper) {    helpers[name] = helper;};var helpers = template.helpers = utils.$helpers;/** * 模板错误事件(可由外部重写此方法) * @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 (typeof console === 'object') {        console.error(message);    }};// 模板调试器var showDebugInfo = function (e) {    template.onerror(e);        return function () {        return '{Template Error}';    };};/** * 编译模板 * 2012-6-6 @TooBug: define 方法名改为 compile,与 Node Express 保持一致 * @name    template.compile * @param   {String}    模板字符串 * @param   {Object}    编译选项 * *      - openTag       {String} *      - closeTag      {String} *      - filename      {String} *      - escape        {Boolean} *      - compress      {Boolean} *      - debug         {Boolean} *      - cache         {Boolean} *      - parser        {Function} * * @return  {Function}  渲染方法 */var compile = template.compile = function (source, options) {        // 合并默认配置    options = options || {};    for (var name in defaults) {        if (options[name] === undefined) {            options[name] = defaults[name];        }    }    var filename = options.filename;    try {                var Render = compiler(source, options);            } catch (e) {            e.filename = filename || 'anonymous';        e.name = 'Syntax Error';        return showDebugInfo(e);            }            // 对编译结果进行一次包装    function render (data) {                try {                        return new Render(data, filename) + '';                    } catch (e) {                        // 运行时出错后自动开启调试模式重新编译            if (!options.debug) {                options.debug = true;                return compile(source, options)(data);            }                        return showDebugInfo(e)();                    }            }        render.prototype = Render.prototype;    render.toString = function () {        return Render.toString();    };    if (filename && options.cache) {        cacheStore[filename] = render;    }        return render;};// 数组迭代var forEach = utils.$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*\.\s*[$\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 SPLIT2_RE = /^$|,+/;// 获取变量function getVariable (code) {    return code    .replace(REMOVE_RE, '')    .replace(SPLIT_RE, ',')    .replace(KEYWORDS_RE, '')    .replace(NUMBER_RE, '')    .replace(BOUNDARY_RE, '')    .split(SPLIT2_RE);};// 字符串转义function stringify (code) {    return "'" + code    // 单引号与反斜杠转义    .replace(/('|\\)/g, '\\$1')    // 换行符转义(windows + linux)    .replace(/\r/g, '\\r')    .replace(/\n/g, '\\n') + "'";}function compiler (source, options) {        var debug = options.debug;    var openTag = options.openTag;    var closeTag = options.closeTag;    var parser = options.parser;    var compress = options.compress;    var escape = options.escape;            var line = 1;    var uniq = {$data:1,$filename:1,$utils:1,$helpers:1,$out:1,$line:1};        var isNewEngine = ''.trim;// '__proto__' in {}    var replaces = isNewEngine    ? ["$out='';", "$out+=", ";", "$out"]    : ["$out=[];", "$out.push(", ");", "$out.join('')"];    var concat = isNewEngine        ? "$out+=text;return $out;"        : "$out.push(text);";              var print = "function(){"    +      "var text=''.concat.apply('',arguments);"    +       concat    +  "}";    var include = "function(filename,data){"    +      "data=data||$data;"    +      "var text=$utils.$include(filename,data,$filename);"    +       concat    +   "}";    var headerCode = "'use strict';"    + "var $utils=this,$helpers=$utils.$helpers,"    + (debug ? "$line=0," : "");        var mainCode = replaces[0];    var footerCode = "return new String(" + replaces[3] + ");"        // html与逻辑语法分离    forEach(source.split(openTag), function (code) {        code = code.split(closeTag);                var $0 = code[0];        var $1 = code[1];                // code: [html]        if (code.length === 1) {                        mainCode += html($0);                 // code: [logic, html]        } else {                        mainCode += logic($0);                        if ($1) {                mainCode += html($1);            }        }            });        var code = headerCode + mainCode + footerCode;        // 调试语句    if (debug) {        code = "try{" + code + "}catch(e){"        +       "throw {"        +           "filename:$filename,"        +           "name:'Render Error',"        +           "message:e.message,"        +           "line:$line,"        +           "source:" + stringify(source)        +           ".split(/\\n/)[$line-1].replace(/^\\s+/,'')"        +       "};"        + "}";    }                try {                        var Render = new Function("$data", "$filename", code);        Render.prototype = utils;        return Render;            } catch (e) {        e.temp = "function anonymous($data,$filename) {" + code + "}";        throw e;    }        // 处理 HTML 语句    function html (code) {                // 记录行号        line += code.split(/\n/).length - 1;        // 压缩多余空白与注释        if (compress) {            code = code            .replace(/\s+/g, ' ')            .replace(/<!--[\w\W]*?-->/g, '');        }                if (code) {            code = replaces[1] + stringify(code) + replaces[2] + "\n";        }        return code;    }            // 处理逻辑语句    function logic (code) {        var thisLine = line;               if (parser) {                     // 语法转换插件钩子            code = parser(code, options);                    } else if (debug) {                    // 记录行号            code = code.replace(/\n/g, function () {                line ++;                return "$line=" + line +  ";";            });                    }                        // 输出语句. 编码: <%=value%> 不编码:<%=#value%>        // <%=#value%> 等同 v2.0.3 之前的 <%==value%>        if (code.indexOf('=') === 0) {            var escapeSyntax = escape && !/^=[=#]/.test(code);            code = code.replace(/^=[=#]?|[\s;]*$/g, '');            // 对内容编码            if (escapeSyntax) {                var name = code.replace(/\s*\([^\)]+\)/, '');                // 排除 utils.* | include | print                                if (!utils[name] && !/^(include|print)$/.test(name)) {                    code = "$escape(" + code + ")";                }            // 不编码            } else {                code = "$string(" + code + ")";            }                        code = replaces[1] + code + replaces[2];        }                if (debug) {            code = "$line=" + thisLine + ";" + code;        }                // 提取模板中的变量名        forEach(getVariable(code), function (name) {                        // name 值可能为空,在安卓低版本浏览器下            if (!name || uniq[name]) {                return;            }            var value;            // 声明模板变量            // 赋值优先级:            // [include, print] > utils > helpers > data            if (name === 'print') {                value = print;            } else if (name === 'include') {                                value = include;                            } else if (utils[name]) {                value = "$utils." + name;            } else if (helpers[name]) {                value = "$helpers." + name;            } else {                value = "$data." + name;            }                        headerCode += name + "=" + value + ",";            uniq[name] = true;                                });                return code + "\n";    }        };// 定义模板引擎的语法defaults.openTag = '{{';defaults.closeTag = '}}';var filtered = function (js, filter) {    var parts = filter.split(':');    var name = parts.shift();    var args = parts.join(':') || '';    if (args) {        args = ', ' + args;    }    return '$helpers.' + name + '(' + js + args + ')';}defaults.parser = function (code, options) {    // var match = code.match(/([\w\$]*)(\b.*)/);    // var key = match[1];    // var args = match[2];    // var split = args.split(' ');    // split.shift();    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 'print':        case 'include':            code = key + '(' + split.join(',') + ');';            break;        default:            // 过滤器(辅助方法)            // {{value | filterA:'abcd' | filterB}}            // >>> $helpers.filterB($helpers.filterA(value, 'abcd'))            // TODO: {{ddd||aaa}} 不包含空格            if (/^\s*\|\s*[\w\$]/.test(args)) {                var escape = true;                // {{#value | link}}                if (code.indexOf('#') === 0) {                    code = code.substr(1);                    escape = false;                }                var i = 0;                var array = code.split('|');                var len = array.length;                var val = array[i++];                for (; i < len; i ++) {                    val = filtered(val, array[i]);                }                code = (escape ? '=' : '=#') + val;            // 即将弃用 {{helperName value}}            } else if (template.helpers[key]) {                                code = '=#' + key + '(' + split.join(',') + ');';                        // 内容直接输出 {{value}}            } else {                code = '=' + code;            }            break;    }            return code;};// CommonJsif (typeof exports === 'object' && typeof module !== 'undefined') {    module.exports = template;// RequireJS && SeaJS} else if (typeof define === 'function') {    define(function() {        return template;    });} else {    this.template = template;}})();
 |