css-select
Advanced tools
Comparing version 1.3.0-rc0 to 2.0.0
56
index.js
@@ -5,19 +5,32 @@ "use strict"; | ||
var DomUtils = require("domutils"), | ||
falseFunc = require("boolbase").falseFunc, | ||
compileFactory = require("./lib/compile.js"), | ||
defaultCompile = compileFactory(DomUtils); | ||
var DomUtils = require("domutils"); | ||
var falseFunc = require("boolbase").falseFunc; | ||
var compileRaw = require("./lib/compile.js"); | ||
function adapterCompile(adapter){ | ||
return adapter === DomUtils ? defaultCompile : compileFactory(adapter); | ||
function wrapCompile(func){ | ||
return function addAdapter(selector, options, context){ | ||
options = options || {}; | ||
options.adapter = options.adapter || DomUtils; | ||
return func(selector, options, context); | ||
}; | ||
} | ||
var compile = wrapCompile(compileRaw); | ||
var compileUnsafe = wrapCompile(compileRaw.compileUnsafe); | ||
function getSelectorFunc(searchFunc){ | ||
return function select(query, elems, options){ | ||
options = options || {} | ||
options = options || {}; | ||
options.adapter = options.adapter || DomUtils; | ||
var compile = adapterCompile(options.adapter); | ||
if(typeof query !== "function") query = compile.compileUnsafe(query, options, elems); | ||
if(query.shouldTestNextSiblings) elems = appendNextSiblings((options && options.context) || elems, options.adapter); | ||
if(typeof query !== "function"){ | ||
query = compileUnsafe(query, options, elems); | ||
} | ||
if(query.shouldTestNextSiblings){ | ||
elems = appendNextSiblings( | ||
(options && options.context) || elems, | ||
options.adapter | ||
); | ||
} | ||
if(!Array.isArray(elems)) elems = options.adapter.getChildren(elems); | ||
@@ -50,13 +63,16 @@ else elems = options.adapter.removeSubsets(elems); | ||
var selectAll = getSelectorFunc(function selectAll(query, elems, options){ | ||
return (query === falseFunc || !elems || elems.length === 0) ? [] : options.adapter.findAll(query, elems); | ||
return query === falseFunc || !elems || elems.length === 0 | ||
? [] | ||
: options.adapter.findAll(query, elems); | ||
}); | ||
var selectOne = getSelectorFunc(function selectOne(query, elems, options){ | ||
return (query === falseFunc || !elems || elems.length === 0) ? null : options.adapter.findOne(query, elems); | ||
return query === falseFunc || !elems || elems.length === 0 | ||
? null | ||
: options.adapter.findOne(query, elems); | ||
}); | ||
function is(elem, query, options){ | ||
options = options || {} | ||
options = options || {}; | ||
options.adapter = options.adapter || DomUtils; | ||
var compile = adapterCompile(options.adapter); | ||
return (typeof query === "function" ? query : compile(query, options))(elem); | ||
@@ -72,5 +88,5 @@ } | ||
CSSselect.compile = defaultCompile; | ||
CSSselect.filters = defaultCompile.Pseudos.filters; | ||
CSSselect.pseudos = defaultCompile.Pseudos.pseudos; | ||
CSSselect.compile = compile; | ||
CSSselect.filters = compileRaw.Pseudos.filters; | ||
CSSselect.pseudos = compileRaw.Pseudos.pseudos; | ||
@@ -83,7 +99,7 @@ CSSselect.selectAll = selectAll; | ||
//legacy methods (might be removed) | ||
CSSselect.parse = defaultCompile; | ||
CSSselect.parse = compile; | ||
CSSselect.iterate = selectAll; | ||
//hooks | ||
CSSselect._compileUnsafe = defaultCompile.compileUnsafe; | ||
CSSselect._compileToken = defaultCompile.compileToken; | ||
CSSselect._compileUnsafe = compileUnsafe; | ||
CSSselect._compileToken = compileRaw.compileToken; |
@@ -6,177 +6,196 @@ var falseFunc = require("boolbase").falseFunc; | ||
function factory(adapter){ | ||
/* | ||
attribute selectors | ||
*/ | ||
var attributeRules = { | ||
__proto__: null, | ||
equals: function(next, data){ | ||
var name = data.name, | ||
value = data.value; | ||
/* | ||
attribute selectors | ||
*/ | ||
var attributeRules = { | ||
__proto__: null, | ||
equals: function(next, data, options){ | ||
var name = data.name; | ||
var value = data.value; | ||
var adapter = options.adapter; | ||
if(data.ignoreCase){ | ||
value = value.toLowerCase(); | ||
if(data.ignoreCase){ | ||
value = value.toLowerCase(); | ||
return function equalsIC(elem){ | ||
var attr = adapter.getAttributeValue(elem, name); | ||
return attr != null && attr.toLowerCase() === value && next(elem); | ||
}; | ||
} | ||
return function equals(elem){ | ||
return adapter.getAttributeValue(elem, name) === value && next(elem); | ||
return function equalsIC(elem){ | ||
var attr = adapter.getAttributeValue(elem, name); | ||
return attr != null && attr.toLowerCase() === value && next(elem); | ||
}; | ||
}, | ||
hyphen: function(next, data){ | ||
var name = data.name, | ||
value = data.value, | ||
len = value.length; | ||
} | ||
if(data.ignoreCase){ | ||
value = value.toLowerCase(); | ||
return function equals(elem){ | ||
return adapter.getAttributeValue(elem, name) === value && next(elem); | ||
}; | ||
}, | ||
hyphen: function(next, data, options){ | ||
var name = data.name; | ||
var value = data.value; | ||
var len = value.length; | ||
var adapter = options.adapter; | ||
return function hyphenIC(elem){ | ||
var attr = adapter.getAttributeValue(elem, name); | ||
return attr != null && | ||
(attr.length === len || attr.charAt(len) === "-") && | ||
attr.substr(0, len).toLowerCase() === value && | ||
next(elem); | ||
}; | ||
} | ||
if(data.ignoreCase){ | ||
value = value.toLowerCase(); | ||
return function hyphen(elem){ | ||
return function hyphenIC(elem){ | ||
var attr = adapter.getAttributeValue(elem, name); | ||
return attr != null && | ||
attr.substr(0, len) === value && | ||
(attr.length === len || attr.charAt(len) === "-") && | ||
next(elem); | ||
return ( | ||
attr != null && | ||
(attr.length === len || attr.charAt(len) === "-") && | ||
attr.substr(0, len).toLowerCase() === value && | ||
next(elem) | ||
); | ||
}; | ||
}, | ||
element: function(next, data){ | ||
var name = data.name, | ||
value = data.value; | ||
} | ||
if(/\s/.test(value)){ | ||
return falseFunc; | ||
} | ||
return function hyphen(elem){ | ||
var attr = adapter.getAttributeValue(elem, name); | ||
return ( | ||
attr != null && | ||
attr.substr(0, len) === value && | ||
(attr.length === len || attr.charAt(len) === "-") && | ||
next(elem) | ||
); | ||
}; | ||
}, | ||
element: function(next, data, options){ | ||
var name = data.name; | ||
var value = data.value; | ||
var adapter = options.adapter; | ||
value = value.replace(reChars, "\\$&"); | ||
if(/\s/.test(value)){ | ||
return falseFunc; | ||
} | ||
var pattern = "(?:^|\\s)" + value + "(?:$|\\s)", | ||
flags = data.ignoreCase ? "i" : "", | ||
regex = new RegExp(pattern, flags); | ||
value = value.replace(reChars, "\\$&"); | ||
return function element(elem){ | ||
var attr = adapter.getAttributeValue(elem, name); | ||
return attr != null && regex.test(attr) && next(elem); | ||
}; | ||
}, | ||
exists: function(next, data){ | ||
var name = data.name; | ||
return function exists(elem){ | ||
return adapter.hasAttrib(elem, name) && next(elem); | ||
}; | ||
}, | ||
start: function(next, data){ | ||
var name = data.name, | ||
value = data.value, | ||
len = value.length; | ||
var pattern = "(?:^|\\s)" + value + "(?:$|\\s)", | ||
flags = data.ignoreCase ? "i" : "", | ||
regex = new RegExp(pattern, flags); | ||
if(len === 0){ | ||
return falseFunc; | ||
} | ||
return function element(elem){ | ||
var attr = adapter.getAttributeValue(elem, name); | ||
return attr != null && regex.test(attr) && next(elem); | ||
}; | ||
}, | ||
exists: function(next, data, options){ | ||
var name = data.name; | ||
var adapter = options.adapter; | ||
if(data.ignoreCase){ | ||
value = value.toLowerCase(); | ||
return function exists(elem){ | ||
return adapter.hasAttrib(elem, name) && next(elem); | ||
}; | ||
}, | ||
start: function(next, data, options){ | ||
var name = data.name; | ||
var value = data.value; | ||
var len = value.length; | ||
var adapter = options.adapter; | ||
return function startIC(elem){ | ||
var attr = adapter.getAttributeValue(elem, name); | ||
return attr != null && attr.substr(0, len).toLowerCase() === value && next(elem); | ||
}; | ||
} | ||
if(len === 0){ | ||
return falseFunc; | ||
} | ||
return function start(elem){ | ||
if(data.ignoreCase){ | ||
value = value.toLowerCase(); | ||
return function startIC(elem){ | ||
var attr = adapter.getAttributeValue(elem, name); | ||
return attr != null && attr.substr(0, len) === value && next(elem); | ||
return ( | ||
attr != null && | ||
attr.substr(0, len).toLowerCase() === value && | ||
next(elem) | ||
); | ||
}; | ||
}, | ||
end: function(next, data){ | ||
var name = data.name, | ||
value = data.value, | ||
len = -value.length; | ||
} | ||
if(len === 0){ | ||
return falseFunc; | ||
} | ||
return function start(elem){ | ||
var attr = adapter.getAttributeValue(elem, name); | ||
return attr != null && attr.substr(0, len) === value && next(elem); | ||
}; | ||
}, | ||
end: function(next, data, options){ | ||
var name = data.name; | ||
var value = data.value; | ||
var len = -value.length; | ||
var adapter = options.adapter; | ||
if(data.ignoreCase){ | ||
value = value.toLowerCase(); | ||
if(len === 0){ | ||
return falseFunc; | ||
} | ||
return function endIC(elem){ | ||
var attr = adapter.getAttributeValue(elem, name); | ||
return attr != null && attr.substr(len).toLowerCase() === value && next(elem); | ||
}; | ||
} | ||
if(data.ignoreCase){ | ||
value = value.toLowerCase(); | ||
return function end(elem){ | ||
return function endIC(elem){ | ||
var attr = adapter.getAttributeValue(elem, name); | ||
return attr != null && attr.substr(len) === value && next(elem); | ||
return ( | ||
attr != null && attr.substr(len).toLowerCase() === value && next(elem) | ||
); | ||
}; | ||
}, | ||
any: function(next, data){ | ||
var name = data.name, | ||
value = data.value; | ||
} | ||
if(value === ""){ | ||
return falseFunc; | ||
} | ||
return function end(elem){ | ||
var attr = adapter.getAttributeValue(elem, name); | ||
return attr != null && attr.substr(len) === value && next(elem); | ||
}; | ||
}, | ||
any: function(next, data, options){ | ||
var name = data.name; | ||
var value = data.value; | ||
var adapter = options.adapter; | ||
if(data.ignoreCase){ | ||
var regex = new RegExp(value.replace(reChars, "\\$&"), "i"); | ||
if(value === ""){ | ||
return falseFunc; | ||
} | ||
return function anyIC(elem){ | ||
var attr = adapter.getAttributeValue(elem, name); | ||
return attr != null && regex.test(attr) && next(elem); | ||
}; | ||
} | ||
if(data.ignoreCase){ | ||
var regex = new RegExp(value.replace(reChars, "\\$&"), "i"); | ||
return function any(elem){ | ||
return function anyIC(elem){ | ||
var attr = adapter.getAttributeValue(elem, name); | ||
return attr != null && attr.indexOf(value) >= 0 && next(elem); | ||
return attr != null && regex.test(attr) && next(elem); | ||
}; | ||
}, | ||
not: function(next, data){ | ||
var name = data.name, | ||
value = data.value; | ||
} | ||
if(value === ""){ | ||
return function notEmpty(elem){ | ||
return !!adapter.getAttributeValue(elem, name) && next(elem); | ||
}; | ||
} else if(data.ignoreCase){ | ||
value = value.toLowerCase(); | ||
return function any(elem){ | ||
var attr = adapter.getAttributeValue(elem, name); | ||
return attr != null && attr.indexOf(value) >= 0 && next(elem); | ||
}; | ||
}, | ||
not: function(next, data, options){ | ||
var name = data.name; | ||
var value = data.value; | ||
var adapter = options.adapter; | ||
return function notIC(elem){ | ||
var attr = adapter.getAttributeValue(elem, name); | ||
return attr != null && attr.toLowerCase() !== value && next(elem); | ||
}; | ||
} | ||
if(value === ""){ | ||
return function notEmpty(elem){ | ||
return !!adapter.getAttributeValue(elem, name) && next(elem); | ||
}; | ||
} else if(data.ignoreCase){ | ||
value = value.toLowerCase(); | ||
return function not(elem){ | ||
return adapter.getAttributeValue(elem, name) !== value && next(elem); | ||
return function notIC(elem){ | ||
var attr = adapter.getAttributeValue(elem, name); | ||
return attr != null && attr.toLowerCase() !== value && next(elem); | ||
}; | ||
} | ||
}; | ||
return { | ||
compile: function(next, data, options){ | ||
if(options && options.strict && ( | ||
data.ignoreCase || data.action === "not" | ||
)) throw new Error("Unsupported attribute selector"); | ||
return attributeRules[data.action](next, data); | ||
}, | ||
rules: attributeRules | ||
}; | ||
} | ||
return function not(elem){ | ||
return adapter.getAttributeValue(elem, name) !== value && next(elem); | ||
}; | ||
} | ||
}; | ||
module.exports = factory; | ||
module.exports = { | ||
compile: function(next, data, options){ | ||
if( | ||
options && | ||
options.strict && | ||
(data.ignoreCase || data.action === "not") | ||
){ | ||
throw new Error("Unsupported attribute selector"); | ||
} | ||
return attributeRules[data.action](next, data, options); | ||
}, | ||
rules: attributeRules | ||
}; |
@@ -5,204 +5,224 @@ /* | ||
module.exports = compileFactory; | ||
module.exports = compile; | ||
var parse = require("css-what"), | ||
BaseFuncs = require("boolbase"), | ||
sortRules = require("./sort.js"), | ||
procedure = require("./procedure.json"), | ||
rulesFactory = require("./general.js"), | ||
pseudosFactory = require("./pseudos.js"), | ||
trueFunc = BaseFuncs.trueFunc, | ||
falseFunc = BaseFuncs.falseFunc; | ||
var parse = require("css-what"); | ||
var BaseFuncs = require("boolbase"); | ||
var sortRules = require("./sort.js"); | ||
var procedure = require("./procedure.json"); | ||
var Rules = require("./general.js"); | ||
var Pseudos = require("./pseudos.js"); | ||
var trueFunc = BaseFuncs.trueFunc; | ||
var falseFunc = BaseFuncs.falseFunc; | ||
function compileFactory(adapter){ | ||
var Pseudos = pseudosFactory(adapter), | ||
filters = Pseudos.filters, | ||
Rules = rulesFactory(adapter, Pseudos); | ||
var filters = Pseudos.filters; | ||
function compile(selector, options, context){ | ||
var next = compileUnsafe(selector, options, context); | ||
return wrap(next); | ||
} | ||
function compile(selector, options, context){ | ||
var next = compileUnsafe(selector, options, context); | ||
return wrap(next, options); | ||
} | ||
function wrap(next){ | ||
return function base(elem){ | ||
return adapter.isTag(elem) && next(elem); | ||
}; | ||
} | ||
function wrap(next, options){ | ||
var adapter = options.adapter; | ||
function compileUnsafe(selector, options, context){ | ||
var token = parse(selector, options); | ||
return compileToken(token, options, context); | ||
} | ||
return function base(elem){ | ||
return adapter.isTag(elem) && next(elem); | ||
}; | ||
} | ||
function includesScopePseudo(t){ | ||
return t.type === "pseudo" && ( | ||
t.name === "scope" || ( | ||
Array.isArray(t.data) && | ||
function compileUnsafe(selector, options, context){ | ||
var token = parse(selector, options); | ||
return compileToken(token, options, context); | ||
} | ||
function includesScopePseudo(t){ | ||
return ( | ||
t.type === "pseudo" && | ||
(t.name === "scope" || | ||
(Array.isArray(t.data) && | ||
t.data.some(function(data){ | ||
return data.some(includesScopePseudo); | ||
}) | ||
) | ||
); | ||
} | ||
}))) | ||
); | ||
} | ||
var DESCENDANT_TOKEN = {type: "descendant"}, | ||
FLEXIBLE_DESCENDANT_TOKEN = {type: "_flexibleDescendant"}, | ||
SCOPE_TOKEN = {type: "pseudo", name: "scope"}, | ||
PLACEHOLDER_ELEMENT = {}; | ||
var DESCENDANT_TOKEN = { type: "descendant" }; | ||
var FLEXIBLE_DESCENDANT_TOKEN = { type: "_flexibleDescendant" }; | ||
var SCOPE_TOKEN = { type: "pseudo", name: "scope" }; | ||
var PLACEHOLDER_ELEMENT = {}; | ||
//CSS 4 Spec (Draft): 3.3.1. Absolutizing a Scope-relative Selector | ||
//http://www.w3.org/TR/selectors4/#absolutizing | ||
function absolutize(token, context){ | ||
//TODO better check if context is document | ||
var hasContext = !!context && !!context.length && context.every(function(e){ | ||
//CSS 4 Spec (Draft): 3.3.1. Absolutizing a Scope-relative Selector | ||
//http://www.w3.org/TR/selectors4/#absolutizing | ||
function absolutize(token, options, context){ | ||
var adapter = options.adapter; | ||
//TODO better check if context is document | ||
var hasContext = | ||
!!context && | ||
!!context.length && | ||
context.every(function(e){ | ||
return e === PLACEHOLDER_ELEMENT || !!adapter.getParent(e); | ||
}); | ||
token.forEach(function(t){ | ||
if(t.length > 0 && isTraversal(t[0]) && t[0].type !== "descendant"){ | ||
//don't return in else branch | ||
} else if(hasContext && !includesScopePseudo(t)){ | ||
t.unshift(DESCENDANT_TOKEN); | ||
} else { | ||
return; | ||
} | ||
token.forEach(function(t){ | ||
if(t.length > 0 && isTraversal(t[0]) && t[0].type !== "descendant"){ | ||
//don't return in else branch | ||
} else if(hasContext && !includesScopePseudo(t)){ | ||
t.unshift(DESCENDANT_TOKEN); | ||
} else { | ||
return; | ||
} | ||
t.unshift(SCOPE_TOKEN); | ||
}); | ||
} | ||
t.unshift(SCOPE_TOKEN); | ||
}); | ||
} | ||
function compileToken(token, options, context){ | ||
token = token.filter(function(t){ | ||
return t.length > 0; | ||
}); | ||
function compileToken(token, options, context){ | ||
token = token.filter(function(t){ return t.length > 0; }); | ||
token.forEach(sortRules); | ||
token.forEach(sortRules); | ||
var isArrayContext = Array.isArray(context); | ||
var isArrayContext = Array.isArray(context); | ||
context = (options && options.context) || context; | ||
context = (options && options.context) || context; | ||
if(context && !isArrayContext) context = [context]; | ||
if(context && !isArrayContext) context = [context]; | ||
absolutize(token, options, context); | ||
absolutize(token, context); | ||
var shouldTestNextSiblings = false; | ||
var shouldTestNextSiblings = false; | ||
var query = token | ||
.map(function(rules){ | ||
if(rules[0] && rules[1] && rules[0].name === "scope"){ | ||
var ruleType = rules[1].type; | ||
if(isArrayContext && ruleType === "descendant") rules[1] = FLEXIBLE_DESCENDANT_TOKEN; | ||
else if(ruleType === "adjacent" || ruleType === "sibling") shouldTestNextSiblings = true; | ||
var query = token | ||
.map(function(rules){ | ||
if(rules[0] && rules[1] && rules[0].name === "scope"){ | ||
var ruleType = rules[1].type; | ||
if(isArrayContext && ruleType === "descendant"){ | ||
rules[1] = FLEXIBLE_DESCENDANT_TOKEN; | ||
} else if(ruleType === "adjacent" || ruleType === "sibling"){ | ||
shouldTestNextSiblings = true; | ||
} | ||
return compileRules(rules, options, context); | ||
}) | ||
.reduce(reduceRules, falseFunc); | ||
} | ||
return compileRules(rules, options, context); | ||
}) | ||
.reduce(reduceRules, falseFunc); | ||
query.shouldTestNextSiblings = shouldTestNextSiblings; | ||
query.shouldTestNextSiblings = shouldTestNextSiblings; | ||
return query; | ||
} | ||
return query; | ||
} | ||
function isTraversal(t){ | ||
return procedure[t.type] < 0; | ||
} | ||
function isTraversal(t){ | ||
return procedure[t.type] < 0; | ||
} | ||
function compileRules(rules, options, context){ | ||
return rules.reduce(function(func, rule){ | ||
if(func === falseFunc) return func; | ||
return Rules[rule.type](func, rule, options, context); | ||
}, options && options.rootFunc || trueFunc); | ||
} | ||
function compileRules(rules, options, context){ | ||
return rules.reduce(function(func, rule){ | ||
if(func === falseFunc) return func; | ||
function reduceRules(a, b){ | ||
if(b === falseFunc || a === trueFunc){ | ||
return a; | ||
if(!(rule.type in Rules)){ | ||
throw new Error( | ||
"Rule type " + rule.type + " is not supported by css-select" | ||
); | ||
} | ||
if(a === falseFunc || b === trueFunc){ | ||
return b; | ||
} | ||
return function combine(elem){ | ||
return a(elem) || b(elem); | ||
}; | ||
} | ||
return Rules[rule.type](func, rule, options, context); | ||
}, (options && options.rootFunc) || trueFunc); | ||
} | ||
function containsTraversal(t){ | ||
return t.some(isTraversal); | ||
function reduceRules(a, b){ | ||
if(b === falseFunc || a === trueFunc){ | ||
return a; | ||
} | ||
if(a === falseFunc || b === trueFunc){ | ||
return b; | ||
} | ||
//:not, :has and :matches have to compile selectors | ||
//doing this in lib/pseudos.js would lead to circular dependencies, | ||
//so we add them here | ||
filters.not = function(next, token, options, context){ | ||
var opts = { | ||
xmlMode: !!(options && options.xmlMode), | ||
strict: !!(options && options.strict) | ||
}; | ||
return function combine(elem){ | ||
return a(elem) || b(elem); | ||
}; | ||
} | ||
if(opts.strict){ | ||
if(token.length > 1 || token.some(containsTraversal)){ | ||
throw new Error("complex selectors in :not aren't allowed in strict mode"); | ||
} | ||
function containsTraversal(t){ | ||
return t.some(isTraversal); | ||
} | ||
//:not, :has and :matches have to compile selectors | ||
//doing this in lib/pseudos.js would lead to circular dependencies, | ||
//so we add them here | ||
filters.not = function(next, token, options, context){ | ||
var opts = { | ||
xmlMode: !!(options && options.xmlMode), | ||
strict: !!(options && options.strict), | ||
adapter: options.adapter | ||
}; | ||
if(opts.strict){ | ||
if(token.length > 1 || token.some(containsTraversal)){ | ||
throw new Error( | ||
"complex selectors in :not aren't allowed in strict mode" | ||
); | ||
} | ||
} | ||
var func = compileToken(token, opts, context); | ||
var func = compileToken(token, opts, context); | ||
if(func === falseFunc) return next; | ||
if(func === trueFunc) return falseFunc; | ||
if(func === falseFunc) return next; | ||
if(func === trueFunc) return falseFunc; | ||
return function(elem){ | ||
return !func(elem) && next(elem); | ||
}; | ||
return function not(elem){ | ||
return !func(elem) && next(elem); | ||
}; | ||
}; | ||
filters.has = function(next, token, options){ | ||
var opts = { | ||
xmlMode: !!(options && options.xmlMode), | ||
strict: !!(options && options.strict) | ||
}; | ||
filters.has = function(next, token, options){ | ||
var adapter = options.adapter; | ||
var opts = { | ||
xmlMode: !!(options && options.xmlMode), | ||
strict: !!(options && options.strict), | ||
adapter: adapter | ||
}; | ||
//FIXME: Uses an array as a pointer to the current element (side effects) | ||
var context = token.some(containsTraversal) ? [PLACEHOLDER_ELEMENT] : null; | ||
//FIXME: Uses an array as a pointer to the current element (side effects) | ||
var context = token.some(containsTraversal) ? [PLACEHOLDER_ELEMENT] : null; | ||
var func = compileToken(token, opts, context); | ||
var func = compileToken(token, opts, context); | ||
if(func === falseFunc) return falseFunc; | ||
if(func === trueFunc){ | ||
return function(elem){ | ||
return adapter.getChildren(elem).some(adapter.isTag) && next(elem); | ||
}; | ||
} | ||
if(func === falseFunc) return falseFunc; | ||
if(func === trueFunc){ | ||
return function hasChild(elem){ | ||
return adapter.getChildren(elem).some(adapter.isTag) && next(elem); | ||
}; | ||
} | ||
func = wrap(func); | ||
func = wrap(func, options); | ||
if(context){ | ||
return function has(elem){ | ||
return next(elem) && ( | ||
(context[0] = elem), adapter.existsOne(func, adapter.getChildren(elem)) | ||
); | ||
}; | ||
} | ||
if(context){ | ||
return function has(elem){ | ||
return next(elem) && adapter.existsOne(func, adapter.getChildren(elem)); | ||
return ( | ||
next(elem) && | ||
((context[0] = elem), | ||
adapter.existsOne(func, adapter.getChildren(elem))) | ||
); | ||
}; | ||
} | ||
return function has(elem){ | ||
return next(elem) && adapter.existsOne(func, adapter.getChildren(elem)); | ||
}; | ||
}; | ||
filters.matches = function(next, token, options, context){ | ||
var opts = { | ||
xmlMode: !!(options && options.xmlMode), | ||
strict: !!(options && options.strict), | ||
rootFunc: next | ||
}; | ||
return compileToken(token, opts, context); | ||
filters.matches = function(next, token, options, context){ | ||
var opts = { | ||
xmlMode: !!(options && options.xmlMode), | ||
strict: !!(options && options.strict), | ||
rootFunc: next, | ||
adapter: options.adapter | ||
}; | ||
compile.compileToken = compileToken; | ||
compile.compileUnsafe = compileUnsafe; | ||
compile.Pseudos = Pseudos; | ||
return compileToken(token, opts, context); | ||
}; | ||
return compile; | ||
} | ||
compile.compileToken = compileToken; | ||
compile.compileUnsafe = compileUnsafe; | ||
compile.Pseudos = Pseudos; |
@@ -1,99 +0,117 @@ | ||
var attributeFactory = require("./attributes.js"); | ||
var attributes = require("./attributes.js"); | ||
var Pseudos = require("./pseudos"); | ||
function generalFactory(adapter, Pseudos){ | ||
/* | ||
all available rules | ||
*/ | ||
return { | ||
__proto__: null, | ||
/* | ||
all available rules | ||
*/ | ||
module.exports = { | ||
__proto__: null, | ||
attribute: attributeFactory(adapter).compile, | ||
pseudo: Pseudos.compile, | ||
attribute: attributes.compile, | ||
pseudo: Pseudos.compile, | ||
//tags | ||
tag: function(next, data){ | ||
var name = data.name; | ||
return function tag(elem){ | ||
return adapter.getName(elem) === name && next(elem); | ||
}; | ||
}, | ||
//tags | ||
tag: function(next, data, options){ | ||
var name = data.name; | ||
var adapter = options.adapter; | ||
//traversal | ||
descendant: function(next){ | ||
return function descendant(elem){ | ||
return function tag(elem){ | ||
return adapter.getName(elem) === name && next(elem); | ||
}; | ||
}, | ||
var found = false; | ||
//traversal | ||
descendant: function(next, data, options){ | ||
// eslint-disable-next-line no-undef | ||
var isFalseCache = typeof WeakSet !== "undefined" ? new WeakSet() : null; | ||
var adapter = options.adapter; | ||
while(!found && (elem = adapter.getParent(elem))){ | ||
return function descendant(elem){ | ||
var found = false; | ||
while(!found && (elem = adapter.getParent(elem))){ | ||
if(!isFalseCache || !isFalseCache.has(elem)){ | ||
found = next(elem); | ||
if(!found && isFalseCache){ | ||
isFalseCache.add(elem); | ||
} | ||
} | ||
} | ||
return found; | ||
}; | ||
}, | ||
_flexibleDescendant: function(next){ | ||
// Include element itself, only used while querying an array | ||
return function descendant(elem){ | ||
return found; | ||
}; | ||
}, | ||
_flexibleDescendant: function(next, data, options){ | ||
var adapter = options.adapter; | ||
var found = next(elem); | ||
// Include element itself, only used while querying an array | ||
return function descendant(elem){ | ||
var found = next(elem); | ||
while(!found && (elem = adapter.getParent(elem))){ | ||
found = next(elem); | ||
} | ||
while(!found && (elem = adapter.getParent(elem))){ | ||
found = next(elem); | ||
} | ||
return found; | ||
}; | ||
}, | ||
parent: function(next, data, options){ | ||
if(options && options.strict) throw new Error("Parent selector isn't part of CSS3"); | ||
return found; | ||
}; | ||
}, | ||
parent: function(next, data, options){ | ||
if(options && options.strict){ | ||
throw new Error("Parent selector isn't part of CSS3"); | ||
} | ||
return function parent(elem){ | ||
return adapter.getChildren(elem).some(test); | ||
}; | ||
var adapter = options.adapter; | ||
function test(elem){ | ||
return adapter.isTag(elem) && next(elem); | ||
} | ||
}, | ||
child: function(next){ | ||
return function child(elem){ | ||
var parent = adapter.getParent(elem); | ||
return !!parent && next(parent); | ||
}; | ||
}, | ||
sibling: function(next){ | ||
return function sibling(elem){ | ||
var siblings = adapter.getSiblings(elem); | ||
return function parent(elem){ | ||
return adapter.getChildren(elem).some(test); | ||
}; | ||
for(var i = 0; i < siblings.length; i++){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem) break; | ||
if(next(siblings[i])) return true; | ||
} | ||
function test(elem){ | ||
return adapter.isTag(elem) && next(elem); | ||
} | ||
}, | ||
child: function(next, data, options){ | ||
var adapter = options.adapter; | ||
return function child(elem){ | ||
var parent = adapter.getParent(elem); | ||
return !!parent && next(parent); | ||
}; | ||
}, | ||
sibling: function(next, data, options){ | ||
var adapter = options.adapter; | ||
return function sibling(elem){ | ||
var siblings = adapter.getSiblings(elem); | ||
for(var i = 0; i < siblings.length; i++){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem) break; | ||
if(next(siblings[i])) return true; | ||
} | ||
} | ||
return false; | ||
}; | ||
}, | ||
adjacent: function(next){ | ||
return function adjacent(elem){ | ||
var siblings = adapter.getSiblings(elem), | ||
lastElement; | ||
return false; | ||
}; | ||
}, | ||
adjacent: function(next, data, options){ | ||
var adapter = options.adapter; | ||
for(var i = 0; i < siblings.length; i++){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem) break; | ||
lastElement = siblings[i]; | ||
} | ||
return function adjacent(elem){ | ||
var siblings = adapter.getSiblings(elem), | ||
lastElement; | ||
for(var i = 0; i < siblings.length; i++){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem) break; | ||
lastElement = siblings[i]; | ||
} | ||
} | ||
return !!lastElement && next(lastElement); | ||
}; | ||
}, | ||
universal: function(next){ | ||
return next; | ||
} | ||
}; | ||
} | ||
module.exports = generalFactory; | ||
return !!lastElement && next(lastElement); | ||
}; | ||
}, | ||
universal: function(next){ | ||
return next; | ||
} | ||
}; |
{ | ||
"universal": 50, | ||
"tag": 30, | ||
"attribute": 1, | ||
"pseudo": 0, | ||
"descendant": -1, | ||
"child": -1, | ||
"parent": -1, | ||
"sibling": -1, | ||
"adjacent": -1 | ||
"universal": 50, | ||
"tag": 30, | ||
"attribute": 1, | ||
"pseudo": 0, | ||
"descendant": -1, | ||
"child": -1, | ||
"parent": -1, | ||
"sibling": -1, | ||
"adjacent": -1 | ||
} |
@@ -14,345 +14,362 @@ /* | ||
var getNCheck = require("nth-check"), | ||
BaseFuncs = require("boolbase"), | ||
attributesFactory = require("./attributes.js"), | ||
trueFunc = BaseFuncs.trueFunc, | ||
falseFunc = BaseFuncs.falseFunc; | ||
var getNCheck = require("nth-check"); | ||
var BaseFuncs = require("boolbase"); | ||
var attributes = require("./attributes.js"); | ||
var trueFunc = BaseFuncs.trueFunc; | ||
var falseFunc = BaseFuncs.falseFunc; | ||
function filtersFactory(adapter){ | ||
var attributes = attributesFactory(adapter), | ||
checkAttrib = attributes.rules.equals; | ||
var checkAttrib = attributes.rules.equals; | ||
//helper methods | ||
function equals(a, b){ | ||
if(typeof adapter.equals === "function") return adapter.equals(a, b); | ||
function getAttribFunc(name, value){ | ||
var data = { name: name, value: value }; | ||
return function attribFunc(next, rule, options){ | ||
return checkAttrib(next, data, options); | ||
}; | ||
} | ||
return a === b; | ||
} | ||
function getChildFunc(next, adapter){ | ||
return function(elem){ | ||
return !!adapter.getParent(elem) && next(elem); | ||
}; | ||
} | ||
function getAttribFunc(name, value){ | ||
var data = {name: name, value: value}; | ||
return function attribFunc(next){ | ||
return checkAttrib(next, data); | ||
var filters = { | ||
contains: function(next, text, options){ | ||
var adapter = options.adapter; | ||
return function contains(elem){ | ||
return next(elem) && adapter.getText(elem).indexOf(text) >= 0; | ||
}; | ||
} | ||
}, | ||
icontains: function(next, text, options){ | ||
var itext = text.toLowerCase(); | ||
var adapter = options.adapter; | ||
function getChildFunc(next){ | ||
return function(elem){ | ||
return !!adapter.getParent(elem) && next(elem); | ||
return function icontains(elem){ | ||
return ( | ||
next(elem) && | ||
adapter | ||
.getText(elem) | ||
.toLowerCase() | ||
.indexOf(itext) >= 0 | ||
); | ||
}; | ||
} | ||
}, | ||
var filters = { | ||
contains: function(next, text){ | ||
return function contains(elem){ | ||
return next(elem) && adapter.getText(elem).indexOf(text) >= 0; | ||
}; | ||
}, | ||
icontains: function(next, text){ | ||
var itext = text.toLowerCase(); | ||
return function icontains(elem){ | ||
return next(elem) && | ||
adapter.getText(elem).toLowerCase().indexOf(itext) >= 0; | ||
}; | ||
}, | ||
//location specific methods | ||
"nth-child": function(next, rule, options){ | ||
var func = getNCheck(rule); | ||
var adapter = options.adapter; | ||
//location specific methods | ||
"nth-child": function(next, rule){ | ||
var func = getNCheck(rule); | ||
if(func === falseFunc) return func; | ||
if(func === trueFunc) return getChildFunc(next, adapter); | ||
if(func === falseFunc) return func; | ||
if(func === trueFunc) return getChildFunc(next); | ||
return function nthChild(elem){ | ||
var siblings = adapter.getSiblings(elem); | ||
return function nthChild(elem){ | ||
var siblings = adapter.getSiblings(elem); | ||
for(var i = 0, pos = 0; i < siblings.length; i++){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem) break; | ||
else pos++; | ||
} | ||
for(var i = 0, pos = 0; i < siblings.length; i++){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem) break; | ||
else pos++; | ||
} | ||
} | ||
return func(pos) && next(elem); | ||
}; | ||
}, | ||
"nth-last-child": function(next, rule){ | ||
var func = getNCheck(rule); | ||
return func(pos) && next(elem); | ||
}; | ||
}, | ||
"nth-last-child": function(next, rule, options){ | ||
var func = getNCheck(rule); | ||
var adapter = options.adapter; | ||
if(func === falseFunc) return func; | ||
if(func === trueFunc) return getChildFunc(next); | ||
if(func === falseFunc) return func; | ||
if(func === trueFunc) return getChildFunc(next, adapter); | ||
return function nthLastChild(elem){ | ||
var siblings = adapter.getSiblings(elem); | ||
return function nthLastChild(elem){ | ||
var siblings = adapter.getSiblings(elem); | ||
for(var pos = 0, i = siblings.length - 1; i >= 0; i--){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem) break; | ||
else pos++; | ||
} | ||
for(var pos = 0, i = siblings.length - 1; i >= 0; i--){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem) break; | ||
else pos++; | ||
} | ||
} | ||
return func(pos) && next(elem); | ||
}; | ||
}, | ||
"nth-of-type": function(next, rule){ | ||
var func = getNCheck(rule); | ||
return func(pos) && next(elem); | ||
}; | ||
}, | ||
"nth-of-type": function(next, rule, options){ | ||
var func = getNCheck(rule); | ||
var adapter = options.adapter; | ||
if(func === falseFunc) return func; | ||
if(func === trueFunc) return getChildFunc(next); | ||
if(func === falseFunc) return func; | ||
if(func === trueFunc) return getChildFunc(next, adapter); | ||
return function nthOfType(elem){ | ||
var siblings = adapter.getSiblings(elem); | ||
return function nthOfType(elem){ | ||
var siblings = adapter.getSiblings(elem); | ||
for(var pos = 0, i = 0; i < siblings.length; i++){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem) break; | ||
if(adapter.getName(siblings[i]) === adapter.getName(elem)) pos++; | ||
} | ||
for(var pos = 0, i = 0; i < siblings.length; i++){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem) break; | ||
if(adapter.getName(siblings[i]) === adapter.getName(elem)) pos++; | ||
} | ||
} | ||
return func(pos) && next(elem); | ||
}; | ||
}, | ||
"nth-last-of-type": function(next, rule){ | ||
var func = getNCheck(rule); | ||
return func(pos) && next(elem); | ||
}; | ||
}, | ||
"nth-last-of-type": function(next, rule, options){ | ||
var func = getNCheck(rule); | ||
var adapter = options.adapter; | ||
if(func === falseFunc) return func; | ||
if(func === trueFunc) return getChildFunc(next); | ||
if(func === falseFunc) return func; | ||
if(func === trueFunc) return getChildFunc(next, adapter); | ||
return function nthLastOfType(elem){ | ||
var siblings = adapter.getSiblings(elem); | ||
return function nthLastOfType(elem){ | ||
var siblings = adapter.getSiblings(elem); | ||
for(var pos = 0, i = siblings.length - 1; i >= 0; i--){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem) break; | ||
if(adapter.getName(siblings[i]) === adapter.getName(elem)) pos++; | ||
} | ||
for(var pos = 0, i = siblings.length - 1; i >= 0; i--){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem) break; | ||
if(adapter.getName(siblings[i]) === adapter.getName(elem)) pos++; | ||
} | ||
} | ||
return func(pos) && next(elem); | ||
}; | ||
}, | ||
return func(pos) && next(elem); | ||
}; | ||
}, | ||
//TODO determine the actual root element | ||
root: function(next){ | ||
return function(elem){ | ||
return !adapter.getParent(elem) && next(elem); | ||
}; | ||
}, | ||
//TODO determine the actual root element | ||
root: function(next, rule, options){ | ||
var adapter = options.adapter; | ||
scope: function(next, rule, options, context){ | ||
if(!context || context.length === 0){ | ||
//equivalent to :root | ||
return filters.root(next); | ||
} | ||
return function(elem){ | ||
return !adapter.getParent(elem) && next(elem); | ||
}; | ||
}, | ||
if(context.length === 1){ | ||
//NOTE: can't be unpacked, as :has uses this for side-effects | ||
return function(elem){ | ||
return equals(context[0], elem) && next(elem); | ||
}; | ||
} | ||
scope: function(next, rule, options, context){ | ||
var adapter = options.adapter; | ||
if(!context || context.length === 0){ | ||
//equivalent to :root | ||
return filters.root(next, rule, options); | ||
} | ||
function equals(a, b){ | ||
if(typeof adapter.equals === "function") return adapter.equals(a, b); | ||
return a === b; | ||
} | ||
if(context.length === 1){ | ||
//NOTE: can't be unpacked, as :has uses this for side-effects | ||
return function(elem){ | ||
return context.indexOf(elem) >= 0 && next(elem); | ||
return equals(context[0], elem) && next(elem); | ||
}; | ||
}, | ||
} | ||
//jQuery extensions (others follow as pseudos) | ||
checkbox: getAttribFunc("type", "checkbox"), | ||
file: getAttribFunc("type", "file"), | ||
password: getAttribFunc("type", "password"), | ||
radio: getAttribFunc("type", "radio"), | ||
reset: getAttribFunc("type", "reset"), | ||
image: getAttribFunc("type", "image"), | ||
submit: getAttribFunc("type", "submit") | ||
}; | ||
return filters; | ||
} | ||
return function(elem){ | ||
return context.indexOf(elem) >= 0 && next(elem); | ||
}; | ||
}, | ||
function pseudosFactory(adapter){ | ||
//helper methods | ||
function getFirstElement(elems){ | ||
for(var i = 0; elems && i < elems.length; i++){ | ||
if(adapter.isTag(elems[i])) return elems[i]; | ||
} | ||
//jQuery extensions (others follow as pseudos) | ||
checkbox: getAttribFunc("type", "checkbox"), | ||
file: getAttribFunc("type", "file"), | ||
password: getAttribFunc("type", "password"), | ||
radio: getAttribFunc("type", "radio"), | ||
reset: getAttribFunc("type", "reset"), | ||
image: getAttribFunc("type", "image"), | ||
submit: getAttribFunc("type", "submit") | ||
}; | ||
//helper methods | ||
function getFirstElement(elems, adapter){ | ||
for(var i = 0; elems && i < elems.length; i++){ | ||
if(adapter.isTag(elems[i])) return elems[i]; | ||
} | ||
} | ||
//while filters are precompiled, pseudos get called when they are needed | ||
var pseudos = { | ||
empty: function(elem){ | ||
return !adapter.getChildren(elem).some(function(elem){ | ||
return adapter.isTag(elem) || elem.type === "text"; | ||
}); | ||
}, | ||
//while filters are precompiled, pseudos get called when they are needed | ||
var pseudos = { | ||
empty: function(elem, adapter){ | ||
return !adapter.getChildren(elem).some(function(elem){ | ||
return adapter.isTag(elem) || elem.type === "text"; | ||
}); | ||
}, | ||
"first-child": function(elem){ | ||
return getFirstElement(adapter.getSiblings(elem)) === elem; | ||
}, | ||
"last-child": function(elem){ | ||
var siblings = adapter.getSiblings(elem); | ||
"first-child": function(elem, adapter){ | ||
return getFirstElement(adapter.getSiblings(elem), adapter) === elem; | ||
}, | ||
"last-child": function(elem, adapter){ | ||
var siblings = adapter.getSiblings(elem); | ||
for(var i = siblings.length - 1; i >= 0; i--){ | ||
if(siblings[i] === elem) return true; | ||
if(adapter.isTag(siblings[i])) break; | ||
} | ||
for(var i = siblings.length - 1; i >= 0; i--){ | ||
if(siblings[i] === elem) return true; | ||
if(adapter.isTag(siblings[i])) break; | ||
} | ||
return false; | ||
}, | ||
"first-of-type": function(elem){ | ||
var siblings = adapter.getSiblings(elem); | ||
return false; | ||
}, | ||
"first-of-type": function(elem, adapter){ | ||
var siblings = adapter.getSiblings(elem); | ||
for(var i = 0; i < siblings.length; i++){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem) return true; | ||
if(adapter.getName(siblings[i]) === adapter.getName(elem)) break; | ||
} | ||
for(var i = 0; i < siblings.length; i++){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem) return true; | ||
if(adapter.getName(siblings[i]) === adapter.getName(elem)) break; | ||
} | ||
} | ||
return false; | ||
}, | ||
"last-of-type": function(elem){ | ||
var siblings = adapter.getSiblings(elem); | ||
return false; | ||
}, | ||
"last-of-type": function(elem, adapter){ | ||
var siblings = adapter.getSiblings(elem); | ||
for(var i = siblings.length - 1; i >= 0; i--){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem) return true; | ||
if(adapter.getName(siblings[i]) === adapter.getName(elem)) break; | ||
} | ||
for(var i = siblings.length - 1; i >= 0; i--){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem) return true; | ||
if(adapter.getName(siblings[i]) === adapter.getName(elem)) break; | ||
} | ||
} | ||
return false; | ||
}, | ||
"only-of-type": function(elem){ | ||
var siblings = adapter.getSiblings(elem); | ||
return false; | ||
}, | ||
"only-of-type": function(elem, adapter){ | ||
var siblings = adapter.getSiblings(elem); | ||
for(var i = 0, j = siblings.length; i < j; i++){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem) continue; | ||
if(adapter.getName(siblings[i]) === adapter.getName(elem)) return false; | ||
for(var i = 0, j = siblings.length; i < j; i++){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem) continue; | ||
if(adapter.getName(siblings[i]) === adapter.getName(elem)){ | ||
return false; | ||
} | ||
} | ||
} | ||
return true; | ||
}, | ||
"only-child": function(elem){ | ||
var siblings = adapter.getSiblings(elem); | ||
return true; | ||
}, | ||
"only-child": function(elem, adapter){ | ||
var siblings = adapter.getSiblings(elem); | ||
for(var i = 0; i < siblings.length; i++){ | ||
if(adapter.isTag(siblings[i]) && siblings[i] !== elem) return false; | ||
} | ||
for(var i = 0; i < siblings.length; i++){ | ||
if(adapter.isTag(siblings[i]) && siblings[i] !== elem) return false; | ||
} | ||
return true; | ||
}, | ||
return true; | ||
}, | ||
//:matches(a, area, link)[href] | ||
link: function(elem){ | ||
return adapter.hasAttrib(elem, "href"); | ||
}, | ||
visited: falseFunc, //seems to be a valid implementation | ||
//TODO: :any-link once the name is finalized (as an alias of :link) | ||
//:matches(a, area, link)[href] | ||
link: function(elem, adapter){ | ||
return adapter.hasAttrib(elem, "href"); | ||
}, | ||
visited: falseFunc, //Valid implementation | ||
//TODO: :any-link once the name is finalized (as an alias of :link) | ||
//forms | ||
//to consider: :target | ||
//forms | ||
//to consider: :target | ||
//:matches([selected], select:not([multiple]):not(> option[selected]) > option:first-of-type) | ||
selected: function(elem){ | ||
if(adapter.hasAttrib(elem, "selected")) return true; | ||
else if(adapter.getName(elem) !== "option") return false; | ||
//:matches([selected], select:not([multiple]):not(> option[selected]) > option:first-of-type) | ||
selected: function(elem, adapter){ | ||
if(adapter.hasAttrib(elem, "selected")) return true; | ||
else if(adapter.getName(elem) !== "option") return false; | ||
//the first <option> in a <select> is also selected | ||
var parent = adapter.getParent(elem); | ||
//the first <option> in a <select> is also selected | ||
var parent = adapter.getParent(elem); | ||
if( | ||
!parent || | ||
adapter.getName(parent) !== "select" || | ||
adapter.hasAttrib(parent, "multiple") | ||
) return false; | ||
if( | ||
!parent || | ||
adapter.getName(parent) !== "select" || | ||
adapter.hasAttrib(parent, "multiple") | ||
){ | ||
return false; | ||
} | ||
var siblings = adapter.getChildren(parent), | ||
sawElem = false; | ||
var siblings = adapter.getChildren(parent); | ||
var sawElem = false; | ||
for(var i = 0; i < siblings.length; i++){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem){ | ||
sawElem = true; | ||
} else if(!sawElem){ | ||
return false; | ||
} else if(adapter.hasAttrib(siblings[i], "selected")){ | ||
return false; | ||
} | ||
for(var i = 0; i < siblings.length; i++){ | ||
if(adapter.isTag(siblings[i])){ | ||
if(siblings[i] === elem){ | ||
sawElem = true; | ||
} else if(!sawElem){ | ||
return false; | ||
} else if(adapter.hasAttrib(siblings[i], "selected")){ | ||
return false; | ||
} | ||
} | ||
} | ||
return sawElem; | ||
}, | ||
//https://html.spec.whatwg.org/multipage/scripting.html#disabled-elements | ||
//:matches( | ||
// :matches(button, input, select, textarea, menuitem, optgroup, option)[disabled], | ||
// optgroup[disabled] > option), | ||
// fieldset[disabled] * //TODO not child of first <legend> | ||
//) | ||
disabled: function(elem){ | ||
return adapter.hasAttrib(elem, "disabled"); | ||
}, | ||
enabled: function(elem){ | ||
return !adapter.hasAttrib(elem, "disabled"); | ||
}, | ||
//:matches(:matches(:radio, :checkbox)[checked], :selected) (TODO menuitem) | ||
checked: function(elem){ | ||
return adapter.hasAttrib(elem, "checked") || pseudos.selected(elem); | ||
}, | ||
//:matches(input, select, textarea)[required] | ||
required: function(elem){ | ||
return adapter.hasAttrib(elem, "required"); | ||
}, | ||
//:matches(input, select, textarea):not([required]) | ||
optional: function(elem){ | ||
return !adapter.hasAttrib(elem, "required"); | ||
}, | ||
return sawElem; | ||
}, | ||
//https://html.spec.whatwg.org/multipage/scripting.html#disabled-elements | ||
//:matches( | ||
// :matches(button, input, select, textarea, menuitem, optgroup, option)[disabled], | ||
// optgroup[disabled] > option), | ||
// fieldset[disabled] * //TODO not child of first <legend> | ||
//) | ||
disabled: function(elem, adapter){ | ||
return adapter.hasAttrib(elem, "disabled"); | ||
}, | ||
enabled: function(elem, adapter){ | ||
return !adapter.hasAttrib(elem, "disabled"); | ||
}, | ||
//:matches(:matches(:radio, :checkbox)[checked], :selected) (TODO menuitem) | ||
checked: function(elem, adapter){ | ||
return ( | ||
adapter.hasAttrib(elem, "checked") || pseudos.selected(elem, adapter) | ||
); | ||
}, | ||
//:matches(input, select, textarea)[required] | ||
required: function(elem, adapter){ | ||
return adapter.hasAttrib(elem, "required"); | ||
}, | ||
//:matches(input, select, textarea):not([required]) | ||
optional: function(elem, adapter){ | ||
return !adapter.hasAttrib(elem, "required"); | ||
}, | ||
//jQuery extensions | ||
//jQuery extensions | ||
//:not(:empty) | ||
parent: function(elem){ | ||
return !pseudos.empty(elem); | ||
}, | ||
//:matches(h1, h2, h3, h4, h5, h6) | ||
header: function(elem){ | ||
var name = adapter.getName(elem); | ||
return name === "h1" || | ||
name === "h2" || | ||
name === "h3" || | ||
name === "h4" || | ||
name === "h5" || | ||
name === "h6"; | ||
}, | ||
//:not(:empty) | ||
parent: function(elem, adapter){ | ||
return !pseudos.empty(elem, adapter); | ||
}, | ||
//:matches(h1, h2, h3, h4, h5, h6) | ||
header: namePseudo(["h1", "h2", "h3", "h4", "h5", "h6"]), | ||
//:matches(button, input[type=button]) | ||
button: function(elem){ | ||
var name = adapter.getName(elem); | ||
return name === "button" || | ||
name === "input" && | ||
adapter.getAttributeValue(elem, "type") === "button"; | ||
}, | ||
//:matches(input, textarea, select, button) | ||
input: function(elem){ | ||
var name = adapter.getName(elem); | ||
return name === "input" || | ||
name === "textarea" || | ||
name === "select" || | ||
name === "button"; | ||
}, | ||
//input:matches(:not([type!='']), [type='text' i]) | ||
text: function(elem){ | ||
var attr; | ||
return adapter.getName(elem) === "input" && ( | ||
!(attr = adapter.getAttributeValue(elem, "type")) || | ||
attr.toLowerCase() === "text" | ||
); | ||
} | ||
//:matches(button, input[type=button]) | ||
button: function(elem, adapter){ | ||
var name = adapter.getName(elem); | ||
return ( | ||
name === "button" || | ||
(name === "input" && adapter.getAttributeValue(elem, "type") === "button") | ||
); | ||
}, | ||
//:matches(input, textarea, select, button) | ||
input: namePseudo(["input", "textarea", "select", "button"]), | ||
//input:matches(:not([type!='']), [type='text' i]) | ||
text: function(elem, adapter){ | ||
var attr; | ||
return ( | ||
adapter.getName(elem) === "input" && | ||
(!(attr = adapter.getAttributeValue(elem, "type")) || | ||
attr.toLowerCase() === "text") | ||
); | ||
} | ||
}; | ||
function namePseudo(names){ | ||
if(typeof Set !== "undefined"){ | ||
// eslint-disable-next-line no-undef | ||
var nameSet = new Set(names); | ||
return function(elem, adapter){ | ||
return nameSet.has(adapter.getName(elem)); | ||
}; | ||
} | ||
return function(elem, adapter){ | ||
return names.indexOf(adapter.getName(elem)) >= 0; | ||
}; | ||
return pseudos; | ||
} | ||
@@ -362,8 +379,10 @@ | ||
if(subselect === null){ | ||
if(func.length > 1 && name !== "scope"){ | ||
if(func.length > 2 && name !== "scope"){ | ||
throw new Error("pseudo-selector :" + name + " requires an argument"); | ||
} | ||
} else { | ||
if(func.length === 1){ | ||
throw new Error("pseudo-selector :" + name + " doesn't have any arguments"); | ||
if(func.length === 2){ | ||
throw new Error( | ||
"pseudo-selector :" + name + " doesn't have any arguments" | ||
); | ||
} | ||
@@ -376,36 +395,37 @@ } | ||
function factory(adapter){ | ||
var pseudos = pseudosFactory(adapter); | ||
var filters = filtersFactory(adapter); | ||
module.exports = { | ||
compile: function(next, data, options, context){ | ||
var name = data.name; | ||
var subselect = data.data; | ||
var adapter = options.adapter; | ||
return { | ||
compile: function(next, data, options, context){ | ||
var name = data.name, | ||
subselect = data.data; | ||
if(options && options.strict && !re_CSS3.test(name)){ | ||
throw new Error(":" + name + " isn't part of CSS3"); | ||
} | ||
if(options && options.strict && !re_CSS3.test(name)){ | ||
throw new Error(":" + name + " isn't part of CSS3"); | ||
if(typeof filters[name] === "function"){ | ||
return filters[name](next, subselect, options, context); | ||
} else if(typeof pseudos[name] === "function"){ | ||
var func = pseudos[name]; | ||
verifyArgs(func, name, subselect); | ||
if(func === falseFunc){ | ||
return func; | ||
} | ||
if(typeof filters[name] === "function"){ | ||
verifyArgs(filters[name], name, subselect); | ||
return filters[name](next, subselect, options, context); | ||
} else if(typeof pseudos[name] === "function"){ | ||
var func = pseudos[name]; | ||
verifyArgs(func, name, subselect); | ||
if(next === trueFunc) return func; | ||
return function pseudoArgs(elem){ | ||
return func(elem, subselect) && next(elem); | ||
if(next === trueFunc){ | ||
return function pseudoRoot(elem){ | ||
return func(elem, adapter, subselect); | ||
}; | ||
} else { | ||
throw new Error("unmatched pseudo-class :" + name); | ||
} | ||
}, | ||
filters: filters, | ||
pseudos: pseudos | ||
}; | ||
} | ||
module.exports = factory; | ||
return function pseudoArgs(elem){ | ||
return func(elem, adapter, subselect) && next(elem); | ||
}; | ||
} else { | ||
throw new Error("unmatched pseudo-class :" + name); | ||
} | ||
}, | ||
filters: filters, | ||
pseudos: pseudos | ||
}; |
{ | ||
"name": "css-select", | ||
"version": "1.3.0-rc0", | ||
"version": "2.0.0", | ||
"description": "a CSS selector compiler/engine", | ||
@@ -17,2 +17,3 @@ "author": "Felix Boehm <me@feedic.com>", | ||
"index.js", | ||
"index.d.ts", | ||
"lib" | ||
@@ -23,14 +24,14 @@ ], | ||
"css-what": "2.1", | ||
"domutils": "1.5.1", | ||
"domutils": "^1.7.0", | ||
"nth-check": "^1.0.1" | ||
}, | ||
"devDependencies": { | ||
"cheerio-soupselect": "*", | ||
"coveralls": "*", | ||
"eslint": "^3.0.0", | ||
"expect.js": "*", | ||
"htmlparser2": "*", | ||
"istanbul": "*", | ||
"mocha": "*", | ||
"mocha-lcov-reporter": "*" | ||
"cheerio-soupselect": "^0.1.1", | ||
"coveralls": "^3.0.0", | ||
"eslint": "^4.18.2", | ||
"expect.js": "^0.3.1", | ||
"htmlparser2": "^3.9.2", | ||
"istanbul": "^0.4.5", | ||
"mocha": "^5.0.4", | ||
"mocha-lcov-reporter": "^1.3.0" | ||
}, | ||
@@ -43,3 +44,4 @@ "scripts": { | ||
}, | ||
"license": "BSD-like" | ||
"license": "BSD-like", | ||
"types": "index.d.ts" | ||
} |
@@ -60,5 +60,7 @@ # css-select [![NPM version](http://img.shields.io/npm/v/css-select.svg)](https://npmjs.org/package/css-select) [![Build Status](https://travis-ci.org/fb55/css-select.svg?branch=master)](http://travis-ci.org/fb55/css-select) [![Downloads](https://img.shields.io/npm/dm/css-select.svg)](https://npmjs.org/package/css-select) [![Coverage](https://coveralls.io/repos/fb55/css-select/badge.svg?branch=master)](https://coveralls.io/r/fb55/css-select) | ||
```js | ||
var CSSselect = require("css-select"); | ||
const CSSselect = require("css-select"); | ||
``` | ||
__Note:__ css-select throws errors when invalid selectors are passed to it, contrary to the behavior in browsers, which swallow them. This is done to aid with writing css selectors, but can be unexpected when processing arbitrary strings. | ||
#### `CSSselect(query, elems, options)` | ||
@@ -65,0 +67,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
46285
11
1155
1
210
+ Addeddomutils@1.7.0(transitive)
- Removeddomutils@1.5.1(transitive)
Updateddomutils@^1.7.0