UNPKG

2.95 kBJavaScriptView Raw
1/*
2 compiles a selector to an executable function
3*/
4
5module.exports = compile;
6module.exports.compileUnsafe = compileUnsafe;
7
8var 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
18function compile(selector, options){
19 var next = compileUnsafe(selector, options);
20 return wrap(next);
21}
22
23function wrap(next){
24 return function base(elem){
25 return isTag(elem) && next(elem);
26 };
27}
28
29function compileUnsafe(selector, options){
30 var token = parse(selector, options);
31 token.forEach(sortRules);
32
33 return compileToken(token, options);
34}
35
36function compileToken(token, options){
37 return token
38 .map(compileRules, options)
39 .reduce(reduceRules, falseFunc);
40}
41
42function isTraversal(t){
43 return procedure[t.type] < 0;
44}
45
46function 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
57function 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//:not and :has have to compile selectors
71//doing this in lib/pseudos.js would lead to circular dependencies,
72//so we add them here
73
74var Pseudos = require("./pseudos.js"),
75 filters = Pseudos.filters,
76 existsOne = DomUtils.existsOne,
77 isTag = DomUtils.isTag,
78 getChildren = DomUtils.getChildren;
79
80
81function containsTraversal(t){
82 return t.some(isTraversal);
83}
84
85filters.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 //TODO don't parse the selector again
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
105filters.has = function(next, selector, options){
106 //:has will never be reached with options.strict == true
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};