/**
 * The MIT License
 * Copyright (c) 2018 Dmitriy Kubyshkin
 * Copied from https://github.com/grassator/insert-text-at-cursor
 */ let browserSupportsTextareaTextNodes ; param { HTMLElement } input return { boolean } function canManipulateViaTextNodes ( input ) { if ( input . nodeName !== 'TEXTAREA' ) { return false ; } if ( typeof browserSupportsTextareaTextNodes === 'undefined' ) { const textarea = document . createElement ( 'textarea' ) ; textarea . value = '1' ; browserSupportsTextareaTextNodes = ! ! textarea . firstChild ; } return browserSupportsTextareaTextNodes ; } param { string } val param { number } cursorIdx param { HTMLTextAreaElement | HTMLInputElement } input return { void } export const insertAtLineStart = ( val , cursorIdx , input ) : void => { const content = input . value ; let startIdx = 0 ; while ( cursorIdx -- ) { let char = content [ cursorIdx ] ; if ( char === '\n' ) { startIdx = cursorIdx + 1 ; break ; } } input . focus ( ) ; input . setRangeText ( val , startIdx , startIdx ) ; input . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ; } ; param { HTMLTextAreaElement | HTMLInputElement } input param { string } text returns { void } export function insertTextAtPosition ( input , text ) { input . focus ( ) ; if ( ( document as any ) . selection ) { const ieRange = ( document as any ) . selection . createRange ( ) ; ieRange . text = text ; ieRange . collapse ( false ) ; ieRange . select ( ) ; return ; } const isSuccess = document . execCommand && document . execCommand ( 'insertText' , false , text ) ; if ( ! isSuccess ) { const start = input . selectionStart ! ; const end = input . selectionEnd ! ; if ( typeof input . setRangeText === 'function' ) { input . setRangeText ( text ) ; } else { const range = document . createRange ( ) ; const textNode = document . createTextNode ( text ) ; if ( canManipulateViaTextNodes ( input ) ) { let node = input . firstChild ; if ( ! node ) { input . appendChild ( textNode ) ; } else { let offset = 0 ; let startNode = null ; let endNode = null ; while ( node && ( startNode === null || endNode === null ) ) { const nodeLength = node . nodeValue ! . length ; if ( start >= offset && start <= offset + nodeLength ) { range . setStart ( ( startNode = node ) , start - offset ) ; } if ( end >= offset && end <= offset + nodeLength ) { range . setEnd ( ( endNode = node ) , end - offset ) ; } offset += nodeLength ; node = node . nextSibling ; } if ( start !== end ) { range . deleteContents ( ) ; } } } if ( canManipulateViaTextNodes ( input ) && range . commonAncestorContainer . nodeName === '#text' ) { range . insertNode ( textNode ) ; } else { const value = input . value ; input . value = value . slice ( 0 , start ) + text + value . slice ( end ) ; } } input . setSelectionRange ( start + text . length , start + text . length ) ; const e = document . createEvent ( 'UIEvent' ) ; e . initEvent ( 'input' , true , false ) ; input . dispatchEvent ( e ) ; } }