import Taro from '@tarojs/api' ; import { CanvasContext } from '../canvas/CanvasContext' ; import { findDOM } from '../utils' ; import { NodesRef } from './nodesRef' ; type TElement = Document | HTMLElement | Element ; interface ISelectorQueryQueue { component : TaroGeneral . IAnyObject ; selector : string ; single : boolean ; fields ; } type TSelectorQueryQueueCallback = ( res : ISelectorQueryQueue ) => void ; function filter ( fields , dom ? , selector ? ) { if ( ! dom ) return null ; const isViewport = selector === '.taro_page' ; const { id , dataset , rect , size , scrollOffset , properties = [ ] , computedStyle = [ ] , nodeCanvasType , node , context } = fields ; const res = { } ; if ( nodeCanvasType && node ) { const tagName = dom . tagName ; res . node = { id : dom . id , $taroElement : dom } ; if ( /^taro-canvas-core/i . test ( tagName ) ) { const type = ( dom as any ) . type ! || '' ; res . nodeCanvasType = type ; const canvas = dom . getElementsByTagName ( 'canvas' ) [ 0 ] ; if ( /^(2d|webgl)/i . test ( type ) && canvas ) { res . node = canvas ; } else { res . node = null ; } } else { res . nodeCanvasType = '' ; res . node = dom ; } return res ; } if ( context ) { const tagName = dom . tagName ; if ( /^taro-video-core/i . test ( tagName ) ) { return { context : dom as unknown as Taro . VideoContext } ; } else if ( /^taro-canvas-core/i . test ( tagName ) ) { const type = ( dom as any ) . type ! || '2d' ; const canvas = dom ?. querySelector ( 'canvas' ) as HTMLCanvasElement ; const ctx = canvas ?. getContext ( type ) as CanvasRenderingContext2D ; return { context : new CanvasContext ( canvas , ctx ) } ; } else if ( /^taro-live-player-core/i . test ( tagName ) ) { console . error ( '暂时不支持通过 NodesRef.context 获取 LivePlayerContext' ) ; } else if ( /^taro-editor-core/i . test ( tagName ) ) { console . error ( '暂时不支持通过 NodesRef.context 获取 EditorContext' ) ; } else if ( /^taro-map-core/i . test ( tagName ) ) { console . error ( '暂时不支持通过 NodesRef.context 获取 MapContext' ) ; } return ; } if ( id ) res . id = dom . id ; if ( dataset ) res . dataset = Object . assign ( { } , dom . dataset ) ; if ( rect || size ) { const { left , right , top , bottom , width , height } = dom . getBoundingClientRect ( ) ; if ( rect ) { if ( ! isViewport ) { res . left = left ; res . right = right ; res . top = top ; res . bottom = bottom ; } else { res . left = 0 ; res . right = 0 ; res . top = 0 ; res . bottom = 0 ; } } if ( size ) { if ( ! isViewport ) { res . width = width ; res . height = height ; } else { res . width = dom . clientWidth ; res . height = dom . clientHeight ; } } } if ( scrollOffset ) { res . scrollLeft = dom . scrollLeft ; res . scrollTop = dom . scrollTop ; } if ( properties . length ) { properties . forEach ( prop => { const attr = dom . getAttribute ( prop ) ; if ( attr ) res [ prop ] = attr ; } ) ; } if ( computedStyle . length ) { const styles = window . getComputedStyle ( dom ) ; computedStyle . forEach ( key => { const value = styles . getPropertyValue ( key ) || styles [ key ] ; if ( value ) res [ key ] = value ; } ) ; } return res ; } return { Object } function queryBat ( queue , cb ) { const result = [ ] ; queue . forEach ( item => { const { selector , single , fields , component } = item ; const container = ( component !== null ? ( findDOM ( component ) as HTMLElement || document ) : document ) ; let selectSelf = false ; if ( container !== document ) { const $nodeList = container . parentNode ?. querySelectorAll ( selector ) ; if ( $nodeList ) { for ( let i = 0 , len = $nodeList . length ; i < len ; ++ i ) { if ( container === $nodeList [ i ] ) { selectSelf = true ; break ; } } } } if ( single ) { const el = selectSelf === true ? container : container . querySelector ( selector ) ; result . push ( filter ( fields , el as HTMLElement , selector ) ) ; } else { const $children = container . querySelectorAll ( selector ) ; const children = [ ] ; selectSelf === true && children . push ( container ) ; for ( let i = 0 , len = $children . length ; i < len ; ++ i ) { children . push ( $children [ i ] ) ; } result . push ( children . map ( dom => filter ( fields , dom as HTMLElement ) ) ) ; } } ) ; cb ( result ) ; } export class SelectorQuery implements Taro . SelectorQuery { _defaultWebviewId : string | null ; _webviewId : string | null ; _queue : ISelectorQueryQueue [ ] ; _queueCb : ( TSelectorQueryQueueCallback | null ) [ ] ; _component ? : TaroGeneral . IAnyObject ; constructor ( ) { this . _defaultWebviewId = null ; this . _webviewId = null ; this . _queue = [ ] ; this . _queueCb = [ ] ; this . _component ; } in ( component ) { this . _component = component ; return this ; } select ( selector ) { if ( typeof selector === 'string' ) selector = selector . replace ( '>>>' , '>' ) ; return new NodesRef ( selector , this , true ) ; } selectAll ( selector ) { if ( typeof selector === 'string' ) selector = selector . replace ( '>>>' , '>' ) ; return new NodesRef ( selector , this , false ) ; } selectViewport ( ) { return new NodesRef ( '.taro_page' , this , true ) ; } exec ( cb ) { queryBat ( this . _queue , res => { const _queueCb = this . _queueCb ; res . forEach ( ( item , index ) => { const cb = _queueCb [ index ] ; typeof cb === 'function' && cb . call ( this , item ) ; } ) ; typeof cb === 'function' && cb . call ( this , res ) ; } ) ; return this as unknown as Taro . NodesRef ; } _push ( selector , component , single , fields , callback = null ) { this . _queue . push ( { component , selector , single , fields } ) ; this . _queueCb . push ( callback ) ; } }