import { getUIValue } from '.' ; import { prepareInterceptor } from './interceptor' ; const UISelection = Symbol ( 'Displayed selection in UI' ) ; interface Value extends Number { [ UISelection ] ? : typeof UISelection ; } export interface UISelectionRange { startOffset : number ; endOffset : number ; } export interface UISelection { anchorOffset : number ; focusOffset : number ; } declare global { interface Element { [ UISelection ] ? : UISelection ; } } export function prepareSelectionInterceptor ( element ) { prepareInterceptor ( element , 'setSelectionRange' , function interceptorImpl ( this , start , end , direction = 'none' ) { const isUI = start && typeof start === 'object' && start [ UISelection ] ; if ( ! isUI ) { this [ UISelection ] = undefined ; } return { realArgs : [ Number ( start ) , end , direction ] as [ number , number , typeof direction ] , } ; } ) ; prepareInterceptor ( element , 'selectionStart' , function interceptorImpl ( this , v ) { this [ UISelection ] = undefined ; return { realArgs : v } ; } ) ; prepareInterceptor ( element , 'selectionEnd' , function interceptorImpl ( this , v ) { this [ UISelection ] = undefined ; return { realArgs : v } ; } ) ; } export function setUISelection ( element , { focusOffset : focusOffsetParam , anchorOffset : anchorOffsetParam = focusOffsetParam , } , mode = 'replace' ) { const valueLength = getUIValue ( element ) . length ; const sanitizeOffset = ( o ) => Math . max ( 0 , Math . min ( valueLength , o ) ) ; const anchorOffset = mode === 'replace' || element [ UISelection ] === undefined ? sanitizeOffset ( anchorOffsetParam ) : ( element [ UISelection ] as UISelection ) . anchorOffset ; const focusOffset = sanitizeOffset ( focusOffsetParam ) ; const startOffset = Math . min ( anchorOffset , focusOffset ) ; const endOffset = Math . max ( anchorOffset , focusOffset ) ; element [ UISelection ] = { anchorOffset , focusOffset , } ; if ( element . selectionStart === startOffset && element . selectionEnd === endOffset ) { return ; } const startObj = new Number ( startOffset ) ; ( startObj as Value ) [ UISelection ] = UISelection ; try { element . setSelectionRange ( startObj as number , endOffset ) ; } catch { } } export function getUISelection ( element ) { const sel = element [ UISelection ] ?? { anchorOffset : element . selectionStart ?? 0 , focusOffset : element . selectionEnd ?? 0 , } ; return { ... sel , startOffset : Math . min ( sel . anchorOffset , sel . focusOffset ) , endOffset : Math . max ( sel . anchorOffset , sel . focusOffset ) , } ; } export function clearUISelection ( element ) { element [ UISelection ] = undefined ; }