marko/compiler/ast/TextOutput.js
2015-12-03 16:07:11 -07:00

138 lines
3.3 KiB
JavaScript

'use strict';
var Node = require('./Node');
var Literal = require('./Literal');
function trim(textOutputNode) {
var text = textOutputNode.argument.value;
var isFirst = textOutputNode.isFirst;
var isLast = textOutputNode.isLast;
if (isFirst) {
//First child
text = text.replace(/^\n\s*/g, '');
}
if (isLast) {
//Last child
text = text.replace(/\n\s*$/g, '');
}
if (/^\n\s*$/.test(text)) {
//Whitespace between elements
text = '';
}
text = text.replace(/\s+/g, ' ');
textOutputNode.argument.value = text;
}
class TextOutput extends Node {
constructor(def) {
super('TextOutput');
this.argument = def.argument;
this.escape = def.escape;
this.normalized = false;
this.isFirst = false;
this.isLast = false;
}
isLiteral() {
return this.argument instanceof Node && this.argument.type === 'Literal';
}
generateHtmlCode(generator) {
this.normalizeText();
var argument = this.argument;
if (argument instanceof Literal) {
if (!argument.value) {
return;
}
} else {
let builder = generator.builder;
// TODO Only escape if necessary
argument = builder.functionCall(
'escapeXml',
[argument]);
}
generator.addWrite(argument);
}
normalizeText(generator) {
if (this.normalized) {
return;
}
var container = this.container;
if (!container) {
return;
}
var isFirst = true;
var currentTextLiteral = null;
var literalTextNodes = [];
container.forEach((curChild) => {
if (curChild.noOutput) {
// Skip over AST nodes that produce no HTML output
return;
}
if (curChild.type === 'TextOutput') {
curChild.normalized = true;
}
if (curChild.type === 'TextOutput' && curChild.isLiteral()) {
if (currentTextLiteral) {
currentTextLiteral.argument.value += curChild.argument.value;
curChild.detach();
} else {
currentTextLiteral = curChild;
literalTextNodes.push(currentTextLiteral);
if (isFirst) {
currentTextLiteral.isFirst = true;
}
}
} else {
currentTextLiteral = null;
}
isFirst = false;
});
if (currentTextLiteral) {
// Last child text
currentTextLiteral.isLast = true;
}
literalTextNodes.forEach(trim);
}
isWhitespace() {
var argument = this.argument;
return (argument instanceof Literal) &&
(typeof argument.value === 'string') &&
(argument.value.trim() === '');
}
appendText(text) {
if (!this.isLiteral()) {
throw new Error('Text cannot be appended to a non-literal TextOutput node');
}
this.argument.value += text;
}
toJSON() {
return {
type: this.type,
argument: this.argument
};
}
}
module.exports = TextOutput;