import { isElementType } from '../misc/isElementType' ; import { getUISelection , setUISelection , UISelectionRange } from '../../document' ; import { editableInputTypes } from '../edit/isEditable' ; import { isContentEditable , getContentEditable } from '../edit/isContentEditable' ; /**
 * Backward-compatible selection.
 *
 * Handles input elements and contenteditable if it only contains a single text node.
 */ export function setSelectionRange ( element , anchorOffset , focusOffset ) { if ( hasOwnSelection ( element ) ) { return setSelection ( { focusNode : element , anchorOffset , focusOffset , } ) ; } if ( isContentEditable ( element ) && element . firstChild ?. nodeType === 3 ) { return setSelection ( { focusNode : element . firstChild , anchorOffset , focusOffset , } ) ; } throw new Error ( 'Not implemented. The result of this interaction is unreliable.' ) ; } /**
 * Determine if the element has its own selection implementation
 * and does not interact with the Document Selection API.
 */ export function hasOwnSelection ( node ) { return ( isElement ( node ) && ( isElementType ( node , 'textarea' ) || ( isElementType ( node , 'input' ) && node . type in editableInputTypes ) ) ) ; } function isElement ( node ) { return node . nodeType === 1 ; } /**
 * Determine which selection logic and selection ranges to consider.
 */ function getTargetTypeAndSelection ( node ) { const element = getElement ( node ) ; if ( element && hasOwnSelection ( element ) ) { return { type : 'input' , selection : getUISelection ( element ) , } as const ; } const selection = element ?. ownerDocument . getSelection ( ) ; const isCE = getContentEditable ( node ) && selection ?. anchorNode && getContentEditable ( selection . anchorNode ) ; return { type : isCE ? 'contenteditable' : 'default' , selection , } as const ; } function getElement ( node ) { return node . nodeType === 1 ? ( node as Element ) : node . parentElement ; } /**
 * Reset the Document Selection when moving focus into an element
 * with own selection implementation.
 */ export function updateSelectionOnFocus ( element ) { const selection = element . ownerDocument . getSelection ( ) ; if ( ! selection ?. focusNode ) { return ; } if ( hasOwnSelection ( element ) ) { const contenteditable = getContentEditable ( selection . focusNode ) ; if ( contenteditable ) { if ( ! selection . isCollapsed ) { const focusNode = contenteditable . firstChild ?. nodeType === 3 ? contenteditable . firstChild : contenteditable ; selection . setBaseAndExtent ( focusNode , 0 , focusNode , 0 ) ; } } else { selection . setBaseAndExtent ( element , 0 , element , 0 ) ; } } } /**
 * Get the range that would be overwritten by input.
 */ export function getInputRange ( focusNode ) { const typeAndSelection = getTargetTypeAndSelection ( focusNode ) ; if ( typeAndSelection . type === 'input' ) { return typeAndSelection . selection ; } else if ( typeAndSelection . type === 'contenteditable' ) { return typeAndSelection . selection ?. getRangeAt ( 0 ) ; } } /**
 * Extend/shrink the selection like with Shift+Arrows or Shift+Mouse
 */ export function modifySelection ( { focusNode , focusOffset , } ) { const typeAndSelection = getTargetTypeAndSelection ( focusNode ) ; if ( typeAndSelection . type === 'input' ) { return setUISelection ( focusNode as HTMLInputElement , { anchorOffset : typeAndSelection . selection . anchorOffset , focusOffset , } , 'modify' ) ; } focusNode . ownerDocument ?. getSelection ( ) ?. extend ( focusNode , focusOffset ) ; } /**
 * Set the selection
 */ export function setSelection ( { focusNode , focusOffset , anchorNode = focusNode , anchorOffset = focusOffset , } ) { const typeAndSelection = getTargetTypeAndSelection ( focusNode ) ; if ( typeAndSelection . type === 'input' ) { return setUISelection ( focusNode as HTMLInputElement , { anchorOffset , focusOffset , } ) ; } anchorNode . ownerDocument ?. getSelection ( ) ?. setBaseAndExtent ( anchorNode , anchorOffset , focusNode , focusOffset ) ; }