1
/*! Copyright (c) 2011, Lloyd Hilaiel, ISC License */
3
* This is the JSONSelect reference implementation, in javascript.
7
var // localize references
8
toString = Object.prototype.toString;
10
function jsonParse(str) {
12
if(JSON && JSON.parse){
13
return JSON.parse(str);
15
return (new Function("return " + str))();
21
// emitted error codes.
23
"ijs": "invalid json string",
24
"mpc": "multiple pseudo classes (:xxx) not allowed",
25
"mepf": "malformed expression in pseudo-function",
26
"nmi": "multiple ids not allowed",
27
"se": "selector expected",
28
"sra": "string required after '.'",
29
"uc": "unrecognized char",
30
"ujs": "unclosed json string",
31
"upc": "unrecognized pseudo class"
34
// throw an error message
36
throw new Error(errorCodes[ec]);
41
psc: 1, // pseudo class
42
psf: 2, // pseudo class function
47
var pat = /^(?:([\r\n\t\ ]+)|([*.,>])|(string|boolean|null|array|object|number)|(:(?:root|first-child|last-child|only-child))|(:(?:nth-child|nth-last-child))|(:\w+)|(\"(?:[^\\]|\\[^\"])*\")|(\")|((?:[_a-zA-Z]|[^\0-\0177]|\\[^\r\n\f0-9a-fA-F])(?:[_a-zA-Z0-9\-]|[^\u0000-\u0177]|(?:\\[^\r\n\f0-9a-fA-F]))*))/;
48
var exprPat = /^\s*\(\s*(?:([+\-]?)([0-9]*)n\s*(?:([+\-])\s*([0-9]))?|(odd|even)|([+\-]?[0-9]+))\s*\)/;
49
var lex = function (str, off) {
51
var m = pat.exec(str.substr(off));
52
if (!m) return undefined;
55
if (m[1]) a = [off, " "];
56
else if (m[2]) a = [off, m[0]];
57
else if (m[3]) a = [off, toks.typ, m[0]];
58
else if (m[4]) a = [off, toks.psc, m[0]];
59
else if (m[5]) a = [off, toks.psf, m[0]];
60
else if (m[6]) te("upc");
61
else if (m[7]) a = [off, toks.str, jsonParse(m[0])];
62
else if (m[8]) te("ujs");
63
else if (m[9]) a = [off, toks.str, m[0].replace(/\\([^\r\n\f0-9a-fA-F])/g,"$1")];
69
var parse = function (str) {
70
var a = [], off = 0, am;
73
var s = parse_selector(str, off);
75
s = lex(str, off = s[0]);
76
if (s && s[1] === " ") s = lex(str, off = s[0]);
78
// now we've parsed a selector, and have something else...
82
} else if (s[1] === ",") {
83
if (am === undefined) am = [ ",", a ];
93
var parse_selector = function(str, off) {
96
var l = lex(str, off);
98
if (l && l[1] === " ") { soff = off = l[0]; l = lex(str, off); }
99
if (l && l[1] === toks.typ) {
101
l = lex(str, (off = l[0]));
102
} else if (l && l[1] === "*") {
103
// don't bother representing the universal sel, '*' in the
104
// parse tree, cause it's the default
105
l = lex(str, (off = l[0]));
108
// now support either an id or a pc
110
if (l === undefined) {
112
} else if (l[1] === ".") {
113
l = lex(str, (off = l[0]));
114
if (!l || l[1] !== toks.str) te("sra");
117
} else if (l[1] === toks.psc) {
118
if (s.pc || s.pf) te("mpc");
119
// collapse first-child and last-child into nth-child expressions
120
if (l[2] === ":first-child") {
124
} else if (l[2] === ":last-child") {
125
s.pf = ":nth-last-child";
131
} else if (l[1] === toks.psf) {
132
if (s.pc || s.pf ) te("mpc");
134
var m = exprPat.exec(str.substr(l[0]));
138
s.b = (m[5] === "odd") ? 1 : 0;
141
s.b = parseInt(m[6], 10);
143
s.a = parseInt((m[1] ? m[1] : "+") + (m[2] ? m[2] : "1"),10);
144
s.b = m[3] ? parseInt(m[3] + m[4],10) : 0;
150
l = lex(str, (off = l[0]));
153
// now if we didn't actually parse anything it's an error
154
if (soff === off) te("se");
161
function isArray(o) {
162
return Array.isArray ? Array.isArray(o) :
163
toString.call(o) === "[object Array]";
166
function mytypeof(o) {
167
if (o === null) return "null";
169
if (to === "object" && isArray(o)) to = "array";
173
function mn(node, sel, id, num, tot) {
175
var cs = (sel[0] === ">") ? sel[1] : sel[0];
177
if (cs.type) m = m && (cs.type === mytypeof(node));
178
if (cs.id) m = m && (cs.id === id);
180
if (cs.pf === ":nth-last-child") num = tot - num;
185
mod = ((num - cs.b) % cs.a);
187
m = (!mod && ((num*cs.a + cs.b) >= 0));
191
// should we repeat this selector for descendants?
192
if (sel[0] !== ">" && sel[0].pc !== ":root") sels.push(sel);
195
// is there a fragment that we should pass down?
196
if (sel[0] === ">") { if (sel.length > 2) { m = false; sels.push(sel.slice(2)); } }
197
else if (sel.length > 1) { m = false; sels.push(sel.slice(1)); }
203
function forEach(sel, obj, fun, id, num, tot) {
204
var a = (sel[0] === ",") ? sel.slice(1) : [sel],
207
i = 0, j = 0, l = 0, k, x;
208
for (i = 0; i < a.length; i++) {
209
x = mn(obj, a[i], id, num, tot);
213
for (j = 0; j < x[1].length; j++) {
217
if (a0.length && typeof obj === "object") {
218
if (a0.length >= 1) {
222
for (i = 0; i < obj.length; i++) {
223
forEach(a0, obj[i], fun, undefined, i, obj.length);
226
// it's a shame to do this for :last-child and other
227
// properties which count from the end when we don't
228
// even know if they're present. Also, the stream
229
// parser is going to be pissed.
232
if (obj.hasOwnProperty(k)) {
238
if (obj.hasOwnProperty(k)) {
239
forEach(a0, obj[k], fun, k, i++, l);
249
function match(sel, obj) {
251
forEach(sel, obj, function(x) {
257
function compile(sel) {
260
match: function(obj){
261
return match(this.sel, obj);
263
forEach: function(obj, fun) {
264
return forEach(this.sel, obj, fun);
270
exports._parse = parse;
271
exports.match = function (sel, obj) {
272
return compile(sel).match(obj);
274
exports.forEach = function(sel, obj, fun) {
275
return compile(sel).forEach(obj, fun);
277
exports.compile = compile;
278
})(typeof exports === "undefined" ? (window.JSONSelect = {}) : exports);