| import * as boolbase from "boolbase"; | |
| /** | |
| * All reserved characters in a regex, used for escaping. | |
| * | |
| * Taken from XRegExp, (c) 2007-2020 Steven Levithan under the MIT license | |
| * https://github.com/slevithan/xregexp/blob/95eeebeb8fac8754d54eafe2b4743661ac1cf028/src/xregexp.js#L794 | |
| */ | |
| const reChars = /[-[\]{}()*+?.,\\^$|#\s]/g; | |
| function escapeRegex(value) { | |
| return value.replace(reChars, "\\$&"); | |
| } | |
| /** | |
| * Attributes that are case-insensitive in HTML. | |
| * | |
| * @private | |
| * @see https://html.spec.whatwg.org/multipage/semantics-other.html#case-sensitivity-of-selectors | |
| */ | |
| const caseInsensitiveAttributes = new Set([ | |
| "accept", | |
| "accept-charset", | |
| "align", | |
| "alink", | |
| "axis", | |
| "bgcolor", | |
| "charset", | |
| "checked", | |
| "clear", | |
| "codetype", | |
| "color", | |
| "compact", | |
| "declare", | |
| "defer", | |
| "dir", | |
| "direction", | |
| "disabled", | |
| "enctype", | |
| "face", | |
| "frame", | |
| "hreflang", | |
| "http-equiv", | |
| "lang", | |
| "language", | |
| "link", | |
| "media", | |
| "method", | |
| "multiple", | |
| "nohref", | |
| "noresize", | |
| "noshade", | |
| "nowrap", | |
| "readonly", | |
| "rel", | |
| "rev", | |
| "rules", | |
| "scope", | |
| "scrolling", | |
| "selected", | |
| "shape", | |
| "target", | |
| "text", | |
| "type", | |
| "valign", | |
| "valuetype", | |
| "vlink", | |
| ]); | |
| function shouldIgnoreCase(selector, options) { | |
| return typeof selector.ignoreCase === "boolean" | |
| ? selector.ignoreCase | |
| : selector.ignoreCase === "quirks" | |
| ? !!options.quirksMode | |
| : !options.xmlMode && caseInsensitiveAttributes.has(selector.name); | |
| } | |
| /** | |
| * Attribute selectors | |
| */ | |
| export const attributeRules = { | |
| equals(next, data, options) { | |
| const { adapter } = options; | |
| const { name } = data; | |
| let { value } = data; | |
| if (shouldIgnoreCase(data, options)) { | |
| value = value.toLowerCase(); | |
| return (elem) => { | |
| const attr = adapter.getAttributeValue(elem, name); | |
| return (attr != null && | |
| attr.length === value.length && | |
| attr.toLowerCase() === value && | |
| next(elem)); | |
| }; | |
| } | |
| return (elem) => adapter.getAttributeValue(elem, name) === value && next(elem); | |
| }, | |
| hyphen(next, data, options) { | |
| const { adapter } = options; | |
| const { name } = data; | |
| let { value } = data; | |
| const len = value.length; | |
| if (shouldIgnoreCase(data, options)) { | |
| value = value.toLowerCase(); | |
| return function hyphenIC(elem) { | |
| const attr = adapter.getAttributeValue(elem, name); | |
| return (attr != null && | |
| (attr.length === len || attr.charAt(len) === "-") && | |
| attr.substr(0, len).toLowerCase() === value && | |
| next(elem)); | |
| }; | |
| } | |
| return function hyphen(elem) { | |
| const attr = adapter.getAttributeValue(elem, name); | |
| return (attr != null && | |
| (attr.length === len || attr.charAt(len) === "-") && | |
| attr.substr(0, len) === value && | |
| next(elem)); | |
| }; | |
| }, | |
| element(next, data, options) { | |
| const { adapter } = options; | |
| const { name, value } = data; | |
| if (/\s/.test(value)) { | |
| return boolbase.falseFunc; | |
| } | |
| const regex = new RegExp(`(?:^|\\s)${escapeRegex(value)}(?:$|\\s)`, shouldIgnoreCase(data, options) ? "i" : ""); | |
| return function element(elem) { | |
| const attr = adapter.getAttributeValue(elem, name); | |
| return (attr != null && | |
| attr.length >= value.length && | |
| regex.test(attr) && | |
| next(elem)); | |
| }; | |
| }, | |
| exists(next, { name }, { adapter }) { | |
| return (elem) => adapter.hasAttrib(elem, name) && next(elem); | |
| }, | |
| start(next, data, options) { | |
| const { adapter } = options; | |
| const { name } = data; | |
| let { value } = data; | |
| const len = value.length; | |
| if (len === 0) { | |
| return boolbase.falseFunc; | |
| } | |
| if (shouldIgnoreCase(data, options)) { | |
| value = value.toLowerCase(); | |
| return (elem) => { | |
| const attr = adapter.getAttributeValue(elem, name); | |
| return (attr != null && | |
| attr.length >= len && | |
| attr.substr(0, len).toLowerCase() === value && | |
| next(elem)); | |
| }; | |
| } | |
| return (elem) => !!adapter.getAttributeValue(elem, name)?.startsWith(value) && | |
| next(elem); | |
| }, | |
| end(next, data, options) { | |
| const { adapter } = options; | |
| const { name } = data; | |
| let { value } = data; | |
| const len = -value.length; | |
| if (len === 0) { | |
| return boolbase.falseFunc; | |
| } | |
| if (shouldIgnoreCase(data, options)) { | |
| value = value.toLowerCase(); | |
| return (elem) => adapter | |
| .getAttributeValue(elem, name) | |
| ?.substr(len) | |
| .toLowerCase() === value && next(elem); | |
| } | |
| return (elem) => !!adapter.getAttributeValue(elem, name)?.endsWith(value) && | |
| next(elem); | |
| }, | |
| any(next, data, options) { | |
| const { adapter } = options; | |
| const { name, value } = data; | |
| if (value === "") { | |
| return boolbase.falseFunc; | |
| } | |
| if (shouldIgnoreCase(data, options)) { | |
| const regex = new RegExp(escapeRegex(value), "i"); | |
| return function anyIC(elem) { | |
| const attr = adapter.getAttributeValue(elem, name); | |
| return (attr != null && | |
| attr.length >= value.length && | |
| regex.test(attr) && | |
| next(elem)); | |
| }; | |
| } | |
| return (elem) => !!adapter.getAttributeValue(elem, name)?.includes(value) && | |
| next(elem); | |
| }, | |
| not(next, data, options) { | |
| const { adapter } = options; | |
| const { name } = data; | |
| let { value } = data; | |
| if (value === "") { | |
| return (elem) => !!adapter.getAttributeValue(elem, name) && next(elem); | |
| } | |
| if (shouldIgnoreCase(data, options)) { | |
| value = value.toLowerCase(); | |
| return (elem) => { | |
| const attr = adapter.getAttributeValue(elem, name); | |
| return ((attr == null || | |
| attr.length !== value.length || | |
| attr.toLowerCase() !== value) && | |
| next(elem)); | |
| }; | |
| } | |
| return (elem) => adapter.getAttributeValue(elem, name) !== value && next(elem); | |
| }, | |
| }; | |
| //# sourceMappingURL=attributes.js.map |