You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
5.5 KiB
144 lines
5.5 KiB
import { attributeRules } from "./attributes.js"; |
|
import { compilePseudoSelector } from "./pseudo-selectors/index.js"; |
|
import { SelectorType } from "css-what"; |
|
function getElementParent(node, adapter) { |
|
const parent = adapter.getParent(node); |
|
if (parent && adapter.isTag(parent)) { |
|
return parent; |
|
} |
|
return null; |
|
} |
|
/* |
|
* All available rules |
|
*/ |
|
export function compileGeneralSelector(next, selector, options, context, compileToken) { |
|
const { adapter, equals } = options; |
|
switch (selector.type) { |
|
case SelectorType.PseudoElement: { |
|
throw new Error("Pseudo-elements are not supported by css-select"); |
|
} |
|
case SelectorType.ColumnCombinator: { |
|
throw new Error("Column combinators are not yet supported by css-select"); |
|
} |
|
case SelectorType.Attribute: { |
|
if (selector.namespace != null) { |
|
throw new Error("Namespaced attributes are not yet supported by css-select"); |
|
} |
|
if (!options.xmlMode || options.lowerCaseAttributeNames) { |
|
selector.name = selector.name.toLowerCase(); |
|
} |
|
return attributeRules[selector.action](next, selector, options); |
|
} |
|
case SelectorType.Pseudo: { |
|
return compilePseudoSelector(next, selector, options, context, compileToken); |
|
} |
|
// Tags |
|
case SelectorType.Tag: { |
|
if (selector.namespace != null) { |
|
throw new Error("Namespaced tag names are not yet supported by css-select"); |
|
} |
|
let { name } = selector; |
|
if (!options.xmlMode || options.lowerCaseTags) { |
|
name = name.toLowerCase(); |
|
} |
|
return function tag(elem) { |
|
return adapter.getName(elem) === name && next(elem); |
|
}; |
|
} |
|
// Traversal |
|
case SelectorType.Descendant: { |
|
if (options.cacheResults === false || |
|
typeof WeakSet === "undefined") { |
|
return function descendant(elem) { |
|
let current = elem; |
|
while ((current = getElementParent(current, adapter))) { |
|
if (next(current)) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
}; |
|
} |
|
// @ts-expect-error `ElementNode` is not extending object |
|
const isFalseCache = new WeakSet(); |
|
return function cachedDescendant(elem) { |
|
let current = elem; |
|
while ((current = getElementParent(current, adapter))) { |
|
if (!isFalseCache.has(current)) { |
|
if (adapter.isTag(current) && next(current)) { |
|
return true; |
|
} |
|
isFalseCache.add(current); |
|
} |
|
} |
|
return false; |
|
}; |
|
} |
|
case "_flexibleDescendant": { |
|
// Include element itself, only used while querying an array |
|
return function flexibleDescendant(elem) { |
|
let current = elem; |
|
do { |
|
if (next(current)) |
|
return true; |
|
} while ((current = getElementParent(current, adapter))); |
|
return false; |
|
}; |
|
} |
|
case SelectorType.Parent: { |
|
return function parent(elem) { |
|
return adapter |
|
.getChildren(elem) |
|
.some((elem) => adapter.isTag(elem) && next(elem)); |
|
}; |
|
} |
|
case SelectorType.Child: { |
|
return function child(elem) { |
|
const parent = adapter.getParent(elem); |
|
return parent != null && adapter.isTag(parent) && next(parent); |
|
}; |
|
} |
|
case SelectorType.Sibling: { |
|
return function sibling(elem) { |
|
const siblings = adapter.getSiblings(elem); |
|
for (let i = 0; i < siblings.length; i++) { |
|
const currentSibling = siblings[i]; |
|
if (equals(elem, currentSibling)) |
|
break; |
|
if (adapter.isTag(currentSibling) && next(currentSibling)) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
}; |
|
} |
|
case SelectorType.Adjacent: { |
|
if (adapter.prevElementSibling) { |
|
return function adjacent(elem) { |
|
const previous = adapter.prevElementSibling(elem); |
|
return previous != null && next(previous); |
|
}; |
|
} |
|
return function adjacent(elem) { |
|
const siblings = adapter.getSiblings(elem); |
|
let lastElement; |
|
for (let i = 0; i < siblings.length; i++) { |
|
const currentSibling = siblings[i]; |
|
if (equals(elem, currentSibling)) |
|
break; |
|
if (adapter.isTag(currentSibling)) { |
|
lastElement = currentSibling; |
|
} |
|
} |
|
return !!lastElement && next(lastElement); |
|
}; |
|
} |
|
case SelectorType.Universal: { |
|
if (selector.namespace != null && selector.namespace !== "*") { |
|
throw new Error("Namespaced universal selectors are not yet supported by css-select"); |
|
} |
|
return next; |
|
} |
|
} |
|
} |
|
//# sourceMappingURL=general.js.map
|