# HG changeset patch # User cin # Date 1512028882 -10800 # Node ID 7c22fc01fcecaa2c010f3d8578fbc2ec5148bde5 # Parent 9718e8de0cb2b9ccce3e79a98d0a7805d639ddfb rewritten ./text/template-compile diff -r 9718e8de0cb2 -r 7c22fc01fcec src/implab/text/template-compile.js --- a/src/implab/text/template-compile.js Fri Oct 06 09:17:41 2017 +0300 +++ b/src/implab/text/template-compile.js Thu Nov 30 11:01:22 2017 +0300 @@ -1,51 +1,130 @@ define( - [ "dojo/request", "./format" ], - function(request, format) { - var compile = function(str) { - var code = "var p=[],print=function(){p.push(format.apply(null,arguments));};" + - // Introduce the data as local variables using with(){} - "with(obj){p.push('" + - // Convert the template into pure JavaScript - str.replace(/[\r\t\n]/g, " ").split("<%").join("\t").replace( - /(^|%>)[^\t]*/g, - function(x) { - return x.replace(/('|\\)/g, "\\$1"); - }).replace(/\t=(.*?)%>/g, "',$1,'").split("\t").join("');") - .split("%>").join("p.push('") + "');}return p.join('');"; - /* jshint -W054 */ - try { - var compiled = new Function("obj, format, nls", code); + ["dojo/request", "./format"], + function (request, format) { + + // разбивает строку шаблона на токены, возвращает контекст для + // дальнейшей обработки в visitTemplate + var parseTemplate = function (str) { + var tokens = str.split(/(<%=|<%|%>)/); + var pos = -1; + var data = [], + code = []; - /** - * Функция форматирования по шаблону - * - * @type{Function} - * @param{Object} obj объект с параметрами для подстановки - */ - return function(obj) { - return compiled(obj || {}, format); - }; - } catch (e) { - if (console && console.error) { - console.error(code); + return { + next: function () { + pos++; + return pos < tokens.length; + }, + token: function () { + return tokens[pos]; + }, + pushData: function () { + var i = data.length; + data.push.apply(data, arguments); + return i; + }, + pushCode : function() { + var i = code.length; + code.push.apply(code, arguments); + return i; + }, + compile: function () { + var text = "var $p = [];\n" + + "var print = function(){\n" + + " $p.push(format.apply(null,arguments));\n" + + "};\n" + + // Introduce the data as local variables using with(){} + "with(obj){\n" + + code.join("\n") + + "}\n" + + "return $p.join('');"; + + try { + var compiled = new Function("obj, format, $data", text); + /** + * Функция форматирования по шаблону + * + * @type{Function} + * @param{Object} obj объект с параметрами для подстановки + */ + return function (obj) { + return compiled(obj || {}, format, data); + }; + } catch (e) { + if (console && console.error) { + console.error(text); + console.log(data); + } + } } } }; + function visitTemplate(context) { + while (context.next()) { + switch (context.token()) { + case "<%": + visitCode(context); + break; + case "<%=": + visitInline(context); + break; + default: + visitTextFragment(context); + break; + } + } + } + + function visitInline(context) { + var code = ["$p.push("]; + while (context.next()) { + if (context.token() == "%>") + break; + code.push(context.token()); + } + code.push(");"); + context.pushCode(code.join('')); + } + + function visitCode(context) { + var code = []; + while (context.next()) { + if (context.token() == "%>") + break; + code.push(context.token()); + } + context.pushCode(code.join('')); + } + + function visitTextFragment(context) { + var i = context.pushData(context.token()); + context.pushCode("$p.push($data["+i+"]);"); + } + + var compile = function (str) { + if (!str) + return function() { return "";}; + + var ctx = parseTemplate(str); + visitTemplate(ctx); + return ctx.compile(); + }; + var cache = {}; - compile.load = function(id, require, callback) { + compile.load = function (id, require, callback) { var url = require.toUrl(id); if (url in cache) { callback(cache[url]); } else { - request(url).then(compile).then(function(tc) { + request(url).then(compile).then(function (tc) { callback(cache[url] = tc); - }, function(err) { - require.signal("error", [ { - error : err, - src : 'implab/text/template-compile' - } ]); + }, function (err) { + require.signal("error", [{ + error: err, + src: 'implab/text/template-compile' + }]); }); } };