import { CompileToken } from "./../types" ; import type { Selector } from "css-what" ; import { trueFunc , falseFunc } from "boolbase" ; import type { CompiledQuery , InternalOptions , Adapter } from "../types" ; import { isTraversal } from "../procedure" ; /** Used as a placeholder for :has. Will be replaced with the actual element. */ export const PLACEHOLDER_ELEMENT = { } ; export function ensureIsTag < Node , ElementNode extends Node > ( next , adapter ) { if ( next === falseFunc ) return falseFunc ; return ( elem ) => adapter . isTag ( elem ) && next ( elem ) ; } export type Subselect = < Node , ElementNode extends Node > ( next : CompiledQuery < ElementNode > , subselect : Selector [ ] [ ] , options : InternalOptions < Node , ElementNode > , context : Node [ ] | undefined , compileToken : CompileToken < Node , ElementNode > ) => CompiledQuery < ElementNode > ; export function getNextSiblings < Node , ElementNode extends Node > ( elem , adapter ) { const siblings = adapter . getSiblings ( elem ) ; if ( siblings . length <= 1 ) return [ ] ; const elemIndex = siblings . indexOf ( elem ) ; if ( elemIndex < 0 || elemIndex === siblings . length - 1 ) return [ ] ; return siblings . slice ( elemIndex + 1 ) . filter ( adapter . isTag ) ; } const is = ( next , token , options , context , compileToken ) => { const opts = { xmlMode : ! ! options . xmlMode , adapter : options . adapter , equals : options . equals , } ; const func = compileToken ( token , opts , context ) ; return ( elem ) => func ( elem ) && next ( elem ) ; } ; export const subselects = { is , /**
     * `:matches` and `:where` are aliases for `:is`.
     */ matches : is , where : is , not ( next , token , options , context , compileToken ) { const opts = { xmlMode : ! ! options . xmlMode , adapter : options . adapter , equals : options . equals , } ; const func = compileToken ( token , opts , context ) ; if ( func === falseFunc ) return next ; if ( func === trueFunc ) return falseFunc ; return function not ( elem ) { return ! func ( elem ) && next ( elem ) ; } ; } , has < Node , ElementNode extends Node > ( next , subselect , options , _context , compileToken ) { const { adapter } = options ; const opts = { xmlMode : ! ! options . xmlMode , adapter , equals : options . equals , } ; const context = subselect . some ( ( s ) => s . some ( isTraversal ) ) ? [ PLACEHOLDER_ELEMENT ] : undefined ; const compiled = compileToken ( subselect , opts , context ) ; if ( compiled === falseFunc ) return falseFunc ; if ( compiled === trueFunc ) { return ( elem ) => adapter . getChildren ( elem ) . some ( adapter . isTag ) && next ( elem ) ; } const hasElement = ensureIsTag ( compiled , adapter ) ; const { shouldTestNextSiblings = false } = compiled ; if ( context ) { return ( elem ) => { context [ 0 ] = elem ; const childs = adapter . getChildren ( elem ) ; const nextElements = shouldTestNextSiblings ? [ ... childs , ... getNextSiblings ( elem , adapter ) ] : childs ; return ( next ( elem ) && adapter . existsOne ( hasElement , nextElements ) ) ; } ; } return ( elem ) => next ( elem ) && adapter . existsOne ( hasElement , adapter . getChildren ( elem ) ) ; } , } ;