1 |
|
2 |
|
3 |
|
4 |
|
5 | module.exports = compile;
|
6 | module.exports.compileUnsafe = compileUnsafe;
|
7 |
|
8 | var parse = require("CSSwhat"),
|
9 | DomUtils = require("domutils"),
|
10 | isTag = DomUtils.isTag,
|
11 | Rules = require("./general.js"),
|
12 | sortRules = require("./sort.js"),
|
13 | BaseFuncs = require("boolbase"),
|
14 | trueFunc = BaseFuncs.trueFunc,
|
15 | falseFunc = BaseFuncs.falseFunc,
|
16 | procedure = require("./procedure.json");
|
17 |
|
18 | function compile(selector, options){
|
19 | var next = compileUnsafe(selector, options);
|
20 | return wrap(next);
|
21 | }
|
22 |
|
23 | function wrap(next){
|
24 | return function base(elem){
|
25 | return isTag(elem) && next(elem);
|
26 | };
|
27 | }
|
28 |
|
29 | function compileUnsafe(selector, options){
|
30 | var token = parse(selector, options);
|
31 | token.forEach(sortRules);
|
32 |
|
33 | return compileToken(token, options);
|
34 | }
|
35 |
|
36 | function compileToken(token, options){
|
37 | return token
|
38 | .map(compileRules, options)
|
39 | .reduce(reduceRules, falseFunc);
|
40 | }
|
41 |
|
42 | function isTraversal(t){
|
43 | return procedure[t.type] < 0;
|
44 | }
|
45 |
|
46 | function compileRules(rules){
|
47 | if(rules.length === 0) return falseFunc;
|
48 |
|
49 | var options = this;
|
50 |
|
51 | return rules.reduce(function(func, rule){
|
52 | if(func === falseFunc) return func;
|
53 | return Rules[rule.type](func, rule, options);
|
54 | }, options && options.rootFunc || trueFunc);
|
55 | }
|
56 |
|
57 | function reduceRules(a, b){
|
58 | if(b === falseFunc || a === trueFunc){
|
59 | return a;
|
60 | }
|
61 | if(a === falseFunc || b === trueFunc){
|
62 | return b;
|
63 | }
|
64 |
|
65 | return function combine(elem){
|
66 | return a(elem) || b(elem);
|
67 | };
|
68 | }
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 | var Pseudos = require("./pseudos.js"),
|
75 | filters = Pseudos.filters,
|
76 | existsOne = DomUtils.existsOne,
|
77 | isTag = DomUtils.isTag,
|
78 | getChildren = DomUtils.getChildren;
|
79 |
|
80 |
|
81 | function containsTraversal(t){
|
82 | return t.some(isTraversal);
|
83 | }
|
84 |
|
85 | filters.not = function(next, select, options){
|
86 | if(options && options.strict){
|
87 | var tokens = parse(select);
|
88 | if(tokens.length > 1 || tokens.some(containsTraversal)){
|
89 | throw new SyntaxError("complex selectors in :not aren't allowed in strict mode");
|
90 | }
|
91 |
|
92 | }
|
93 |
|
94 | var opts = options ? {xmlMode: !!options.xmlMode, strict: !!options.strict} : null;
|
95 | var func = compileUnsafe(select, opts);
|
96 |
|
97 | if(func === falseFunc) return next;
|
98 | if(func === trueFunc) return falseFunc;
|
99 |
|
100 | return function(elem){
|
101 | return !func(elem) && next(elem);
|
102 | };
|
103 | };
|
104 |
|
105 | filters.has = function(next, selector, options){
|
106 |
|
107 | var opts = options ? {xmlMode: !!options.xmlMode, strict: false} : null;
|
108 | var func = compileUnsafe(selector, opts);
|
109 |
|
110 | if(func === falseFunc) return falseFunc;
|
111 | if(func === trueFunc) return function(elem){
|
112 | return getChildren(elem).some(isTag) && next(elem);
|
113 | };
|
114 |
|
115 | func = wrap(func);
|
116 |
|
117 | return function has(elem){
|
118 | return next(elem) && existsOne(func, getChildren(elem));
|
119 | };
|
120 | };
|