Skip to content

Commit aec62ec

Browse files
committed
Add support for SVG
Related to wooorm/property-information#6.
1 parent d0ce669 commit aec62ec

File tree

9 files changed

+101
-60
lines changed

9 files changed

+101
-60
lines changed

index.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,18 @@ exports.matches = matches
1717
exports.selectAll = selectAll
1818
exports.select = select
1919

20-
function matches(selector, node) {
21-
return Boolean(any(parse(selector), node, {one: true, shallow: true})[0])
20+
function matches(selector, node, space) {
21+
return Boolean(
22+
any(parse(selector), node, {space: space, one: true, shallow: true})[0]
23+
)
2224
}
2325

24-
function select(selector, node) {
25-
return any(parse(selector), node, {one: true})[0] || null
26+
function select(selector, node, space) {
27+
return any(parse(selector), node, {space: space, one: true})[0] || null
2628
}
2729

28-
function selectAll(selector, node) {
29-
return any(parse(selector), node, {})
30+
function selectAll(selector, node, space) {
31+
return any(parse(selector), node, {space: space})
3032
}
3133

3234
function parse(selector) {

lib/any.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
module.exports = match
44

55
var zwitch = require('zwitch')
6+
var html = require('property-information/html')
7+
var svg = require('property-information/svg')
68
var needsIndex = require('./pseudo').needsIndex
79
var test = require('./test')
810
var nest = require('./nest')
@@ -39,7 +41,12 @@ function ruleSet(query, node, state) {
3941

4042
function rule(query, tree, state) {
4143
var collect = collector(state.one)
42-
var opts = {iterator: match, one: state.one, shallow: state.shallow}
44+
var opts = {
45+
schema: state.space === 'svg' ? svg : html,
46+
iterator: match,
47+
one: state.one,
48+
shallow: state.shallow
49+
}
4350

4451
if (state.shallow && query.rule) {
4552
throw new Error('Expected selector without nesting')

lib/attribute.js

Lines changed: 28 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@
22

33
module.exports = match
44

5-
var camelcase = require('camelcase')
65
var zwitch = require('zwitch')
76
var has = require('hast-util-has-property')
8-
var information = require('property-information')
9-
var spaceSeparated = require('space-separated-tokens')
10-
var commaSeparated = require('comma-separated-tokens')
7+
var find = require('property-information/find')
8+
var spaceSeparated = require('space-separated-tokens').stringify
9+
var commaSeparated = require('comma-separated-tokens').stringify
1110

1211
var handle = zwitch('operator')
1312
var handlers = handle.handlers
@@ -23,7 +22,7 @@ handlers['^='] = begins
2322
handlers['$='] = ends
2423
handlers['*='] = contains
2524

26-
function match(query, node) {
25+
function match(query, node, schema) {
2726
var attrs = query.attrs
2827
var length = attrs.length
2928
var index = -1
@@ -32,8 +31,7 @@ function match(query, node) {
3231

3332
while (++index < length) {
3433
attr = attrs[index]
35-
info = information(attr.name) || {}
36-
attr.propertyName = info.propertyName || camelcase(attr.name)
34+
info = find(schema, attr.name)
3735

3836
if (!handle(attr, node, info)) {
3937
return false
@@ -44,30 +42,28 @@ function match(query, node) {
4442
}
4543

4644
/* [attr] */
47-
function exists(query, node) {
48-
return has(node, query.propertyName)
45+
function exists(query, node, info) {
46+
return has(node, info.property)
4947
}
5048

5149
/* [attr=value] */
5250
function exact(query, node, info) {
53-
if (!has(node, query.propertyName)) {
51+
if (!has(node, info.property)) {
5452
return false
5553
}
5654

57-
return (
58-
normalizeValue(node.properties[query.propertyName], info) === query.value
59-
)
55+
return normalizeValue(node.properties[info.property], info) === query.value
6056
}
6157

6258
/* [attr~=value] */
6359
function spaceSeparatedList(query, node, info) {
6460
var val
6561

66-
if (!has(node, query.propertyName)) {
62+
if (!has(node, info.property)) {
6763
return false
6864
}
6965

70-
val = node.properties[query.propertyName]
66+
val = node.properties[info.property]
7167

7268
/* If this is a comma-separated list, and the query is contained in it,
7369
* return true. */
@@ -88,11 +84,11 @@ function spaceSeparatedList(query, node, info) {
8884
function exactOrPrefix(query, node, info) {
8985
var value
9086

91-
if (!has(node, query.propertyName)) {
87+
if (!has(node, info.property)) {
9288
return false
9389
}
9490

95-
value = normalizeValue(node.properties[query.propertyName], info)
91+
value = normalizeValue(node.properties[info.property], info)
9692

9793
return Boolean(
9894
value === query.value ||
@@ -103,39 +99,38 @@ function exactOrPrefix(query, node, info) {
10399

104100
/* [attr^=value] */
105101
function begins(query, node, info) {
106-
if (!has(node, query.propertyName)) {
102+
var value
103+
104+
if (!has(node, info.property)) {
107105
return false
108106
}
109107

110-
return (
111-
normalizeValue(node.properties[query.propertyName], info).slice(
112-
0,
113-
query.value.length
114-
) === query.value
115-
)
108+
value = normalizeValue(node.properties[info.property], info)
109+
110+
return value.slice(0, query.value.length) === query.value
116111
}
117112

118113
/* [attr$=value] */
119114
function ends(query, node, info) {
120-
if (!has(node, query.propertyName)) {
115+
if (!has(node, info.property)) {
121116
return false
122117
}
123118

124119
return (
125-
normalizeValue(node.properties[query.propertyName], info).slice(
120+
normalizeValue(node.properties[info.property], info).slice(
126121
-query.value.length
127122
) === query.value
128123
)
129124
}
130125

131126
/* [attr*=value] */
132127
function contains(query, node, info) {
133-
if (!has(node, query.propertyName)) {
128+
if (!has(node, info.property)) {
134129
return false
135130
}
136131

137132
return (
138-
normalizeValue(node.properties[query.propertyName], info).indexOf(
133+
normalizeValue(node.properties[info.property], info).indexOf(
139134
query.value
140135
) !== -1
141136
)
@@ -148,19 +143,12 @@ function unknownOperator(query) {
148143

149144
/* Stringify a HAST value back to its HTML form. */
150145
function normalizeValue(value, info) {
151-
if (info.commaSeparated) {
152-
value = commaSeparated.stringify(value)
153-
} else if (info.spaceSeparated) {
154-
value = spaceSeparated.stringify(value)
155-
} else if (info.boolean) {
156-
/* `false` is ignored by `has()`. */
157-
value = info.name
158-
} else if (info.overloadedBoolean) {
159-
if (value === true) {
160-
value = info.name
161-
}
162-
} else if (info.positiveNumeric || info.numeric) {
146+
if (typeof value === 'number') {
163147
value = String(value)
148+
} else if (typeof value === 'boolean') {
149+
value = info.attribute
150+
} else if (typeof value === 'object' && 'length' in value) {
151+
value = (info.commaSeparated ? commaSeparated : spaceSeparated)(value)
164152
}
165153

166154
return value

lib/nest.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict'
22

33
var zwitch = require('zwitch')
4+
var svg = require('property-information/svg')
45

56
module.exports = zwitch('nestingOperator')
67

@@ -27,8 +28,6 @@ function unknownNesting(query) {
2728
function topScan(query, node, index, parent, state) {
2829
/* istanbul ignore if - Shouldn’t happen. */
2930
if (parent) {
30-
// We would like to avoid spinning an extra loop through the starting
31-
// node's siblings just to count its typeIndex.
3231
throw new Error('topScan is supposed to be called from the root node')
3332
}
3433

@@ -99,6 +98,15 @@ function walkIterator(query, parent, state) {
9998
var nodes = parent.children
10099
var typeIndex = state.index ? createTypeIndex() : null
101100
var delayed = []
101+
var parentSchema = state.schema
102+
103+
if (
104+
parentSchema.space === 'html' &&
105+
parent.type === 'element' &&
106+
parent.tagName === 'svg'
107+
) {
108+
state.schema = svg
109+
}
102110

103111
return {
104112
prefillTypeIndex: rangeDefaults(prefillTypeIndex),
@@ -110,6 +118,8 @@ function walkIterator(query, parent, state) {
110118
var length = delayed.length
111119
var index = -1
112120

121+
state.schema = parentSchema
122+
113123
while (++index < length) {
114124
delayed[index]()
115125

lib/test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ function test(query, node, index, parent, state) {
1515
(!query.tagName || name(query, node)) &&
1616
(!query.classNames || classNames(query, node)) &&
1717
(!query.id || id(query, node)) &&
18-
(!query.attrs || attributes(query, node)) &&
18+
(!query.attrs || attributes(query, node, state.schema)) &&
1919
(!query.pseudos || pseudos(query, node, index, parent, state))
2020
)
2121
}

package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,14 @@
2020
"Titus Wormer <[email protected]> (http://wooorm.com)"
2121
],
2222
"dependencies": {
23-
"camelcase": "^3.0.0",
2423
"comma-separated-tokens": "^1.0.2",
2524
"css-selector-parser": "^1.3.0",
2625
"hast-util-has-property": "^1.0.0",
2726
"hast-util-is-element": "^1.0.0",
2827
"hast-util-whitespace": "^1.0.0",
2928
"not": "^0.1.0",
3029
"nth-check": "^1.0.1",
31-
"property-information": "^3.1.0",
30+
"property-information": "^4.0.0",
3231
"space-separated-tokens": "^1.1.0",
3332
"zwitch": "^1.0.0"
3433
},
@@ -39,7 +38,7 @@
3938
"devDependencies": {
4039
"browserify": "^16.0.0",
4140
"esmangle": "^1.0.1",
42-
"hastscript": "^3.0.1",
41+
"hastscript": "^4.0.0",
4342
"nyc": "^12.0.0",
4443
"prettier": "^1.13.5",
4544
"remark-cli": "^5.0.0",

readme.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ npm install hast-util-select
1919

2020
## API
2121

22-
### `select.matches(selector, node)`
22+
### `select.matches(selector, node[, space])`
2323

2424
This only checks the element itself, not the surrounding tree. Thus, nesting
2525
in selectors is not supported (`p b`, `p > b`), neither are selectors like
@@ -44,16 +44,18 @@ matches('[lang|=en]', h('a', {lang: 'en-GB'})) // => true
4444

4545
##### Parameters
4646

47-
* `node` (`Node`)
48-
— Thing to check, could be anything, but should be an [element][]
4947
* `selector` (`string`)
5048
— CSS selectors (`,` is also supported)
49+
* `node` (`Node`)
50+
— Thing to check, could be anything, but should be an [element][]
51+
* `space` (enum, `'svg'` or `'html'`, default: `'html'`)
52+
— Which space the node exists in
5153

5254
##### Returns
5355

5456
`boolean` — Whether the node matches the selector.
5557

56-
### `select.select(selector, tree)`
58+
### `select.select(selector, tree[, space])`
5759

5860
##### Usage
5961

@@ -89,14 +91,16 @@ tree itself).
8991

9092
##### Parameters
9193

92-
* `tree` (`Node`) — Thing to search.
9394
* `selector` (`string`) — CSS selectors (`,` is also supported)
95+
* `tree` (`Node`) — Thing to search.
96+
* `space` (enum, `'svg'` or `'html'`, default: `'html'`)
97+
— Which v the tree exists in
9498

9599
##### Returns
96100

97101
`Element?` — The found element, if any.
98102

99-
### `select.selectAll(selector, tree)`
103+
### `select.selectAll(selector, tree[, space])`
100104

101105
Select all nodes matching `selector` in the given `tree` (could include the
102106
tree itself).
@@ -138,8 +142,10 @@ Yields:
138142

139143
##### Parameters
140144

141-
* `tree` (`Node`) — Thing to search.
142145
* `selector` (`string`) — CSS selectors (`,` is also supported)
146+
* `tree` (`Node`) — Thing to search.
147+
* `space` (enum, `'svg'` or `'html'`, default: `'html'`)
148+
— Which space the tree exists in
143149

144150
##### Returns
145151

test/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ require('./matches')
44
require('./select')
55
require('./select-all')
66
require('./all')
7+
require('./svg')

test/svg.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use strict'
2+
3+
var test = require('tape')
4+
var u = require('unist-builder')
5+
var s = require('hastscript/svg')
6+
var select = require('..').select
7+
8+
test('svg', function(t) {
9+
t.deepEqual(
10+
select(
11+
'[writing-mode]',
12+
u('root', [s('svg', [s('text', {writingMode: 'lr-tb'}, '!')])])
13+
),
14+
s('text', {writingMode: 'lr-tb'}, '!')
15+
)
16+
17+
t.deepEqual(
18+
select('[writing-mode]', s('text', {writingMode: 'lr-tb'}, '!')),
19+
null
20+
)
21+
22+
t.deepEqual(
23+
select('[writing-mode]', s('text', {writingMode: 'lr-tb'}, '!'), 'svg'),
24+
s('text', {writingMode: 'lr-tb'}, '!')
25+
)
26+
27+
t.end()
28+
})

0 commit comments

Comments
 (0)