import { Preconditions } from 'polar-shared/src/Preconditions' ; import { arrayStream } from "polar-shared/src/util/ArrayStreams" ; export class Ranges { /**
     * Create duplicate of the given ranges so that we can know that we have
     * our own unique copies that can't be reset.
     *
     */ public static cloneRanges ( ranges ) { return ranges . map ( range => range . cloneRange ( ) ) ; } /**
     * Split a text node and get the new / starting node.
     */ public static splitTextNode ( container , offset , useStartBoundary ) { if ( container . nodeType !== Node . TEXT_NODE && container . nodeType !== Node . COMMENT_NODE && container . nodeType !== Node . CDATA_SECTION_NODE ) { if ( offset > 0 ) { return container . childNodes [ offset ] ; } return container ; } const newNode = ( < Text > container ) . splitText ( offset ) ; if ( useStartBoundary ) { return newNode ; } else { return newNode . previousSibling ! ; } } /**
     * Return HTML content of the range selected.
     *
     */ public static toHTML ( range ) { let result = "" ; const docFragment = range . cloneContents ( ) ; docFragment . childNodes . forEach ( childNode => { if ( childNode . nodeType === Node . TEXT_NODE ) { result += childNode . textContent ; } else { result += ( < HTMLElement > childNode ) . outerHTML ; } } ) ; return result ; } public static toText ( range ) { const docFragment = range . cloneContents ( ) ; function childNodeToText ( childNode ) { if ( childNode . nodeType === Node . TEXT_NODE ) { return childNode . textContent ; } else { return ( < HTMLElement > childNode ) . innerText ; } } return arrayStream ( Array . from ( docFragment . childNodes ) ) . map ( childNodeToText ) . filter ( current => current !== null ) . map ( current => current ! ) . collect ( ) . join ( "" ) ; } public static toText2 ( range ) { const documentFragment = range . cloneContents ( ) ; const div = document . createElement ( 'div' ) ; div . appendChild ( documentFragment ) ; return div . innerText ; } param range { Range } return { Array < Node > } public static getTextNodes ( range ) { Preconditions . assertPresent ( range , "range" ) ; const startNode = Ranges . splitTextNode ( range . startContainer , range . startOffset , true ) ; const endNode = Ranges . splitTextNode ( range . endContainer , range . endOffset , false ) ; Preconditions . assertPresent ( startNode , "startNode" ) ; Preconditions . assertPresent ( endNode , "endNode" ) ; const doc = range . startContainer . ownerDocument ! ; const treeWalker = doc . createTreeWalker ( range . commonAncestorContainer ) ; const result = [ ] ; let node ; let inSelection = false ; while ( node = treeWalker . nextNode ( ) ) { if ( startNode === node ) { inSelection = true ; break ; } } while ( node ) { if ( node . nodeType === Node . TEXT_NODE ) { result . push ( node ) ; } if ( endNode === node ) { break ; } node = treeWalker . nextNode ( ) ; } return result ; } /**
     * Return true if the range has text.
     */ public static hasText ( range ) { const doc = range . startContainer . ownerDocument ; const contents = range . cloneContents ( ) ; const div = doc ! . createElement ( 'div' ) ; div . appendChild ( contents ) ; const text = div . innerText ; return text . trim ( ) !== '' ; } /**
     * Similar to getTextNodes but we return true if the nodes have text in them.
     */ public static hasTextWithNodeSplit ( range ) { Preconditions . assertPresent ( range , "range" ) ; const startNode = Ranges . splitTextNode ( range . startContainer , range . startOffset , true ) ; const endNode = Ranges . splitTextNode ( range . endContainer , range . endOffset , false ) ; if ( ! startNode || ! endNode ) { return false ; } const doc = range . startContainer . ownerDocument ! ; const treeWalker = doc . createTreeWalker ( range . commonAncestorContainer ) ; const result = [ ] ; let node ; let inSelection = false ; while ( node = treeWalker . nextNode ( ) ) { if ( startNode === node ) { inSelection = true ; break ; } } while ( node ) { if ( node . nodeType === Node . TEXT_NODE ) { if ( node . textContent && node . textContent . trim ( ) !== '' ) { return true ; } } if ( endNode === node ) { break ; } node = treeWalker . nextNode ( ) ; } return false ; } public static describeNode ( node ) { return ( < HTMLElement > node . cloneNode ( false ) ) . outerHTML ; } }