razjs
Version:
JS-version of the Razor-Express library (Razor-like template engine for NodeJS Express).
1,379 lines (1,191 loc) • 97.6 kB
JavaScript
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
module.exports = class HtmlString {
constructor(html) {
this.html = html;
}
toString() {
return this.html;
}
}
},{}],2:[function(require,module,exports){
'use strict';
window.raz = {
set debug(value) {
require('../core/dbg/debugger').isDebugMode = value;
},
get debug(){
return require('../core/dbg/debugger').isDebugMode;
},
render(template, model) {
if (!this.parser)
this.parser = require('./parser')();
return this.parser.compileSync(template, model);
}
}
},{"../core/dbg/debugger":3,"./parser":8}],3:[function(require,module,exports){
module.exports = {
isDebugMode: false,
isBrowser: (typeof window !== 'undefined')
}
},{}],4:[function(require,module,exports){
const htmlEncode = require('../libs/js-htmlencode');
class RazorError extends Error {
constructor(message, captureFrame) {
super(message);
if (Error.captureStackTrace)
Error.captureStackTrace(this, captureFrame || this.constructor);
}
static new(args) {
let exc = new RazorError(args.message, args.capture || this.new);
this.extend(exc, args);
return exc;
}
static extend(exc, args) {
const { isDebugMode, isBrowser } = require('../dbg/debugger');
exc.isRazorError = true;
if (!isDebugMode)
{
exc.html = () => {
const errorRefUrl = (isBrowser) ? "https://www.npmjs.com/package/razjs#example-2-handling-and-displaying-errors" : "https://github.com/DevelAx/RazorExpress/blob/master/docs/Debugging.md#production--development-modes";
const error = `Razor template compilation error occured.<br/>Turn <a href="${errorRefUrl}" target="_blank">DEBUG MODE</a> on to get details.`;
if (isBrowser)
return `<div style="color: red;">${htmlEncode(exc.message)}</div><hr/>${error}`;
else
return error;
}
return;
}
if (exc.data) {
var oldData = exc.data;
}
exc.data = Object.assign({ line: args.line, pos: args.pos, len: args.len }, args.info);
if (exc.__dbg && exc.__dbg.pos)
exc.data = Object.assign({ posRange: { start: exc.__dbg.pos.start, end: exc.__dbg.pos.end } }, exc.data);
if (oldData)
exc.data.inner = oldData;
if (!exc.html)
exc.html = RazorError.prototype.html;
}
html() {
let codeHtml = '', mainInfo = { title: '' };
let stackHtml = stackToHtml(this, this.data, mainInfo);
for (var data = this.data; data; data = data.inner) {
if (Utils.isServer)
codeHtml += "<div class='arrow'>⇩</div>"
codeHtml += dataToHtml(data, mainInfo);
codeHtml += '<hr />'
}
var html = `
<!DOCTYPE html>
<html>
<head>
<style type="text/css" scoped>
h1, h2, h3{
font-weight: normal;
}
div.filepath {
font-weight: bold;
font-size: 1.05rem;
}
body {
font-size: .813em;
font-family: Consolas, "Courier New", courier, monospace;
}
.stack{
color: orange;
white-space: pre;
}
.stack .error{
color: #e20000;
white-space: pre-wrap;
font-weight: bold;
}
ol {
color: darkgray;
}
ol li {
background-color: #fbfbfb;
white-space: pre;
}
ol li.highlight{
color: red;// darkslategray;
background-color: #f8f9b3;
}
ol li span {
color: #196684;//darkslategray;
}
ol li.highlight span {
color: black;
}
ol li span.highlight {
border: solid 1px red;
}
ol li span.multilight {
background-color: #ebef00;
font-weight: bold;
color: red;
}
ol li.comment {
color: lightgray;
}
ol li.comment span {
color: darkgray;
}
.arrow{
font-size: 1.5rem;
color: orange;
margin-left: 1rem;
}
hr {
opacity: 0.5;
}
</style>
</head>
<body>
<h1>A template compilation error occured</h1>
<div class="stack">${stackHtml}</div>
<hr />
${codeHtml}
</body>
</html>
`;
return html;
}
}
module.exports = RazorError;
function stackToHtml(exc, data, mainInfo) {
let lines = exc.stack.split('\n');
let fireFox = (typeof navigator !== 'undefined') && navigator.userAgent.toLowerCase().indexOf('firefox') !== -1; // for compatibility with FireFox
if (fireFox) {
let message = `${exc.name}: ${exc.message}`;
lines.unshift(message);
}
let html = '<div>';
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
if (fireFox){
let parts = line.split('@');
if (parts.length === 2){
if (!parts[0]) // empty
parts[0] = "<anonymous>";
line = `at ${parts[0]} (${parts[1]})`;
}
}
else if (i === 0 && (line.startsWith("evalmachine.") || line.startsWith("undefined:"))) {
let nextLineExists = i < lines.length + 1;
if (nextLineExists && data.jshtml && data.posRange) { // This is likely HTML parsing error (not code runtime error).
// Let's try to narrow the error area by the data from the stack.
let codeLine = lines[i + 1].trimRight();
let errorCodeFragment = data.jshtml.substring(data.posRange.start, data.posRange.end);
let codePos = errorCodeFragment.indexOf(codeLine);
// Check if it exists and only once in the `errorCodeFragment`.
if (codePos !== -1 && codePos === errorCodeFragment.lastIndexOf(codeLine)) {
// Set a more precise location of the error.
data.posRange.start = data.posRange.start + codePos;
data.posRange.end = data.posRange.start + codeLine.length;
// Include '@' symbol.
if (data.posRange.start > 0 && data.jshtml[data.posRange.start - 1] === '@')
data.posRange.start -= 1;
}
}
continue; // skip the very first line like "evalmachine.<anonymous>:22"
}
if (mainInfo.title && !pointer){
var trim = line.substring(dLen);
var pointer = trim;
}
else{
trim = line.trim();
}
var dLen = line.length - trim.length;
let encodedLine = htmlEncode(trim);
let style = '';
if (trim && trim !== '^' && !trim.startsWith("at ")) {
if (trim.startsWith('RazorError') || mainInfo.title){
style = 'id="error" class="error"'; // the second line is the error description
}
else {
mainInfo.errorLine = trim;
style = 'class="error"';
}
if (mainInfo.title)
mainInfo.title += '\r\n';
mainInfo.title += encodedLine;
}
html += `<span ${style}>${encodedLine}</span><br/>`;
}
html += '</div>';
return html;
}
function dataToHtml(data, mainInfo) {
let html;
if (data.jshtml) {
let textCursor = 0;
lines = data.jshtml.split('\n');
let startLine = data.startLine ? data.startLine : 0;
html = `<ol start='${startLine}'>`;
let isLastData = !data.inner;
let hasErrorCoordinates = data.posRange && data.posRange.start || data.pos;
if (isLastData && !hasErrorCoordinates && mainInfo.errorLine) {
let occur = data.jshtml.numberOfOccurrences(mainInfo.errorLine);
if (occur.num === 1) {
let extend = 0;
if (occur.pos > 0 && data.jshtml[occur.pos - 1] === '@')
extend = 1; // Include the '@' symbol for beauty.
data.posRange = {
start: occur.pos - extend,
end: occur.pos + mainInfo.errorLine.length
};
}
}
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
let highlight, htmlLine, comment, multilight;
let textCursorEnd = textCursor + line.length + 1; // + '\n'
if (data.posRange && data.posRange.start < data.posRange.end) {
if (data.posRange.start >= textCursor && data.posRange.start < textCursorEnd) {
var pos = data.posRange.start - textCursor;
if (data.posRange.end < textCursorEnd) {
var len = data.posRange.end - data.posRange.start;
data.posRange = null; // prevent further useless computation during the next iterations of this cycle
}
else {
len = line.length;
data.posRange.start = textCursorEnd; // move to the beginning of the next line
}
multilight = "multilight";
}
}
else if (data.line === i) {
pos = data.pos;
len = data.len || 1;
}
if (pos != null && typeof pos !== 'undefined') {
if (pos < line.length) {
let start = htmlEncode(line.substring(0, pos));
let one = htmlEncode(line.substring(pos, pos + len));
let end = htmlEncode(line.substring(pos + len));
htmlLine = `<span>${start}</span><span class='${multilight || "highlight"} source-error' title='${mainInfo.title}'>${one}</span><span>${end}</span>`;
highlight = "class='highlight'";
}
pos = null;
}
else {
let trim = line.trim();
if (trim.length > 6 && trim.startsWith("<!--") && trim.endsWith("-->"))
comment = "class='comment'";
//htmlLine = `<span class="comment">${htmlEncode(trim)}</span>`;
}
html += `<li ${comment || highlight}><span>`;
html += htmlLine ? htmlLine : htmlEncode(line);
html += "</span></li>";
textCursor = textCursorEnd;
}// for
//let fileFolder = path.dirname(data.filename);
let fileName = `<div class="filepath">${Utils.isServer ? Utils.path.basename(data.filename) : "Template:"}</div>`;
html += "</ol>";
html = `
<div class="code">
${fileName}
${html}
</div>
`;
}// if (this.data.jshtml)
return html;
}
// /**
// * HELPERS
// */
// function getIndentifier(codeLine, startPos){
// let ch = codeLine[startPos];
// let isIdn = Char.isLetter(ch) || '_$'.includes(ch); // is it identifier
// let result = ch;
// for(let i = startPos + 1, ch = codeLine[i]; i < codeLine.length && (isIdn ? Char.isIdentifier(ch) : !Char.isIdentifier(ch)); i++, ch = codeLine[i])
// result += ch;
// return result;
// }
},{"../dbg/debugger":3,"../libs/js-htmlencode":7}],5:[function(require,module,exports){
const RazorError = require('./RazorError');
class ParserErrorFactory {
constructor(templateInfo, linesBaseNumber) {
this.startLineNum = linesBaseNumber;
this.info = templateInfo;
this.info.startLine = linesBaseNumber;
}
endOfFileFoundAfterAtSign(lineNum, posNum) {
var message = `End-of-file was found after the "@" character at line ${lineNum + this.startLineNum} pos ${posNum + 1}. "@" must be followed by a valid code block. If you want to output an "@", escape it using the sequence: "@@"`;
return RazorError.new({ message, info: this.info, line: lineNum, pos: posNum, capture: this.endOfFileFoundAfterAtSign });
}
unexpectedCharacter(ch, lineNum, posNum, line) {
var message = `Unexpected '${ch}' character at line ${lineNum + this.startLineNum} pos ${posNum + 1} after '${line}'`;
return RazorError.new({ message, info: this.info, line: lineNum, pos: posNum, capture: this.unexpectedCharacter });
}
unexpectedAtCharacter(lineNum, posNum) {
var message = `Unexpected '@' character at line ${lineNum + this.startLineNum} pos ${posNum + 1}. Once inside the body of a code block (@if {}, @{}, etc.) or a section (@section{}) you do not need to use "@" character to switch to code.`;
return RazorError.new({ message, info: this.info, line: lineNum, pos: posNum, capture: this.unexpectedAtCharacter });
}
notValidStartOfCodeBlock(ch, lineNum, posNum) {
var message = `"${ch}" is not valid at the start of a code block at line ${lineNum + this.startLineNum} pos ${posNum + 1}. Only identifiers, keywords, "(" and "{" are valid.`;
return RazorError.new({ message, info: this.info, line: lineNum, pos: posNum, capture: this.notValidStartOfCodeBlock });
}
unexpectedEndOfFile(text) {
var message = `Unexpected end of file after '${text}'.`;
return RazorError.new({ message, info: this.info, capture: this.unexpectedEndOfFile });
}
characterExpected(ch, line, pos) {
var message = `'${ch}' character is expected at line ${line + this.startLineNum} pos ${pos + 1}.`;
return RazorError.new({ message, info: this.info, line, pos, capture: this.characterExpected });
}
characterExpectedAfter(ch, line, pos, after) {
var message = `'${ch}' character is expected after '${after}' at line ${line + this.startLineNum} pos ${pos + 1}.`;
return RazorError.new({ message, info: this.info, line, pos, capture: this.characterExpectedAfter });
}
expressionMissingEnd(expr, ch, line, pos) {
var message = `The explicit expression "${expr}" is missing a closing character "${ch}" at line ${line + this.startLineNum} pos ${pos + 1}.`;
return RazorError.new({ message, info: this.info, line, pos, capture: this.expressionMissingEnd });
}
jsCodeBlockMissingClosingChar(line, codeFirstLine) {
var message = `The code or section block is missing a closing "}" character. Make sure you have a matching "}" character for all the "{" characters within this block, and that none of the "}" characters are being interpreted as markup. The block starts at line ${line + this.startLineNum} with text: "${codeFirstLine}"`;
return RazorError.new({ message, info: this.info, line, capture: this.jsCodeBlockMissingClosingChar });
}
wordExpected(word, line, pos, len) {
var message = `'${word}' expected at line ${line + this.startLineNum} pos ${pos + 1}.`;
return RazorError.new({ message, info: this.info, line, pos, len, capture: this.wordExpected });
}
missingMatchingStartTag(tag, line, pos) {
var message = `'${tag}' tag at line ${line + this.startLineNum} pos ${pos + 1} is missing matching start tag.`;
return RazorError.new({ message, info: this.info, line, pos, len: tag.length, capture: this.missingMatchingStartTag });
}
missingMatchingEndTag(tag, line, pos) {
var message = `'${tag}' tag at line ${line + this.startLineNum} pos ${pos + 1} is missing matching end tag.`;
return RazorError.new({ message, info: this.info, line, pos, len: tag.length, capture: this.missingMatchingEndTag });
}
invalidExpressionChar(ch, line, pos, afterText) {
var message = `Invalid "${ch}" symbol in expression at line ${line + this.startLineNum} pos ${pos + 1}` + (afterText ? ` after "${afterText}".` : ".");
return RazorError.new({ message, info: this.info, line, pos, capture: this.invalidExpressionChar });
}
invalidHtmlTag(tag, line, pos) {
var message = `Invalid HTML-tag: '${tag}'`;
return RazorError.new({ message, info: this.info, line, pos, len: tag && tag.length, capture: this.invalidHtmlTag });
}
// forbiddenViewName(viewName) {
// var message = `The file "${viewName}" is not available.`;
// return new RazorError(message, this.info);
// }
whiteSpaceExpectedAfter(keyword, line, pos) {
var message = `A whitespace expected after the "${keyword}" keyword at line ${line + this.startLineNum} pos ${pos + 1}.`;
return RazorError.new({ message, info: this.info, line, pos, capture: this.whiteSpaceExpectedAfter }); // cannot be tested.
}
tagNameExpected(line, pos) {
var message = `Tag name expected at line ${line + this.startLineNum} pos ${pos + 1}.`;
return RazorError.new({ message, info: this.info, line, pos, capture: this.tagNameExpected });
}
sectionNameExpectedAfter(keyword, line, pos) {
var message = `A section name expected after the "${keyword}" keyword at line ${line + this.startLineNum} pos ${pos + 1}.`;
return RazorError.new({ message, info: this.info, line, pos, capture: this.sectionNameExpectedAfter });
}
sectionNameCannotStartWith(ch, line, pos) {
var message = `A section name cannot start with '${ch}' at line ${line + this.startLineNum} pos ${pos + 1}.`;
return RazorError.new({ message, info: this.info, line, pos, capture: this.sectionNameCannotStartWith });
}
sectionNameCannotInclude(ch, line, pos) {
var message = `A section name cannot include '${ch}' character at line ${line + this.startLineNum} pos ${pos + 1}.`;
return RazorError.new({ message, info: this.info, line, pos, capture: this.sectionNameCannotInclude });
}
unexpectedLiteralFollowingTheSection(ch, line, pos) {
var message = `Unexpected literal '${ch}' following the 'section' directive at line ${line + this.startLineNum} pos ${pos + 1}. Expected '{'.`;
return RazorError.new({ message, info: this.info, line, pos, capture: this.unexpectedLiteralFollowingTheSection });
}
sectionIsAlreadyDefined(sectionName, line, pos, viewFilePath) {
var message = `Section '${sectionName}' at line ${line + this.startLineNum} pos ${pos + 1} has been already defined in the file '${viewFilePath}'. You cannot assign the same name to different sections in the same file.`;
return RazorError.new({ message, info: this.info, line, pos, len: sectionName.length, capture: this.sectionIsAlreadyDefined });
}
// sectionBlockIsMissingClosingBrace(sectionName) {
// var message = `The section block '${sectionName}' is missing a closing "}" character.`;
// return new RazorError(message, this.info);
// }
sectionsCannotBeNested(line, pos) {
var message = `Section blocks cannot be nested at line ${line + this.startLineNum} pos ${pos + 1}.`;
return RazorError.new({ message, info: this.info, line, pos, capture: this.sectionsCannotBeNested });
}
sectionIsNotFound(sectionName, filePath) {
var message = `View '${filePath}' requires the section '${sectionName}' which cannot be found.`;
return RazorError.new({ message, info: this.info, capture: this.sectionIsNotFound });
}
sectionIsNotCompiled(sectionName, filePath) {
var message = `You try to render the section '${sectionName}' from the '${filePath}' view. This section has not been compiled yet. Make sure it is defined before the '@Html.section' method is called.`;
return RazorError.new({ message, info: this.info, capture: this.sectionIsNotCompiled });
}
sectionsAlreadyRendered(sectionName, renderedBy, attemptedBy) {
var message = `Sections named '${sectionName}' has already been rendered by '${renderedBy}'. There is an atempt to rendered it again by '${attemptedBy}'.`;
return RazorError.new({ message, info: this.info, capture: this.sectionsAlreadyRendered });
}
sectionNeverRendered(sectionName, viewPath) {
var message = `Section '${sectionName}' in '${viewPath}' has never been rendered. If a section exists it must be rendered.`;
return RazorError.new({ message, info: this.info, capture: this.sectionNeverRendered });
}
partialViewNotFound(partialView, searchedLocations) {
let viewTypeName = (this.isLayout) ? "layout" : "partial";
let message = `The view "${this.info.filename}" cannot find the ${viewTypeName} view "${partialView}".\nThe following locations were searched:\n${searchedLocations.map(l => `"${l}"`).join("\n")}`;
return RazorError.new({ message, info: this.info, capture: this.partialViewNotFound });
}
errorReadingFile(error) {
let message = `Reading file '${this.info.filename}' caused an error: ${error}`;
let parserError = RazorError.new({ message, info: this.info, capture: this.errorReadingFile });
setInnerError(parserError, error);
return parserError;
}
errorReadingView(filename, error) {
let message = `Reading view file '${filename}' caused an error: ${error}`;
let parserError = RazorError.new({ message, info: this.info, capture: this.errorReadingView });
setInnerError(parserError, error);
return parserError;
}
partialLayoutNameExpected() {
let message = "Partial layout name is expected."
return RazorError.new({ message, info: this.info, capture: this.partialLayoutNameExpected });
}
// invalidViewExtension(viewName, expectedExtension){
// let message = `The view '${viewName}' includes invalid extension. Expected extension '${expectedExtension}'`;
// return new ParserError(message, this.args);
// }
/**
*
* Doesn't produce a `ParserError`, just extends the existant one in other prevent VM from adding additional lines to the `.Stack` when rethrowing.
*/
extendError(exc) {
RazorError.extend(exc, { info: this.info });
}
}
ParserErrorFactory.templateShouldBeString = 'The [template] argument should be a string.';
function setInnerError(parserError, error) {
if (error.message)
parserError.inner = error;
}
module.exports = ParserErrorFactory;
},{"./RazorError":4}],6:[function(require,module,exports){
module.exports = require("./errors.en");
},{"./errors.en":5}],7:[function(require,module,exports){
/**
* [js-htmlencode]{@link https://github.com/emn178/js-htmlencode}
*
* @version 0.3.0
* @author Chen, Yi-Cyuan [emn178@gmail.com]
* @copyright Chen, Yi-Cyuan 2014-2017
* @license MIT
*/
/*jslint bitwise: true */
(function () {
'use strict';
var HTML_ENTITIES = {
' ' : '\u00A0',
'¡' : '\u00A1',
'¢' : '\u00A2',
'£' : '\u00A3',
'¤' : '\u00A4',
'¥' : '\u00A5',
'¦' : '\u00A6',
'§' : '\u00A7',
'¨' : '\u00A8',
'©' : '\u00A9',
'ª' : '\u00AA',
'«' : '\u00AB',
'¬' : '\u00AC',
'­' : '\u00AD',
'®' : '\u00AE',
'¯' : '\u00AF',
'°' : '\u00B0',
'±' : '\u00B1',
'²' : '\u00B2',
'³' : '\u00B3',
'´' : '\u00B4',
'µ' : '\u00B5',
'¶' : '\u00B6',
'·' : '\u00B7',
'¸' : '\u00B8',
'¹' : '\u00B9',
'º' : '\u00BA',
'»' : '\u00BB',
'¼' : '\u00BC',
'½' : '\u00BD',
'¾' : '\u00BE',
'¿' : '\u00BF',
'À' : '\u00C0',
'Á' : '\u00C1',
'Â' : '\u00C2',
'Ã' : '\u00C3',
'Ä' : '\u00C4',
'Å' : '\u00C5',
'Æ' : '\u00C6',
'Ç' : '\u00C7',
'È' : '\u00C8',
'É' : '\u00C9',
'Ê' : '\u00CA',
'Ë' : '\u00CB',
'Ì' : '\u00CC',
'Í' : '\u00CD',
'Î' : '\u00CE',
'Ï' : '\u00CF',
'Ð' : '\u00D0',
'Ñ' : '\u00D1',
'Ò' : '\u00D2',
'Ó' : '\u00D3',
'Ô' : '\u00D4',
'Õ' : '\u00D5',
'Ö' : '\u00D6',
'×' : '\u00D7',
'Ø' : '\u00D8',
'Ù' : '\u00D9',
'Ú' : '\u00DA',
'Û' : '\u00DB',
'Ü' : '\u00DC',
'Ý' : '\u00DD',
'Þ' : '\u00DE',
'ß' : '\u00DF',
'à' : '\u00E0',
'á' : '\u00E1',
'â' : '\u00E2',
'ã' : '\u00E3',
'ä' : '\u00E4',
'å' : '\u00E5',
'æ' : '\u00E6',
'ç' : '\u00E7',
'è' : '\u00E8',
'é' : '\u00E9',
'ê' : '\u00EA',
'ë' : '\u00EB',
'ì' : '\u00EC',
'í' : '\u00ED',
'î' : '\u00EE',
'ï' : '\u00EF',
'ð' : '\u00F0',
'ñ' : '\u00F1',
'ò' : '\u00F2',
'ó' : '\u00F3',
'ô' : '\u00F4',
'õ' : '\u00F5',
'ö' : '\u00F6',
'÷' : '\u00F7',
'ø' : '\u00F8',
'ù' : '\u00F9',
'ú' : '\u00FA',
'û' : '\u00FB',
'ü' : '\u00FC',
'ý' : '\u00FD',
'þ' : '\u00FE',
'ÿ' : '\u00FF',
'"' : '\u0022',
'&' : '\u0026',
'<' : '\u003C',
'>' : '\u003E',
''' : '\u0027',
'Œ' : '\u0152',
'œ' : '\u0153',
'Š' : '\u0160',
'š' : '\u0161',
'Ÿ' : '\u0178',
'ˆ' : '\u02C6',
'˜' : '\u02DC',
' ' : '\u2002',
' ' : '\u2003',
' ' : '\u2009',
'‌' : '\u200C',
'‍' : '\u200D',
'‎' : '\u200E',
'‏' : '\u200F',
'–' : '\u2013',
'—' : '\u2014',
'‘' : '\u2018',
'’' : '\u2019',
'‚' : '\u201A',
'“' : '\u201C',
'”' : '\u201D',
'„' : '\u201E',
'†' : '\u2020',
'‡' : '\u2021',
'‰' : '\u2030',
'‹' : '\u2039',
'›' : '\u203A',
'€' : '\u20AC',
'ƒ' : '\u0192',
'Α' : '\u0391',
'Β' : '\u0392',
'Γ' : '\u0393',
'Δ' : '\u0394',
'Ε' : '\u0395',
'Ζ' : '\u0396',
'Η' : '\u0397',
'Θ' : '\u0398',
'Ι' : '\u0399',
'Κ' : '\u039A',
'Λ' : '\u039B',
'Μ' : '\u039C',
'Ν' : '\u039D',
'Ξ' : '\u039E',
'Ο' : '\u039F',
'Π' : '\u03A0',
'Ρ' : '\u03A1',
'Σ' : '\u03A3',
'Τ' : '\u03A4',
'Υ' : '\u03A5',
'Φ' : '\u03A6',
'Χ' : '\u03A7',
'Ψ' : '\u03A8',
'Ω' : '\u03A9',
'α' : '\u03B1',
'β' : '\u03B2',
'γ' : '\u03B3',
'δ' : '\u03B4',
'ε' : '\u03B5',
'ζ' : '\u03B6',
'η' : '\u03B7',
'θ' : '\u03B8',
'ι' : '\u03B9',
'κ' : '\u03BA',
'λ' : '\u03BB',
'μ' : '\u03BC',
'ν' : '\u03BD',
'ξ' : '\u03BE',
'ο' : '\u03BF',
'π' : '\u03C0',
'ρ' : '\u03C1',
'ς' : '\u03C2',
'σ' : '\u03C3',
'τ' : '\u03C4',
'υ' : '\u03C5',
'φ' : '\u03C6',
'χ' : '\u03C7',
'ψ' : '\u03C8',
'ω' : '\u03C9',
'ϑ' : '\u03D1',
'ϒ' : '\u03D2',
'ϖ' : '\u03D6',
'•' : '\u2022',
'…' : '\u2026',
'′' : '\u2032',
'″' : '\u2033',
'‾' : '\u203E',
'⁄' : '\u2044',
'℘' : '\u2118',
'ℑ' : '\u2111',
'ℜ' : '\u211C',
'™' : '\u2122',
'ℵ' : '\u2135',
'←' : '\u2190',
'↑' : '\u2191',
'→' : '\u2192',
'↓' : '\u2193',
'↔' : '\u2194',
'↵' : '\u21B5',
'⇐' : '\u21D0',
'⇑' : '\u21D1',
'⇒' : '\u21D2',
'⇓' : '\u21D3',
'⇔' : '\u21D4',
'∀' : '\u2200',
'∂' : '\u2202',
'∃' : '\u2203',
'∅' : '\u2205',
'∇' : '\u2207',
'∈' : '\u2208',
'∉' : '\u2209',
'∋' : '\u220B',
'∏' : '\u220F',
'∑' : '\u2211',
'−' : '\u2212',
'∗' : '\u2217',
'√' : '\u221A',
'∝' : '\u221D',
'∞' : '\u221E',
'∠' : '\u2220',
'∧' : '\u2227',
'∨' : '\u2228',
'∩' : '\u2229',
'∪' : '\u222A',
'∫' : '\u222B',
'∴' : '\u2234',
'∼' : '\u223C',
'≅' : '\u2245',
'≈' : '\u2248',
'≠' : '\u2260',
'≡' : '\u2261',
'≤' : '\u2264',
'≥' : '\u2265',
'⊂' : '\u2282',
'⊃' : '\u2283',
'⊄' : '\u2284',
'⊆' : '\u2286',
'⊇' : '\u2287',
'⊕' : '\u2295',
'⊗' : '\u2297',
'⊥' : '\u22A5',
'⋅' : '\u22C5',
'⌈' : '\u2308',
'⌉' : '\u2309',
'⌊' : '\u230A',
'⌋' : '\u230B',
'⟨' : '\u2329',
'⟩' : '\u232A',
'◊' : '\u25CA',
'♠' : '\u2660',
'♣' : '\u2663',
'♥' : '\u2665',
'♦' : '\u2666'
};
var decodeEntity = function (code) {
// name type
if (code.charAt(1) !== '#') {
return HTML_ENTITIES[code] || code;
}
var n, c = code.charAt(2);
// hex number
if (c === 'x' || c === 'X') {
c = code.substring(3, code.length - 1);
n = parseInt(c, 16);
} else {
c = code.substring(2, code.length - 1);
n = parseInt(c);
}
return isNaN(n) ? code : String.fromCharCode(n);
};
var htmlEncode = function (str) {
return str.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''')
.replace(/</g, '<').replace(/>/g, '>');
};
var htmlDecode = function (str) {
return str.replace(/&#?\w+;/g, decodeEntity);
};
module.exports = htmlEncode;
htmlEncode.htmlEncode = htmlEncode;
htmlEncode.htmlDecode = htmlDecode;
})();
},{}],8:[function(require,module,exports){
'use strict';
require('./utils');
function compilePageSync(html, model, viewData, scope, isDebugMode) {
let vm = html._vm;
if (vm) {
let sandbox = html._sandbox;
// Creates cope variables.
if (scope) {
Object.keys(scope).forEach((k) => {
defineConstant(sandbox, k, scope[k]);
});
}
defineConstant(sandbox, "Html", html);
defineConstant(sandbox, "Model", model);
defineConstant(sandbox, "ViewData", viewData);
defineConstant(sandbox, "debug", isDebugMode);
vm.runInNewContext(html._js, sandbox);
}
else {
const argNames = ["Html", "Model", "ViewData", "debug"];
const argValues = [html, model, viewData, isDebugMode];
if (scope) {
// Add cope variables (we should but can't make them constants because of `eval` limitation in sctict-mode).
Object.keys(scope).forEach((k) => {
argNames.push(k);
argValues.push(scope[k]);
});
}
// Put the JS-scipt to be executed.
argNames.push(html._js);
// Execute JS-script via function with arguments.
Function.apply(undefined, argNames).apply(undefined, argValues);
}
function defineConstant(obj, name, value) {
Object.defineProperty(obj, name, {
value,
writable: false
});
}
}
function compilePage(html, model, viewData, scope, isDebugMode, done) {
try {
compilePageSync(html, model, viewData, scope, isDebugMode);
return html.__renderLayout(done);
}
catch (exc) {
done(exc);
}
}
module.exports = function (opts) {
opts = opts || {};
const dbg = require('../core/dbg/debugger');
const debugMode = dbg.isDebugMode;
const isBrowser = dbg.isBrowser;
const log = opts.log || { debug: () => { } };
log.debug(`Parse debug mode is '${!!debugMode}'.`);
const HtmlString = require('./HtmlString');
const htmlEncode = require('./libs/js-htmlencode');
const vm = opts.vm;
////////////////////
/// Html class
////////////////////
function Html(args) {
// Non-user section.
this._vm = vm;
if (debugMode && !isBrowser) {
this._sandbox = Object.create(null);
vm.createContext(this._sandbox);
}
// function (process,...){...}() prevents [this] to exist for the 'vm.runInNewContext()' method
this._js = `
'use strict';
(function (process, window, global, module, require, compilePage, compilePageSync, navigator, undefined) {
delete Html._js;
delete Html._vm;
delete Html._sandbox;
${args.js}
}).call();`;
// User section.
if (debugMode)
this.__dbg = { viewName: args.filePath, template: args.template, pos: [] }
this.$ =
this.layout = null;
// Private
let sectionName = null;
let sections = args.parsedSections;
this.__val = function (i) {
return args.jsValues.getAt(i);
};
this.__renderLayout = (done) => {
if (!this.layout) // if the layout is not defined..
return Promise.resolve().then(() => done(null, args.html)), null;
// looking for the `Layout`..
args.er.isLayout = true; // the crutch
args.findPartial(this.layout, args.filePath, args.er, (err, result) => {
args.er.isLayout = false;
if (err) return done(err);
let compileOpt = {
scope: args.scope,
template: result.data,
filePath: result.filePath,
model: args.model,
bodyHtml: args.html,
findPartial: args.findPartial,
findPartialSync: args.findPartialSync,
parsedSections: args.parsedSections,
partialsCache: args.partialsCache,
viewData: args.viewData
};
compile(compileOpt, done);
});
};
this.__sec = function (name) { // in section
if (!sectionName) {
sectionName = name;
}
else if (sectionName === name) {
sections[sectionName][args.filePath].compiled = true;
sectionName = null;
}
else {
throw new Error(`Unexpected section name = '${name}'.`); // Cannot be tested via user-inputs.
}
};
this.raw = function (val) { // render
if (!isVisibleValue(val)) // 'undefined' can be passed when `Html.raw()` is used by user in the view, in this case it will be wrapped into `Html.ecnode()` anyway an it will call `Html.raw` passing 'undefined' to it.
return;
if (sectionName) {
let sec = sections[sectionName][args.filePath];
if (!sec.compiled) // it could have been compiled already if it's defined in a partial view which is rendred more than once
sec.html += val;
}
else {
args.html += val;
}
};
this.encode = function (val) {
var encoded = this.getEncoded(val);
this.raw(encoded);
};
this.getEncoded = function (val) {
if (!isVisibleValue(val))
return '';
if (typeof val === "number" || val instanceof Number || val instanceof HtmlString)
return val;
if (String.is(val))
return htmlEncode(val);
return htmlEncode(val.toString());
};
this.body = function () {
return new HtmlString(args.bodyHtml);
};
this.section = function (name, required) {
if (!args.filePath)
throw new Error("'args.filePath' is not set.");
let secGroup = sections[name];
if (secGroup) {
if (secGroup.renderedBy)
throw args.er.sectionsAlreadyRendered(name, secGroup.renderedBy, args.filePath); // TESTME:
let html = '';
for (var key in secGroup) {
if (secGroup.hasOwnProperty(key)) {
let sec = secGroup[key];
if (!sec.compiled)
throw args.er.sectionIsNotCompiled(name, args.filePath); // [#3.2]
html += sec.html;
}
}
secGroup.renderedBy = args.filePath;
return new HtmlString(html);
}
else {
if (required)
throw args.er.sectionIsNotFound(name, args.filePath); // [#3.3]
}
return '';
};
this.getPartial = function (viewName, viewModel) {
let compileOpt = {
scope: args.scope,
model: viewModel === undefined ? args.model : viewModel, // if is not set explicitly, set default (parent) model
findPartial: args.findPartial,
findPartialSync: args.findPartialSync,
sections,
parsedSections: args.parsedSections,
partialsCache: args.partialsCache,
viewData: args.viewData
};
// Read file and complie to JS.
let partial = args.findPartialSync(viewName, args.filePath, args.er, args.partialsCache);
compileOpt.template = partial.data;
compileOpt.filePath = partial.filePath;
if (partial.js) { // if it's taken from cache
compileOpt.js = partial.js;
compileOpt.jsValues = partial.jsValues;
}
let { html, precompiled } = compileSync(compileOpt);
partial.js = precompiled.js; // put to cache
partial.jsValues = precompiled.jsValues; // put to cache
return html;
};
this.partial = function (viewName, viewModel) {
var partialHtml = this.getPartial(viewName, viewModel);
this.raw(partialHtml)
};
}
class Block {
constructor(type, name) {
this.type = type;
if (name)
this.name = name;
this.text = '';
}
append(ch) {
this.text += ch;
//this.text += (ch === '"') ? '\\"' : ch;
}
toScript(jsValues) {
return toScript(this, jsValues);
}
}
function isVisibleValue(val) {
return (val != null && val !== '');
}
function toScript(block, jsValues) {
if (block.type === blockType.section) {
let secMarker = `\r\nHtml.__sec("${block.name}");`;
let script = secMarker;
for (let n = 0; n < block.blocks.length; n++) {
let sectionBlock = block.blocks[n];
script += toScript(sectionBlock, jsValues);
}
script += secMarker;
return script;
}
else {
let i;
switch (block.type) {
case blockType.html:
i = jsValues.enq(block.text);
return "\r\nHtml.raw(Html.__val(" + i + "));";
case blockType.expr:
i = jsValues.enq(block.text);
let code = `Html.encode(eval(Html.__val(${i})));`;
return debugMode ? setDbg(code, block) : "\r\n" + code;
case blockType.code:
return debugMode ? setDbg(block.text, block) : "\r\n" + block.text;
default:
throw new Error(`Unexpected block type = "${blockType}".`);
}
}
throw new Error(`Unexpected code behaviour, block type = "${blockType}".`);
}
function setDbg(code, block) {
return `
Html.__dbg.pos = { start:${block.posStart}, end: ${block.posEnd} };
${code}
Html.__dbg.pos = null;`;
}
class Queue {
constructor() {
this._items = [];
}
enq(item) {
//if (opts.debug) log.debug(item);
return this._items.push(item) - 1;
}
getAt(i) {
if (opts.debug) {
let item = this._items[i];
//log.debug(item);
return item;
}
else {
return this._items[i];
}
}
}
const _sectionKeyword = "section";
//const _functionKeyword = "function";
const blockType = { none: 0, html: 1, code: 2, expr: 3, section: 4 };
const ErrorsFactory = require('./errors/errors');
const voidTags = "area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr".toUpperCase().split("|").map(s => s.trim());
////////////////
// PARSER //
////////////////
class Parser {
constructor(args) {
args.filePath = args.filePath || "js-script";
let linesBaseNumber = (debugMode && opts.express) ? 0 : 1; // in debug-mode the file-path of a template is added as a very first line comment
this.args = args;
this.er = new ErrorsFactory({ filename: args.filePath, jshtml: args.template }, linesBaseNumber);
}
compile(done) {
log.debug();
let errorFactory = this.er;
try {
var htmlObj = this.getHtml({}, done);
}
catch (exc) {
return error(exc);
}
compilePage(htmlObj, this.args.model, this.args.viewData, this.args.scope, debugMode, (err, html) => {
if (err)
return error(err, htmlObj.__dbg);
try {
this.checkSections();
}
catch (exc) {
return error(exc, htmlObj.__dbg);
}
return done(null, html);
});
function error(err, dbg) {
err.__dbg = dbg;
var parserError = toParserError(err, errorFactory);
return Promise.resolve().then(() => done(parserError)), null;
}
}
compileSync() {
try {
log.debug();
var htmlArgs = {};
var html = this.getHtml(htmlArgs);
compilePageSync(html, this.args.model, this.args.viewData, this.args.scope, debugMode);
this.checkSections();
}
catch (exc) {
exc.__dbg = html && html.__dbg;
throw toParserError(exc, this.er);
}
return { html: htmlArgs.html, precompiled: { js: htmlArgs.js, jsValues: htmlArgs.jsValues } };
}
getHtml(htmlArgs) {
log.debug(this.args.filePath);
// extract scope..
var model = this.args.model;
if (model && model.$) {
this.args.scope = model.$;
delete model.$;
}
this.args.parsedSections = this.args.parsedSections || {};
this.args.viewData = this.args.viewData || this.args.ViewData || {};
this.args.partialsCache = this.args.partialsCache || {};
let js = this.args.js;
let jsValues = this.args.jsValues;
let template = this.args.template;
if (!js) {
var isString = String.is(template);
if (!isString)
throw new Error(ErrorsFactory.templateShouldBeString);
this.text = template;
this.line = '', this.lineNum = 0, this.pos = 0, this.padding = '';
this.inSection = false;
this.blocks = [];
this.parseHtml(this.blocks);
jsValues = new Queue();
var scripts = this.blocks.map(b => b.toScript(jsValues));
js = scripts.join("");
}
Object.assign(htmlArgs, {
html: '',
jsValues,
js,
template,
er: this.er
});
Object.assign(htmlArgs, this.args);
var html = new Html(htmlArgs);
return html;
}
// Check if all sections have been rendered.
checkSections() {
if (!this.args.root)
return;
let sections = this.args.parsedSections;
for (var key in sections) {
if (sections.hasOwnProperty(key)) {
let secGroup = sections[key];
if (!secGroup.renderedBy) {
let sec = secGroup[Object.keys(secGroup)[0]]; // just any section from the group
throw this.er.sectionNeverRendered(key, sec.filePath);
}
}
}
}
parseHtml(blocks, outerWaitTag) {
log.debug();
const docTypeName = "!DOCTYPE";
const textQuotes = `'"\``;
var quotes = [];
const tagKinds = { open: 0, close: 1, selfclose: 2 };
var openTags = [];
var tag = '', lineLastLiteral = '', lastLiteral = '';
var block = this.newBlock(blockType.html, blocks);
let stop = false, inComments = false;
let inJs = "script".equal(outerWaitTag, true);
var lastCh = '';
for (var ch = this.pickChar(); ch; ch = this.pickChar()) {
let isSpace = Char.isWhiteSpace(ch);
let nextCh = this.pickNextChar();
let inQuotes = (quotes.length > 0);
if (inComments) {
if (ch === '-') {
if (!tag || tag === '-')
tag += ch;
else
tag = '';
}
else if (ch === '>') {
if (tag === '--')
inComments = false;
tag = '';
}
else {
tag = '';
}
}
else if (ch === '@') {
if (nextCh === '@') { // checking for '@@' that means just text '@'
ch = this.fetchChar(); // skip the next '@'
nextCh = this.pickNextChar();
}
else {
this.fetchChar();
this.parseCode(blocks);
if (tag === '<' || tag === '</')
tag = '';
block = this.newBlock(blockType.html, blocks);