|
41 | 41 |
|
42 | 42 | // throw an error message |
43 | 43 | function te(ec) { |
44 | | - throw new Error(errorCodes[ec]); |
| 44 | + throw new Error(errorCodes[ec]); |
45 | 45 | } |
46 | 46 |
|
47 | 47 | // THE LEXER |
|
52 | 52 | str: 4 // string |
53 | 53 | }; |
54 | 54 |
|
55 | | - var pat = /^(?:([\r\n\t\ ]+)|([*.,>\)\(])|(string|boolean|null|array|object|number)|(:(?:root|first-child|last-child|only-child))|(:(?:nth-child|nth-last-child|has|expr|val|contains))|(:\w+)|(\"(?:[^\\]|\\[^\"])*\")|(\")|((?:[_a-zA-Z]|[^\0-\0177]|\\[^\r\n\f0-9a-fA-F])(?:[_a-zA-Z0-9\-]|[^\u0000-\u0177]|(?:\\[^\r\n\f0-9a-fA-F]))*))/; |
| 55 | + var pat = /^(?:([\r\n\t\ ]+)|([~*.,>\)\(])|(string|boolean|null|array|object|number)|(:(?:root|first-child|last-child|only-child))|(:(?:nth-child|nth-last-child|has|expr|val|contains))|(:\w+)|(\"(?:[^\\]|\\[^\"])*\")|(\")|((?:[_a-zA-Z]|[^\0-\0177]|\\[^\r\n\f0-9a-fA-F])(?:[_a-zA-Z0-9\-]|[^\u0000-\u0177]|(?:\\[^\r\n\f0-9a-fA-F]))*))/; |
56 | 56 | var nthPat = /^\s*\(\s*(?:([+\-]?)([0-9]*)n\s*(?:([+\-])\s*([0-9]))?|(odd|even)|([+\-]?[0-9]+))\s*\)/; |
57 | 57 | function lex(str, off) { |
58 | 58 | if (!off) off = 0; |
|
190 | 190 |
|
191 | 191 | // THE PARSER |
192 | 192 |
|
193 | | - function parse(str, off, nested) { |
| 193 | + function parse(str, off, nested, hints) { |
| 194 | + if (!nested) hints = {}; |
| 195 | + |
194 | 196 | var a = [], am, readParen; |
195 | 197 | if (!off) off = 0; |
196 | 198 |
|
197 | 199 | while (true) { |
198 | | - var s = parse_selector(str, off); |
| 200 | + var s = parse_selector(str, off, hints); |
199 | 201 | a.push(s[1]); |
200 | 202 | s = lex(str, off = s[0]); |
201 | 203 | if (s && s[1] === " ") s = lex(str, off = s[0]); |
202 | 204 | if (!s) break; |
203 | 205 | // now we've parsed a selector, and have something else... |
204 | | - if (s[1] === ">") { |
205 | | - a.push(">"); |
| 206 | + if (s[1] === ">" || s[1] === "~") { |
| 207 | + if (s[1] === "~") hints.usesSiblingOp = true; |
| 208 | + a.push(s[1]); |
206 | 209 | off = s[0]; |
207 | 210 | } else if (s[1] === ",") { |
208 | 211 | if (am === undefined) am = [ ",", a ]; |
|
218 | 221 | } |
219 | 222 | if (nested && !readParen) te("mcp"); |
220 | 223 | if (am) am.push(a); |
221 | | - return [off, am ? am : a]; |
| 224 | + var rv; |
| 225 | + if (!nested && hints.usesSiblingOp) { |
| 226 | + rv = normalize(am ? am : a); |
| 227 | + } else { |
| 228 | + rv = am ? am : a; |
| 229 | + } |
| 230 | + return [off, rv]; |
222 | 231 | } |
223 | 232 |
|
224 | | - function parse_selector(str, off) { |
| 233 | + function normalizeOne(sel) { |
| 234 | + var sels = [], s; |
| 235 | + for (var i = 0; i < sel.length; i++) { |
| 236 | + if (sel[i] === '~') { |
| 237 | + // `A ~ B` maps to `:has(:root > A) > B` |
| 238 | + // `Z A ~ B` maps to `Z :has(:root > A) > B, Z:has(:root > A) > B` |
| 239 | + // This first clause, takes care of the first case, and the first half of the latter case. |
| 240 | + if (i < 2 || sel[i-2] != '>') { |
| 241 | + s = sel.slice(0,i-1); |
| 242 | + s = s.concat([{has:[[{pc: ":root"}, ">", sel[i-1]]]}, ">"]); |
| 243 | + s = s.concat(sel.slice(i+1)); |
| 244 | + sels.push(s); |
| 245 | + } |
| 246 | + // here we take care of the second half of above: |
| 247 | + // (`Z A ~ B` maps to `Z :has(:root > A) > B, Z :has(:root > A) > B`) |
| 248 | + // and a new case: |
| 249 | + // Z > A ~ B maps to Z:has(:root > A) > B |
| 250 | + if (i > 1) { |
| 251 | + var at = sel[i-2] === '>' ? i-3 : i-2; |
| 252 | + s = sel.slice(0,at); |
| 253 | + var z = {}; |
| 254 | + for (var k in sel[at]) if (sel[at].hasOwnProperty(k)) z[k] = sel[at][k]; |
| 255 | + if (!z.has) z.has = []; |
| 256 | + z.has.push([{pc: ":root"}, ">", sel[i-1]]); |
| 257 | + s = s.concat(z, '>', sel.slice(i+1)); |
| 258 | + sels.push(s); |
| 259 | + } |
| 260 | + break; |
| 261 | + } |
| 262 | + } |
| 263 | + if (i == sel.length) return sel; |
| 264 | + return sels.length > 1 ? [','].concat(sels) : sels[0]; |
| 265 | + } |
| 266 | + |
| 267 | + function normalize(sels) { |
| 268 | + if (sels[0] === ',') { |
| 269 | + var r = [","]; |
| 270 | + for (var i = i; i < sels.length; i++) { |
| 271 | + var s = normalizeOne(s[i]); |
| 272 | + r = r.concat(s[0] === "," ? s.slice(1) : s); |
| 273 | + } |
| 274 | + return r; |
| 275 | + } else { |
| 276 | + return normalizeOne(sels); |
| 277 | + } |
| 278 | + } |
| 279 | + |
| 280 | + function parse_selector(str, off, hints) { |
225 | 281 | var soff = off; |
226 | 282 | var s = { }; |
227 | 283 | var l = lex(str, off); |
|
441 | 497 | }; |
442 | 498 | exports.compile = compile; |
443 | 499 | })(typeof exports === "undefined" ? (window.JSONSelect = {}) : exports); |
444 | | - |
445 | | - |
|
0 commit comments