changeset 21:7c22fc01fcec

rewritten ./text/template-compile
author cin
date Thu, 30 Nov 2017 11:01:22 +0300
parents 9718e8de0cb2
children 60f6493e2892
files src/implab/text/template-compile.js
diffstat 1 files changed, 114 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- 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'
+                    }]);
                 });
             }
         };