module import { text } from '../static' ; import { isTag , domEach , camelCase , cssCase } from '../utils' ; import type { Node , Element } from 'domhandler' ; import type { Cheerio } from '../cheerio' ; import { innerText , textContent } from 'domutils' ; const hasOwn = Object . prototype . hasOwnProperty ; const rspace = /\s+/ ; const dataAttrPrefix = 'data-' ; const primitives = { null : null , true : true , false : false , } ; const rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i ; const rbrace = /^{[^]*}$|^\[[^]*]$/ ; private category param elem param name returns function getAttr ( elem , name , xmlMode ? ) ; function getAttr ( elem , name , xmlMode ? ) ; function getAttr ( elem , name , xmlMode ? ) { if ( ! elem || ! isTag ( elem ) ) return undefined ; elem . attribs ??= { } ; if ( ! name ) { return elem . attribs ; } if ( hasOwn . call ( elem . attribs , name ) ) { return ! xmlMode && rboolean . test ( name ) ? name : elem . attribs [ name ] ; } if ( elem . name === 'option' && name === 'value' ) { return text ( elem . children ) ; } if ( elem . name === 'input' && ( elem . attribs . type === 'radio' || elem . attribs . type === 'checkbox' ) && name === 'value' ) { return 'on' ; } return undefined ; } private param el param name param value function setAttr ( el , name , value ) { if ( value === null ) { removeAttribute ( el , name ) ; } else { el . attribs [ name ] = `${ value }` ; } } category example param name returns see https export function attr < T extends Node > ( this , name ) ; category example returns see https export function attr < T extends Node > ( this ) ; category example param name param value returns see https export function attr < T extends Node > ( this , name , value ? ) ; category example param values returns see https export function attr < T extends Node > ( this , values ) ; export function attr < T extends Node > ( this , name ? , value ? ) { if ( typeof name === 'object' || value !== undefined ) { if ( typeof value === 'function' ) { if ( typeof name !== 'string' ) { { throw new Error ( 'Bad combination of arguments.' ) ; } } return domEach ( this , ( el , i ) => { if ( isTag ( el ) ) setAttr ( el , name , value . call ( el , i , el . attribs [ name ] ) ) ; } ) ; } return domEach ( this , ( el ) => { if ( ! isTag ( el ) ) return ; if ( typeof name === 'object' ) { Object . keys ( name ) . forEach ( ( objName ) => { const objValue = name [ objName ] ; setAttr ( el , objName , objValue ) ; } ) ; } else { setAttr ( el , name as string , value as string ) ; } } ) ; } return arguments . length > 1 ? this : getAttr ( this [ 0 ] , name as string , this . options . xmlMode ) ; } private category param el param name returns function getProp ( el , name , xmlMode ? ) { if ( ! el || ! isTag ( el ) ) return ; return name in el ? el [ name ] : ! xmlMode && rboolean . test ( name ) ? getAttr ( el , name , false ) !== undefined : getAttr ( el , name , xmlMode ) ; } private param el param name param value function setProp ( el , name , value , xmlMode ? ) { if ( name in el ) { el [ name ] = value ; } else { setAttr ( el , name , ! xmlMode && rboolean . test ( name ) ? ( value ? '' : null ) : `${ value }` ) ; } } interface StyleProp { length : number ; [ key ] : string | number ; [ index ] : string ; } category example param name param value returns see https export function prop < T extends Node > ( this , name ) ; export function prop < T extends Node > ( this , name ) ; export function prop < T extends Node > ( this , name ) ; export function prop < T extends Node , K extends keyof Element > ( this , name ) ; export function prop < T extends Node , K extends keyof Element > ( this , name , value ) ; export function prop < T extends Node > ( this , name ) ; export function prop < T extends Node > ( this , name , value ) ; export function prop < T extends Node > ( this , name ) ; export function prop < T extends Node > ( this , name , value ? ) { if ( typeof name === 'string' && value === undefined ) { switch ( name ) { case 'style' : { const property = this . css ( ) as StyleProp ; const keys = Object . keys ( property ) ; keys . forEach ( ( p , i ) => { property [ i ] = p ; } ) ; property . length = keys . length ; return property ; } case 'tagName' : case 'nodeName' : { const el = this [ 0 ] ; return isTag ( el ) ? el . name . toUpperCase ( ) : undefined ; } case 'innerText' : return innerText ( this [ 0 ] ) ; case 'textContent' : return textContent ( this [ 0 ] ) ; case 'outerHTML' : return this . clone ( ) . wrap ( '<container />' ) . parent ( ) . html ( ) ; case 'innerHTML' : return this . html ( ) ; default : return getProp ( this [ 0 ] , name , this . options . xmlMode ) ; } } if ( typeof name === 'object' || value !== undefined ) { if ( typeof value === 'function' ) { if ( typeof name === 'object' ) { throw new Error ( 'Bad combination of arguments.' ) ; } return domEach ( this , ( el , i ) => { if ( isTag ( el ) ) setProp ( el , name , value . call ( el , i , getProp ( el , name , this . options . xmlMode ) ) , this . options . xmlMode ) ; } ) ; } return domEach ( this , ( el ) => { if ( ! isTag ( el ) ) return ; if ( typeof name === 'object' ) { Object . keys ( name ) . forEach ( ( key ) => { const val = name [ key ] ; setProp ( el , key , val , this . options . xmlMode ) ; } ) ; } else { setProp ( el , name , value , this . options . xmlMode ) ; } } ) ; } return undefined ; } interface DataElement extends Element { data ? : Record < string , unknown > ; } private param el param name param value function setData ( el , name , value ? ) { const elem = el ; elem . data ??= { } ; if ( typeof name === 'object' ) Object . assign ( elem . data , name ) ; else if ( typeof name === 'string' && value !== undefined ) { elem . data [ name ] = value ; } } private category param el param name returns function readData ( el , name ? ) { let domNames ; let jsNames ; let value ; if ( name == null ) { domNames = Object . keys ( el . attribs ) . filter ( ( attrName ) => attrName . startsWith ( dataAttrPrefix ) ) ; jsNames = domNames . map ( ( domName ) => camelCase ( domName . slice ( dataAttrPrefix . length ) ) ) ; } else { domNames = [ dataAttrPrefix + cssCase ( name ) ] ; jsNames = [ name ] ; } for ( let idx = 0 ; idx < domNames . length ; ++ idx ) { const domName = domNames [ idx ] ; const jsName = jsNames [ idx ] ; if ( hasOwn . call ( el . attribs , domName ) && ! hasOwn . call ( ( el as DataElement ) . data , jsName ) ) { value = el . attribs [ domName ] ; if ( hasOwn . call ( primitives , value ) ) { value = primitives [ value ] ; } else if ( value === String ( Number ( value ) ) ) { value = Number ( value ) ; } else if ( rbrace . test ( value ) ) { try { value = JSON . parse ( value ) ; } catch ( e ) { } } ( el . data as Record < string , unknown > ) [ jsName ] = value ; } } return name == null ? el . data : value ; } category example param name returns see https export function data < T extends Node > ( this , name ) ; category example returns see https export function data < T extends Node > ( this ) ; category example param name param value returns see https export function data < T extends Node > ( this , name , value ) ; category example param values returns see https export function data < T extends Node > ( this , values ) ; export function data < T extends Node > ( this , name ? , value ? ) { const elem = this [ 0 ] ; if ( ! elem || ! isTag ( elem ) ) return ; const dataEl = elem ; dataEl . data ??= { } ; if ( ! name ) { return readData ( dataEl ) ; } if ( typeof name === 'object' || value !== undefined ) { domEach ( this , ( el ) => { if ( isTag ( el ) ) if ( typeof name === 'object' ) setData ( el , name ) ; else setData ( el , name , value as unknown ) ; } ) ; return this ; } if ( hasOwn . call ( dataEl . data , name ) ) { return dataEl . data [ name ] ; } return readData ( dataEl , name ) ; } category example returns see https export function val < T extends Node > ( this ) ; category example param value returns see https export function val < T extends Node > ( this , value ) ; export function val < T extends Node > ( this , value ? ) { const querying = arguments . length === 0 ; const element = this [ 0 ] ; if ( ! element || ! isTag ( element ) ) return querying ? undefined : this ; switch ( element . name ) { case 'textarea' : return this . text ( value as string ) ; case 'select' : { const option = this . find ( 'option:selected' ) ; if ( ! querying ) { if ( this . attr ( 'multiple' ) == null && typeof value === 'object' ) { return this ; } this . find ( 'option' ) . removeAttr ( 'selected' ) ; const values = typeof value !== 'object' ? [ value ] : value ; for ( let i = 0 ; i < values . length ; i ++ ) { this . find ( `option[value="${ values [ i ] }"]` ) . attr ( 'selected' , '' ) ; } return this ; } return this . attr ( 'multiple' ) ? option . toArray ( ) . map ( ( el ) => text ( el . children ) ) : option . attr ( 'value' ) ; } case 'input' : case 'option' : return querying ? this . attr ( 'value' ) : this . attr ( 'value' , value as string ) ; } return undefined ; } private param elem param name function removeAttribute ( elem , name ) { if ( ! elem . attribs || ! hasOwn . call ( elem . attribs , name ) ) return ; delete elem . attribs [ name ] ; } category param names returns function splitNames ( names ? ) { return names ? names . trim ( ) . split ( rspace ) : [ ] ; } category example param name returns see https export function removeAttr < T extends Node > ( this , name ) { const attrNames = splitNames ( name ) ; for ( let i = 0 ; i < attrNames . length ; i ++ ) { domEach ( this , ( elem ) => { if ( isTag ( elem ) ) removeAttribute ( elem , attrNames [ i ] ) ; } ) ; } return this ; } category example param className returns see https export function hasClass < T extends Node > ( this , className ) { return this . toArray ( ) . some ( ( elem ) => { const clazz = isTag ( elem ) && elem . attribs . class ; let idx = - 1 ; if ( clazz && className . length ) { while ( ( idx = clazz . indexOf ( className , idx + 1 ) ) > - 1 ) { const end = idx + className . length ; if ( ( idx === 0 || rspace . test ( clazz [ idx - 1 ] ) ) && ( end === clazz . length || rspace . test ( clazz [ end ] ) ) ) { return true ; } } } return false ; } ) ; } category example param value returns see https export function addClass < T extends Node , R extends ArrayLike < T > > ( this , value ? ) { if ( typeof value === 'function' ) { return domEach ( this , ( el , i ) => { if ( isTag ( el ) ) { const className = el . attribs . class || '' ; addClass . call ( [ el ] , value . call ( el , i , className ) ) ; } } ) ; } if ( ! value || typeof value !== 'string' ) return this ; const classNames = value . split ( rspace ) ; const numElements = this . length ; for ( let i = 0 ; i < numElements ; i ++ ) { const el = this [ i ] ; if ( ! isTag ( el ) ) continue ; const className = getAttr ( el , 'class' , false ) ; if ( ! className ) { setAttr ( el , 'class' , classNames . join ( ' ' ) . trim ( ) ) ; } else { let setClass = ` ${ className } ` ; for ( let j = 0 ; j < classNames . length ; j ++ ) { const appendClass = `${ classNames [ j ] } ` ; if ( ! setClass . includes ( ` ${ appendClass }` ) ) setClass += appendClass ; } setAttr ( el , 'class' , setClass . trim ( ) ) ; } } return this ; } category example param name returns see https export function removeClass < T extends Node , R extends ArrayLike < T > > ( this , name ? ) { if ( typeof name === 'function' ) { return domEach ( this , ( el , i ) => { if ( isTag ( el ) ) removeClass . call ( [ el ] , name . call ( el , i , el . attribs . class || '' ) ) ; } ) ; } const classes = splitNames ( name ) ; const numClasses = classes . length ; const removeAll = arguments . length === 0 ; return domEach ( this , ( el ) => { if ( ! isTag ( el ) ) return ; if ( removeAll ) { el . attribs . class = '' ; } else { const elClasses = splitNames ( el . attribs . class ) ; let changed = false ; for ( let j = 0 ; j < numClasses ; j ++ ) { const index = elClasses . indexOf ( classes [ j ] ) ; if ( index >= 0 ) { elClasses . splice ( index , 1 ) ; changed = true ; j -- ; } } if ( changed ) { el . attribs . class = elClasses . join ( ' ' ) ; } } } ) ; } category example param value param stateVal returns see https export function toggleClass < T extends Node , R extends ArrayLike < T > > ( this , value ? , stateVal ? ) { if ( typeof value === 'function' ) { return domEach ( this , ( el , i ) => { if ( isTag ( el ) ) { toggleClass . call ( [ el ] , value . call ( el , i , el . attribs . class || '' , stateVal ) , stateVal ) ; } } ) ; } if ( ! value || typeof value !== 'string' ) return this ; const classNames = value . split ( rspace ) ; const numClasses = classNames . length ; const state = typeof stateVal === 'boolean' ? ( stateVal ? 1 : - 1 ) : 0 ; const numElements = this . length ; for ( let i = 0 ; i < numElements ; i ++ ) { const el = this [ i ] ; if ( ! isTag ( el ) ) continue ; const elementClasses = splitNames ( el . attribs . class ) ; for ( let j = 0 ; j < numClasses ; j ++ ) { const index = elementClasses . indexOf ( classNames [ j ] ) ; if ( state >= 0 && index < 0 ) { elementClasses . push ( classNames [ j ] ) ; } else if ( state <= 0 && index >= 0 ) { elementClasses . splice ( index , 1 ) ; } } el . attribs . class = elementClasses . join ( ' ' ) ; } return this ; }