js-select
Advanced tools
Comparing version 0.2.0 to 0.4.0
134
index.js
var traverse = require("traverse"), | ||
JSONSelect = require("JSONSelect"); | ||
module.exports = function(obj, sel) { | ||
sel = sel || "*"; | ||
module.exports = function(obj, string) { | ||
sels = parseSelectors(string); | ||
@@ -18,5 +18,5 @@ return { | ||
traverse(obj).forEach(function(node) { | ||
if (isMatch(sel, this)) { | ||
this.matches = function(sel) { | ||
return isMatch(sel, this); | ||
if (matchesAny(sels, this)) { | ||
this.matches = function(string) { | ||
return matchesAny(parseSelectors(string), this); | ||
}; | ||
@@ -31,18 +31,36 @@ // inherit context from js-traverse | ||
function isMatch(sel, context) { | ||
var parts = JSONSelect._parse(sel)[1], | ||
path = traverse.clone(context.path), | ||
i = 0; | ||
parts.reverse(); | ||
path.reverse(); | ||
path.push(""); | ||
function parseSelectors(string) { | ||
var parsed = JSONSelect._parse(string || "*")[1]; | ||
return getSelectors(parsed); | ||
} | ||
function getSelectors(parsed) { | ||
if (parsed[0] == ",") { | ||
return parsed.slice(1); | ||
} | ||
return [parsed]; | ||
} | ||
function matchesAny(sels, context) { | ||
for (var i = 0; i < sels.length; i++) { | ||
if (matches(sels[i], context)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
function matches(sel, context) { | ||
var path = context.parents.concat([context]), | ||
i = path.length - 1, | ||
j = sel.length - 1; | ||
// walk up the ancestors | ||
var must = true; | ||
while(parts.length && path.length) { | ||
var part = parts[0], | ||
key = path[0]; | ||
while(j >= 0 && i >= 0) { | ||
var part = sel[j], | ||
context = path[i]; | ||
if (part == ">") { | ||
parts.shift(); | ||
j--; | ||
must = true; | ||
@@ -52,4 +70,4 @@ continue; | ||
if (matchesKey(part, key)) { | ||
parts.shift(); | ||
if (matchesKey(part, context)) { | ||
j--; | ||
} | ||
@@ -60,26 +78,86 @@ else if(must) { | ||
path.shift(); | ||
i--; | ||
must = false; | ||
} | ||
return parts.length == 0; | ||
return j == -1; | ||
} | ||
function matchesKey(part, key) { | ||
if (part.id && part.id != key) { | ||
function matchesKey(part, context) { | ||
var key = context.key, | ||
node = context.node, | ||
parent = context.parent; | ||
if (part.id && key != part.id) { | ||
return false; | ||
} | ||
if (part.type) { | ||
if (part.type == "null") { | ||
if (context.node !== null) { | ||
return false; | ||
} | ||
} | ||
else if (part.type == "array") { | ||
if (!isArray(context.node)) { | ||
return false; | ||
} | ||
} | ||
else if (part.type == "object") { | ||
if (typeof context.node != "object" | ||
|| context.node === null || isArray(context.node)) { | ||
return false; | ||
} | ||
} | ||
else if (part.type != typeof context.node) { | ||
return false; | ||
} | ||
} | ||
if (part.pf == ":nth-child") { | ||
if (part.a == 0 | ||
&& (parseInt(key) + 1) !== part.b) { | ||
var index = parseInt(key) + 1; | ||
if ((part.a == 0 && index !== part.b) // :nth-child(i) | ||
|| (part.a == 1 && !(index >= -part.b)) // :nth-child(n) | ||
|| (part.a == -1 && !(index <= part.b)) // :nth-child(-n + 1) | ||
|| (part.a == 2 && index % 2 != part.b)) { // :nth-child(even) | ||
return false; | ||
} | ||
else if (part.a == 2 | ||
&& (parseInt(key) % 2) != part.b) { | ||
return false ; | ||
} | ||
if (part.pf == ":nth-last-child") { | ||
var n = context.parent && context.parent.node.length; | ||
if (!n || key != n - part.b) { | ||
return false; | ||
} | ||
} | ||
if (part.pc == ":root" && key != "") { | ||
if (part.pc == ":only-child") { | ||
var n = context.parent && context.parent.node.length; | ||
if (!n || n != 1) { | ||
return false; | ||
} | ||
} | ||
if (part.pc == ":root" && key !== undefined) { | ||
return false; | ||
} | ||
if (part.has) { | ||
var sels = getSelectors(part.has[0]), | ||
match = false; | ||
traverse(node).forEach(function(child) { | ||
if (matchesAny(sels, this)) { | ||
match = true; | ||
} | ||
}); | ||
if (!match) { | ||
return false; | ||
} | ||
} | ||
if (part.expr) { | ||
var expr = part.expr, lhs = expr[0], op = expr[1], rhs = expr[2]; | ||
if (typeof node != "string" | ||
|| (!lhs && op == "=" && node != rhs) // :val("str") | ||
|| (!lhs && op == "*=" && node.indexOf(rhs) == -1)) { // :contains("substr") | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
var isArray = Array.isArray || function(obj) { | ||
return toString.call(obj) === '[object Array]'; | ||
} |
{ | ||
"name": "js-select", | ||
"description": "Traverse and modify objects with JSONSelect selectors", | ||
"version": "0.2.0", | ||
"version": "0.4.0", | ||
"author": "Heather Arthur <fayearthur@gmail.com>", | ||
@@ -16,4 +16,6 @@ "repository": { | ||
"devDependencies": { | ||
"nomnom": "0.6.x", | ||
"color": "0.3.x" | ||
}, | ||
"keywords": ["json"] | ||
} |
@@ -6,4 +6,2 @@ # js-select | ||
```javascript | ||
var select = require("js-select"); | ||
var people = { | ||
@@ -32,3 +30,3 @@ george: { | ||
if (this.matches(".mary > .movie")) { | ||
this.update(node.toLowerCase()); | ||
this.remove(); | ||
} | ||
@@ -40,19 +38,29 @@ } | ||
js-select supports the following [JSONSelect](http://jsonselect.org/) selectors so far: | ||
js-select supports the following [JSONSelect](http://jsonselect.org/) selectors: | ||
``` | ||
"*" | ||
".key" | ||
".ancestor .key" | ||
".parent > .key" | ||
":root" | ||
":nth-child(n)" | ||
":nth-child(even)" | ||
":nth-child(odd)" | ||
":first-child" | ||
* | ||
type | ||
.key | ||
.ancestor .key | ||
.parent > .key | ||
.sibling ~ .key | ||
:root | ||
:nth-child(n) | ||
:nth-child(even) | ||
:nth-child(odd) | ||
:nth-last-child(n) | ||
:first-child | ||
:last-child | ||
:only-child | ||
:has(selector) | ||
:val("string") | ||
:contains("substring") | ||
``` | ||
See [details](http://jsonselect.org/#docs/overview) on each selector, and [try them](http://jsonselect.org/#tryit) out on the JSONSelect website. | ||
# install | ||
Download the code, then with [npm](http://npmjs.org): | ||
For [node](http://nodejs.org), install with [npm](http://npmjs.org): | ||
@@ -63,2 +71,14 @@ ```bash | ||
Get a browser file after npm-installing using [browserify](https://github.com/substack/node-browserify) | ||
For the browser, download the select.js file or fetch the latest version from [npm](http://npmjs.org) and build a browser file using [browserify](https://github.com/substack/node-browserify): | ||
```bash | ||
npm install browserify -g | ||
npm install js-select | ||
browserify --require js-select --outfile select.js | ||
``` | ||
this will build a browser file with `require('js-select')` available. | ||
# propers | ||
Huge thanks to [@substack](http://github.com/substack) for the ingenious [js-traverse](https://github.com/substack/js-traverse) and [@lloyd](https://github.com/lloyd) for the ingenious [JSONSelect spec](http://http://jsonselect.org/) and [selector parser](http://search.npmjs.org/#/JSONSelect). |
16
test.js
@@ -1,2 +0,4 @@ | ||
var select = require("./index"); | ||
var select = require("./index"), | ||
JSONSelect = require("JSONSelect"), | ||
traverse = require("traverse"); | ||
@@ -6,5 +8,5 @@ var people = { | ||
age: 15, | ||
movies: [{ | ||
movies: [{ | ||
name: "Twilight", | ||
stars: 3 | ||
stars: false | ||
}, | ||
@@ -14,5 +16,11 @@ { name: "Clueless", | ||
}] | ||
}, | ||
"george": { | ||
lang: "eng", | ||
stars: 6 | ||
} | ||
}; | ||
console.log(select(people, ":root").nodes()) | ||
var selector = ':contains("Clueless")'; | ||
console.log(select(people, selector).nodes()) | ||
console.log(JSONSelect.match(selector, people)) |
@@ -28,7 +28,10 @@ var assert = require("assert"), | ||
}] | ||
}, | ||
"chris" : { | ||
car: null, | ||
male: true | ||
} | ||
} | ||
var people2 = traverse.clone(people); | ||
assert.deepEqual(select(people, "*").nodes(), [{"george":{"age":35,"movies":[{"name":"Repo Man","stars":5}]},"mary":{"age":15,"movies":[{"name":"Twilight","stars":3},{"name":"Trudy","stars":2},{"name":"The Fighter","stars":4}]}},{"age":35,"movies":[{"name":"Repo Man","stars":5}]},35,[{"name":"Repo Man","stars":5}],{"name":"Repo Man","stars":5},"Repo Man",5,{"age":15,"movies":[{"name":"Twilight","stars":3},{"name":"Trudy","stars":2},{"name":"The Fighter","stars":4}]},15,[{"name":"Twilight","stars":3},{"name":"Trudy","stars":2},{"name":"The Fighter","stars":4}],{"name":"Twilight","stars":3},"Twilight",3,{"name":"Trudy","stars":2},"Trudy",2,{"name":"The Fighter","stars":4},"The Fighter",4]); | ||
assert.deepEqual(select(people, "*").nodes(), [{"george":{"age":35,"movies":[{"name":"Repo Man","stars":5}]},"mary":{"age":15,"movies":[{"name":"Twilight","stars":3},{"name":"Trudy","stars":2},{"name":"The Fighter","stars":4}]},"chris":{"car":null,"male":true}},{"age":35,"movies":[{"name":"Repo Man","stars":5}]},35,[{"name":"Repo Man","stars":5}],{"name":"Repo Man","stars":5},"Repo Man",5,{"age":15,"movies":[{"name":"Twilight","stars":3},{"name":"Trudy","stars":2},{"name":"The Fighter","stars":4}]},15,[{"name":"Twilight","stars":3},{"name":"Trudy","stars":2},{"name":"The Fighter","stars":4}],{"name":"Twilight","stars":3},"Twilight",3,{"name":"Trudy","stars":2},"Trudy",2,{"name":"The Fighter","stars":4},"The Fighter",4,{"car":null,"male":true},null,true]); | ||
assert.deepEqual(select(people, ".george").nodes(), [{"age":35,"movies":[{"name":"Repo Man","stars":5}]}]); | ||
@@ -42,8 +45,37 @@ assert.deepEqual(select(people, ".george .age").nodes(), [35]); | ||
assert.deepEqual(select(people, ":first-child").nodes(), [{"name":"Repo Man","stars":5},{"name":"Twilight","stars":3}]); | ||
assert.deepEqual(select(people, ":nth-child(1)").nodes(), select(people, ":first-child").nodes()); | ||
assert.deepEqual(select(people, ":nth-child(2)").nodes(), [{"name":"Trudy","stars":2}]); | ||
assert.deepEqual(select(people, ":nth-child(even)").nodes(), [{"name":"Repo Man","stars":5},{"name":"Twilight","stars":3},{"name":"The Fighter","stars":4}]); | ||
assert.deepEqual(select(people, ":nth-child(odd)").nodes(), [{"name":"Trudy","stars":2}]); | ||
assert.deepEqual(select(people, ":root").nodes(), [{"george":{"age":35,"movies":[{"name":"Repo Man","stars":5}]},"mary":{"age":15,"movies":[{"name":"Twilight","stars":3},{"name":"Trudy","stars":2},{"name":"The Fighter","stars":4}]}}]); | ||
assert.deepEqual(select(people, ":nth-child(odd)").nodes(), [{"name":"Repo Man","stars":5},{"name":"Twilight","stars":3},{"name":"The Fighter","stars":4}]); | ||
assert.deepEqual(select(people, ":nth-child(even)").nodes(), [{"name":"Trudy","stars":2}]); | ||
assert.deepEqual(select(people, ":nth-child(-n+1)").nodes(), select(people, ":first-child").nodes()); | ||
assert.deepEqual(select(people, ":nth-child(-n+2)").nodes(), [{"name":"Repo Man","stars":5},{"name":"Twilight","stars":3},{"name":"Trudy","stars":2}]); | ||
assert.deepEqual(select(people, ":nth-child(n)").nodes(), [{"name":"Repo Man","stars":5},{"name":"Twilight","stars":3},{"name":"Trudy","stars":2},{"name":"The Fighter","stars":4}]); | ||
assert.deepEqual(select(people, ":nth-child(n-1)").nodes(), select(people, ":nth-child(n)").nodes()); | ||
assert.deepEqual(select(people, ":nth-child(n-2)").nodes(), [{"name":"Trudy","stars":2},{"name":"The Fighter","stars":4}]); | ||
assert.deepEqual(select(people, ":last-child").nodes(), [{"name":"Repo Man","stars":5},{"name":"The Fighter","stars":4}]); | ||
assert.deepEqual(select(people, ":nth-last-child(1)").nodes(), select(people, ":last-child").nodes()); | ||
assert.deepEqual(select(people, ":nth-last-child(2)").nodes(), [{"name":"Trudy","stars":2}]); | ||
assert.deepEqual(select(people, ":only-child").nodes(), [{"name":"Repo Man","stars":5}]); | ||
assert.deepEqual(select(people, ":root").nodes(),[{"george":{"age":35,"movies":[{"name":"Repo Man","stars":5}]},"mary":{"age":15,"movies":[{"name":"Twilight","stars":3},{"name":"Trudy","stars":2},{"name":"The Fighter","stars":4}]},"chris":{"car":null,"male":true}}]) | ||
assert.deepEqual(select(people, "string").nodes(),["Repo Man","Twilight","Trudy","The Fighter"]); | ||
assert.deepEqual(select(people, "number").nodes(),[35,5,15,3,2,4]); | ||
assert.deepEqual(select(people, "boolean").nodes(),[true]); | ||
assert.deepEqual(select(people, "object").nodes(),[{"george":{"age":35,"movies":[{"name":"Repo Man","stars":5}]},"mary":{"age":15,"movies":[{"name":"Twilight","stars":3},{"name":"Trudy","stars":2},{"name":"The Fighter","stars":4}]},"chris":{"car":null,"male":true}},{"age":35,"movies":[{"name":"Repo Man","stars":5}]},{"name":"Repo Man","stars":5},{"age":15,"movies":[{"name":"Twilight","stars":3},{"name":"Trudy","stars":2},{"name":"The Fighter","stars":4}]},{"name":"Twilight","stars":3},{"name":"Trudy","stars":2},{"name":"The Fighter","stars":4},{"car":null,"male":true}]); | ||
assert.deepEqual(select(people, "array").nodes(),[[{"name":"Repo Man","stars":5}],[{"name":"Twilight","stars":3},{"name":"Trudy","stars":2},{"name":"The Fighter","stars":4}]]); | ||
assert.deepEqual(select(people, "null").nodes(),[null]); | ||
assert.deepEqual(select(people, "number, string, boolean").nodes(), [35,"Repo Man",5,15,"Twilight",3,"Trudy",2,"The Fighter",4,true]) | ||
assert.deepEqual(select(people, ":has(.car) > .male").nodes(), [true]); | ||
assert.deepEqual(select(people, ".male ~ .car").nodes(), [null]) | ||
assert.deepEqual(select(people, ':val("Twilight")').nodes(), ["Twilight"]) | ||
assert.deepEqual(select(people, ':val("Twi")').nodes(), []) | ||
assert.deepEqual(select(people, ':contains("Twi")').nodes(), ["Twilight"]) | ||
assert.deepEqual(select(people, ':contains("weif")').nodes(), []) | ||
// invalid | ||
@@ -74,6 +106,20 @@ assert.deepEqual(select(people, ".hmmm").nodes(), []); | ||
console.time("big") | ||
assert.deepEqual(select(timeline, ".bug .id").nodes().length, 126); | ||
assert.deepEqual(select(timeline, ".id").nodes().length, 141); | ||
assert.deepEqual(select(timeline, ".comments .id").nodes().length, 115); | ||
console.timeEnd("big") | ||
console.time("select time"); | ||
assert.equal(select(timeline, ".bug .id").nodes().length, 126); | ||
assert.equal(select(timeline, ".id").nodes().length, 141); | ||
assert.equal(select(timeline, ".comments .id").nodes().length, 115); | ||
assert.equal(select(timeline, ":nth-child(n-2)").nodes().length, 335); | ||
assert.equal(select(timeline, "object").nodes().length, 927); | ||
assert.equal(select(timeline, "*").nodes().length, 3281); | ||
console.timeEnd("select time") | ||
var sel = require("JSONSelect"); | ||
console.time("JSONSelect time") | ||
assert.equal(sel.match(".bug .id", timeline).length, 126); | ||
assert.equal(sel.match(".id", timeline).length, 141); | ||
assert.equal(sel.match(".comments .id", timeline).length, 115); | ||
assert.equal(sel.match(":nth-child(n-2)", timeline).length, 335); | ||
assert.equal(sel.match("object", timeline).length, 927); | ||
assert.equal(sel.match("*", timeline).length, 3281); | ||
console.timeEnd("JSONSelect time") |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
99825
15
504
81
2
2
1