Skip to content

Commit

Permalink
更新 tpl
Browse files Browse the repository at this point in the history
  • Loading branch information
xuld committed Sep 3, 2014
1 parent 6bdd2d6 commit fdf6e07
Showing 1 changed file with 164 additions and 164 deletions.
328 changes: 164 additions & 164 deletions src/text/tpl.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
/**
* @author xuld
*/

//#include core/class.js


/**
* @class Tpl
Expand All @@ -26,206 +20,212 @@
* 4.3 $index 在 for 循环中,表示当前循环的次号。
* 4.4 $value 在 for 循环中,表示当前被循环的目标。
*/
var Tpl = Class({

/**
function Tpl(tpl, strictMode) {
/**
* 把一个模板编译为函数。
* @param {String} tpl 表示模板的字符串。
* @param {Boolean} strictMode 严格模式,不支持省略.前面的对象。
* @return {Function} 返回的函数。
*/
constructor: function (tpl, strictMode) {
tpl = this.build(this.lexer(tpl), strictMode);
this.parser = new Function("_", tpl);
},
tpl = this.source = this.build(this.lexer(tpl), strictMode);
this.parser = new Function("_", tpl);
}

Tpl.prototype = {

/**
/**
* 对一个模板进行词法解析。返回拆分的数据单元。
* @param {String} tpl 表示模板的字符串。
* @return {Array} 返回解析后的数据单元。
*/
lexer: function (tpl) {
lexer: function (tpl) {

/**
/**
* [字符串1, 代码块1, 字符串2, 代码块2, ...]
*/
var output = [],
var output = [],

// 块的开始位置。
blockStart = -1,

// 块的结束位置。
blockEnd = -1;

while ((blockStart = tpl.indexOf('{', blockStart + 1)) >= 0) {
while ((blockStart = tpl.indexOf('{', blockStart + 1)) >= 0) {

// 如果 { 后面是 { , 忽略之。
if (tpl.charAt(blockStart + 1) === '{') {
blockStart++;
continue;
}

// 如果 { 后面是 { , 忽略之。
if (tpl.charAt(blockStart + 1) === '{') {
blockStart++;
continue;
}
// 放入第一个数据区块。
output.push(tpl.substring(blockEnd + 1, blockStart));

// 放入第一个数据区块。
output.push(tpl.substring(blockEnd + 1, blockStart));
// 从 blockStart 处搜索 }
blockEnd = blockStart;

// 从 blockStart 处搜索 }
blockEnd = blockStart;
// 搜索 } 字符,如果找到的字符尾随一个 } 则跳过。
do {
blockEnd = tpl.indexOf('}', blockEnd + 1);

// 搜索 } 字符,如果找到的字符尾随一个 } 则跳过。
do {
blockEnd = tpl.indexOf('}', blockEnd + 1);
if (tpl.charAt(blockEnd + 1) !== '}' || tpl.charAt(blockEnd + 2) === '}') {
break;
}

if (tpl.charAt(blockEnd + 1) !== '}' || tpl.charAt(blockEnd + 2) === '}') {
break;
}
blockEnd++;
} while (true);

blockEnd++;
} while (true);
if (blockEnd == -1) {
this.throwError("缺少 '}'", tpl.substr(blockStart));
} else {
output.push(tpl.substring(blockStart + 1, blockStart = blockEnd).trim());
}

if (blockEnd == -1) {
this.throwError("缺少 '}'", tpl.substr(blockStart));
} else {
output.push(tpl.substring(blockStart + 1, blockStart = blockEnd).trim());
}
}

}
// 剩余的部分。
output.push(tpl.substring(blockEnd + 1, tpl.length));

// 剩余的部分。
output.push(tpl.substring(blockEnd + 1, tpl.length));
for (var i = output.length - 1; i >= 0; i--) {
output[i] = output[i].replace(/([\{\}])\1/g, '$1');
}

return output;
},
return output;
},

/**
/**
* 将词法解析器的结果编译成函数源码。
*/
build: function (lexerOutput, strictMode) {
build: function (lexerOutput, strictMode) {

var output = "var __OUTPUT__=\"\",__INDEX__,__KEY__,__TARGET__,__TMP__;" + (strictMode ? '{' : 'with(_){');
var output = ["var __OUTPUT__=[],__INDEX__,__KEY__,__TMP__;", (strictMode ? '{' : 'with(_){')];

for (var i = 0, len = lexerOutput.length, source, isString = true; i < len; i++) {
source = lexerOutput[i].replace(/([\{\}]){2}/g, "$1");
for (var i = 0, len = lexerOutput.length, source, isString = true; i < len; i++) {
source = lexerOutput[i].replace(/([\{\}]){2}/g, "$1");

if (isString) {
output += "__OUTPUT__+=\"" + source.replace(/[\"\\\n\r]/g, Tpl.replaceSpecialChars) + "\";";
} else {
output += this.parseMacro(source);
}
if (isString) {
output.push("__OUTPUT__.push(\"" + source.replace(/[\"\\\n\r]/g, function (specialChar) {
return Tpl.specialChars[specialChar] || specialChar;
}) + "\");");
} else {
output.push(this.parseMacro(source));
}

isString = !isString;
}
isString = !isString;
}

output += "};return __OUTPUT__";
if (this.depth !== 0) {
this.throwError(this.depth > 0 ? '缺少 ' + this.depth + ' 个 {end}' : ('多余 ' + -this.depth + ' 个 {end}'), lexerOutput[lexerOutput.length - 1]);
}

return output;
},
output.push("};", "return __OUTPUT__.join('')");

render: function (data, scope) {
return this.parser.call(scope, data);
},
return output.join('\n');
},

parseMacro: function (macro) {
var command = (macro.match(/^\w+\b/) || [''])[0],
render: function (data, scope) {
return this.parser.call(scope, data || {});
},

depth: 0,

parseMacro: function (macro) {
var command = (macro.match(/^\w+\b/) || [''])[0],
params = macro.substr(command.length);

switch (command) {
case "end":
return "}";
case 'if':
case 'while':
if (!params)
this.throwError("'" + command + "' 语句缺少条件, '" + command + "' 语句的格式为 {" + command + " condition}", macro);
macro = command + "(Array.isArray(__TMP__=" + params + ")?__TMP__.length:__TMP__){";
break;
case 'for':
if (command = /^\s*(var\s+)?([\w$]+)\s+in\s+/.exec(params)) {
macro = "__INDEX__=0;__TARGET__=" + params.substr(command[0].length) + ";for(__KEY__ in __TARGET__){if(!__TARGET__.hasOwnProperty(__KEY__))continue;__INDEX__++;var " + command[2] + "=__TARGET__[__KEY__];";
} else if (/^\(/.test(params)) {
return macro + "{";
} else {
this.throwError("'for' 语法错误, 'for' 语句的格式为 {for var_name in obj}", macro);
}
break;
case 'else':
return '}else ' + (/^\s*if\b/.exec(params) ? this.parseMacro(params.trim()) : '{');
case 'var':
macro += ';';
break;
case 'function':
macro += '{';
break;
case 'break':
case 'continue':
break;
case 'eval':
macro = params + ";";
break;
default:
macro = '__TMP__=' + macro + ';if(__TMP__!=undefined)__OUTPUT__+=__TMP__;';
break;
}

return macro.replace(/@(\w+)\b/g, Tpl.replaceConsts);
},

throwError: function (message, tpl) {
throw new SyntaxError("Tpl#constructor: " + message + "。 (在 '" + tpl + "' 附近)");
}

});

Object.extend(Tpl, {

instances: {},

/**
* 使用指定的数据解析模板,并返回生成的内容。
* @param {String} tpl 表示模板的字符串。
* @param {Object} data 数据。
* @param {Object} scope 模板中 this 的指向。
* @return {String} 处理后的字符串。
*/
parse: function (tpl, data, scope) {
return (Tpl.instances[tpl] || (Tpl.instances[tpl] = new Tpl(tpl))).render(data, scope);
},

isLast: function (obj, index) {
if (typeof obj.length === 'number')
return index >= obj.length - 1;

for (var p in obj) {
indeui--;
}

return !index;
},

consts: {
'data': '_',
'target': '__TARGET__',
'key': '__KEY__',
'index': '__INDEX__',
'first': '__INDEX__==0',
'last': 'Tpl.isLast(__TARGET__,__INDEX__)',
'odd': '__INDEX__%2===1',
'even': '__INDEX__%2'
},

replaceConsts: function (_, consts) {
return Tpl.consts[consts] || _;
},

specialChars: {
'"': '\\"',
'\n': '\\n',
'\r': '\\r',
'\\': '\\\\'
},

replaceSpecialChars: function (specialChar) {
return Tpl.specialChars[specialChar] || specialChar;
}

});
switch (command) {
case "end":
this.depth--;
return "}";
case 'if':
case 'while':
this.depth++;
if (!params)
this.throwError("'" + command + "' 语句缺少条件, '" + command + "' 语句的格式为 {" + command + " condition}", macro);
macro = command + "(Array.isArray(__TMP__=" + params + ")?__TMP__.length:__TMP__){";
break;
case 'for':
this.depth++;
if (command = /^\s*(var\s+)?([\w$]+)\s+in\s+/.exec(params)) {
macro = "var __INDEX#__=0,__TARGET#__={target};\nfor(__KEY__ in __TARGET#__){\nif(!__TARGET#__.hasOwnProperty(__KEY__))continue;\n__INDEX#__++;\nvar {key}=__TARGET#__[__KEY__];\n".replace(/#/g, this.depth).replace('{target}', params.substr(command[0].length)).replace('{key}', command[2]);
} else if (/^\(/.test(params)) {
return macro + "{";
} else {
this.throwError("'for' 语法错误, 'for' 语句的格式为 {for var_name in obj}", macro);
}
break;
case 'else':
return '}else ' + (/^\s*if\b/.exec(params) ? this.parseMacro(params.trim()) : '{');
case 'var':
macro += ';';
break;
case 'function':
this.depth++;
macro += '{';
break;
case 'break':
case 'continue':
break;
case 'eval':
macro = params + ";";
break;
default:
macro = '__TMP__=' + macro + ';if(__TMP__!=undefined)__OUTPUT__.push(__TMP__);';
break;
}

var me = this;
return macro.replace(/@(\w+)\b/g, function (_, consts) {
return (Tpl.consts[consts] || _).replace(/#/g, me.depth);
});
},

throwError: function (message, tpl) {
throw new SyntaxError("模块语法错误: " + message + "。 (在 '" + tpl + "' 附近)");
}

};

Tpl.instances = {};

/**
* 使用指定的数据解析模板,并返回生成的内容。
* @param {String} tpl 表示模板的字符串。
* @param {Object} data 数据。
* @param {Object} scope 模板中 this 的指向。
* @return {String} 处理后的字符串。
*/
Tpl.parse = function (tpl, data, scope) {
return (Tpl.instances[tpl] || (Tpl.instances[tpl] = new Tpl(tpl))).render(data, scope);
};

Tpl.isLast = function (obj, index) {
if (typeof obj.length === 'number')
return index >= obj.length - 1;

for (var p in obj) {
indeui--;
}

return !index;
};

Tpl.consts = {
'data': '_',
'target': '__TARGET#__',
'key': '__KEY__',
'index': '__INDEX#__',
'first': '__INDEX#__==0',
'last': 'Tpl.isLast(__TARGET#__,__INDEX#__)',
'odd': '__INDEX#__%2===1',
'even': '__INDEX#__%2'
};

Tpl.specialChars = {
'"': '\\"',
'\n': '\\n',
'\r': '\\r',
'\\': '\\\\'
};

0 comments on commit fdf6e07

Please sign in to comment.