Skip to content

Commit da8851b

Browse files
committed
Initial commit
0 parents  commit da8851b

21 files changed

+3091
-0
lines changed

.editorconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
root = true
2+
3+
[*]
4+
indent_style = space
5+
indent_size = 2
6+
end_of_line = lf
7+
charset = utf-8
8+
trim_trailing_whitespace = true
9+
insert_final_newline = true

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.DS_Store
2+
*.log
3+
.nyc_output/
4+
coverage/
5+
node_modules/
6+
hast-util-select.js
7+
hast-util-select.min.js

.travis.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
language: node_js
2+
node_js:
3+
- '4.0'
4+
- '6.0'
5+
after_script: bash <(curl -s https://codecov.io/bash)
6+
deploy:
7+
- provider: npm
8+
9+
api_key:
10+
secure: WU/Ei+6g5ZaqF8qpsu/xGklg4P/LnXC4mkGh6Gsh3b5Nx54Fev+Ahw0Bur7xwJhCnVEBZBmLXyjsNPIbUpvmdKpvmuRUhZrDBRdSACel8wnNw5uwAbJdzw4xg4bagciS0F7F81fTqp0GEuOWfS9tIlF9gFBqC4VMJwePPlrvg5Qf8BbAtOavUN1f4Gm6x3+yvfTL770NSXZD7uUqCwaHXbOjGYZ9EMhiqakPcrVten8YKbdtqOO9SBbdHui8N9rGE14o7EIhLAwQ9tbuukvGLemKvBw3NxR9hBLBuP00FJw8wcdOf95drWh35tqclnypO4GRaIMGwZImKgt/l7IXEX/t9DGfGGQIyZQGO3r5DTkJdKYtlZIoNBj8kDkVd+Ns4UuhgDxjl+0OqZ9EadJzvhHOqHXIEkKK7xHiE/uRLUwNZsJpC/exdB+l3IisGoFOEayMDR9a2dxZ4ZeqOEC3kgb7rpuDVLkJCw766tmFoW4YmSgNWMqjLXVmeWddJvXsAoly9HVpXdqaqdnA5P5CHEbxcPoKSE+GyZzvvkUHtqZ0ltQM2J25H/cHxVPzbE4+5uB+umgt+Q81yCjxOyskcj5KNy1936t4adR8XiTTlAMdWAZS8+VrqOopFhknqHhUfQJwNn735m/86gs6AKF42CJD4yWBB30nXWPjRN7LilE=
11+
on:
12+
tags: true
13+
node: '4.0'
14+
- provider: releases
15+
api_key:
16+
secure: C5PDLRBnypcx44UYfgy7dlx9RyrhFIXYque3CCtCfTS3jC02HfSTrXMXR4mEPW378Rly+Rtw0OlE+XV05kDEXZ85aEzpiiLIyavVrgjtWQSHNJsToUVLamFEF44ad93Q3ZnBg/KiqtwblMZo1ue9w3lTASiFbKW949WwFkWvur7lJcoX42qeneYq0Y2486bv/gu3V+2t5RyI+MdYt8D7ttCKHrxiNzbtU4pMjp39nRJannWEw1oXOpilB1J7CVRRTD7bR8iatbdZkRl2CF7i4QayV/j/mjTingMUAKRAtG728qzoCmc3sc4F+tqBPP1aS/TYrDLtfDI+7atUgrZ2o8pF4squ+3FasGAVnjmUHTnwrxlwtmAyKfHU2L7uay9ixSLy29aHB9JiMaXTrVBmXLsOmC2ItuK0AbNmDw+meZiSoIJM7Ehp2zx1z4o+NHnLTkUUgsEepLmknWnQzSpCQ6/rKxdLrRiPQEFumHpzoMjMjvfsOapSi4x0rKyu9XhBo7PeLr7P+UC1aUOM4cJnB6bVDh+YTcqJptS2Zh017oYZwv4tRw5lVBMPV4mklqfkN2uoQwzLptDlF0dJ5ajh16qo924AKhhBuzF+Nmouhd+KhuPGI+8TV/HcL8ROAp8sAjHwIRHFBIl9uqIFlJBKBxxoUgFpoFMgKwmRHrzECVw=
17+
file:
18+
- "hast-util-select.js"
19+
- "hast-util-select.min.js"
20+
on:
21+
tags: true
22+
node: '7.0'

LICENSE

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
(The MIT License)
2+
3+
Copyright (c) 2017 Titus Wormer <[email protected]>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining
6+
a copy of this software and associated documentation files (the
7+
'Software'), to deal in the Software without restriction, including
8+
without limitation the rights to use, copy, modify, merge, publish,
9+
distribute, sublicense, and/or sell copies of the Software, and to
10+
permit persons to whom the Software is furnished to do so, subject to
11+
the following conditions:
12+
13+
The above copyright notice and this permission notice shall be
14+
included in all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

index.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use strict';
2+
3+
var Parser = require('css-selector-parser').CssSelectorParser;
4+
var attributes = require('./lib/attribute');
5+
var pseudos = require('./lib/pseudo');
6+
var any = require('./lib/any');
7+
var nesting = require('./lib/nest');
8+
var compile = require('./lib/compile');
9+
10+
var parser = new Parser();
11+
12+
parser.registerAttrEqualityMods.apply(parser, attributes.support);
13+
parser.registerSelectorPseudos.apply(parser, pseudos.selectorPseudoSupport);
14+
parser.registerNestingOperators.apply(parser, nesting.support);
15+
16+
exports.matches = matches;
17+
exports.selectAll = selectAll;
18+
exports.select = select;
19+
20+
function matches(selector, node) {
21+
return Boolean(any(parse(selector), node, {one: true, shallow: true})[0]);
22+
}
23+
24+
function select(selector, node) {
25+
return any(parse(selector), node, {one: true})[0] || null;
26+
}
27+
28+
function selectAll(selector, node) {
29+
return any(parse(selector), node, {});
30+
}
31+
32+
function parse(selector) {
33+
if (typeof selector !== 'string') {
34+
throw new Error('Expected `string` as selector, not `' + selector + '`');
35+
}
36+
37+
return compile(parser.parse(selector));
38+
}

lib/any.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
'use strict';
2+
3+
module.exports = match;
4+
5+
var zwitch = require('zwitch');
6+
var needsIndex = require('./pseudo').needsIndex;
7+
var test = require('./test');
8+
var nest = require('./nest');
9+
10+
var type = zwitch('type');
11+
var handlers = type.handlers;
12+
13+
type.unknown = unknownType;
14+
type.invalid = invalidType;
15+
handlers.selectors = selectors;
16+
handlers.ruleSet = ruleSet;
17+
handlers.rule = rule;
18+
19+
function match(query, node, state) {
20+
return query && node ? type(query, node, state) : [];
21+
}
22+
23+
function selectors(query, node, state) {
24+
var collect = collector(state.one);
25+
var ruleSets = query.selectors;
26+
var length = ruleSets.length;
27+
var index = -1;
28+
29+
while (++index < length) {
30+
collect(ruleSet(ruleSets[index], node, state));
31+
}
32+
33+
return collect.result;
34+
}
35+
36+
function ruleSet(query, node, state) {
37+
return rule(query.rule, node, state);
38+
}
39+
40+
function rule(query, tree, state) {
41+
var collect = collector(state.one);
42+
var opts = {iterator: match, one: state.one, shallow: state.shallow};
43+
44+
if (state.shallow && query.rule) {
45+
throw new Error('Expected selector without nesting');
46+
}
47+
48+
nest(query, tree, 0, null, configure(query, opts));
49+
50+
return collect.result;
51+
52+
function match(query, node, index, parent, state) {
53+
if (test(query, node, index, parent, state)) {
54+
if (query.rule) {
55+
nest(query.rule, node, index, parent, configure(query.rule, state));
56+
} else {
57+
collect(node);
58+
state.found = true;
59+
}
60+
}
61+
}
62+
63+
function configure(query, state) {
64+
var pseudos = query.pseudos;
65+
var length = pseudos && pseudos.length;
66+
var index = -1;
67+
68+
while (++index < length) {
69+
if (needsIndex.indexOf(pseudos[index].name) !== -1) {
70+
state.index = true;
71+
break;
72+
}
73+
}
74+
75+
return state;
76+
}
77+
}
78+
79+
/* istanbul ignore next - Shouldn’t be invoked, all data is handled. */
80+
function unknownType(query) {
81+
throw new Error('Unknown type `' + query.type + '`');
82+
}
83+
84+
/* istanbul ignore next - Shouldn’t be invoked, parser gives correct data. */
85+
function invalidType() {
86+
throw new Error('Invalid type');
87+
}
88+
89+
function collector(one) {
90+
var result = [];
91+
var found;
92+
93+
collect.result = result;
94+
95+
return collect;
96+
97+
/* Append elements to array, filtering out duplicates. */
98+
function collect(source) {
99+
if ('length' in source) {
100+
collectAll();
101+
} else {
102+
collectOne(source);
103+
}
104+
105+
function collectAll() {
106+
var length = source.length;
107+
var index = -1;
108+
109+
while (++index < length) {
110+
collectOne(source[index]);
111+
}
112+
}
113+
114+
function collectOne(element) {
115+
if (one) {
116+
/* istanbul ignore if - shouldn’t happen, safeguards performance problems. */
117+
if (found) {
118+
throw new Error('Cannot collect multiple nodes');
119+
}
120+
121+
found = true;
122+
}
123+
124+
if (result.indexOf(element) === -1) {
125+
result.push(element);
126+
}
127+
}
128+
}
129+
}

lib/attribute.js

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
'use strict';
2+
3+
module.exports = match;
4+
5+
var camelcase = require('camelcase');
6+
var zwitch = require('zwitch');
7+
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');
11+
12+
var handle = zwitch('operator');
13+
var handlers = handle.handlers;
14+
15+
match.support = ['~', '|', '^', '$', '*'];
16+
17+
handle.unknown = unknownOperator;
18+
handle.invalid = exists;
19+
handlers['='] = exact;
20+
handlers['~='] = spaceSeparatedList;
21+
handlers['|='] = exactOrPrefix;
22+
handlers['^='] = begins;
23+
handlers['$='] = ends;
24+
handlers['*='] = contains;
25+
26+
function match(query, node) {
27+
var attrs = query.attrs;
28+
var length = attrs.length;
29+
var index = -1;
30+
var info;
31+
var attr;
32+
33+
while (++index < length) {
34+
attr = attrs[index];
35+
info = information(attr.name) || {};
36+
attr.propertyName = info.propertyName || camelcase(attr.name);
37+
38+
if (!handle(attr, node, info)) {
39+
return false;
40+
}
41+
}
42+
43+
return true;
44+
}
45+
46+
/* [attr] */
47+
function exists(query, node) {
48+
return has(node, query.propertyName);
49+
}
50+
51+
/* [attr=value] */
52+
function exact(query, node, info) {
53+
if (!has(node, query.propertyName)) {
54+
return false;
55+
}
56+
57+
return normalizeValue(node.properties[query.propertyName], info) === query.value;
58+
}
59+
60+
/* [attr~=value] */
61+
function spaceSeparatedList(query, node, info) {
62+
var val;
63+
64+
if (!has(node, query.propertyName)) {
65+
return false;
66+
}
67+
68+
val = node.properties[query.propertyName];
69+
70+
/* If this is a comma-separated list, and the query is contained in it,
71+
* return true. */
72+
if (
73+
typeof val === 'object' &&
74+
!info.commaSeparated &&
75+
val.indexOf(query.value) !== -1
76+
) {
77+
return true;
78+
}
79+
80+
/* For all other values (including comma-separated lists),
81+
* return whether this is an exact match. */
82+
return normalizeValue(val, info) === query.value;
83+
}
84+
85+
/* [attr|=value] */
86+
function exactOrPrefix(query, node, info) {
87+
var value;
88+
89+
if (!has(node, query.propertyName)) {
90+
return false;
91+
}
92+
93+
value = normalizeValue(node.properties[query.propertyName], info);
94+
95+
return Boolean(
96+
value === query.value ||
97+
(
98+
value.slice(0, query.value.length) === query.value &&
99+
value.charAt(query.value.length) === '-'
100+
)
101+
);
102+
}
103+
104+
/* [attr^=value] */
105+
function begins(query, node, info) {
106+
if (!has(node, query.propertyName)) {
107+
return false;
108+
}
109+
110+
return normalizeValue(node.properties[query.propertyName], info)
111+
.slice(0, query.value.length) === query.value;
112+
}
113+
114+
/* [attr$=value] */
115+
function ends(query, node, info) {
116+
if (!has(node, query.propertyName)) {
117+
return false;
118+
}
119+
120+
return normalizeValue(node.properties[query.propertyName], info)
121+
.slice(-query.value.length) === query.value;
122+
}
123+
124+
/* [attr*=value] */
125+
function contains(query, node, info) {
126+
if (!has(node, query.propertyName)) {
127+
return false;
128+
}
129+
130+
return normalizeValue(node.properties[query.propertyName], info)
131+
.indexOf(query.value) !== -1;
132+
}
133+
134+
/* istanbul ignore next - Shouldn’t be invoked, Parser throws an error instead. */
135+
function unknownOperator(query) {
136+
throw new Error('Unknown operator `' + query.operator + '`');
137+
}
138+
139+
/* Stringify a HAST value back to its HTML form. */
140+
function normalizeValue(value, info) {
141+
if (info.commaSeparated) {
142+
value = commaSeparated.stringify(value);
143+
} else if (info.spaceSeparated) {
144+
value = spaceSeparated.stringify(value);
145+
} else if (info.boolean) {
146+
/* `false` is ignored by `has()`. */
147+
value = info.name;
148+
} else if (info.overloadedBoolean) {
149+
if (value === true) {
150+
value = info.name;
151+
}
152+
} else if (info.positiveNumeric || info.numeric) {
153+
value = String(value);
154+
}
155+
156+
return value;
157+
}

0 commit comments

Comments
 (0)