Skip to content

Commit 8c490a7

Browse files
committed
Add support for SVG
Related to wooorm/property-information#6. Related to GH-6. Related to GH-4.
1 parent 2c43757 commit 8c490a7

File tree

7 files changed

+917
-450
lines changed

7 files changed

+917
-450
lines changed

factory.js

+194
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
'use strict'
2+
3+
var find = require('property-information/find')
4+
var normalize = require('property-information/normalize')
5+
var parseSelector = require('hast-util-parse-selector')
6+
var spaces = require('space-separated-tokens').parse
7+
var commas = require('comma-separated-tokens').parse
8+
9+
module.exports = factory
10+
11+
function factory(schema, defaultTagName) {
12+
return h
13+
14+
/* Hyperscript compatible DSL for creating virtual HAST trees. */
15+
function h(selector, properties, children) {
16+
var node = parseSelector(selector, defaultTagName)
17+
var property
18+
19+
if (!children && properties && isChildren(properties, node)) {
20+
children = properties
21+
properties = null
22+
}
23+
24+
if (properties) {
25+
for (property in properties) {
26+
addProperty(node.properties, property, properties[property])
27+
}
28+
}
29+
30+
addChild(node.children, children)
31+
32+
if (node.tagName === 'template') {
33+
node.content = {type: 'root', children: node.children}
34+
node.children = []
35+
}
36+
37+
return node
38+
}
39+
40+
function addProperty(properties, key, value) {
41+
var info
42+
var property
43+
var result
44+
45+
/* Ignore nully and NaN values. */
46+
if (value === null || value === undefined || value !== value) {
47+
return
48+
}
49+
50+
info = find(schema, key)
51+
property = info.property
52+
result = value
53+
54+
/* Handle list values. */
55+
if (typeof result === 'string') {
56+
if (info.spaceSeparated) {
57+
result = spaces(result)
58+
} else if (info.commaSeparated) {
59+
result = commas(result)
60+
} else if (info.commaOrSpaceSeparated) {
61+
result = spaces(commas(result).join(' '))
62+
}
63+
}
64+
65+
/* Accept `object` on style. */
66+
if (property === 'style' && typeof value !== 'string') {
67+
result = style(result)
68+
}
69+
70+
/* Class-names (which can be added both on the `selector` and here). */
71+
if (property === 'className' && properties.className) {
72+
result = properties.className.concat(result)
73+
}
74+
75+
properties[property] = parsePrimitives(info, property, result)
76+
}
77+
}
78+
79+
function isChildren(value, node) {
80+
return (
81+
typeof value === 'string' ||
82+
'length' in value ||
83+
isNode(node.tagName, value)
84+
)
85+
}
86+
87+
function isNode(tagName, value) {
88+
var type = value.type
89+
90+
if (tagName === 'input' || !type || typeof type !== 'string') {
91+
return false
92+
}
93+
94+
if (typeof value.children === 'object' && 'length' in value.children) {
95+
return true
96+
}
97+
98+
type = type.toLowerCase()
99+
100+
if (tagName === 'button') {
101+
return (
102+
type !== 'menu' &&
103+
type !== 'submit' &&
104+
type !== 'reset' &&
105+
type !== 'button'
106+
)
107+
}
108+
109+
return 'value' in value
110+
}
111+
112+
function addChild(nodes, value) {
113+
var index
114+
var length
115+
116+
if (value === null || value === undefined) {
117+
return
118+
}
119+
120+
if (typeof value === 'string' || typeof value === 'number') {
121+
nodes.push({type: 'text', value: String(value)})
122+
return
123+
}
124+
125+
if (typeof value === 'object' && 'length' in value) {
126+
index = -1
127+
length = value.length
128+
129+
while (++index < length) {
130+
addChild(nodes, value[index])
131+
}
132+
133+
return
134+
}
135+
136+
if (typeof value !== 'object' || !('type' in value)) {
137+
throw new Error('Expected node, nodes, or string, got `' + value + '`')
138+
}
139+
140+
nodes.push(value)
141+
}
142+
143+
/* Parse a (list of) primitives. */
144+
function parsePrimitives(info, name, value) {
145+
var index
146+
var length
147+
var result
148+
149+
if (typeof value !== 'object' || !('length' in value)) {
150+
return parsePrimitive(info, name, value)
151+
}
152+
153+
length = value.length
154+
index = -1
155+
result = []
156+
157+
while (++index < length) {
158+
result[index] = parsePrimitive(info, name, value[index])
159+
}
160+
161+
return result
162+
}
163+
164+
/* Parse a single primitives. */
165+
function parsePrimitive(info, name, value) {
166+
var result = value
167+
168+
if (info.number || info.positiveNumber) {
169+
if (!isNaN(result) && result !== '') {
170+
result = Number(result)
171+
}
172+
} else if (info.boolean || info.overloadedBoolean) {
173+
/* Accept `boolean` and `string`. */
174+
if (
175+
typeof result === 'string' &&
176+
(result === '' || normalize(value) === normalize(name))
177+
) {
178+
result = true
179+
}
180+
}
181+
182+
return result
183+
}
184+
185+
function style(value) {
186+
var result = []
187+
var key
188+
189+
for (key in value) {
190+
result.push([key, value[key]].join(': '))
191+
}
192+
193+
return result.join('; ')
194+
}

html.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
'use strict'
2+
3+
var schema = require('property-information/html')
4+
var factory = require('./factory')
5+
6+
var html = factory(schema, 'div')
7+
html.displayName = 'html'
8+
9+
module.exports = html

index.js

+1-178
Original file line numberDiff line numberDiff line change
@@ -1,180 +1,3 @@
11
'use strict'
22

3-
var parseSelector = require('hast-util-parse-selector')
4-
var camelcase = require('camelcase')
5-
var propertyInformation = require('property-information')
6-
var spaces = require('space-separated-tokens').parse
7-
var commas = require('comma-separated-tokens').parse
8-
9-
module.exports = h
10-
11-
/* Hyperscript compatible DSL for creating virtual HAST
12-
* trees. */
13-
function h(selector, properties, children) {
14-
var node = parseSelector(selector)
15-
var property
16-
17-
if (
18-
properties &&
19-
!children &&
20-
(typeof properties === 'string' ||
21-
'length' in properties ||
22-
isNode(node.tagName, properties))
23-
) {
24-
children = properties
25-
properties = null
26-
}
27-
28-
if (properties) {
29-
for (property in properties) {
30-
addProperty(node.properties, property, properties[property])
31-
}
32-
}
33-
34-
addChild(node.children, children)
35-
36-
if (node.tagName === 'template') {
37-
node.content = {type: 'root', children: node.children}
38-
node.children = []
39-
}
40-
41-
return node
42-
}
43-
44-
/* Check if `value` is a valid child node of `tagName`. */
45-
function isNode(tagName, value) {
46-
var type = value.type
47-
48-
if (typeof type === 'string') {
49-
type = type.toLowerCase()
50-
}
51-
52-
if (tagName === 'input' || !type || typeof type !== 'string') {
53-
return false
54-
}
55-
56-
if (typeof value.children === 'object' && 'length' in value.children) {
57-
return true
58-
}
59-
60-
if (tagName === 'button') {
61-
return (
62-
type !== 'menu' &&
63-
type !== 'submit' &&
64-
type !== 'reset' &&
65-
type !== 'button'
66-
)
67-
}
68-
69-
return 'value' in value
70-
}
71-
72-
/* Add `value` as a child to `nodes`. */
73-
function addChild(nodes, value) {
74-
var index
75-
var length
76-
77-
if (value === null || value === undefined) {
78-
return
79-
}
80-
81-
if (typeof value === 'string' || typeof value === 'number') {
82-
value = {type: 'text', value: String(value)}
83-
}
84-
85-
if (typeof value === 'object' && 'length' in value) {
86-
index = -1
87-
length = value.length
88-
89-
while (++index < length) {
90-
addChild(nodes, value[index])
91-
}
92-
93-
return
94-
}
95-
96-
if (typeof value !== 'object' || !('type' in value)) {
97-
throw new Error('Expected node, nodes, or string, got `' + value + '`')
98-
}
99-
100-
nodes.push(value)
101-
}
102-
103-
/* Add `name` and its `value` to `properties`. `properties` can
104-
* be prefilled by `parseSelector`: it can have `id` and `className`
105-
* properties. */
106-
function addProperty(properties, name, value) {
107-
var info = propertyInformation(name) || {}
108-
var result = value
109-
var key
110-
111-
/* Ignore nully and NaN values. */
112-
if (value === null || value === undefined || value !== value) {
113-
return
114-
}
115-
116-
/* Handle values. */
117-
if (name === 'style') {
118-
/* Accept `object`. */
119-
if (typeof value !== 'string') {
120-
result = []
121-
122-
for (key in value) {
123-
result.push([key, value[key]].join(': '))
124-
}
125-
126-
result = result.join('; ')
127-
}
128-
} else if (info.spaceSeparated) {
129-
/* Accept both `string` and `Array`. */
130-
result = typeof value === 'string' ? spaces(result) : result
131-
132-
/* Class-names (which can be added both on
133-
* the `selector` and here). */
134-
if (name === 'class' && properties.className) {
135-
result = properties.className.concat(result)
136-
}
137-
} else if (info.commaSeparated) {
138-
/* Accept both `string` and `Array`. */
139-
result = typeof value === 'string' ? commas(result) : result
140-
}
141-
142-
result = parsePrimitive(info, name, result)
143-
144-
properties[info.propertyName || camelcase(name)] = result
145-
}
146-
147-
/* Parse a (list of) primitives. */
148-
function parsePrimitive(info, name, value) {
149-
var result = value
150-
var index
151-
var length
152-
153-
if (typeof value === 'object' && 'length' in value) {
154-
length = value.length
155-
index = -1
156-
result = []
157-
158-
while (++index < length) {
159-
result[index] = parsePrimitive(info, name, value[index])
160-
}
161-
162-
return result
163-
}
164-
165-
if (info.numeric || info.positiveNumeric) {
166-
if (!isNaN(result) && result !== '') {
167-
result = Number(result)
168-
}
169-
} else if (info.boolean || info.overloadedBoolean) {
170-
/* Accept `boolean` and `string`. */
171-
if (
172-
typeof result === 'string' &&
173-
(result === '' || value.toLowerCase() === name)
174-
) {
175-
result = true
176-
}
177-
}
178-
179-
return result
180-
}
3+
module.exports = require('./html')

0 commit comments

Comments
 (0)