/* compiles a selector to an executable function */ module.exports = compile; module.exports.compileUnsafe = compileUnsafe; var parse = require("CSSwhat"), DomUtils = require("domutils"), isTag = DomUtils.isTag, Rules = require("./general.js"), sortRules = require("./sort.js"), BaseFuncs = require("./basefunctions.js"), trueFunc = BaseFuncs.trueFunc, falseFunc = BaseFuncs.falseFunc; function compile(selector, options){ var next = compileUnsafe(selector, options); return function base(elem){ return isTag(elem) && next(elem); }; } function compileUnsafe(selector, options){ return parse(selector, options) .map(compileRules) .reduce(reduceRules, falseFunc); } function compileRules(arr){ if(arr.length === 0) return falseFunc; return sortRules(arr).reduce(function(func, rule){ if(func === falseFunc) return func; return Rules[rule.type](func, rule); }, trueFunc); } function reduceRules(a, b){ if(b === falseFunc || a === trueFunc){ return a; } if(a === falseFunc || b === trueFunc){ return b; } return function combine(elem){ return a(elem) || b(elem); }; } //:not and :has have to compile selectors //doing this in lib/pseudos.js would lead to circular dependencies, //so we add them here var Pseudos = require("./pseudos.js"), filters = Pseudos.filters, isParent = Pseudos.pseudos.parent, existsOne = DomUtils.existsOne, getChildren = DomUtils.getChildren; filters.not = function(next, select){ var func = compileUnsafe(select); if(func === falseFunc) return next; if(func === trueFunc) return falseFunc; return function(elem){ return !func(elem) && next(elem); }; }; filters.has = function(next, selector){ var func = compile(selector); if(func === falseFunc) return falseFunc; if(func === trueFunc) return function(elem){ return isParent(elem) && next(elem); }; return function has(elem){ return next(elem) && existsOne(func, getChildren(elem)); }; };