1
1
'use strict'
2
2
3
3
var xtend = require ( 'xtend' )
4
+ var svg = require ( 'property-information/svg' )
5
+ var find = require ( 'property-information/find' )
4
6
var spaces = require ( 'space-separated-tokens' ) . stringify
5
7
var commas = require ( 'comma-separated-tokens' ) . stringify
6
- var information = require ( 'property-information' )
7
8
var entities = require ( 'stringify-entities' )
8
- var kebab = require ( 'kebab-case' )
9
9
var ccount = require ( 'ccount' )
10
10
var all = require ( './all' )
11
+ var constants = require ( './constants' )
11
12
12
13
module . exports = element
13
14
14
15
/* Constants. */
15
- var DATA = 'data'
16
16
var EMPTY = ''
17
17
18
18
/* Characters. */
@@ -26,12 +26,37 @@ var SO = '/'
26
26
27
27
/* Stringify an element `node`. */
28
28
function element ( ctx , node , index , parent ) {
29
+ var parentSchema = ctx . schema
29
30
var name = node . tagName
30
- var content = all ( ctx , name === 'template' ? node . content : node )
31
- var selfClosing = ctx . voids . indexOf ( name . toLowerCase ( ) ) !== - 1
32
- var attrs = attributes ( ctx , node . properties )
33
- var omit = ctx . omit
34
31
var value = ''
32
+ var selfClosing
33
+ var close
34
+ var omit
35
+ var root = node
36
+ var content
37
+ var attrs
38
+
39
+ if ( parentSchema . space === 'html' && name === 'svg' ) {
40
+ ctx . schema = svg
41
+ }
42
+
43
+ attrs = attributes ( ctx , node . properties )
44
+
45
+ if ( ctx . schema . space === 'svg' ) {
46
+ omit = false
47
+ close = true
48
+ selfClosing = ctx . closeEmpty
49
+ } else {
50
+ omit = ctx . omit
51
+ close = ctx . close
52
+ selfClosing = ctx . voids . indexOf ( name . toLowerCase ( ) ) !== - 1
53
+
54
+ if ( name === 'template' ) {
55
+ root = node . content
56
+ }
57
+ }
58
+
59
+ content = all ( ctx , root )
35
60
36
61
/* If the node is categorised as void, but it has
37
62
* children, remove the categorisation. This
@@ -43,7 +68,7 @@ function element(ctx, node, index, parent) {
43
68
if ( attrs || ! omit || ! omit . opening ( node , index , parent ) ) {
44
69
value = LT + name + ( attrs ? SPACE + attrs : EMPTY )
45
70
46
- if ( selfClosing && ctx . close ) {
71
+ if ( selfClosing && close ) {
47
72
if ( ! ctx . tightClose || attrs . charAt ( attrs . length - 1 ) === SO ) {
48
73
value += SPACE
49
74
}
@@ -60,6 +85,8 @@ function element(ctx, node, index, parent) {
60
85
value += LT + SO + name + GT
61
86
}
62
87
88
+ ctx . schema = parentSchema
89
+
63
90
return value
64
91
}
65
92
@@ -92,7 +119,11 @@ function attributes(ctx, props) {
92
119
93
120
while ( ++ index < length ) {
94
121
result = values [ index ]
95
- last = ctx . tight && result . charAt ( result . length - 1 )
122
+ last = null
123
+
124
+ if ( ctx . schema . space === 'html' && ctx . tight ) {
125
+ last = result . charAt ( result . length - 1 )
126
+ }
96
127
97
128
/* In tight mode, don’t add a space after quoted attributes. */
98
129
if ( index !== length - 1 && last !== DQ && last !== SQ ) {
@@ -105,49 +136,50 @@ function attributes(ctx, props) {
105
136
106
137
/* Stringify one attribute. */
107
138
function attribute ( ctx , key , value ) {
108
- var info = information ( key ) || { }
139
+ var schema = ctx . schema
140
+ var space = schema . space
141
+ var info = find ( schema , key )
109
142
var name
110
143
111
144
if (
112
145
value == null ||
146
+ value === false ||
113
147
( typeof value === 'number' && isNaN ( value ) ) ||
114
- ( ! value && info . boolean ) ||
115
- ( value === false && info . overloadedBoolean )
148
+ ( ! value && info . boolean )
116
149
) {
117
150
return EMPTY
118
151
}
119
152
120
- name = attributeName ( ctx , key )
153
+ name = attributeName ( ctx , info . attribute )
121
154
122
- if ( ( value && info . boolean ) || ( value === true && info . overloadedBoolean ) ) {
155
+ if ( value === true || ( value && info . boolean ) ) {
156
+ value = name
157
+ }
158
+
159
+ if ( space === 'html' && value === name ) {
123
160
return name
124
161
}
125
162
126
- return name + attributeValue ( ctx , key , value )
163
+ return name + attributeValue ( ctx , key , value , info )
127
164
}
128
165
129
166
/* Stringify the attribute name. */
130
- function attributeName ( ctx , key ) {
131
- var info = information ( key ) || { }
132
- var name = info . name || kebab ( key )
167
+ function attributeName ( ctx , name ) {
168
+ // Always encode without parse errors in non-HTML.
169
+ var valid = ctx . schema . space === 'html' ? ctx . valid : 1
170
+ var subset = constants . name [ valid ] [ ctx . safe ]
133
171
134
- if (
135
- name . slice ( 0 , DATA . length ) === DATA &&
136
- / \d / . test ( name . charAt ( DATA . length ) )
137
- ) {
138
- name = DATA + '-' + name . slice ( 4 )
139
- }
140
-
141
- return entities ( name , xtend ( ctx . entities , { subset : ctx . NAME } ) )
172
+ return entities ( name , xtend ( ctx . entities , { subset : subset } ) )
142
173
}
143
174
144
175
/* Stringify the attribute value. */
145
- function attributeValue ( ctx , key , value ) {
146
- var info = information ( key ) || { }
176
+ function attributeValue ( ctx , key , value , info ) {
147
177
var options = ctx . entities
148
178
var quote = ctx . quote
149
179
var alternative = ctx . alternative
180
+ var space = ctx . schema . space
150
181
var unquoted
182
+ var subset
151
183
152
184
if ( typeof value === 'object' && 'length' in value ) {
153
185
/* `spaces` doesn’t accept a second argument, but it’s
@@ -159,31 +191,30 @@ function attributeValue(ctx, key, value) {
159
191
160
192
value = String ( value )
161
193
162
- if ( value || ! ctx . collapseEmpty ) {
194
+ if ( space !== 'html' || value || ! ctx . collapseEmpty ) {
163
195
unquoted = value
164
196
165
197
/* Check unquoted value. */
166
- if ( ctx . unquoted ) {
198
+ if ( space === 'html' && ctx . unquoted ) {
199
+ subset = constants . unquoted [ ctx . valid ] [ ctx . safe ]
167
200
unquoted = entities (
168
201
value ,
169
- xtend ( options , { subset : ctx . UNQUOTED , attribute : true } )
202
+ xtend ( options , { subset : subset , attribute : true } )
170
203
)
171
204
}
172
205
173
206
/* If `value` contains entities when unquoted... */
174
- if ( ! ctx . unquoted || unquoted !== value ) {
207
+ if ( space !== 'html' || ! ctx . unquoted || unquoted !== value ) {
175
208
/* If the alternative is less common than `quote`, switch. */
176
209
if ( alternative && ccount ( value , quote ) > ccount ( value , alternative ) ) {
177
210
quote = alternative
178
211
}
179
212
180
- value = entities (
181
- value ,
182
- xtend ( options , {
183
- subset : quote === SQ ? ctx . SINGLE_QUOTED : ctx . DOUBLE_QUOTED ,
184
- attribute : true
185
- } )
186
- )
213
+ subset = quote === SQ ? constants . single : constants . double
214
+ // Always encode without parse errors in non-HTML.
215
+ subset = subset [ space === 'html' ? ctx . valid : 1 ] [ ctx . safe ]
216
+
217
+ value = entities ( value , xtend ( options , { subset : subset , attribute : true } ) )
187
218
188
219
value = quote + value + quote
189
220
}
0 commit comments