Skip to content

Commit 3c10a63

Browse files
committed
Initial commit
0 parents  commit 3c10a63

File tree

7 files changed

+246
-0
lines changed

7 files changed

+246
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
.nyc_output
3+
coverage

.travis.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
sudo: false
2+
language: node_js
3+
node_js:
4+
- "0.10"
5+
- "0.12"
6+
- "4"
7+
- "5"
8+
- "6"
9+
- "7"

LICENSE

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

README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# CSS Selector Tree
2+
3+
> A small helper to split CSS selectors into subjects and conditions
4+
5+
[![Build Status](https://travis-ci.org/rasshofer/cssSelectorTree.svg)](https://travis-ci.org/rasshofer/css-selector-tree)
6+
[![Coverage Status](https://coveralls.io/repos/github/rasshofer/css-selector-tree/badge.svg)](https://coveralls.io/github/rasshofer/css-selector-tree)
7+
[![Dependency Status](https://david-dm.org/rasshofer/css-selector-tree/status.svg)](https://david-dm.org/rasshofer/css-selector-tree)
8+
[![Dependency Status](https://david-dm.org/rasshofer/css-selector-tree/dev-status.svg)](https://david-dm.org/rasshofer/css-selector-tree)
9+
10+
## Installation
11+
12+
```shell
13+
npm install css-selector-tree --save-dev
14+
```
15+
16+
## API
17+
18+
### cssSelectorTree.tree(selector)
19+
20+
Accepts a CSS selector and returns its tree (subject and conditions).
21+
22+
In case no valid CSS selector is provided, `false` is returned.
23+
24+
### cssSelectorTree.subject(selector)
25+
26+
Accepts a CSS selector and returns its subject.
27+
28+
In case no valid CSS selector is provided, `false` is returned.
29+
30+
### cssSelectorTree.conditions(selector)
31+
32+
Accepts a CSS selector and returns its conditions.
33+
34+
In case no valid CSS selector is provided, `false` is returned.
35+
36+
## Changelog
37+
38+
* 0.0.1
39+
* Initial version
40+
41+
## License
42+
43+
Copyright (c) 2016 [Thomas Rasshofer](http://thomasrasshofer.com/)
44+
Licensed under the MIT license.
45+
46+
See LICENSE for more info.

css-selector-tree.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
var tokenizer = require('css-selector-tokenizer');
2+
3+
function generateSelectorTree (selector) {
4+
5+
if (!selector) {
6+
return false;
7+
}
8+
9+
selector = selector.split(/,/)[0];
10+
11+
var parsed = tokenizer.parse(selector);
12+
13+
var filtered = parsed.nodes.filter(function (node) {
14+
return node.type === 'selector' && node.nodes && node.nodes.length;
15+
});
16+
17+
if (filtered.length === 0) {
18+
return false;
19+
}
20+
21+
var conditions = filtered.shift().nodes.filter(function (subnode) {
22+
if (subnode.type === 'spacing') {
23+
subnode.type = 'child';
24+
delete subnode.value;
25+
}
26+
return subnode;
27+
});
28+
29+
return conditions.reverse();
30+
31+
}
32+
33+
function extractSelectorSubject (selector) {
34+
35+
var tree = generateSelectorTree(selector);
36+
37+
if (tree === false) {
38+
return false;
39+
}
40+
41+
return tree[0];
42+
43+
}
44+
45+
function extractSelectorConditions (selector) {
46+
47+
var tree = generateSelectorTree(selector);
48+
49+
if (tree === false) {
50+
return false;
51+
}
52+
53+
return tree.slice(1);
54+
55+
}
56+
57+
module.exports = {
58+
tree: generateSelectorTree,
59+
subject: extractSelectorSubject,
60+
conditions: extractSelectorConditions
61+
};

package.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "css-selector-tree",
3+
"version": "0.0.1",
4+
"description": "A small helper to split CSS selectors into subjects and conditions",
5+
"author": {
6+
"name": "Thomas Rasshofer",
7+
"email": "hello@thomasrasshofer.com",
8+
"url": "http://thomasrasshofer.com/"
9+
},
10+
"license": "MIT",
11+
"repository": {
12+
"type": "git",
13+
"url": "git://github.com/rasshofer/css-selector-tree.git"
14+
},
15+
"bugs": {
16+
"url": "https://github.com/rasshofer/css-selector-tree/issues"
17+
},
18+
"main": "css-selector-tree.js",
19+
"keywords": [
20+
"css-selector-tree",
21+
"css",
22+
"complexity"
23+
],
24+
"devDependencies": {
25+
"jshint": "^2.9.3",
26+
"tap": "^7.1.1"
27+
},
28+
"dependencies": {
29+
"css-selector-tokenizer": "^0.7.0"
30+
},
31+
"scripts": {
32+
"test": "jshint css-selector-tree.js && tap tests.js --cov"
33+
}
34+
}

tests.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
var fs = require('fs');
2+
var path = require('path');
3+
var tap = require('tap');
4+
var cssSelectorTree = require('./css-selector-tree');
5+
6+
// API
7+
8+
tap.equal(typeof cssSelectorTree, 'object');
9+
tap.equal(typeof cssSelectorTree.tree, 'function');
10+
tap.equal(typeof cssSelectorTree.subject, 'function');
11+
tap.equal(typeof cssSelectorTree.conditions, 'function');
12+
13+
// Tree API
14+
15+
tap.equal(cssSelectorTree.tree(null), false);
16+
tap.equal(cssSelectorTree.tree(), false);
17+
tap.equal(cssSelectorTree.tree(''), false);
18+
tap.equal(cssSelectorTree.tree(new Array(3).join(' ')), false);
19+
tap.deepEqual(cssSelectorTree.tree('.grid-homepage .teaser .article-title .headline'), [{
20+
type: 'class',
21+
name: 'headline'
22+
}, {
23+
type: 'child'
24+
}, {
25+
type: 'class',
26+
name: 'article-title'
27+
}, {
28+
type: 'child'
29+
}, {
30+
type: 'class',
31+
name: 'teaser'
32+
}, {
33+
type: 'child'
34+
}, {
35+
type: 'class',
36+
name: 'grid-homepage'
37+
}]);
38+
39+
// Subject API
40+
41+
tap.equal(cssSelectorTree.subject(null), false);
42+
tap.equal(cssSelectorTree.subject(), false);
43+
tap.equal(cssSelectorTree.subject(''), false);
44+
tap.equal(cssSelectorTree.subject(new Array(3).join(' ')), false);
45+
tap.deepEqual(cssSelectorTree.subject('.grid-homepage .teaser .article-title .headline'), {
46+
type: 'class',
47+
name: 'headline'
48+
});
49+
50+
// Conditions API
51+
52+
tap.equal(cssSelectorTree.conditions(null), false);
53+
tap.equal(cssSelectorTree.conditions(), false);
54+
tap.equal(cssSelectorTree.conditions(''), false);
55+
tap.equal(cssSelectorTree.conditions(new Array(3).join(' ')), false);
56+
tap.deepEqual(cssSelectorTree.conditions('.grid-homepage .teaser .article-title .headline'), [{
57+
type: 'child'
58+
}, {
59+
type: 'class',
60+
name: 'article-title'
61+
}, {
62+
type: 'child'
63+
}, {
64+
type: 'class',
65+
name: 'teaser'
66+
}, {
67+
type: 'child'
68+
}, {
69+
type: 'class',
70+
name: 'grid-homepage'
71+
}]);

0 commit comments

Comments
 (0)