├── .gitignore ├── .npmignore ├── History.md ├── Makefile ├── Readme.md ├── index.js ├── package.json └── test ├── fixtures └── selectors.js └── parse.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | support 2 | test 3 | examples 4 | *.sock 5 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 1.0.1 / 2015-03-25 3 | ================== 4 | 5 | * fix filter regexp 6 | 7 | 1.0.0 / 2015-03-20 8 | ================== 9 | 10 | * Initial release 11 | 12 | 0.0.1 / 2010-01-03 13 | ================== 14 | 15 | * Initial commit 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | test: 3 | @./node_modules/.bin/mocha \ 4 | --reporter spec 5 | 6 | .PHONY: test 7 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 2 | # x-ray-parse 3 | 4 | x-ray's selector parser. 5 | 6 | ## Usage 7 | 8 | ```js 9 | parse('a[class][target="_blank"] @ href | trim left') 10 | { 11 | selector: 'a[class][target="_blank"]', 12 | attribute: 'href', 13 | filters: [{ name: trim, args: ['left'] }] 14 | } 15 | ``` 16 | 17 | ## Installation 18 | 19 | ``` 20 | npm install x-ray-parse 21 | ``` 22 | 23 | ## Test 24 | 25 | ``` 26 | npm install 27 | make test 28 | ``` 29 | 30 | ## License 31 | 32 | (The MIT License) 33 | 34 | Copyright (c) 2015 Matthew Mueller <matt@lapwinglabs.com> 35 | 36 | Permission is hereby granted, free of charge, to any person obtaining 37 | a copy of this software and associated documentation files (the 38 | 'Software'), to deal in the Software without restriction, including 39 | without limitation the rights to use, copy, modify, merge, publish, 40 | distribute, sublicense, and/or sell copies of the Software, and to 41 | permit persons to whom the Software is furnished to do so, subject to 42 | the following conditions: 43 | 44 | The above copyright notice and this permission notice shall be 45 | included in all copies or substantial portions of the Software. 46 | 47 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 48 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 49 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 50 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 51 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 52 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 53 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 54 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Export `parse` 3 | */ 4 | 5 | module.exports = parse; 6 | 7 | /** 8 | * Module Dependencies 9 | */ 10 | 11 | var filter_parser = require('format-parser'); 12 | 13 | /** 14 | * Regexps 15 | */ 16 | 17 | var rselector = /^([^@]*)(?:@\s*([\w-_:]+))?$/; 18 | var rfilters = /\s*\|(?!\=)\s*/; 19 | 20 | /** 21 | * Initialize `parse` 22 | * 23 | * @param {String} 24 | * @return {Object} 25 | */ 26 | 27 | function parse(str) { 28 | var filters = str.split(rfilters); 29 | var z = filters.shift(); 30 | var m = z.match(rselector) || []; 31 | 32 | return { 33 | selector: m[1] ? m[1].trim() : m[1], 34 | attribute: m[2], 35 | filters: filters.length ? filter_parser(filters.join('|')) : [] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "x-ray-parse", 3 | "version": "1.0.1", 4 | "description": "Selector parser", 5 | "keywords": [], 6 | "author": "Matthew Mueller ", 7 | "repository": { 8 | "type": "git", 9 | "url": "git://github.com/lapwinglabs/x-ray-parse.git" 10 | }, 11 | "dependencies": { 12 | "format-parser": "0.0.2" 13 | }, 14 | "devDependencies": { 15 | "mocha": "*" 16 | }, 17 | "main": "index" 18 | } -------------------------------------------------------------------------------- /test/fixtures/selectors.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | ".intro", 3 | "#firstname", 4 | "*", 5 | "p", 6 | "div, p", 7 | "div p", 8 | "div > p", 9 | "div + p", 10 | "p ~ ul", 11 | "[target]", 12 | "[target=_blank]", 13 | "[title~=flower]", 14 | "[lang|=en]", 15 | "a[href^=\"https\"]", 16 | "a[href$=\".pdf\"]", 17 | "a[href*=\"w3schools\"]", 18 | "a:active", 19 | "p::after", 20 | "p::before", 21 | "input:checked", 22 | "input:disabled", 23 | "p:empty", 24 | "input:enabled", 25 | "p:first-child", 26 | "p::first-letter", 27 | "p::first-line", 28 | "p:first-of-type", 29 | "input:focus", 30 | "a:hover", 31 | "input:in-range", 32 | "input:invalid", 33 | "p:lang(it)", 34 | "p:last-child", 35 | "p:last-of-type", 36 | "a:link", 37 | ":not(p)", 38 | "p:nth-child(2)", 39 | "p:nth-last-child(2)", 40 | "p:nth-last-of-type(2)", 41 | "p:nth-of-type(2)", 42 | "p:only-of-type", 43 | "p:only-child", 44 | "input:optional", 45 | "input:out-of-range", 46 | "input:read-only", 47 | "input:read-write", 48 | "input:required", 49 | ":root", 50 | "::selection", 51 | "#news:target", 52 | "input:valid", 53 | "a:visited" 54 | ]; 55 | -------------------------------------------------------------------------------- /test/parse.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module Dependencies 3 | */ 4 | 5 | var selectors = require('./fixtures/selectors'); 6 | var assert = require('assert'); 7 | var parse = require('../'); 8 | 9 | /** 10 | * Tests 11 | */ 12 | 13 | describe('parse', function() { 14 | 15 | it('should extract single element selectors', function () { 16 | for (var i = 0, l = selectors.length; i < l; i++) { 17 | assert.equal(selectors[i], parse(selectors[i]).selector); 18 | assert.equal(undefined, parse(selectors[i]).attribute); 19 | assert.deepEqual([], parse(selectors[i]).filters); 20 | } 21 | }); 22 | 23 | it('should extract any combination of element and attribute selectors', function() { 24 | for (var i = 0, l = selectors.length; i < l; i++) { 25 | var selector = selectors[i] + '@ href'; 26 | assert.equal(selectors[i], parse(selector).selector); 27 | assert.equal('href', parse(selector).attribute); 28 | assert.deepEqual([], parse(selector).filters); 29 | } 30 | }); 31 | 32 | it('should extract any combination of element and attribute selectors with spaces and hypens', function() { 33 | for (var i = 0, l = selectors.length; i < l; i++) { 34 | var selector = selectors[i] + ' @ data-item'; 35 | assert.equal(selectors[i], parse(selector).selector); 36 | assert.equal('data-item', parse(selector).attribute); 37 | assert.deepEqual([], parse(selector).filters); 38 | } 39 | }); 40 | 41 | it('should extract any combination of element and attribute selectors', function() { 42 | for (var i = 0, l = selectors.length; i < l; i++) { 43 | assert.equal(selectors[i], parse(selectors[i]).selector); 44 | assert.equal(undefined, parse(selectors[i]).attribute); 45 | assert.deepEqual([], parse(selectors[i]).filters); 46 | } 47 | }); 48 | 49 | it('should support a single attribute', function() { 50 | assert.equal('href', parse('@ href').attribute); 51 | assert.equal('href', parse('@href').attribute); 52 | }); 53 | 54 | it('should support filters', function() { 55 | var selector = 'a[href][class] @ html | filter1 | filter2'; 56 | assert.equal('a[href][class]', parse(selector).selector); 57 | assert.equal('html', parse(selector).attribute); 58 | assert.deepEqual([{ name: 'filter1', args: [] }, { name: 'filter2', args: [] }], parse(selector).filters); 59 | }) 60 | 61 | it('should support filters with arguments', function() { 62 | var selector = 'a[href][class] @ html | filter1: "%Y %M %d" | filter2: matt 25'; 63 | assert.equal('a[href][class]', parse(selector).selector); 64 | assert.equal('html', parse(selector).attribute); 65 | assert.deepEqual([{ name: 'filter1', args: [ '%Y %M %d' ] }, { name: 'filter2', args: ['matt', 25] }], parse(selector).filters); 66 | }) 67 | 68 | it('should support everything with no spaces', function() { 69 | var selector = 'a@href|href|uppercase'; 70 | assert.equal('a', parse(selector).selector); 71 | assert.equal('href', parse(selector).attribute); 72 | assert.deepEqual([{ name: 'href', args: [] }, { name: 'uppercase', args: [] }], parse(selector).filters); 73 | }) 74 | }) 75 | --------------------------------------------------------------------------------