Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80728 views
1
"use strict";
2
var Visitor = require("./visitor")["default"];
3
4
function WhitespaceControl() {
5
}
6
WhitespaceControl.prototype = new Visitor();
7
8
WhitespaceControl.prototype.Program = function(program) {
9
var isRoot = !this.isRootSeen;
10
this.isRootSeen = true;
11
12
var body = program.body;
13
for (var i = 0, l = body.length; i < l; i++) {
14
var current = body[i],
15
strip = this.accept(current);
16
17
if (!strip) {
18
continue;
19
}
20
21
var _isPrevWhitespace = isPrevWhitespace(body, i, isRoot),
22
_isNextWhitespace = isNextWhitespace(body, i, isRoot),
23
24
openStandalone = strip.openStandalone && _isPrevWhitespace,
25
closeStandalone = strip.closeStandalone && _isNextWhitespace,
26
inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace;
27
28
if (strip.close) {
29
omitRight(body, i, true);
30
}
31
if (strip.open) {
32
omitLeft(body, i, true);
33
}
34
35
if (inlineStandalone) {
36
omitRight(body, i);
37
38
if (omitLeft(body, i)) {
39
// If we are on a standalone node, save the indent info for partials
40
if (current.type === 'PartialStatement') {
41
// Pull out the whitespace from the final line
42
current.indent = (/([ \t]+$)/).exec(body[i-1].original)[1];
43
}
44
}
45
}
46
if (openStandalone) {
47
omitRight((current.program || current.inverse).body);
48
49
// Strip out the previous content node if it's whitespace only
50
omitLeft(body, i);
51
}
52
if (closeStandalone) {
53
// Always strip the next node
54
omitRight(body, i);
55
56
omitLeft((current.inverse || current.program).body);
57
}
58
}
59
60
return program;
61
};
62
WhitespaceControl.prototype.BlockStatement = function(block) {
63
this.accept(block.program);
64
this.accept(block.inverse);
65
66
// Find the inverse program that is involed with whitespace stripping.
67
var program = block.program || block.inverse,
68
inverse = block.program && block.inverse,
69
firstInverse = inverse,
70
lastInverse = inverse;
71
72
if (inverse && inverse.chained) {
73
firstInverse = inverse.body[0].program;
74
75
// Walk the inverse chain to find the last inverse that is actually in the chain.
76
while (lastInverse.chained) {
77
lastInverse = lastInverse.body[lastInverse.body.length-1].program;
78
}
79
}
80
81
var strip = {
82
open: block.openStrip.open,
83
close: block.closeStrip.close,
84
85
// Determine the standalone candiacy. Basically flag our content as being possibly standalone
86
// so our parent can determine if we actually are standalone
87
openStandalone: isNextWhitespace(program.body),
88
closeStandalone: isPrevWhitespace((firstInverse || program).body)
89
};
90
91
if (block.openStrip.close) {
92
omitRight(program.body, null, true);
93
}
94
95
if (inverse) {
96
var inverseStrip = block.inverseStrip;
97
98
if (inverseStrip.open) {
99
omitLeft(program.body, null, true);
100
}
101
102
if (inverseStrip.close) {
103
omitRight(firstInverse.body, null, true);
104
}
105
if (block.closeStrip.open) {
106
omitLeft(lastInverse.body, null, true);
107
}
108
109
// Find standalone else statments
110
if (isPrevWhitespace(program.body)
111
&& isNextWhitespace(firstInverse.body)) {
112
113
omitLeft(program.body);
114
omitRight(firstInverse.body);
115
}
116
} else {
117
if (block.closeStrip.open) {
118
omitLeft(program.body, null, true);
119
}
120
}
121
122
return strip;
123
};
124
125
WhitespaceControl.prototype.MustacheStatement = function(mustache) {
126
return mustache.strip;
127
};
128
129
WhitespaceControl.prototype.PartialStatement =
130
WhitespaceControl.prototype.CommentStatement = function(node) {
131
/* istanbul ignore next */
132
var strip = node.strip || {};
133
return {
134
inlineStandalone: true,
135
open: strip.open,
136
close: strip.close
137
};
138
};
139
140
141
function isPrevWhitespace(body, i, isRoot) {
142
if (i === undefined) {
143
i = body.length;
144
}
145
146
// Nodes that end with newlines are considered whitespace (but are special
147
// cased for strip operations)
148
var prev = body[i-1],
149
sibling = body[i-2];
150
if (!prev) {
151
return isRoot;
152
}
153
154
if (prev.type === 'ContentStatement') {
155
return (sibling || !isRoot ? (/\r?\n\s*?$/) : (/(^|\r?\n)\s*?$/)).test(prev.original);
156
}
157
}
158
function isNextWhitespace(body, i, isRoot) {
159
if (i === undefined) {
160
i = -1;
161
}
162
163
var next = body[i+1],
164
sibling = body[i+2];
165
if (!next) {
166
return isRoot;
167
}
168
169
if (next.type === 'ContentStatement') {
170
return (sibling || !isRoot ? (/^\s*?\r?\n/) : (/^\s*?(\r?\n|$)/)).test(next.original);
171
}
172
}
173
174
// Marks the node to the right of the position as omitted.
175
// I.e. {{foo}}' ' will mark the ' ' node as omitted.
176
//
177
// If i is undefined, then the first child will be marked as such.
178
//
179
// If mulitple is truthy then all whitespace will be stripped out until non-whitespace
180
// content is met.
181
function omitRight(body, i, multiple) {
182
var current = body[i == null ? 0 : i + 1];
183
if (!current || current.type !== 'ContentStatement' || (!multiple && current.rightStripped)) {
184
return;
185
}
186
187
var original = current.value;
188
current.value = current.value.replace(multiple ? (/^\s+/) : (/^[ \t]*\r?\n?/), '');
189
current.rightStripped = current.value !== original;
190
}
191
192
// Marks the node to the left of the position as omitted.
193
// I.e. ' '{{foo}} will mark the ' ' node as omitted.
194
//
195
// If i is undefined then the last child will be marked as such.
196
//
197
// If mulitple is truthy then all whitespace will be stripped out until non-whitespace
198
// content is met.
199
function omitLeft(body, i, multiple) {
200
var current = body[i == null ? body.length - 1 : i - 1];
201
if (!current || current.type !== 'ContentStatement' || (!multiple && current.leftStripped)) {
202
return;
203
}
204
205
// We omit the last node if it's whitespace only and not preceeded by a non-content node.
206
var original = current.value;
207
current.value = current.value.replace(multiple ? (/\s+$/) : (/[ \t]+$/), '');
208
current.leftStripped = current.value !== original;
209
return current.leftStripped;
210
}
211
212
exports["default"] = WhitespaceControl;
213