import { AnnotationBarCallbacks , ControlledAnnotationBar } from './ControlledAnnotationBar' ; import * as React from 'react' ; import { ActiveSelectionEvent , ActiveSelections } from '../popup/ActiveSelections' ; import { ControlledPopupProps } from '../popup/ControlledPopup' ; import * as ReactDOM from 'react-dom' ; import { Point } from '../../Point' ; import { Optional } from 'polar-shared/src/util/ts/Optional' ; import { Points } from '../../Points' ; import { HighlightCreatedEvent } from '../../comments/react/HighlightCreatedEvent' ; import { Elements } from "../../util/Elements" ; import { isPresent } from 'polar-shared/src/Preconditions' ; import { FileType } from "../../apps/main/file_loaders/FileType" ; export interface RegisterOpts { readonly fileType : FileType ; readonly docViewerElementProvider : ( ) => HTMLElement ; } export namespace ControlledAnnotationBars { export function create ( controlledPopupProps , annotationBarCallbacks , opts ) { registerEventListener ( annotationBarCallbacks , opts ) ; } function registerEventListener ( annotationBarCallbacks , opts ) { const handleTarget = ( target ) => { let annotationBar ; interface AnnotationPageInfo { readonly pageNum : number ; /**
                 * The container to hold the annotation bar.
                 */ readonly popupContainer : HTMLElement ; } ActiveSelections . addEventListener ( activeSelectionEvent => { const computeAnnotationPageInfo = ( ) : AnnotationPageInfo | undefined => { function getPageNumberForPageElement ( pageElement ) { return parseInt ( pageElement . getAttribute ( "data-page-number" ) ! , 10 ) ; } const computeForPDF = ( ) : AnnotationPageInfo | undefined => { const pageElement = Elements . untilRoot ( activeSelectionEvent . element , ".page" ) ; if ( ! pageElement ) { return undefined ; } const pageNum = getPageNumberForPageElement ( pageElement ) ; return { popupContainer : pageElement , pageNum } ; } ; const computeForEPUB = ( ) : AnnotationPageInfo | undefined => { const { docViewerElementProvider } = opts ; const docViewerElement = docViewerElementProvider ( ) ; const pageElement = docViewerElement . querySelector ( '.page' ) as HTMLElement ; const pageNum = getPageNumberForPageElement ( pageElement ) ; return { popupContainer : target , pageNum } ; } ; switch ( opts . fileType ) { case "pdf" : return computeForPDF ( ) ; case "epub" : return computeForEPUB ( ) ; } } ; const annotationPageInfo = computeAnnotationPageInfo ( ) ; if ( ! annotationPageInfo ) { return ; } switch ( activeSelectionEvent . type ) { case 'created' : annotationBar = createAnnotationBar ( annotationPageInfo . pageNum , annotationPageInfo . popupContainer , annotationBarCallbacks , activeSelectionEvent ) ; break ; case 'destroyed' : if ( annotationBar ) { destroyAnnotationBar ( annotationBar ) ; } break ; } if ( activeSelectionEvent . type === 'destroyed' ) { return ; } } , target ) ; } ; const targets = computeTargets ( opts . fileType , opts . docViewerElementProvider ) ; for ( const target of targets ) { handleTarget ( target ) ; } } export function computeTargets ( fileType , docViewerElementProvider ) { const docViewerElement = docViewerElementProvider ( ) ; function computeTargetsForPDF ( ) { return Array . from ( docViewerElement . querySelectorAll ( ".page" ) ) as HTMLElement [ ] ; } function computeTargetsForEPUB ( ) { return Array . from ( docViewerElement . querySelectorAll ( "iframe" ) ) . map ( iframe => iframe . contentDocument ) . filter ( contentDocument => isPresent ( contentDocument ) ) . map ( contentDocument => contentDocument ! ) . map ( contentDocument => contentDocument . documentElement ) ; } switch ( fileType ) { case "pdf" : return computeTargetsForPDF ( ) ; case "epub" : return computeTargetsForEPUB ( ) ; } } function destroyAnnotationBar ( annotationBar ) { if ( annotationBar && annotationBar . parentElement ) { annotationBar . parentElement ! . removeChild ( annotationBar ) ; } } function computePosition ( pageElement , point , offset ) { const origin = Optional . of ( pageElement . getBoundingClientRect ( ) ) . map ( rect => { return { 'x' : rect . left , 'y' : rect . top } ; } ) . get ( ) ; const relativePoint = Points . relativeTo ( origin , point ) ; offset = offset || { x : 0 , y : 0 } ; const left = relativePoint . x + offset . x ; const top = relativePoint . y + offset . y ; return { x : left , y : top } ; } function createAnnotationBar ( pageNum , popupContainer , annotationBarCallbacks , activeSelectionEvent ) { const point = { x : activeSelectionEvent . boundingClientRect . left + ( activeSelectionEvent . boundingClientRect . width / 2 ) , y : activeSelectionEvent . boundingClientRect . top } ; const offset = { x : - 75 , y : - 50 } ; const position = computePosition ( popupContainer , point , offset ) ; const annotationBar = document . createElement ( 'div' ) ; annotationBar . setAttribute ( "class" , 'polar-annotation-bar' ) ; annotationBar . addEventListener ( 'mouseup' , ( event ) => event . stopPropagation ( ) ) ; annotationBar . addEventListener ( 'mousedown' , ( event ) => event . stopPropagation ( ) ) ; const style = `position: absolute; top: ${ position . y }px; left: ${ position . x }px; z-index: 10000;` ; annotationBar . setAttribute ( 'style' , style ) ; popupContainer . insertBefore ( annotationBar , popupContainer . firstChild ) ; const onHighlightedCallback = ( highlightCreatedEvent ) => { annotationBarCallbacks . onHighlighted ( highlightCreatedEvent ) ; destroyAnnotationBar ( annotationBar ) ; } ; ReactDOM . render ( < ControlledAnnotationBar activeSelection = { activeSelectionEvent } onHighlighted = { onHighlightedCallback } type = 'range' pageNum = { pageNum } / > , annotationBar ) ; return annotationBar ; } }