вторник, 16 февраля 2016 г.

JavaScript Template Engine

function Template (textString, optionsObject) { // new Template("Total: <%= value %>", {delimiters: ["<%", '%>"]}).render({value: 10})
    if (!optionsObject) {optionsObject = {};}
    if (!optionsObject.hasOwnProperty("delimiters")) {
        optionsObject.delimiters = ["<%", "%>"];
    }
    var openDelimiter = optionsObject.delimiters[0] // <%
        , closeDelimiter = optionsObject.delimiters[1] // %>
        , stringInsideDelimiters = "([\\s\\S]+?)"          // some string
        , tags = {
              interpolate: openDelimiter + "=" + stringInsideDelimiters + closeDelimiter  // <%= some string %>
            , escape:      openDelimiter + "-" + stringInsideDelimiters + closeDelimiter   // <%- some string %>
            , evaluate:    openDelimiter + stringInsideDelimiters + closeDelimiter             // <% some string %>
          }
        , matcherRegExp = RegExp([
              tags.interpolate
            , tags.escape
            , tags.evaluate // Important!!! Must be last!
          ].join("|") + "|$", "g") // /<%=([\s\S]+?)%>|<%-([\s\S]+?)%>|<%([\s\S]+?)%>|$/g
       , escapeReqExp = new RegExp([
              "\\\\"       // single backslash
            , "'"            // single quote
            , "\\r"         // carriage return
            , "\\n"        // newline
            , "\\u2028" // line separator
            , "\\u2029" // paragraph separator
          ].join("|"), "g") // /\\|'|\r|\n|\u2028|\u2029/g
        , escapeCharacter = function (match) {
            var characters = {
                  "'":          "\\'"
                , "\\":         "\\\\"
                , "\r":         "\\r"
                , "\n":        "\\n"
                , "\u2028": "\\u2028"
                , "\u2029": "\\u2029"
            };
            return characters[match];
          }
        , escapeTags = function (string) {
            string = (string === null) ? "" : "" + string;
            var escapeMap = {
                      "&": "&amp;"
                    , "<": "&lt;"
                    , ">": "&gt;"
                    , '"':   "&quot;"
                    , "'":  "&#x27;"
                    , "`": "&#x60;"
                  }
                , escapeSymbols = function (match) {
                    return escapeMap[match];
                  }
                , getKeys = function (object) {
                    var keys = [];
                    for (var key in object) {
                        if (object.hasOwnProperty(key)) {keys.push(key);}
                    }
                    return keys;
                  }
                , pattern = "(?:" + getKeys(escapeMap).join("|") + ")" // (?:&|<|>|"|'|`)
                , testRegExp = RegExp(pattern)
                , replaceRegExp = RegExp(pattern, "g");
            return testRegExp.test(string) ? string.replace(replaceRegExp, escapeSymbols) : string;
          }
        , renderFunction
        , render;
    try {
        renderFunction = new Function(
                                                "dataObject"
                                              , "escapeTags"
                                              , "var temp"
                                             + "    , result = '';"
                                             + "with (dataObject || {}) {"
                                             + "    result += '" + (function(){
                                                    var resultString = ""
                                                        , index = 0;
                                                    textString.replace(matcherRegExp, function (match, interpolateCodeString, escapeCodeString, evaluateCodeString, offset) {
                                                        resultString += textString.slice(index, offset)
                                                                                                .replace(escapeReqExp, escapeCharacter);
                                                        index = offset + match.length;
                                                        if (interpolateCodeString) {
                                                            resultString += "' + ( ( temp = (" + interpolateCodeString + ") ) === null ? '' : temp) + '";
                                                        } else if (escapeCodeString) {
                                                            resultString += "' + ( ( temp = (" + escapeCodeString + ") ) === null ? '' : escapeTags(temp)) + '";
                                                        } else if (evaluateCodeString) {
                                                            resultString += "';" + evaluateCodeString + "result += '";
                                                        }
                                                        return match;
                                                    });
                                                    return resultString;
                                                })()
                                             + "';"
                                             + "}"
                                             + "return result;"
        );
    } catch (error) {
        throw error;
    }
    this.render = function (dataObject) {
        return renderFunction.call(null, dataObject, escapeTags);
    };
}

console.log(new Template("Total: <% if (true) { %>1<% } %>").render({value: '<div>1</div>'}));
console.log(new Template("Total: <%= value %>").render({value: '<div>1</div>'}));
console.log(new Template("Total: <%- value %>").render({value: '<div>1</div>'}));
console.log(new Template("Total: {{= value }}", {delimiters: ["{{", "}}"]}).render({value: '<div>1</div>'}));

Комментариев нет:

Отправить комментарий