File: /home/f/r/e/frenchy/www/french-american.org/current/node_modules/CSSselect/lib/compile.js
/*
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("boolbase"),
trueFunc = BaseFuncs.trueFunc,
falseFunc = BaseFuncs.falseFunc,
procedure = require("./procedure.json");
function compile(selector, options){
var next = compileUnsafe(selector, options);
return wrap(next);
}
function wrap(next){
return function base(elem){
return isTag(elem) && next(elem);
};
}
function compileUnsafe(selector, options){
var token = parse(selector, options);
token.forEach(sortRules);
return compileToken(token, options);
}
function compileToken(token, options){
return token
.map(compileRules, options)
.reduce(reduceRules, falseFunc);
}
function isTraversal(t){
return procedure[t.type] < 0;
}
function compileRules(rules){
if(rules.length === 0) return falseFunc;
var options = this;
return rules.reduce(function(func, rule){
if(func === falseFunc) return func;
return Rules[rule.type](func, rule, options);
}, options && options.rootFunc || 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,
existsOne = DomUtils.existsOne,
isTag = DomUtils.isTag,
getChildren = DomUtils.getChildren;
function containsTraversal(t){
return t.some(isTraversal);
}
filters.not = function(next, select, options){
if(options && options.strict){
var tokens = parse(select);
if(tokens.length > 1 || tokens.some(containsTraversal)){
throw new SyntaxError("complex selectors in :not aren't allowed in strict mode");
}
//TODO don't parse the selector again
}
var opts = options ? {xmlMode: !!options.xmlMode, strict: !!options.strict} : null;
var func = compileUnsafe(select, opts);
if(func === falseFunc) return next;
if(func === trueFunc) return falseFunc;
return function(elem){
return !func(elem) && next(elem);
};
};
filters.has = function(next, selector, options){
//:has will never be reached with options.strict == true
var opts = options ? {xmlMode: !!options.xmlMode, strict: false} : null;
var func = compileUnsafe(selector, opts);
if(func === falseFunc) return falseFunc;
if(func === trueFunc) return function(elem){
return getChildren(elem).some(isTag) && next(elem);
};
func = wrap(func);
return function has(elem){
return next(elem) && existsOne(func, getChildren(elem));
};
};