UNPKG

atpl

Version:

A complete and fast template engine fully compatible with twig and similar to jinja with zero dependencies.

141 lines (140 loc) 6.12 kB
///<reference path='../imports.d.ts'/> "use strict"; var StringReader_1 = require('./StringReader'); var ExpressionTokenizer_1 = require('./ExpressionTokenizer'); /* export interface TemplateToken { type: string; value: string; params: any; offsetStart: number; offsetEnd: number; rawText: string; } */ var TemplateTokenizer = (function () { function TemplateTokenizer(string) { this.string = string; this.tokens = []; this.regExp = /\{[\{%#]-?/; this.stringOffsetStart = 0; this.stringOffsetEnd = 0; this.state = 0; this.stringReader = new StringReader_1.StringReader(this.string); } TemplateTokenizer.prototype.hasMore = function () { return this.stringReader.hasMore(); }; /** * Return a list of tokens. * * @return list of tokenized tokens. */ TemplateTokenizer.prototype.tokenizeAll = function () { var tokens = []; while (this.hasMore()) { var token = this.readNext(); //console.log(token); if (token === null) break; tokens.push(token); } return tokens; }; TemplateTokenizer.prototype.emitToken = function (type, value, params) { if (type == 'text' && value == '') return undefined; this.stringOffsetEnd = this.stringReader.getOffset(); var token = { type: type, value: value, params: params, offsetStart: this.stringOffsetStart, offsetEnd: this.stringOffsetEnd, rawText: this.stringReader.getSlice(this.stringOffsetStart, this.stringOffsetEnd) }; this.stringOffsetStart = this.stringOffsetEnd; return token; }; TemplateTokenizer.prototype.readNext = function () { var _this = this; var token = undefined; while (true) { if (token !== undefined) return token; switch (this.state) { case 0: if (!this.stringReader.hasMore()) return null; this.stringOffsetStart = this.stringReader.getOffset(); this.openMatch = this.stringReader.findRegexp(this.regExp); // No more tags. if (this.openMatch.position === null) { this.state = 0; token = this.emitToken('text', this.stringReader.readLeft()); } else { this.state = 1; token = this.emitToken('text', this.stringReader.readChars(this.openMatch.position)); } break; case 1: this.openChars3 = this.stringReader.readChars(this.openMatch.length); this.openChars = this.openChars3.substr(0, 2); this.removeSpacesBefore = (this.openChars3.substr(2, 1) == '-'); this.removeSpacesAfter = undefined; this.state = 2; if (this.removeSpacesBefore) token = this.emitToken('trimSpacesBefore'); break; case 2: this.state = 3; //if (openChars.length == 3) switch (this.openChars) { // A comment. case '{#': var closeMatch = this.stringReader.findRegexp(/\-?#}/); if (closeMatch.position === null) throw (new Error("Comment not closed!")); this.stringReader.skipChars(closeMatch.position + closeMatch.length); this.removeSpacesAfter = (closeMatch.length == 3); break; case '{{': case '{%': var expressionTokenizer = new ExpressionTokenizer_1.ExpressionTokenizer(new StringReader_1.StringReader(this.stringReader.getSliceWithCallback(function () { (new ExpressionTokenizer_1.ExpressionTokenizer(_this.stringReader)).tokenizeAll(); }))); var peekMinus = this.stringReader.peekChars(1); if (peekMinus == '-') this.stringReader.skipChars(1); this.removeSpacesAfter = (peekMinus == '-'); var closeChars = this.stringReader.readChars(2); if ((this.openChars == '{{' && closeChars != '}}') || (this.openChars == '{%' && closeChars != '%}') || 0) { throw (new Error('Open type was "' + this.openChars + '" but close type was "' + closeChars + '"')); } if (this.openChars == '{{') { token = this.emitToken('expression', expressionTokenizer); } else { token = this.emitToken('block', expressionTokenizer); } break; default: throw (new Error('Unknown open type "' + this.openChars + '"!')); } break; case 3: if (this.removeSpacesAfter) token = this.emitToken('trimSpacesAfter'); this.state = 0; break; default: throw (new Error("Invalid state")); } } }; return TemplateTokenizer; }()); exports.TemplateTokenizer = TemplateTokenizer;