jsonselect.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. /*! Copyright (c) 2011, Lloyd Hilaiel, ISC License */
  2. /*
  3. * This is the JSONSelect reference implementation, in javascript.
  4. */
  5. (function(exports) {
  6. var // localize references
  7. toString = Object.prototype.toString;
  8. function jsonParse(str) {
  9. try {
  10. if(JSON && JSON.parse){
  11. return JSON.parse(str);
  12. }
  13. return (new Function("return " + str))();
  14. } catch(e) {
  15. te("ijs");
  16. }
  17. }
  18. // emitted error codes.
  19. var errorCodes = {
  20. "ijs": "invalid json string",
  21. "mpc": "multiple pseudo classes (:xxx) not allowed",
  22. "mepf": "malformed expression in pseudo-function",
  23. "nmi": "multiple ids not allowed",
  24. "se": "selector expected",
  25. "sra": "string required after '.'",
  26. "uc": "unrecognized char",
  27. "ujs": "unclosed json string",
  28. "upc": "unrecognized pseudo class"
  29. };
  30. // throw an error message
  31. function te(ec) {
  32. throw new Error(errorCodes[ec]);
  33. }
  34. // THE LEXER
  35. var toks = {
  36. psc: 1, // pseudo class
  37. psf: 2, // pseudo class function
  38. typ: 3, // type
  39. str: 4 // string
  40. };
  41. 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]))*))/;
  42. var exprPat = /^\s*\(\s*(?:([+\-]?)([0-9]*)n\s*(?:([+\-])\s*([0-9]))?|(odd|even)|([+\-]?[0-9]+))\s*\)/;
  43. var lex = function (str, off) {
  44. if (!off) off = 0;
  45. var m = pat.exec(str.substr(off));
  46. if (!m) return undefined;
  47. off+=m[0].length;
  48. var a;
  49. if (m[1]) a = [off, " "];
  50. else if (m[2]) a = [off, m[0]];
  51. else if (m[3]) a = [off, toks.typ, m[0]];
  52. else if (m[4]) a = [off, toks.psc, m[0]];
  53. else if (m[5]) a = [off, toks.psf, m[0]];
  54. else if (m[6]) te("upc");
  55. else if (m[7]) a = [off, toks.str, jsonParse(m[0])];
  56. else if (m[8]) te("ujs");
  57. else if (m[9]) a = [off, toks.str, m[0].replace(/\\([^\r\n\f0-9a-fA-F])/g,"$1")];
  58. return a;
  59. };
  60. // THE PARSER
  61. var parse = function (str) {
  62. var a = [], off = 0, am;
  63. while (true) {
  64. var s = parse_selector(str, off);
  65. a.push(s[1]);
  66. s = lex(str, off = s[0]);
  67. if (s && s[1] === " ") s = lex(str, off = s[0]);
  68. if (!s) break;
  69. // now we've parsed a selector, and have something else...
  70. if (s[1] === ">") {
  71. a.push(">");
  72. off = s[0];
  73. } else if (s[1] === ",") {
  74. if (am === undefined) am = [ ",", a ];
  75. else am.push(a);
  76. a = [];
  77. off = s[0];
  78. }
  79. }
  80. if (am) am.push(a);
  81. return am ? am : a;
  82. };
  83. var parse_selector = function(str, off) {
  84. var soff = off;
  85. var s = { };
  86. var l = lex(str, off);
  87. // skip space
  88. if (l && l[1] === " ") { soff = off = l[0]; l = lex(str, off); }
  89. if (l && l[1] === toks.typ) {
  90. s.type = l[2];
  91. l = lex(str, (off = l[0]));
  92. } else if (l && l[1] === "*") {
  93. // don't bother representing the universal sel, '*' in the
  94. // parse tree, cause it's the default
  95. l = lex(str, (off = l[0]));
  96. }
  97. // now support either an id or a pc
  98. while (true) {
  99. if (l === undefined) {
  100. break;
  101. } else if (l[1] === ".") {
  102. l = lex(str, (off = l[0]));
  103. if (!l || l[1] !== toks.str) te("sra");
  104. if (s.id) te("nmi");
  105. s.id = l[2];
  106. } else if (l[1] === toks.psc) {
  107. if (s.pc || s.pf) te("mpc");
  108. // collapse first-child and last-child into nth-child expressions
  109. if (l[2] === ":first-child") {
  110. s.pf = ":nth-child";
  111. s.a = 0;
  112. s.b = 1;
  113. } else if (l[2] === ":last-child") {
  114. s.pf = ":nth-last-child";
  115. s.a = 0;
  116. s.b = 1;
  117. } else {
  118. s.pc = l[2];
  119. }
  120. } else if (l[1] === toks.psf) {
  121. if (s.pc || s.pf ) te("mpc");
  122. s.pf = l[2];
  123. var m = exprPat.exec(str.substr(l[0]));
  124. if (!m) te("mepf");
  125. if (m[5]) {
  126. s.a = 2;
  127. s.b = (m[5] === "odd") ? 1 : 0;
  128. } else if (m[6]) {
  129. s.a = 0;
  130. s.b = parseInt(m[6], 10);
  131. } else {
  132. s.a = parseInt((m[1] ? m[1] : "+") + (m[2] ? m[2] : "1"),10);
  133. s.b = m[3] ? parseInt(m[3] + m[4],10) : 0;
  134. }
  135. l[0] += m[0].length;
  136. } else {
  137. break;
  138. }
  139. l = lex(str, (off = l[0]));
  140. }
  141. // now if we didn't actually parse anything it's an error
  142. if (soff === off) te("se");
  143. return [off, s];
  144. };
  145. // THE EVALUATOR
  146. function isArray(o) {
  147. return Array.isArray ? Array.isArray(o) :
  148. toString.call(o) === "[object Array]";
  149. }
  150. function mytypeof(o) {
  151. if (o === null) return "null";
  152. var to = typeof o;
  153. if (to === "object" && isArray(o)) to = "array";
  154. return to;
  155. }
  156. function mn(node, sel, id, num, tot) {
  157. var sels = [];
  158. var cs = (sel[0] === ">") ? sel[1] : sel[0];
  159. var m = true, mod;
  160. if (cs.type) m = m && (cs.type === mytypeof(node));
  161. if (cs.id) m = m && (cs.id === id);
  162. if (m && cs.pf) {
  163. if (cs.pf === ":nth-last-child") num = tot - num;
  164. else num++;
  165. if (cs.a === 0) {
  166. m = cs.b === num;
  167. } else {
  168. mod = ((num - cs.b) % cs.a);
  169. m = (!mod && ((num*cs.a + cs.b) >= 0));
  170. }
  171. }
  172. // should we repeat this selector for descendants?
  173. if (sel[0] !== ">" && sel[0].pc !== ":root") sels.push(sel);
  174. if (m) {
  175. // is there a fragment that we should pass down?
  176. if (sel[0] === ">") { if (sel.length > 2) { m = false; sels.push(sel.slice(2)); } }
  177. else if (sel.length > 1) { m = false; sels.push(sel.slice(1)); }
  178. }
  179. return [m, sels];
  180. }
  181. function forEach(sel, obj, fun, id, num, tot) {
  182. var a = (sel[0] === ",") ? sel.slice(1) : [sel],
  183. a0 = [],
  184. call = false,
  185. i = 0, j = 0, l = 0, k, x;
  186. for (i = 0; i < a.length; i++) {
  187. x = mn(obj, a[i], id, num, tot);
  188. if (x[0]) {
  189. call = true;
  190. }
  191. for (j = 0; j < x[1].length; j++) {
  192. a0.push(x[1][j]);
  193. }
  194. }
  195. if (a0.length && typeof obj === "object") {
  196. if (a0.length >= 1) {
  197. a0.unshift(",");
  198. }
  199. if (isArray(obj)) {
  200. for (i = 0; i < obj.length; i++) {
  201. forEach(a0, obj[i], fun, undefined, i, obj.length);
  202. }
  203. } else {
  204. // it's a shame to do this for :last-child and other
  205. // properties which count from the end when we don't
  206. // even know if they're present. Also, the stream
  207. // parser is going to be pissed.
  208. l = 0;
  209. for (k in obj) {
  210. if (obj.hasOwnProperty(k)) {
  211. l++;
  212. }
  213. }
  214. i = 0;
  215. for (k in obj) {
  216. if (obj.hasOwnProperty(k)) {
  217. forEach(a0, obj[k], fun, k, i++, l);
  218. }
  219. }
  220. }
  221. }
  222. if (call && fun) {
  223. fun(obj);
  224. }
  225. }
  226. function match(sel, obj) {
  227. var a = [];
  228. forEach(sel, obj, function(x) {
  229. a.push(x);
  230. });
  231. return a;
  232. }
  233. function compile(sel) {
  234. return {
  235. sel: parse(sel),
  236. match: function(obj){
  237. return match(this.sel, obj);
  238. },
  239. forEach: function(obj, fun) {
  240. return forEach(this.sel, obj, fun);
  241. }
  242. };
  243. }
  244. exports._lex = lex;
  245. exports._parse = parse;
  246. exports.match = function (sel, obj) {
  247. return compile(sel).match(obj);
  248. };
  249. exports.forEach = function(sel, obj, fun) {
  250. return compile(sel).forEach(obj, fun);
  251. };
  252. exports.compile = compile;
  253. })(typeof exports === "undefined" ? (window.JSONSelect = {}) : exports);