import { falseFunc } from "boolbase" ; import { CompiledQuery , InternalOptions } from "./types" ; import type { AttributeSelector , AttributeAction } from "css-what" ; /**
 * 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 , "\\$&" ) ; } private see https 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 < Node , ElementNode extends Node > ( 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 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 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 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 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 ) ; } else 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 ) ; } , } ;