1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/urain39-ij2tpl.js

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
ij2tpl.js 18 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
urain39 Отправлено 09.02.2021 13:35 5ca1b7e
/**
* @file IJ2TPL.js - A Lightweight Template Engine.
* @version v0.1.3
* @author urain39 <urain39@qq.com>
* @copyright (c) 2018-2020 IJ2TPL.js / IJ2TPL.ts Authors.
*/
var _a, _b;
/* eslint-disable prefer-const */
export var version = '0.1.3';
var filterMap = {};
export function setFilterMap(filterMap_) {
filterMap = filterMap_;
}
// See https://github.com/microsoft/TypeScript/issues/14682
var TokenTypeMap = (_a = {},
_a["?" /* IF */] = 0 /* IF */,
_a["!" /* NOT */] = 1 /* NOT */,
_a["*" /* ELSE */] = 2 /* ELSE */,
_a["/" /* END */] = 3 /* END */,
_a["#" /* RAW */] = 5 /* RAW */,
_a["@" /* PARTIAL */] = 8 /* PARTIAL */,
_a);
// NOTE: If we use `IndentedTestRe` with capture-group directly, the `<string>.replace` method
// will always generate a new string. So we need test it before replace it ;)
var IndentedTestRe = /(^|[\n\r])([\t \xA0\uFEFF]+)$/
// ^^^ To support IE6, we cannot use non-capturing groups
, IndentedWhiteSpaceRe = /[\t \xA0\uFEFF]+$/, stripIndentation = function (token, tokens) {
var value, result, indentation = '';
// Remove token's indentation if exists
if (token[0 /* TYPE */] === 4 /* TEXT */) {
token = token;
value = token[1 /* VALUE */];
// eslint-disable-next-line no-cond-assign
if (result = value.match(IndentedTestRe))
indentation = result[2],
value = value.replace(IndentedWhiteSpaceRe, '');
if (value)
token[1 /* VALUE */] = value;
else
tokens.pop(); // Don't save text that has become empty
}
return indentation;
}
// We strip all white spaces to make check section easy(for `buildTree`)
, WhiteSpaceRe = /[\s\xA0\uFEFF]+/g, stripWhiteSpace = function (string_) { return string_.replace(WhiteSpaceRe, ''); };
export function tokenize(source, prefix, suffix) {
var type_, value, indentation, token = [7 /* COMMENT */, ''] // Initialized for first backward check
, tokens = [];
for (var i = 0, j = 0, l = source.length, pl = prefix.length, sl = suffix.length; i < l;) {
// Match '{'
j = source.indexOf(prefix, i);
// Not found the '{'
if (j === -1) {
// Eat the rest of the source
value = source.slice(i);
// Don't save the empty text ''
if (value)
token = [4 /* TEXT */, value], tokens.push(token);
break; // Done
}
// Eat the left side of a token
value = source.slice(i, j);
j += pl; // Skip the '{'
if (value)
token = [4 /* TEXT */, value], tokens.push(token);
// Match the '}'
i = source.indexOf(suffix, j);
// Not found the '}'
if (i === -1)
throw new Error("No matching prefix '" + prefix + "'");
// We don't want to call `source.slice` for comments
if (source.charAt(j) === "-" /* COMMENT */) {
stripIndentation(token, tokens);
i += sl; // Skip the '}' for comments
token = [7 /* COMMENT */, '']; // Don't forget to update `token`
continue; // Tokenize next one
}
// Eat the text between the '{' and '}'
value = source.slice(j, i);
i += sl; // Skip the '}' for tokens
value = stripWhiteSpace(value);
if (!value)
continue; // Skip the empty token, such as '{}'
type_ = value.charAt(0);
switch (type_) {
case "?" /* IF */:
case "!" /* NOT */:
case "*" /* ELSE */:
case "/" /* END */:
case "@" /* PARTIAL */:
indentation = stripIndentation(token, tokens);
// Skip section's newline if exists
if (i < l) {
switch (source.charAt(i)) {
case '\n':
i += 1; // LF
break;
case '\r':
// Have next character?
i += (j = i + 1) < l ?
// Yes, next character is LF?
source.charAt(j) === '\n' ?
2 // Yes, then newline is CRLF
:
1 // No, then newline is CR
:
1 // No, then newline is CR
;
break;
}
}
token = [TokenTypeMap[type_], value.slice(1), indentation],
tokens.push(token);
break;
case "#" /* RAW */:
token = [TokenTypeMap[type_], value.slice(1)],
tokens.push(token);
break;
default:
token = [6 /* FORMAT */, value],
tokens.push(token);
break;
}
}
return tokens;
}
var hasOwnProperty = {}.hasOwnProperty
// See https://github.com/janl/mustache.js/pull/530
, htmlSpecialRe = /["&'\/<=>`]/g // eslint-disable-line no-useless-escape
, htmlSpecialEntityMap = {
'"': '&quot;',
'&': '&amp;',
"'": '&#39;' // eslint-disable-line quotes
,
'/': '&#x2F;',
'<': '&lt;',
'=': '&#x3D;',
'>': '&gt;',
'`': '&#x60;'
}, escapeHTML = function (value) { return String(value).replace(htmlSpecialRe, function (special) { return htmlSpecialEntityMap[special]; }); };
var escapeFunction = escapeHTML; // Escape for HTML by default
export function escape(value) {
return escapeFunction(value);
}
export function setEscapeFunction(escapeFunction_) {
escapeFunction = escapeFunction_;
}
var Context = /** @class */ (function () {
function Context(data, parent) {
this.data = data;
this.cache = { '.': this.data };
this.parent = parent;
}
Context.prototype.resolve = function (name) {
var data, cache, name_, name__, names, filters, value = null, context = this, hasProperties = false;
if (!name[3 /* IS_ACTION */]) {
cache = context.cache;
name_ = name[0 /* NAME */];
// Cached in context?
if (hasOwnProperty.call(cache, name_)) {
value = cache[name_];
}
else { // No cached records found
// eslint-disable-next-line no-cond-assign
if (names = name[1 /* NAMES */]) {
name__ = names[0];
hasProperties = true;
}
else {
name__ = name_;
}
// Try to look up the name in data
do {
data = context.data;
// Find out which context contains name
if (data && hasOwnProperty.call(data, name__)) {
value = data[name__];
// Resolve properties if exists
if (hasProperties) {
for (var i = 1, l = names.length; i < l; i++) {
name__ = names[i];
if (value && hasOwnProperty.call(value, name__)) {
value = value[name__];
}
else {
value = null; // Reset
break;
}
}
}
break;
}
context = context.parent;
} while (context);
// Support for function
if (typeof value === 'function')
value = value(context);
// Cache the name
cache[name_] = value;
}
}
// eslint-disable-next-line no-cond-assign
if (filters = name[2 /* FILTERS */]) {
for (var i = 0, l = filters.length, filterName = void 0; i < l;) {
filterName = filters[i++];
if (hasOwnProperty.call(filterMap, filterName))
value = filterMap[filterName](value, context);
else
throw new Error("Cannot resolve filter '" + filterName + "'");
}
}
return value;
};
return Context;
}());
export { Context };
var isArray = Array.isArray;
if (!isArray) {
var toString_1 = {}.toString;
// XXX: Fix a possible issue
isArray = function (value) {
return toString_1.call(value) === '[object Array]';
};
}
var Renderer = /** @class */ (function () {
function Renderer(treeRoot) {
this.treeRoot = treeRoot;
}
/**
* Do NOT invoke it directly, you should just call `render`
*/
Renderer.prototype.renderTree = function (treeRoot, context, partialMap) {
var BEGINNING_RE = /^(.+)$/gm; // We don't want to indent empty lines
var value, valueLength, section, indentation, buffer = '', isArray_ = false;
for (var i = 0, l = treeRoot.length, token = void 0; i < l;) {
token = treeRoot[i++];
switch (token[0 /* TYPE */]) {
case 0 /* IF */:
section = token;
value = context.resolve(section[1 /* VALUE */]);
isArray_ = isArray(value);
// We can only know true or false after we sure it is array or not
if (isArray_ ? valueLength = value.length : value) {
if (isArray_)
for (var i_1 = 0, l_1 = valueLength, value_ = void 0; i_1 < l_1;) {
value_ = value[i_1++];
buffer += this.renderTree(section[2 /* BLOCK */], new Context(value_, context), partialMap);
}
else
buffer += this.renderTree(section[2 /* BLOCK */], new Context(value, context), partialMap);
}
break;
case 1 /* NOT */:
section = token;
value = context.resolve(section[1 /* VALUE */]);
isArray_ = isArray(value);
if (!(isArray_ ? value.length : value))
buffer += this.renderTree(section[2 /* BLOCK */], context, partialMap);
break;
// FIXME: It may be slower than If-Section + Not-Section(about 1 ops/sec)
case 2 /* ELSE */:
section = token;
value = context.resolve(section[1 /* VALUE */]);
isArray_ = isArray(value);
if (isArray_ ? valueLength = value.length : value) {
if (isArray_)
for (var i_2 = 0, l_2 = valueLength, value_ = void 0; i_2 < l_2;) {
value_ = value[i_2++];
buffer += this.renderTree(section[2 /* BLOCK */], new Context(value_, context), partialMap);
}
else
buffer += this.renderTree(section[2 /* BLOCK */], new Context(value, context), partialMap);
}
else {
buffer += this.renderTree(section[3 /* ELSE_BLOCK */], context, partialMap);
}
break;
case 4 /* TEXT */:
token = token;
value = token[1 /* VALUE */];
// Empty text has been skipped when tokenizing
buffer += value;
break;
case 5 /* RAW */:
token = token;
value = context.resolve(token[1 /* VALUE */]);
// Check if it is non-values(null and undefined)
if (value != null)
buffer += value;
break;
case 6 /* FORMAT */:
token = token;
value = context.resolve(token[1 /* VALUE */]);
if (value != null)
buffer += escapeFunction === escapeHTML && typeof value === 'number' ?
value // Numbers are absolutely safe for HTML
:
escapeFunction(value);
break;
case 8 /* PARTIAL */:
token = token;
value = token[1 /* VALUE */];
indentation = token[2 /* INDENTATION */];
if (value === '&') { // Recursive render with parents
buffer += this.renderTree(this.treeRoot, context, partialMap)
.replace(BEGINNING_RE, indentation + "$&");
}
else if (value === '^') { // Recursive render without parents
buffer += this.renderTree(this.treeRoot, new Context(context.data, null), partialMap)
.replace(BEGINNING_RE, indentation + "$&");
}
else if (partialMap && hasOwnProperty.call(partialMap, value))
buffer += this.renderTree(partialMap[value].treeRoot, context, partialMap)
.replace(BEGINNING_RE, indentation + "$&");
else
throw new Error("Cannot resolve partial '" + value + "'");
break;
}
}
return buffer;
};
Renderer.prototype.render = function (data, partialMap) {
return this.renderTree(this.treeRoot, new Context(data, null), partialMap);
};
return Renderer;
}());
export { Renderer };
var TokenTypeReverseMap = (_b = {},
_b[0 /* IF */] = "?" /* IF */,
_b[1 /* NOT */] = "!" /* NOT */,
_b[2 /* ELSE */] = "*" /* ELSE */,
_b[3 /* END */] = "/" /* END */,
_b);
var processToken = function (token_) {
var name, names, filters, isAction, token;
names = null;
filters = null;
isAction = false;
name = token_[1 /* VALUE */];
// NOTE: Name can be empty
if (name.indexOf('|') !== -1) {
filters = name.split('|');
name = filters[0];
filters = filters.slice(1);
if (!name)
isAction = true;
}
// One '.' means current data
if (name.indexOf('.') > 0)
names = name.split('.');
// NOTE: Filters are just additional part of Token
token = [token_[0 /* TYPE */], [name, names, filters, isAction]];
return token;
};
function buildTree(tokens) {
var type_, value, token, collector, elseBlock, section, sections = [], sectionsLength, treeRoot = [];
collector = treeRoot;
for (var i = 0, l = tokens.length, token_ = void 0; i < l;) {
token_ = tokens[i++];
type_ = token_[0 /* TYPE */];
switch (type_) {
// Enter a section
case 0 /* IF */:
case 1 /* NOT */:
token = processToken(token_); // Make `_Token` -> `Token`
collector.push(token); // Current block saves token
section = token;
sections.push(section); // Stack saves section
// Initialize and switch to section's block
collector = section[2 /* BLOCK */] = [];
section[3 /* ELSE_BLOCK */] = null; // Padding?
break;
// Switch to section's else-block
case 2 /* ELSE */:
// Get entered section
// eslint-disable-next-line no-cond-assign
section = (sectionsLength = sections.length) ?
sections[sectionsLength - 1]
:
void 0x95E2 // Reset
;
value = token_[1 /* VALUE */];
if (!section ||
// `ELSE` are valid for `IF`, invalid for `NOT`
section[0 /* TYPE */] !== 0 /* IF */ ||
value !== section[1 /* VALUE */][0 /* NAME */])
throw new Error("Unexpected token '<type=" + TokenTypeReverseMap[type_] + ", value=" + value + ">'");
// Initialize and switch to section's else-block
collector = section[3 /* ELSE_BLOCK */] = [];
break;
// Leave a section
case 3 /* END */:
section = sections.pop();
value = token_[1 /* VALUE */];
if (!section ||
value !== section[1 /* VALUE */][0 /* NAME */])
throw new Error("Unexpected token '<type=" + TokenTypeReverseMap[type_] + ", value=" + value + ">'");
// Change type for which section contains initialized else-block
if (section[3 /* ELSE_BLOCK */])
section[0 /* TYPE */] = 2 /* ELSE */;
// Re-bind block to parent block
// eslint-disable-next-line no-cond-assign
collector = (sectionsLength = sections.length) ?
// Is parent section has initialized else-block?
(section = sections[sectionsLength - 1]
, elseBlock = section[3 /* ELSE_BLOCK */]) ?
// Yes, then parent block is else-block
elseBlock
:
// No, then parent block is (if-)block
section[2 /* BLOCK */]
:
treeRoot;
break;
// Formatter
case 5 /* RAW */:
case 6 /* FORMAT */:
token = processToken(token_);
collector.push(token);
break;
// Text or Partial
default:
collector.push(token_);
break;
}
}
if (sections.length) {
section = sections.pop();
throw new Error("No matching section '<type=" + TokenTypeReverseMap[section[0 /* TYPE */]] + ", value=" + section[1 /* VALUE */][0 /* NAME */] + ">'");
}
return treeRoot;
}
export function parse(source, prefix, suffix) {
if (prefix === void 0) { prefix = '{'; }
if (suffix === void 0) { suffix = '}'; }
var treeRoot = buildTree(tokenize(source, prefix, suffix));
return new Renderer(treeRoot);
}

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/urain39-ij2tpl.js.git
git@api.gitlife.ru:oschina-mirror/urain39-ij2tpl.js.git
oschina-mirror
urain39-ij2tpl.js
urain39-ij2tpl.js
master