import { Ranges } from './Ranges' ; import { ISelectedContent } from './ISelectedContent' ; import { Selections } from './Selections' ; import { RectTexts } from '../controller/RectTexts' ; import { HTMLSanitizer } from 'polar-html/src/sanitize/HTMLSanitizer' ; import { TextNodeRows } from "./TextNodeRows" ; import { arrayStream } from "polar-shared/src/util/ArrayStreams" ; import { FileType } from "../../../apps/main/file_loaders/FileType" ; import { RectText } from '../controller/RectText' ; import { TextHighlightMerger } from '../../../../../apps/doc/src/text_highlighter/TextHighlightMerger' ; import { Rect } from '../../../Rect' ; export namespace SelectedContents { export interface ComputeOpts { /**
         * When true we do not return rects as this splits/mutates the DOM
         */ readonly noRectTexts ? : boolean ; readonly fileType : FileType ; } export const lineNeedsHyphenElimination = ( line ) => /[^\s]-$/ . test ( line ) ; export const lineNeedsHyphenJoin = ( line ) => /\d+-$/ . test ( line ) || ( /[^\s]-$/ . test ( line ) && /non-$/i . test ( line ) ) ; export function extractText ( rects ) { const mergeRectTexts = ( a , b , withWhitespace ) : RectText => { const rect = new Rect ( TextHighlightMerger . mergeRects ( a . boundingClientRect , b . boundingClientRect ) ) ; const val = { boundingClientRect : rect , text : a . text + ( withWhitespace ? ' ' : '' ) + b . text , selectionRange : a . selectionRange , } ; return val ; } ; const rectPositions = rects . map ( x => x . boundingClientRect ) ; const wordThresholds = TextHighlightMerger . getWordThresholds ( rectPositions ) ; const lineThresholds = TextHighlightMerger . getLineThresholds ( rectPositions ) ; const words = TextHighlightMerger . words ( rects , wordThresholds , a => a . boundingClientRect ) . map ( group => group . reduce ( ( a , b ) => mergeRectTexts ( a , b , false ) ) ) ; const lines = TextHighlightMerger . lines ( words , lineThresholds , a => a . boundingClientRect ) . map ( group => group . reduce ( ( a , b ) => mergeRectTexts ( a , b , true ) ) ) ; let text = '' ; for ( const line of lines ) { const currText = line . text . trim ( ) ; if ( lineNeedsHyphenJoin ( currText ) ) { text += currText ; } else if ( lineNeedsHyphenElimination ( currText ) ) { text += currText . slice ( 0 , - 1 ) ; } else { text += currText + ' ' ; } } return text . replace ( /\s+/g , ' ' ) . trim ( ) ; } /**
     * Compute the SelectedContents based on the page offset, not the
     * client/viewport offset, and include additional metadata including the
     * text of the selection, the html, etc.
     */ export function computeFromWindow ( win , opts ) { const selection = win . getSelection ( ) ! ; return computeFromSelection ( selection , opts ) ; } export function computeFromSelection ( selection , opts ) { const ranges = Ranges . cloneRanges ( Selections . toRanges ( selection ) ) ; const html = HTMLSanitizer . sanitize ( toHTML ( ranges ) ) ; function computeRectTexts ( ) { const textNodes = arrayStream ( ranges ) . map ( Ranges . getTextNodes ) . flatMap ( current => current ) . collect ( ) ; const textNodesRows = TextNodeRows . fromTextNodes ( textNodes ) ; return RectTexts . toRectTexts ( textNodesRows ) ; } function computeOrder ( ) { if ( opts . fileType === 'pdf' ) { return undefined ; } if ( ranges . length === 0 ) { return 0 ; } const scrollY = ranges [ 0 ] . startContainer ?. parentElement ?. ownerDocument ?. defaultView ?. scrollY ; return ranges [ 0 ] . getBoundingClientRect ( ) . top + ( scrollY || 0 ) ; } const rectTexts = opts . noRectTexts === true ? [ ] : computeRectTexts ( ) ; function computeText ( ) { if ( rectTexts . length > 0 ) { return extractText ( rectTexts ) ; } return toText ( ranges ) ; } const text = computeText ( ) ; const order = computeOrder ( ) ; return { text , html , rectTexts , order } ; } /**
     * Compute the given ranges as HTML, factoring in sanitization as well.
     */ export function toHTML ( ranges ) { return ranges . map ( range => Ranges . toHTML ( range ) ) . join ( "" ) ; } export function toText ( ranges ) { return ranges . map ( range => Ranges . toText ( range ) ) . join ( "" ) ; } }