diff options
author | bob <bcz@cs.brown.edu> | 2019-08-19 10:11:59 -0400 |
---|---|---|
committer | bob <bcz@cs.brown.edu> | 2019-08-19 10:11:59 -0400 |
commit | e37bf9124c952aa26c3e29deb9e4faa01cad1a7e (patch) | |
tree | be44ae9bd5e2eb6c5ce392383d41505b5863d061 /src/client/views/pdf/PDFViewer.tsx | |
parent | 07482c3bf435748140addfd4fd338fc668657798 (diff) | |
parent | b037aa89fb564812f880994453ce002054a0ad82 (diff) |
Merge branch 'master' into presentation_f
Diffstat (limited to 'src/client/views/pdf/PDFViewer.tsx')
-rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 743 |
1 files changed, 230 insertions, 513 deletions
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 5eb02a6da..08674720d 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -1,167 +1,114 @@ +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; import * as rp from "request-promise"; import { Dictionary } from "typescript-collections"; -import { Doc, DocListCast, Opt } from "../../../new_fields/Doc"; +import { Doc, DocListCast, FieldResult } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; +import { ScriptField } from "../../../new_fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { emptyFunction, Utils } from "../../../Utils"; +import { Utils, numberRange } from "../../../Utils"; +import { DocServer } from "../../DocServer"; import { Docs, DocUtils } from "../../documents/Documents"; -import { DragManager } from "../../util/DragManager"; -import { PDFBox } from "../nodes/PDFBox"; +import { KeyCodes } from "../../northstar/utils/KeyCodes"; +import { CompileScript, CompiledScript } from "../../util/Scripting"; +import Annotation from "./Annotation"; import Page from "./Page"; import "./PDFViewer.scss"; import React = require("react"); -import { CompileScript, CompileResult } from "../../util/Scripting"; -import { ScriptField } from "../../../new_fields/ScriptField"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import Annotation from "./Annotation"; -import { KeyCodes } from "../../northstar/utils/KeyCodes"; -import { DocServer } from "../../DocServer"; const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer"); export const scale = 2; -interface IPDFViewerProps { - url: string; - loaded: (nw: number, nh: number, np: number) => void; - scrollY: number; - parent: PDFBox; -} - -/** - * Wrapper that loads the PDF and cascades the pdf down - */ -@observer -export class PDFViewer extends React.Component<IPDFViewerProps> { - @observable _pdf: Opt<Pdfjs.PDFDocumentProxy>; - private _mainDiv = React.createRef<HTMLDivElement>(); - - @action - componentDidMount() { - Pdfjs.getDocument(this.props.url).promise.then(pdf => runInAction(() => this._pdf = pdf)); - } - - render() { - return ( - <div className="pdfViewer-viewerCont" ref={this._mainDiv}> - {!this._pdf ? (null) : - <Viewer pdf={this._pdf} loaded={this.props.loaded} scrollY={this.props.scrollY} parent={this.props.parent} mainCont={this._mainDiv} url={this.props.url} />} - </div> - ); - } -} interface IViewerProps { pdf: Pdfjs.PDFDocumentProxy; - loaded: (nw: number, nh: number, np: number) => void; - scrollY: number; - parent: PDFBox; - mainCont: React.RefObject<HTMLDivElement>; url: string; + Document: Doc; + DataDoc?: Doc; + fieldExtensionDoc: Doc; + fieldKey: string; + loaded: (nw: number, nh: number, np: number) => void; + panY: number; + scrollTo: (y: number) => void; + active: () => boolean; + setPanY?: (n: number) => void; + addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void; + addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean; } /** * Handles rendering and virtualization of the pdf */ @observer -export class Viewer extends React.Component<IViewerProps> { - // _visibleElements is the array of JSX elements that gets rendered - @observable.shallow private _visibleElements: JSX.Element[] = []; - // _isPage is an array that tells us whether or not an index is rendered as a page or as a placeholder - @observable private _isPage: string[] = []; +export class PDFViewer extends React.Component<IViewerProps> { + @observable.shallow private _visibleElements: JSX.Element[] = []; // _visibleElements is the array of JSX elements that gets rendered + @observable private _isPage: string[] = [];// _isPage is an array that tells us whether or not an index is rendered as a page or as a placeholder @observable private _pageSizes: { width: number, height: number }[] = []; @observable private _annotations: Doc[] = []; @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>(); - @observable private _script: CompileResult | undefined; + @observable private _script: CompiledScript = CompileScript("return true") as CompiledScript; @observable private _searching: boolean = false; - - @observable public Index: number = -1; + @observable private Index: number = -1; private _pageBuffer: number = 1; private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); private _reactionDisposer?: IReactionDisposer; private _annotationReactionDisposer?: IReactionDisposer; - private _dropDisposer?: DragManager.DragDropDisposer; private _filterReactionDisposer?: IReactionDisposer; - private _viewer: React.RefObject<HTMLDivElement>; - private _mainCont: React.RefObject<HTMLDivElement>; + private _viewer: React.RefObject<HTMLDivElement> = React.createRef(); + private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); private _pdfViewer: any; - // private _textContent: Pdfjs.TextContent[] = []; private _pdfFindController: any; private _searchString: string = ""; private _selectionText: string = ""; - constructor(props: IViewerProps) { - super(props); + @computed get panY(): number { return this.props.panY; } - let scriptfield = Cast(this.props.parent.Document.filterScript, ScriptField); - this._script = scriptfield ? scriptfield.script : CompileScript("return true"); - this._viewer = React.createRef(); - this._mainCont = React.createRef(); - } + // startIndex: where to start rendering pages + @computed get startIndex(): number { return Math.max(0, this.getPageFromScroll(this.panY) - this._pageBuffer); } - setSelectionText = (text: string) => { - this._selectionText = text; + // endIndex: where to end rendering pages + @computed get endIndex(): number { + return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.panY + (this._pageSizes[0] ? this._pageSizes[0].height : 0)) + this._pageBuffer); } - componentDidUpdate = (prevProps: IViewerProps) => { - if (this.scrollY !== prevProps.scrollY) { - this.renderPages(); - } + @computed get filteredAnnotations() { + return this._annotations.filter(anno => { + let run = this._script.run({ this: anno }); + return run.success ? run.result : true; + }); } - @action - componentDidMount = () => { - this._reactionDisposer = reaction( + componentDidUpdate = (prevProps: IViewerProps) => this.panY !== prevProps.panY && this.renderPages(); - () => [this.props.parent.props.active(), this.startIndex, this._pageSizes.length ? this.endIndex : 0], - async () => { - await this.initialLoad(); - this.renderPages(); - }, { fireImmediately: true }); + componentDidMount = async () => { + await this.initialLoad(); - this._annotationReactionDisposer = reaction( - () => { - return this.props.parent && this.props.parent.fieldExtensionDoc && DocListCast(this.props.parent.fieldExtensionDoc.annotations); - }, - (annotations: Doc[]) => { - annotations && annotations.length && this.renderAnnotations(annotations, true); - }, + this._reactionDisposer = reaction( + () => [this.props.active(), this.startIndex, this._pageSizes.length ? this.endIndex : 0], + () => this.renderPages(), { fireImmediately: true }); + this._annotationReactionDisposer = reaction( + () => this.props.fieldExtensionDoc && DocListCast(this.props.fieldExtensionDoc.annotations), + annotations => annotations && annotations.length && this.renderAnnotations(annotations, true), + { fireImmediately: true }); - if (this.props.parent.props.ContainingCollectionView) { - this._filterReactionDisposer = reaction( - () => this.props.parent.Document.filterScript, - () => { - runInAction(() => { - let scriptfield = Cast(this.props.parent.Document.filterScript, ScriptField); - this._script = scriptfield ? scriptfield.script : CompileScript("return true"); - if (this.props.parent.props.ContainingCollectionView) { - let fieldDoc = Doc.resolvedFieldDataDoc(this.props.parent.props.ContainingCollectionView.props.DataDoc ? - this.props.parent.props.ContainingCollectionView.props.DataDoc : this.props.parent.props.ContainingCollectionView.props.Document, this.props.parent.props.ContainingCollectionView.props.fieldKey, "true"); - let ccvAnnos = DocListCast(fieldDoc.annotations); - ccvAnnos.forEach(d => { - if (this._script && this._script.compiled) { - let run = this._script.run(d); - if (run.success) { - d.opacity = run.result ? 1 : 0; - } - } - }); - } - this.Index = -1; - }); - } - ); - } - - if (this._mainCont.current) { - this._dropDisposer = this._mainCont.current && DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } }); - } + this._filterReactionDisposer = reaction( + () => ({ scriptField: Cast(this.props.Document.filterScript, ScriptField), annos: this._annotations.slice() }), + action(({ scriptField, annos }: { scriptField: FieldResult<ScriptField>, annos: Doc[] }) => { + this._script = scriptField && scriptField.script.compiled ? scriptField.script : CompileScript("return true") as CompiledScript; + annos.forEach(d => { + let run = this._script.run(d); + d.opacity = !run.success || run.result ? 1 : 0; + }); + this.Index = -1; + }), + { fireImmediately: true } + ); document.removeEventListener("copy", this.copy); document.addEventListener("copy", this.copy); @@ -171,162 +118,115 @@ export class Viewer extends React.Component<IViewerProps> { this._reactionDisposer && this._reactionDisposer(); this._annotationReactionDisposer && this._annotationReactionDisposer(); this._filterReactionDisposer && this._filterReactionDisposer(); - this._dropDisposer && this._dropDisposer(); document.removeEventListener("copy", this.copy); } - private copy = (e: ClipboardEvent) => { - if (this.props.parent.props.active()) { - let text = this._selectionText; - if (e.clipboardData) { - e.clipboardData.setData("text/plain", text); - e.clipboardData.setData("dash/pdfOrigin", this.props.parent.props.Document[Id]); - let annoDoc = this.makeAnnotationDocument(undefined, 0, "#0390fc"); - e.clipboardData.setData("dash/pdfRegion", annoDoc[Id]); - e.preventDefault(); - } + copy = (e: ClipboardEvent) => { + if (this.props.active() && e.clipboardData) { + e.clipboardData.setData("text/plain", this._selectionText); + e.clipboardData.setData("dash/pdfOrigin", this.props.Document[Id]); + e.clipboardData.setData("dash/pdfRegion", this.makeAnnotationDocument(undefined, "#0390fc")[Id]); + e.preventDefault(); } - // let targetAnnotations = DocListCast(this.props.parent.fieldExtensionDoc.annotations); - // if (targetAnnotations) { - // targetAnnotations.push(destDoc); - // } } paste = (e: ClipboardEvent) => { - if (e.clipboardData) { - if (e.clipboardData.getData("dash/pdfOrigin") === this.props.parent.props.Document[Id]) { - let linkDocId = e.clipboardData.getData("dash/linkDoc"); - if (linkDocId) { - DocServer.GetRefField(linkDocId).then(async (link) => { - if (!(link instanceof Doc)) { - return; - } - let proto = Doc.GetProto(link); - let source = await Cast(proto.anchor1, Doc); - proto.anchor2 = this.makeAnnotationDocument(source, 0, "#0390fc", false); - }); - } - } + if (e.clipboardData && e.clipboardData.getData("dash/pdfOrigin") === this.props.Document[Id]) { + let linkDocId = e.clipboardData.getData("dash/linkDoc"); + linkDocId && DocServer.GetRefField(linkDocId).then(async (link) => + (link instanceof Doc) && (Doc.GetProto(link).anchor2 = this.makeAnnotationDocument(await Cast(Doc.GetProto(link), Doc), "#0390fc", false))); } } - scrollTo(y: number) { - if (this.props.mainCont.current) { - this.props.parent.scrollTo(y); - } - } + searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => this._searchString = e.currentTarget.value; + + pageLoaded = (page: Pdfjs.PDFPageViewport): void => this.props.loaded(page.width, page.height, this.props.pdf.numPages); + + setSelectionText = (text: string) => this._selectionText = text; + + getIndex = () => this.Index; @action initialLoad = async () => { if (this._pageSizes.length === 0) { - let pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages); this._isPage = Array<string>(this.props.pdf.numPages); - // this._textContent = Array<Pdfjs.TextContent>(this.props.pdf.numPages); - const proms: Pdfjs.PDFPromise<any>[] = []; - for (let i = 0; i < this.props.pdf.numPages; i++) { - proms.push(this.props.pdf.getPage(i + 1).then(page => runInAction(() => { - pageSizes[i] = { + this._pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages); + await Promise.all(this._pageSizes.map<Pdfjs.PDFPromise<any>>((val, i) => + this.props.pdf.getPage(i + 1).then(action((page: Pdfjs.PDFPageProxy) => { + this._pageSizes.splice(i, 1, { width: (page.view[page.rotate === 0 || page.rotate === 180 ? 2 : 3] - page.view[page.rotate === 0 || page.rotate === 180 ? 0 : 1]) * scale, height: (page.view[page.rotate === 0 || page.rotate === 180 ? 3 : 2] - page.view[page.rotate === 0 || page.rotate === 180 ? 1 : 0]) * scale - }; - // let x = page.getViewport(scale); - // page.getTextContent().then((text: Pdfjs.TextContent) => { - // // let tc = new Pdfjs.TextContentItem() - // // let tc = {str: } - // this._textContent[i] = text; - // // text.items.forEach(t => { - // // tcStr += t.str; - // // }) - // }); - // pageSizes[i] = { width: x.width, height: x.height }; - }))); - } - await Promise.all(proms); - runInAction(() => - Array.from(Array((this._pageSizes = pageSizes).length).keys()).map(this.getPlaceholderPage)); - this.props.loaded(Math.max(...pageSizes.map(i => i.width)), pageSizes[0].height, this.props.pdf.numPages); - // this.props.loaded(Math.max(...pageSizes.map(i => i.width)), pageSizes[0].height, this.props.pdf.numPages); - - let startY = NumCast(this.props.parent.Document.startY); - let ccv = this.props.parent.props.ContainingCollectionView; - if (ccv) { - ccv.props.Document.panY = startY; - } - this.props.parent.Document.scrollY = 0; - this.props.parent.Document.scrollY = startY + 1; + }); + this.getPlaceholderPage(i); + })))); + this.props.loaded(Math.max(...this._pageSizes.map(i => i.width)), this._pageSizes[0].height, this.props.pdf.numPages); + + let startY = NumCast(this.props.Document.startY, NumCast(this.props.Document.panY)); + this.props.setPanY && this.props.setPanY(startY); } } @action - makeAnnotationDocument = (sourceDoc: Doc | undefined, s: number, color: string, createLink: boolean = true): Doc => { - let annoDocs: Doc[] = []; + makeAnnotationDocument = (sourceDoc: Doc | undefined, color: string, createLink: boolean = true): Doc => { let mainAnnoDoc = Docs.Create.InstanceFromProto(new Doc(), "", {}); - - mainAnnoDoc.title = "Annotation on " + StrCast(this.props.parent.Document.title); - mainAnnoDoc.pdfDoc = this.props.parent.props.Document; + let mainAnnoDocProto = Doc.GetProto(mainAnnoDoc); + let annoDocs: Doc[] = []; let minY = Number.MAX_VALUE; - this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => { - for (let anno of value) { + if (this._savedAnnotations.size() === 1 && this._savedAnnotations.values()[0].length === 1 && !createLink) { + let anno = this._savedAnnotations.values()[0][0]; + let annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: "rgba(255, 0, 0, 0.1)", title: "Annotation on " + StrCast(this.props.Document.title) }); + if (anno.style.left) annoDoc.x = parseInt(anno.style.left); + if (anno.style.top) annoDoc.y = parseInt(anno.style.top); + if (anno.style.height) annoDoc.height = parseInt(anno.style.height); + if (anno.style.width) annoDoc.width = parseInt(anno.style.width); + annoDoc.target = sourceDoc; + annoDoc.group = mainAnnoDoc; + annoDoc.color = color; + annoDoc.type = AnnotationTypes.Region; + annoDocs.push(annoDoc); + annoDoc.isButton = true; + anno.remove(); + this.props.addDocument && this.props.addDocument(annoDoc, false); + mainAnnoDoc = annoDoc; + mainAnnoDocProto = Doc.GetProto(annoDoc); + } else { + this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => value.map(anno => { let annoDoc = new Doc(); - if (anno.style.left) annoDoc.x = parseInt(anno.style.left) / scale; - if (anno.style.top) { - annoDoc.y = parseInt(anno.style.top) / scale; - minY = Math.min(parseInt(anno.style.top), minY); - } - if (anno.style.height) annoDoc.height = parseInt(anno.style.height) / scale; - if (anno.style.width) annoDoc.width = parseInt(anno.style.width) / scale; - annoDoc.page = key; + if (anno.style.left) annoDoc.x = parseInt(anno.style.left); + if (anno.style.top) annoDoc.y = parseInt(anno.style.top); + if (anno.style.height) annoDoc.height = parseInt(anno.style.height); + if (anno.style.width) annoDoc.width = parseInt(anno.style.width); annoDoc.target = sourceDoc; annoDoc.group = mainAnnoDoc; annoDoc.color = color; annoDoc.type = AnnotationTypes.Region; annoDocs.push(annoDoc); anno.remove(); - } - }); + (annoDoc.y !== undefined) && (minY = Math.min(NumCast(annoDoc.y), minY)); + })); - mainAnnoDoc.y = Math.max(minY, 0); - mainAnnoDoc.annotations = new List<Doc>(annoDocs); + mainAnnoDocProto.y = Math.max(minY, 0); + mainAnnoDocProto.annotations = new List<Doc>(annoDocs); + } + mainAnnoDocProto.title = "Annotation on " + StrCast(this.props.Document.title); + mainAnnoDocProto.annotationOn = this.props.Document; if (sourceDoc && createLink) { - DocUtils.MakeLink(sourceDoc, mainAnnoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); + DocUtils.MakeLink(sourceDoc, mainAnnoDocProto, undefined, `Annotation from ${StrCast(this.props.Document.title)}`); } this._savedAnnotations.clear(); this.Index = -1; return mainAnnoDoc; } - drop = async (e: Event, de: DragManager.DropEvent) => { - if (de.data instanceof DragManager.LinkDragData) { - let sourceDoc = de.data.linkSourceDocument; - let destDoc = this.makeAnnotationDocument(sourceDoc, 1, "red"); - de.data.droppedDocuments.push(destDoc); - let targetAnnotations = DocListCast(this.props.parent.fieldExtensionDoc.annotations); - if (targetAnnotations) { - targetAnnotations.push(destDoc); - } - else { - this.props.parent.fieldExtensionDoc.annotations = new List<Doc>([destDoc]); - } - e.stopPropagation(); - } - } - /** - * Called by the Page class when it gets rendered, initializes the lists and - * puts a placeholder with all of the correct page sizes when all of the pages have been loaded. - */ - @action - pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => { - this.props.loaded(page.width, page.height, this.props.pdf.numPages); - } - @action getPlaceholderPage = (page: number) => { if (this._isPage[page] !== "none") { this._isPage[page] = "none"; this._visibleElements[page] = ( <div key={`${this.props.url}-placeholder-${page + 1}`} className="pdfviewer-placeholder" - style={{ width: this._pageSizes[page].width, height: this._pageSizes[page].height }} /> - ); + style={{ width: this._pageSizes[page].width, height: this._pageSizes[page].height }}> + "PAGE IS LOADING... " + </div>); } } @@ -334,25 +234,19 @@ export class Viewer extends React.Component<IViewerProps> { getRenderedPage = (page: number) => { if (this._isPage[page] !== "page") { this._isPage[page] = "page"; - this._visibleElements[page] = ( - <Page - setSelectionText={this.setSelectionText} - size={this._pageSizes[page]} - pdf={this.props.pdf} - page={page} - numPages={this.props.pdf.numPages} - key={`${this.props.url}-rendered-${page + 1}`} - name={`${this.props.pdf.fingerprint + `-page${page + 1}`}`} - pageLoaded={this.pageLoaded} - parent={this.props.parent} - makePin={emptyFunction} - renderAnnotations={this.renderAnnotations} - createAnnotation={this.createAnnotation} - sendAnnotations={this.receiveAnnotations} - makeAnnotationDocuments={this.makeAnnotationDocument} - getScrollFromPage={this.getScrollFromPage} - {...this.props} /> - ); + this._visibleElements[page] = (<Page {...this.props} + size={this._pageSizes[page]} + numPages={this.props.pdf.numPages} + setSelectionText={this.setSelectionText} + page={page} + key={`${this.props.url}-rendered-${page + 1}`} + name={`${this.props.pdf.fingerprint + `-page${page + 1}`}`} + pageLoaded={this.pageLoaded} + renderAnnotations={this.renderAnnotations} + createAnnotation={this.createAnnotation} + sendAnnotations={this.receiveAnnotations} + makeAnnotationDocuments={this.makeAnnotationDocument} + getScrollFromPage={this.getScrollFromPage} />); } } @@ -360,14 +254,12 @@ export class Viewer extends React.Component<IViewerProps> { // file address of the pdf @action getPageImage = async (page: number) => { - let handleError = () => this.getRenderedPage(page); if (this._isPage[page] !== "image") { this._isPage[page] = "image"; - const address = this.props.url; try { - let res = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`))); + let res = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${this.props.url.substring("files/".length, this.props.url.length - ".pdf".length)}-${page + 1}.PNG`))); runInAction(() => this._visibleElements[page] = - <img key={res.path} src={res.path} onError={handleError} + <img key={res.path} src={res.path} onError={() => this.getRenderedPage(page)} style={{ width: `${parseInt(res.width) * scale}px`, height: `${parseInt(res.height) * scale}px` }} />); } catch (e) { console.log(e); @@ -375,33 +267,14 @@ export class Viewer extends React.Component<IViewerProps> { } } - @computed get scrollY(): number { return this.props.scrollY; } - - // startIndex: where to start rendering pages - @computed get startIndex(): number { return Math.max(0, this.getPageFromScroll(this.scrollY) - this._pageBuffer); } - - // endIndex: where to end rendering pages - @computed get endIndex(): number { - return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.scrollY + this._pageSizes[0].height) + this._pageBuffer); - } - - @action renderPages = () => { - for (let i = 0; i < this.props.pdf.numPages; i++) { - if (i < this.startIndex || i > this.endIndex) { - this.getPlaceholderPage(i); // pages outside of the pdf use empty stand-in divs - } else { - if (this.props.parent.props.active()) { - this.getRenderedPage(i); - } else { - this.getPageImage(i); - } - } - } + numberRange(this.props.pdf.numPages).filter(p => this._isPage[p] !== undefined).map(i => + (i < this.startIndex || i > this.endIndex) ? this.getPlaceholderPage(i) : // pages outside of the pdf use empty stand-in divs + this.props.active() ? this.getRenderedPage(i) : this.getPageImage(i)); } @action - private renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => { + renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => { if (removeOldAnnotations) { this._annotations = annotations; } @@ -412,6 +285,21 @@ export class Viewer extends React.Component<IViewerProps> { } @action + prevAnnotation = (e: React.MouseEvent) => { + e.stopPropagation(); + this.Index = Math.max(this.Index - 1, 0); + } + + @action + nextAnnotation = (e: React.MouseEvent) => { + e.stopPropagation(); + this.Index = Math.min(this.Index + 1, this.filteredAnnotations.length - 1); + } + + sendAnnotations = (page: number) => { + return this._savedAnnotations.getValue(page); + } + receiveAnnotations = (annotations: HTMLDivElement[], page: number) => { if (page === -1) { this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove())); @@ -422,28 +310,21 @@ export class Viewer extends React.Component<IViewerProps> { } } - sendAnnotations = (page: number): HTMLDivElement[] | undefined => { - return this._savedAnnotations.getValue(page); - } - // get the page index that the vertical offset passed in is on getPageFromScroll = (vOffset: number) => { let index = 0; let currOffset = vOffset; - while (index < this._pageSizes.length && currOffset - this._pageSizes[index].height > 0) { + while (index < this._pageSizes.length && this._pageSizes[index] && currOffset - this._pageSizes[index].height > 0) { currOffset -= this._pageSizes[index++].height; } return index; } getScrollFromPage = (index: number): number => { - let counter = 0; - for (let i = 0; i < Math.min(this.props.pdf.numPages, index); i++) { - counter += this._pageSizes[i].height; - } - return counter; + return numberRange(Math.min(this.props.pdf.numPages, index)).reduce((counter, i) => counter + this._pageSizes[i].height, 0); } + @action createAnnotation = (div: HTMLDivElement, page: number) => { if (this._annotationLayer.current) { if (div.style.top) { @@ -461,101 +342,30 @@ export class Viewer extends React.Component<IViewerProps> { } } - renderAnnotation = (anno: Doc, index: number): JSX.Element => { - return <Annotation anno={anno} index={index} parent={this} key={`${anno[Id]}-annotation`} />; - } - - @action - pointerDown = () => { - // this._searching = false; - } - @action search = (searchString: string) => { if (this._pdfViewer._pageViewsReady) { - this._pdfFindController.executeCommand('find', - { - caseSensitive: false, - findPrevious: undefined, - highlightAll: true, - phraseSearch: true, - query: searchString - }); + this._pdfFindController.executeCommand('find', { + caseSensitive: false, + findPrevious: undefined, + highlightAll: true, + phraseSearch: true, + query: searchString + }); } - else { - let container = this._mainCont.current; - if (container) { - container.addEventListener("pagesloaded", () => { - console.log("rendered"); - this._pdfFindController.executeCommand('find', - { - caseSensitive: false, - findPrevious: undefined, - highlightAll: true, - phraseSearch: true, - query: searchString - }); - }); - container.addEventListener("pagerendered", () => { - console.log("rendered"); - this._pdfFindController.executeCommand('find', - { - caseSensitive: false, - findPrevious: undefined, - highlightAll: true, - phraseSearch: true, - query: searchString - }); - }); - } + else if (this._mainCont.current) { + let executeFind = () => this._pdfFindController.executeCommand('find', { + caseSensitive: false, + findPrevious: undefined, + highlightAll: true, + phraseSearch: true, + query: searchString + }); + this._mainCont.current.addEventListener("pagesloaded", executeFind); + this._mainCont.current.addEventListener("pagerendered", executeFind); } - - // let viewer = this._viewer.current; - - // if (!this._pdfFindController) { - // if (container && viewer) { - // let simpleLinkService = new SimpleLinkService(); - // let pdfViewer = new PDFJSViewer.PDFViewer({ - // container: container, - // viewer: viewer, - // linkService: simpleLinkService - // }); - // simpleLinkService.setPdf(this.props.pdf); - // container.addEventListener("pagesinit", () => { - // pdfViewer.currentScaleValue = 1; - // }); - // container.addEventListener("pagerendered", () => { - // console.log("rendered"); - // this._pdfFindController.executeCommand('find', - // { - // caseSensitive: false, - // findPrevious: undefined, - // highlightAll: true, - // phraseSearch: true, - // query: searchString - // }); - // }); - // pdfViewer.setDocument(this.props.pdf); - // this._pdfFindController = new PDFJSViewer.PDFFindController(pdfViewer); - // // this._pdfFindController._linkService = pdfLinkService; - // pdfViewer.findController = this._pdfFindController; - // } - // } - // else { - // this._pdfFindController.executeCommand('find', - // { - // caseSensitive: false, - // findPrevious: undefined, - // highlightAll: true, - // phraseSearch: true, - // query: searchString - // }); - // } } - searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => { - this._searchString = e.currentTarget.value; - } @action toggleSearch = (e: React.MouseEvent) => { @@ -563,29 +373,19 @@ export class Viewer extends React.Component<IViewerProps> { this._searching = !this._searching; if (this._searching) { - let container = this._mainCont.current; - let viewer = this._viewer.current; - - if (!this._pdfFindController) { - if (container && viewer) { - let simpleLinkService = new SimpleLinkService(); - this._pdfViewer = new PDFJSViewer.PDFViewer({ - container: container, - viewer: viewer, - linkService: simpleLinkService - }); - simpleLinkService.setPdf(this.props.pdf); - container.addEventListener("pagesinit", () => { - this._pdfViewer.currentScaleValue = 1; - }); - container.addEventListener("pagerendered", () => { - console.log("rendered"); - }); - this._pdfViewer.setDocument(this.props.pdf); - this._pdfFindController = new PDFJSViewer.PDFFindController(this._pdfViewer); - // this._pdfFindController._linkService = pdfLinkService; - this._pdfViewer.findController = this._pdfFindController; - } + if (!this._pdfFindController && this._mainCont.current && this._viewer.current) { + let simpleLinkService = new SimpleLinkService(); + this._pdfViewer = new PDFJSViewer.PDFViewer({ + container: this._mainCont.current, + viewer: this._viewer.current, + linkService: simpleLinkService + }); + simpleLinkService.setPdf(this.props.pdf); + this._mainCont.current.addEventListener("pagesinit", () => this._pdfViewer.currentScaleValue = 1); + this._mainCont.current.addEventListener("pagerendered", () => console.log("rendered")); + this._pdfViewer.setDocument(this.props.pdf); + this._pdfFindController = new PDFJSViewer.PDFFindController(this._pdfViewer); + this._pdfViewer.findController = this._pdfFindController; } } else { @@ -599,120 +399,45 @@ export class Viewer extends React.Component<IViewerProps> { } } - @action - prevAnnotation = (e: React.MouseEvent) => { - e.stopPropagation(); - - // if (this.Index > 0) { - // this.Index--; - // } - this.Index = Math.max(this.Index - 1, 0); - } - - @action - nextAnnotation = (e: React.MouseEvent) => { - e.stopPropagation(); - - let compiled = this._script; - let filtered = this._annotations.filter(anno => { - if (compiled && compiled.compiled) { - let run = compiled.run({ this: anno }); - if (run.success) { - return run.result; - } - } - return true; - }); - this.Index = Math.min(this.Index + 1, filtered.length - 1); - } - - nextResult = () => { - // if (this._viewer.current) { - // let results = this._pdfFindController.pageMatches; - // if (results && results.length) { - // if (this._pageIndex === this.props.pdf.numPages && this._matchIndex === results[this._pageIndex].length - 1) { - // return; - // } - // if (this._pageIndex === -1 || this._matchIndex === results[this._pageIndex].length - 1) { - // this._matchIndex = 0; - // this._pageIndex++; - // } - // else { - // this._matchIndex++; - // } - // this._pdfFindController._nextMatch() - // let nextMatch = this._viewer.current.children[this._pageIndex].children[1].children[results[this._pageIndex][this._matchIndex]]; - // rconsole.log(nextMatch); - // this.props.parent.scrollTo(nextMatch.getBoundingClientRect().top); - // nextMatch.setAttribute("style", nextMatch.getAttribute("style") ? nextMatch.getAttribute("style") + ", background-color: green" : "background-color: green"); - // } - // } - } - render() { - let compiled = this._script; - return ( - <div ref={this._mainCont} style={{ pointerEvents: "all" }} onPointerDown={this.pointerDown}> - <div className="viewer" style={this._searching ? { position: "absolute", top: 0 } : {}}> - {this._visibleElements} - </div> - <div className="pdfViewer-text" ref={this._viewer} onCopy={() => console.log("gello world")} style={{ transform: "scale(1.5)", transformOrigin: "top left" }} /> - <div className="pdfViewer-annotationLayer" - style={{ - height: this.props.parent.Document.nativeHeight, width: `100%`, - pointerEvents: this.props.parent.props.active() ? "none" : "all" - }}> - <div className="pdfViewer-annotationLayer-subCont" ref={this._annotationLayer}> - {this._annotations.filter(anno => { - if (compiled && compiled.compiled) { - let run = compiled.run({ this: anno }); - if (run.success) { - return run.result; - } - } - return true; - }).sort((a: Doc, b: Doc) => NumCast(a.y) - NumCast(b.y)) - .map((anno: Doc, index: number) => this.renderAnnotation(anno, index))} - </div> - </div> - <div className="pdfViewer-overlayCont" onPointerDown={(e) => e.stopPropagation()} - style={{ - bottom: -this.props.scrollY, - left: `${this._searching ? 0 : 100}%` - }}> - <button className="pdfViewer-overlayButton" title="Open Search Bar"></button> - {/* <button title="Previous Result" onClick={() => this.search(this._searchString)}><FontAwesomeIcon icon="arrow-up" size="3x" color="white" /></button> - <button title="Next Result" onClick={this.nextResult}><FontAwesomeIcon icon="arrow-down" size="3x" color="white" /></button> */} - <input onKeyDown={(e: React.KeyboardEvent) => e.keyCode === KeyCodes.ENTER ? this.search(this._searchString) : e.keyCode === KeyCodes.BACKSPACE ? e.stopPropagation() : true} placeholder="Search" className="pdfViewer-overlaySearchBar" onChange={this.searchStringChanged} /> - <button title="Search" onClick={() => this.search(this._searchString)}><FontAwesomeIcon icon="search" size="3x" color="white" /></button> - </div> - <button className="pdfViewer-overlayButton" onClick={this.prevAnnotation} title="Previous Annotation" - style={{ bottom: -this.props.scrollY + 280, right: 10, display: this.props.parent.props.active() ? "flex" : "none" }}> - <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}> - <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-up"} size="3x" /> - </div> - </button> - <button className="pdfViewer-overlayButton" onClick={this.nextAnnotation} title="Next Annotation" - style={{ bottom: -this.props.scrollY + 200, right: 10, display: this.props.parent.props.active() ? "flex" : "none" }}> - <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}> - <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-down"} size="3x" /> - </div> - </button> - <button className="pdfViewer-overlayButton" onClick={this.toggleSearch} title="Open Search Bar" - style={{ bottom: -this.props.scrollY + 10, right: 0, display: this.props.parent.props.active() ? "flex" : "none" }}> - <div className="pdfViewer-overlayButton-arrow" onPointerDown={(e) => e.stopPropagation()}></div> - <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}> - <FontAwesomeIcon style={{ color: "white" }} icon={this._searching ? "times" : "search"} size="3x" /> - </div> - </button> - </div > - ); + return (<div className="pdfViewer-viewer" ref={this._mainCont} > + <div className="pdfViewer-visibleElements" style={this._searching ? { position: "absolute", top: 0 } : {}}> + {this._visibleElements} + </div> + <div className="pdfViewer-text" ref={this._viewer} /> + <div className="pdfViewer-annotationLayer" style={{ height: NumCast(this.props.Document.nativeHeight) }} ref={this._annotationLayer}> + {this.filteredAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) => + <Annotation {...this.props} ParentIndex={this.getIndex} anno={anno} index={index} key={`${anno[Id]}-annotation`} />)} + </div> + <div className="pdfViewer-overlayCont" onPointerDown={(e) => e.stopPropagation()} + style={{ bottom: -this.props.panY, left: `${this._searching ? 0 : 100}%` }}> + <button className="pdfViewer-overlayButton" title="Open Search Bar" /> + <input className="pdfViewer-overlaySearchBar" placeholder="Search" onChange={this.searchStringChanged} + onKeyDown={(e: React.KeyboardEvent) => e.keyCode === KeyCodes.ENTER ? this.search(this._searchString) : e.keyCode === KeyCodes.BACKSPACE ? e.stopPropagation() : true} /> + <button title="Search" onClick={() => this.search(this._searchString)}> + <FontAwesomeIcon icon="search" size="3x" color="white" /></button> + </div> + <button className="pdfViewer-overlayButton" onClick={this.prevAnnotation} title="Previous Annotation" + style={{ bottom: -this.props.panY + 280, right: 10, display: this.props.active() ? "flex" : "none" }}> + <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}> + <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-up"} size="3x" /></div> + </button> + <button className="pdfViewer-overlayButton" onClick={this.nextAnnotation} title="Next Annotation" + style={{ bottom: -this.props.panY + 200, right: 10, display: this.props.active() ? "flex" : "none" }}> + <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}> + <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-down"} size="3x" /></div> + </button> + <button className="pdfViewer-overlayButton" onClick={this.toggleSearch} title="Open Search Bar" + style={{ bottom: -this.props.panY + 10, right: 0, display: this.props.active() ? "flex" : "none" }}> + <div className="pdfViewer-overlayButton-arrow" onPointerDown={(e) => e.stopPropagation()}></div> + <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}> + <FontAwesomeIcon style={{ color: "white" }} icon={this._searching ? "times" : "search"} size="3x" /></div> + </button> + </div >); } } -export enum AnnotationTypes { - Region -} +export enum AnnotationTypes { Region } class SimpleLinkService { externalLinkTarget: any = null; @@ -731,20 +456,12 @@ class SimpleLinkService { cachePageRef() { } - get pagesCount() { - return this.pdf ? this.pdf.numPages : 0; - } + get pagesCount() { return this.pdf ? this.pdf.numPages : 0; } - get page() { - return 0; - } + get page() { return 0; } - setPdf(pdf: any) { - this.pdf = pdf; - } + setPdf(pdf: any) { this.pdf = pdf; } - get rotation() { - return 0; - } + get rotation() { return 0; } set rotation(value: any) { } }
\ No newline at end of file |