-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathnode.coffee
More file actions
265 lines (221 loc) · 7.28 KB
/
Copy pathnode.coffee
File metadata and controls
265 lines (221 loc) · 7.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
{escapeHTML} = require('../util/text')
# Base class for the syntax tree.
#
# This will provide some methods that subclasses must use in order to generate
# some output:
#
# * {#markText}
# * {#markRunningCode}
# * {#markInsertingCode}
#
# Each node must mark the `@opener` attribute and can optionally mark the `@closer`
# attribute.
#
# @abstract
#
module.exports = class Node
# Hidden unicode marker to remove left whitespace after template rendering.
@CLEAR_WHITESPACE_LEFT = '\u0091'
# Hidden unicode marker to remove right whitespace after template rendering.
@CLEAR_WHITESPACE_RIGHT = '\u0092'
# Constructs a syntax node.
#
# @param [String] expression the Haml expression to evaluate
# @param [Object] options the node options
# @option options [Node] parentNode the parent node
# @option options [Number] blockLevel the HTML block level
# @option options [Number] codeBlockLevel the CoffeeScript block level
# @option options [Boolean] escapeHtml whether to escape the rendered HTML or not
# @option options [String] format the template format, either `xhtml`, `html4` or `html5`
#
constructor: (@expression = '', @options = {}) ->
@parentNode = @options.parentNode
@children = []
@opener = @closer = null
# A silent node swallows all output
@silent = false
# Preserve whitespace on all children
@preserveTags = @options.preserveTags.split(',')
@preserve = false
@wsRemoval = {
around: false
inside: false
}
@escapeHtml = @options.escapeHtml
@escapeAttributes = @options.escapeAttributes
@cleanValue = @options.cleanValue
@format = @options.format
@hyphenateDataAttrs = @options.hyphenateDataAttrs
@selfCloseTags = @options.selfCloseTags.split(',')
@uglify = @options.uglify
@codeBlockLevel = @options.codeBlockLevel
@blockLevel = @options.blockLevel
@placement = @options.placement
@namespace = @options.namespace
@name = @options.name
# Add a child node.
#
# @param [Node] child the child node
#
addChild: (child) ->
@children.push child
@
# Get the opening tag for the node.
#
# This may add a hidden unicode control character for
# later whitespace processing:
#
# * `\u0091` Cleanup surrounding whitespace to the left
# * `\u0092` Cleanup surrounding whitespace to the right
#
# @return [String] the opening tag
#
getOpener: ->
@opener.text = Node.CLEAR_WHITESPACE_LEFT + @opener.text if @wsRemoval.around and @opener.text
@opener.text += Node.CLEAR_WHITESPACE_RIGHT if @wsRemoval.inside and @opener.text
@opener
# Get the closing tag for the node.
#
# This may add a hidden unicode control character for
# later whitespace processing:
#
# * `\u0091` Cleanup surrounding whitespace to the left
# * `\u0092` Cleanup surrounding whitespace to the right
#
# @return [String] the closing tag
#
getCloser: ->
@closer.text = Node.CLEAR_WHITESPACE_LEFT + @closer.text if @wsRemoval.inside and @closer.text
@closer.text += Node.CLEAR_WHITESPACE_RIGHT if @wsRemoval.around and @closer.text
@closer
# Traverse up the tree to see if a parent node
# is preserving output space.
#
# @return [Boolean] true when preserved
#
isPreserved: ->
return true if @preserve
if @parentNode
@parentNode.isPreserved()
else
false
# Traverse up the tree to see if a parent node
# is a comment node.
#
# @return [Boolean] true when within a comment
#
isCommented: ->
return true if @constructor.name is 'Comment'
if @parentNode
@parentNode.isCommented()
else
false
# Creates a marker for static outputted text.
#
# @param [String] html the html to output
# @param [Boolean] escape whether to escape the generated output
# @return [Object] the marker
#
markText: (text, escape = false) ->
{
type : 'text'
cw : @codeBlockLevel
hw : if @uglify then 0 else @blockLevel - @codeBlockLevel
text : if escape then escapeHTML(text) else text
}
# Creates a marker for running CoffeeScript
# code that doesn't generate any output.
#
# @param [String] code the CoffeeScript code
# @return [Object] the marker
#
markRunningCode: (code) ->
{
type : 'run'
cw : @codeBlockLevel
code : code
}
# Creates a marker for inserting CoffeeScript
# code that generate an output.
#
# @param [String] code the CoffeeScript code
# @param [Boolean] escape whether to escape the generated output
# @param [Boolean] preserve when preserve all newlines
# @param [Boolean] findAndPreserve when preserve newlines within preserved tags
# @return [Object] the marker
#
markInsertingCode: (code, escape = false, preserve = false, findAndPreserve = false) ->
{
type : 'insert'
cw : @codeBlockLevel
hw : if @uglify then 0 else @blockLevel - @codeBlockLevel
escape : escape
preserve : preserve
findAndPreserve : findAndPreserve
code : code
}
# Template method that must be implemented by each
# Node subclass. This evaluates the `@expression`
# and save marks the output type on the `@opener` and
# `@closer` attributes if applicable.
#
# @abstract
#
evaluate: ->
# Render the node and its children.
#
# Always use `@opener` and `@closer` for content checks,
# but `@getOpener()` and `@getCloser()` for outputting,
# because they may contain whitespace removal control
# characters.
#
# @return [Array] all markers
#
render: ->
output = []
# Swallow child output when silent
return output if @silent
# Nodes without children
if @children.length is 0
# Non self closing tag
if @opener and @closer
# Merge tag into a single line
tag = @getOpener()
tag.text += @getCloser().text
output.push tag
# Self closing tag
else
# Whitespace preserved child tag are outputted by the preserving tag
if not @preserve && @isPreserved()
output.push @getOpener()
# Normal self closing tag
else
output.push @getOpener()
# Nodes with children
else
# Non self closing Haml tag
if @opener and @closer
# Whitespace preserving tag combines children into a single line
if @preserve
# Preserved tags removes the inside whitespace
@wsRemoval.inside = true
output.push @getOpener()
for child in @children
for rendered in child.render()
# Move all children's block level to the preserving tag
rendered.hw = @blockLevel
output.push rendered
output.push @getCloser()
# Non preserving tag
else
output.push @getOpener()
output = output.concat(child.render()) for child in @children
output.push @getCloser()
# Block with only an opener
else if @opener
output.push @getOpener()
output = output.concat(child.render()) for child in @children
# Text and code node or Haml nodes without content
else
output.push @markText(child.render().text) for child in @children
output