11'use strict'
22
33var xtend = require ( 'xtend' )
4+ var svg = require ( 'property-information/svg' )
5+ var find = require ( 'property-information/find' )
46var spaces = require ( 'space-separated-tokens' ) . stringify
57var commas = require ( 'comma-separated-tokens' ) . stringify
6- var information = require ( 'property-information' )
78var entities = require ( 'stringify-entities' )
8- var kebab = require ( 'kebab-case' )
99var ccount = require ( 'ccount' )
1010var all = require ( './all' )
11+ var constants = require ( './constants' )
1112
1213module . exports = element
1314
1415/* Constants. */
15- var DATA = 'data'
1616var EMPTY = ''
1717
1818/* Characters. */
@@ -26,12 +26,37 @@ var SO = '/'
2626
2727/* Stringify an element `node`. */
2828function element ( ctx , node , index , parent ) {
29+ var parentSchema = ctx . schema
2930 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
3431 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 )
3560
3661 /* If the node is categorised as void, but it has
3762 * children, remove the categorisation. This
@@ -43,7 +68,7 @@ function element(ctx, node, index, parent) {
4368 if ( attrs || ! omit || ! omit . opening ( node , index , parent ) ) {
4469 value = LT + name + ( attrs ? SPACE + attrs : EMPTY )
4570
46- if ( selfClosing && ctx . close ) {
71+ if ( selfClosing && close ) {
4772 if ( ! ctx . tightClose || attrs . charAt ( attrs . length - 1 ) === SO ) {
4873 value += SPACE
4974 }
@@ -60,6 +85,8 @@ function element(ctx, node, index, parent) {
6085 value += LT + SO + name + GT
6186 }
6287
88+ ctx . schema = parentSchema
89+
6390 return value
6491}
6592
@@ -92,7 +119,11 @@ function attributes(ctx, props) {
92119
93120 while ( ++ index < length ) {
94121 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+ }
96127
97128 /* In tight mode, don’t add a space after quoted attributes. */
98129 if ( index !== length - 1 && last !== DQ && last !== SQ ) {
@@ -105,49 +136,50 @@ function attributes(ctx, props) {
105136
106137/* Stringify one attribute. */
107138function 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 )
109142 var name
110143
111144 if (
112145 value == null ||
146+ value === false ||
113147 ( typeof value === 'number' && isNaN ( value ) ) ||
114- ( ! value && info . boolean ) ||
115- ( value === false && info . overloadedBoolean )
148+ ( ! value && info . boolean )
116149 ) {
117150 return EMPTY
118151 }
119152
120- name = attributeName ( ctx , key )
153+ name = attributeName ( ctx , info . attribute )
121154
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 ) {
123160 return name
124161 }
125162
126- return name + attributeValue ( ctx , key , value )
163+ return name + attributeValue ( ctx , key , value , info )
127164}
128165
129166/* 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 ]
133171
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 } ) )
142173}
143174
144175/* Stringify the attribute value. */
145- function attributeValue ( ctx , key , value ) {
146- var info = information ( key ) || { }
176+ function attributeValue ( ctx , key , value , info ) {
147177 var options = ctx . entities
148178 var quote = ctx . quote
149179 var alternative = ctx . alternative
180+ var space = ctx . schema . space
150181 var unquoted
182+ var subset
151183
152184 if ( typeof value === 'object' && 'length' in value ) {
153185 /* `spaces` doesn’t accept a second argument, but it’s
@@ -159,31 +191,30 @@ function attributeValue(ctx, key, value) {
159191
160192 value = String ( value )
161193
162- if ( value || ! ctx . collapseEmpty ) {
194+ if ( space !== 'html' || value || ! ctx . collapseEmpty ) {
163195 unquoted = value
164196
165197 /* Check unquoted value. */
166- if ( ctx . unquoted ) {
198+ if ( space === 'html' && ctx . unquoted ) {
199+ subset = constants . unquoted [ ctx . valid ] [ ctx . safe ]
167200 unquoted = entities (
168201 value ,
169- xtend ( options , { subset : ctx . UNQUOTED , attribute : true } )
202+ xtend ( options , { subset : subset , attribute : true } )
170203 )
171204 }
172205
173206 /* If `value` contains entities when unquoted... */
174- if ( ! ctx . unquoted || unquoted !== value ) {
207+ if ( space !== 'html' || ! ctx . unquoted || unquoted !== value ) {
175208 /* If the alternative is less common than `quote`, switch. */
176209 if ( alternative && ccount ( value , quote ) > ccount ( value , alternative ) ) {
177210 quote = alternative
178211 }
179212
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 } ) )
187218
188219 value = quote + value + quote
189220 }
0 commit comments