From 6df3143ead642356b7823946e9710f840f3faa5a Mon Sep 17 00:00:00 2001
From: _stanleyyip <33562077+yipstanley@users.noreply.github.com>
Date: Tue, 14 May 2019 16:02:37 -0400
Subject: argh

---
 src/client/views/nodes/PDFBox.tsx | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index cb27b3f1b..55a37883a 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -21,6 +21,7 @@ import { positionSchema } from "./DocumentView";
 import { pageSchema } from "./ImageBox";
 import { ImageField, PdfField } from "../../../new_fields/URLField";
 import { InkingControl } from "../InkingControl";
+import { PDFViewer } from "../pdf/PDFViewer";
 
 /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx
  * This method renders PDF and puts all kinds of functionalities such as annotation, highlighting, 
@@ -354,10 +355,11 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     }
     render() {
         trace();
+        const pdfUrl = window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf";
         let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
         return (
             <div className={classname} tabIndex={0} ref={this._mainDiv} onPointerDown={this.onPointerDown} onKeyDown={this.onKeyDown} onKeyUp={this.onKeyUp} >
-                {this.pdfRenderer}
+                <PDFViewer url={pdfUrl} />
             </div >
         );
     }
-- 
cgit v1.2.3-70-g09d2


From 2cc62cd88688ccdec8275fcaaba939d448f9baf5 Mon Sep 17 00:00:00 2001
From: Stanley <syip0810@gmail.com>
Date: Sun, 19 May 2019 13:56:30 -0700
Subject: ugh

---
 src/client/views/nodes/PDFBox.tsx | 18 +++++++++---------
 src/client/views/pdf/PDFBox2.tsx  |  2 ++
 2 files changed, 11 insertions(+), 9 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 55a37883a..bf3f299bc 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -70,15 +70,15 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     @computed private get thumbnailPage() { return Cast(this.props.Document.thumbnailPage, "number", -1); }
 
     componentDidMount() {
-        this._reactionDisposer = reaction(
-            () => [SelectionManager.SelectedDocuments().slice()],
-            () => {
-                if (this.curPage > 0 && this.thumbnailPage > 0 && this.curPage !== this.thumbnailPage && !this.props.isSelected()) {
-                    this.saveThumbnail();
-                    this._interactive = true;
-                }
-            },
-            { fireImmediately: true });
+        // this._reactionDisposer = reaction(
+        //     () => [SelectionManager.SelectedDocuments().slice()],
+        //     () => {
+        //         if (this.curPage > 0 && this.thumbnailPage > 0 && this.curPage !== this.thumbnailPage && !this.props.isSelected()) {
+        //             this.saveThumbnail();
+        //             this._interactive = true;
+        //         }
+        //     },
+        //     { fireImmediately: true });
 
     }
 
diff --git a/src/client/views/pdf/PDFBox2.tsx b/src/client/views/pdf/PDFBox2.tsx
index c3f5c19d8..71825c260 100644
--- a/src/client/views/pdf/PDFBox2.tsx
+++ b/src/client/views/pdf/PDFBox2.tsx
@@ -8,6 +8,7 @@ import { PDFViewer } from "./PDFViewer";
 import { RouteStore } from "../../../server/RouteStore";
 import { InkingControl } from "../InkingControl";
 import { observer } from "mobx-react";
+import { trace } from "mobx";
 
 type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
 const PdfDocument = makeInterface(positionSchema, pageSchema);
@@ -17,6 +18,7 @@ export class PDFBox2 extends DocComponent<FieldViewProps, PdfDocument>(PdfDocume
     public static LayoutString() { return FieldView.LayoutString(PDFBox2); }
 
     render() {
+        trace();
         const pdfUrl = "https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf";
         let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool);
         return (
-- 
cgit v1.2.3-70-g09d2


From 3a9f1a40cb6bcd35783aa83e66c3e253812aa39d Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Sun, 19 May 2019 19:10:44 -0400
Subject: stacking !

---
 src/client/documents/Documents.ts  |   7 +-
 src/client/views/nodes/PDFBox.tsx  | 296 ++-----------------------------------
 src/client/views/pdf/PDFViewer.tsx |  31 +---
 3 files changed, 21 insertions(+), 313 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 5752bb096..2df733fd5 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -33,12 +33,9 @@ import { DocServer } from "../DocServer";
 import { StrokeData, InkField } from "../../new_fields/InkField";
 import { dropActionType } from "../util/DragManager";
 import { DateField } from "../../new_fields/DateField";
-<<<<<<< HEAD
 import { PDFBox2 } from "../views/pdf/PDFBox2";
-=======
 import { schema } from "prosemirror-schema-basic";
 import { UndoManager } from "../util/UndoManager";
->>>>>>> 01a223f2e6685506cc1e5db69e9062d5ff0d3246
 
 export interface DocumentOptions {
     x?: number;
@@ -173,8 +170,8 @@ export namespace Docs {
         return textProto;
     }
     function CreatePdfPrototype(): Doc {
-        let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("annotations"),
-            { x: 0, y: 0, nativeWidth: 1200, width: 300, height: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1 });
+        let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", PDFBox.LayoutString(),
+            { x: 0, y: 0, width: 300, height: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1 });
         return pdfProto;
     }
     function CreateWebPrototype(): Doc {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 9b0207d0c..d74dc53c4 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -4,8 +4,8 @@ import { observer } from "mobx-react";
 import 'react-image-lightbox/style.css';
 import Measure from "react-measure";
 //@ts-ignore
-import { Document, Page } from "react-pdf";
-import 'react-pdf/dist/Page/AnnotationLayer.css';
+// import { Document, Page } from "react-pdf";
+// import 'react-pdf/dist/Page/AnnotationLayer.css';
 import { RouteStore } from "../../../server/RouteStore";
 import { Utils } from '../../../Utils';
 import { Annotation } from './Annotation';
@@ -14,7 +14,7 @@ import "./PDFBox.scss";
 import React = require("react");
 import { SelectionManager } from "../../util/SelectionManager";
 import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
-import { Opt } from "../../../new_fields/Doc";
+import { Opt, HeightSym, Doc } from "../../../new_fields/Doc";
 import { DocComponent } from "../DocComponent";
 import { makeInterface } from "../../../new_fields/Schema";
 import { positionSchema } from "./DocumentView";
@@ -53,300 +53,26 @@ const PdfDocument = makeInterface(positionSchema, pageSchema);
 export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocument) {
     public static LayoutString() { return FieldView.LayoutString(PDFBox); }
 
-    private _mainDiv = React.createRef<HTMLDivElement>();
-    private renderHeight = 2400;
-
-    @observable private _renderAsSvg = true;
     @observable private _alt = false;
-
-    private _reactionDisposer?: IReactionDisposer;
-
-    @observable private _perPageInfo: Object[] = []; //stores pageInfo
-    @observable private _pageInfo: any = { area: [], divs: [], anno: [] }; //divs is array of objects linked to anno
-
-    @observable private _currAnno: any = [];
     @observable private _interactive: boolean = false;
-    @observable private _loaded: boolean = false;
-
-    @computed private get curPage() { return NumCast(this.Document.curPage, 1); }
-    @computed private get thumbnailPage() { return NumCast(this.props.Document.thumbnailPage, -1); }
-
-    componentDidMount() {
-        let wasSelected = false;
-        this._reactionDisposer = reaction(
-            () => this.props.isSelected(),
-            () => {
-                if (this.curPage > 0 && this.curPage !== this.thumbnailPage && wasSelected && !this.props.isSelected()) {
-                    this.saveThumbnail();
-                }
-                wasSelected = this._interactive = this.props.isSelected();
-            },
-            { fireImmediately: true });
-
-    }
-
-    componentWillUnmount() {
-        if (this._reactionDisposer) this._reactionDisposer();
-    }
-
-    /**
-     * highlighting helper function
-     */
-    makeEditableAndHighlight = (colour: string) => {
-        var range, sel = window.getSelection();
-        if (sel && sel.rangeCount && sel.getRangeAt) {
-            range = sel.getRangeAt(0);
-        }
-        document.designMode = "on";
-        if (!document.execCommand("HiliteColor", false, colour)) {
-            document.execCommand("HiliteColor", false, colour);
-        }
-
-        if (range && sel) {
-            sel.removeAllRanges();
-            sel.addRange(range);
-
-            let obj: Object = { parentDivs: [], spans: [] };
-            //@ts-ignore
-            if (range.commonAncestorContainer.className === 'react-pdf__Page__textContent') { //multiline highlighting case
-                obj = this.highlightNodes(range.commonAncestorContainer.childNodes);
-            } else { //single line highlighting case
-                let parentDiv = range.commonAncestorContainer.parentElement;
-                if (parentDiv) {
-                    if (parentDiv.className === 'react-pdf__Page__textContent') { //when highlight is overwritten
-                        obj = this.highlightNodes(parentDiv.childNodes);
-                    } else {
-                        parentDiv.childNodes.forEach((child) => {
-                            if (child.nodeName === 'SPAN') {
-                                //@ts-ignore
-                                obj.parentDivs.push(parentDiv);
-                                //@ts-ignore
-                                child.id = "highlighted";
-                                //@ts-ignore
-                                obj.spans.push(child);
-                                // child.addEventListener("mouseover", this.onEnter); //adds mouseover annotation handler
-                            }
-                        });
-                    }
-                }
-            }
-            this._pageInfo.divs.push(obj);
-
-        }
-        document.designMode = "off";
-    }
-
-    highlightNodes = (nodes: NodeListOf<ChildNode>) => {
-        let temp = { parentDivs: [], spans: [] };
-        nodes.forEach((div) => {
-            div.childNodes.forEach((child) => {
-                if (child.nodeName === 'SPAN') {
-                    //@ts-ignore
-                    temp.parentDivs.push(div);
-                    //@ts-ignore
-                    child.id = "highlighted";
-                    //@ts-ignore
-                    temp.spans.push(child);
-                    // child.addEventListener("mouseover", this.onEnter); //adds mouseover annotation handler
-                }
-            });
-
-        });
-        return temp;
-    }
-
-    /**
-     * when the cursor enters the highlight, it pops out annotation. ONLY WORKS FOR SINGLE DIV LINES
-     */
-    @action
-    onEnter = (e: any) => {
-        let span: HTMLSpanElement = e.toElement;
-        let index: any;
-        this._pageInfo.divs.forEach((obj: any) => {
-            obj.spans.forEach((element: any) => {
-                if (element === span && !index) {
-                    index = this._pageInfo.divs.indexOf(obj);
-                }
-            });
-        });
-
-        if (this._pageInfo.anno.length >= index + 1) {
-            if (this._currAnno.length === 0) {
-                this._currAnno.push(this._pageInfo.anno[index]);
-            }
-        } else {
-            if (this._currAnno.length === 0) { //if there are no current annotation
-                let div = span.offsetParent;
-                //@ts-ignore
-                let divX = div.style.left;
-                //@ts-ignore
-                let divY = div.style.top;
-                //slicing "px" from the end
-                divX = divX.slice(0, divX.length - 2); //gets X of the DIV element (parent of Span)
-                divY = divY.slice(0, divY.length - 2); //gets Y of the DIV element (parent of Span)
-                let annotation = <Annotation key={Utils.GenerateGuid()} Span={span} X={divX} Y={divY - 300} Highlights={this._pageInfo.divs} Annotations={this._pageInfo.anno} CurrAnno={this._currAnno} />;
-                this._pageInfo.anno.push(annotation);
-                this._currAnno.push(annotation);
-            }
-        }
-
-    }
 
-    /**
-     * highlight function for highlighting actual text. This works fine. 
-     */
-    highlight = (color: string) => {
-        if (window.getSelection()) {
-            try {
-                if (!document.execCommand("hiliteColor", false, color)) {
-                    this.makeEditableAndHighlight(color);
-                }
-            } catch (ex) {
-                this.makeEditableAndHighlight(color);
-            }
+    getHeight = (): number => {
+        if (this.props.Document) {
+            let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
+            console.log(doc);
+            return NumCast(doc.height);
         }
+        return 0;
     }
 
-    /**
-     * controls the area highlighting (stickies) Kinda temporary
-     */
-    onPointerDown = (e: React.PointerEvent) => {
-        if (this.props.isSelected() && !InkingControl.Instance.selectedTool && e.buttons === 1) {
-            if (e.altKey) {
-                this._alt = true;
-            } else {
-                if (e.metaKey) {
-                    e.stopPropagation();
-                }
-            }
-            document.removeEventListener("pointerup", this.onPointerUp);
-            document.addEventListener("pointerup", this.onPointerUp);
-        }
-        if (this.props.isSelected() && e.buttons === 2) {
-            runInAction(() => this._alt = true);
-            document.removeEventListener("pointerup", this.onPointerUp);
-            document.addEventListener("pointerup", this.onPointerUp);
-        }
-    }
-
-    /**
-     * controls area highlighting and partially highlighting. Kinda temporary
-     */
-    @action
-    onPointerUp = (e: PointerEvent) => {
-        this._alt = false;
-        document.removeEventListener("pointerup", this.onPointerUp);
-        if (this.props.isSelected()) {
-            this.highlight("rgba(76, 175, 80, 0.3)"); //highlights to this default color. 
-        }
-        this._interactive = true;
-    }
-
-
-    @action
-    saveThumbnail = () => {
-        this._renderAsSvg = false;
-        setTimeout(() => {
-            let nwidth = FieldValue(this.Document.nativeWidth, 0);
-            let nheight = FieldValue(this.Document.nativeHeight, 0);
-            htmlToImage.toPng(this._mainDiv.current!, { width: nwidth, height: nheight, quality: 1 })
-                .then(action((dataUrl: string) => {
-                    this.props.Document.thumbnail = new ImageField(new URL(dataUrl));
-                    this.props.Document.thumbnailPage = FieldValue(this.Document.curPage, -1);
-                    this._renderAsSvg = true;
-                }))
-                .catch(function (error: any) {
-                    console.error('oops, something went wrong!', error);
-                });
-        }, 1250);
-    }
-
-    @action
-    onLoaded = (page: any) => {
-        // bcz: the number of pages should really be set when the document is imported.
-        this.props.Document.numPages = page._transport.numPages;
-        if (this._perPageInfo.length === 0) { //Makes sure it only runs once
-            this._perPageInfo = [...Array(page._transport.numPages)];
-        }
-        this._loaded = true;
-    }
-
-    @action
-    setScaling = (r: any) => {
-        // bcz: the nativeHeight should really be set when the document is imported.
-        //      also, the native dimensions could be different for different pages of the canvas
-        //      so this design is flawed.
-        var nativeWidth = FieldValue(this.Document.nativeWidth, 0);
-        if (!FieldValue(this.Document.nativeHeight, 0)) {
-            var nativeHeight = nativeWidth * r.offset.height / r.offset.width;
-            this.props.Document.height = nativeHeight / nativeWidth * FieldValue(this.Document.width, 0);
-            this.props.Document.nativeHeight = nativeHeight;
-        }
-    }
-    @computed
-    get pdfPage() {
-        return <Page height={this.renderHeight} renderTextLayer={false} pageNumber={this.curPage} onLoadSuccess={this.onLoaded} />;
-    }
-    @computed
-    get pdfContent() {
-        trace();
-        let pdfUrl = Cast(this.props.Document[this.props.fieldKey], PdfField);
-        if (!pdfUrl) {
-            return <p>No pdf url to render</p>;
-        }
-        let pdfpage = this.pdfPage;
-        let body = this.Document.nativeHeight ?
-            pdfpage :
-            <Measure offset onResize={this.setScaling}>
-                {({ measureRef }) =>
-                    <div className="pdfBox-page" ref={measureRef}>
-                        {pdfpage}
-                    </div>
-                }
-            </Measure>;
-        let xf = (this.Document.nativeHeight || 0) / this.renderHeight;
-        return <div className="pdfBox-contentContainer" key="container" style={{ transform: `scale(${xf}, ${xf})` }}>
-            <Document file={window.origin + RouteStore.corsProxy + `/${pdfUrl.url}`} renderMode={this._renderAsSvg ? "svg" : "canvas"}>
-                {body}
-            </Document>
-        </div >;
-    }
-
-    @computed
-    get pdfRenderer() {
-        let proxy = this._loaded ? (null) : this.imageProxyRenderer;
-        let pdfUrl = Cast(this.props.Document[this.props.fieldKey], PdfField);
-        if ((!this._interactive && proxy) || !pdfUrl) {
-            return proxy;
-        }
-        return [
-            this._pageInfo.area.filter(() => this._pageInfo.area).map((element: any) => element),
-            this._currAnno.map((element: any) => element),
-            this.pdfContent,
-            proxy
-        ];
-    }
-
-    @computed
-    get imageProxyRenderer() {
-        let thumbField = this.props.Document.thumbnail;
-        if (thumbField) {
-            let path = this.thumbnailPage !== this.curPage ? "https://image.flaticon.com/icons/svg/66/66163.svg" :
-                thumbField instanceof ImageField ? thumbField.url.href : "http://cs.brown.edu/people/bcz/prairie.jpg";
-            return <img src={path} width="100%" />;
-        }
-        return (null);
-    }
-    @action onKeyDown = (e: React.KeyboardEvent) => e.key === "Alt" && (this._alt = true);
-    @action onKeyUp = (e: React.KeyboardEvent) => e.key === "Alt" && (this._alt = false);
     render() {
         trace();
         const pdfUrl = window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf";
         let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
         return (
-            <div className={classname} tabIndex={0} ref={this._mainDiv} onPointerDown={this.onPointerDown} onKeyDown={this.onKeyDown} onKeyUp={this.onKeyUp} >
+            <div style={{ overflow: "scroll", height: `${NumCast(this.props.Document.height)}px` }} onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}>
                 <PDFViewer url={pdfUrl} />
-            </div >
+            </div>
         );
     }
 
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 1b445eae4..26becebf1 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -5,6 +5,7 @@ import { RouteStore } from "../../../server/RouteStore";
 import * as Pdfjs from "pdfjs-dist";
 import { Opt } from "../../../new_fields/Doc";
 import "./PDFViewer.scss";
+import "pdfjs-dist/web/pdf_viewer.css";
 
 interface IPDFViewerProps {
     url: string;
@@ -52,8 +53,8 @@ class Viewer extends React.Component<IViewerProps> {
                         pdf={this.props.pdf}
                         page={i}
                         numPages={numPages}
-                        key={`${this.props.pdf ? this.props.pdf.fingerprint + `page${i + 1}` : "undefined"}`}
-                        name={`${this.props.pdf ? this.props.pdf.fingerprint + `page${i + 1}` : "undefined"}`}
+                        key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
+                        name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
                         {...this.props}
                     />
                 ))} }
@@ -149,24 +150,12 @@ class Page extends React.Component<IPageProps> {
         }
     }
 
-    @action
-    prevPage = (e: React.MouseEvent) => {
-        if (this._currPage > 2 && this._state !== "rendering") {
-            this._currPage = Math.max(this._currPage - 1, 1);
-            this._page = undefined;
-            this.loadPage(this.props.pdf!);
-            this._state = "rendering";
-        }
+    onPointerDown = (e: React.PointerEvent) => {
+        e.stopPropagation();
     }
 
-    @action
-    nextPage = (e: React.MouseEvent) => {
-        if (this._currPage < this.props.numPages - 1 && this._state !== "rendering") {
-            this._currPage = Math.min(this._currPage + 1, this.props.numPages)
-            this._page = undefined;
-            this.loadPage(this.props.pdf!);
-            this._state = "rendering";
-        }
+    onPointerMove = (e: React.PointerEvent) => {
+        e.stopPropagation();
     }
 
     render() {
@@ -175,11 +164,7 @@ class Page extends React.Component<IPageProps> {
                 <div className="canvasContainer">
                     <canvas ref={this.canvas} />
                 </div>
-                <div className="textlayer" ref={this.textLayer} style={{ "position": "relative", "top": `-${this._height}px`, "height": `${this._height}px` }} />
-                {/* <div className="viewer-button-cont" style={{ "width": this._width / 10, "height": this._height / 20, "left": this._width * .9, "top": this._height * .95 }}>
-                    <div className="viewer-previousPage" onClick={this.prevPage}>&lt;</div>
-                    <div className="viewer-nextPage" onClick={this.nextPage}>&gt;</div>
-                </div> */}
+                <div onPointerDown={this.onPointerDown} onPointerMove={this.onPointerMove} className="textlayer" ref={this.textLayer} style={{ "position": "relative", "top": `-${this._height}px`, "height": `${this._height}px` }} />
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From e815a45a50e215e1a5c3ada6404226326a8530a9 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Mon, 20 May 2019 04:02:23 -0400
Subject: YES

---
 src/client/views/nodes/PDFBox.tsx   |  21 ++++-
 src/client/views/pdf/PDFViewer.scss |   4 +
 src/client/views/pdf/PDFViewer.tsx  | 165 +++++++++++++++++++++++++++++++++---
 3 files changed, 176 insertions(+), 14 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index d74dc53c4..62c8e1c99 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -54,7 +54,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     public static LayoutString() { return FieldView.LayoutString(PDFBox); }
 
     @observable private _alt = false;
-    @observable private _interactive: boolean = false;
+    @observable private _scrollY: number = 0;
 
     getHeight = (): number => {
         if (this.props.Document) {
@@ -65,13 +65,28 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
         return 0;
     }
 
+    loaded = (nw: number, nh: number) => {
+        if (this.props.Document) {
+            let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
+            doc.nativeWidth = nw;
+            doc.nativeHeight = nh;
+        }
+    }
+
+    @action
+    onScroll = (e: React.UIEvent<HTMLDivElement>) => {
+        if (e.currentTarget) {
+            this._scrollY = e.currentTarget.scrollTop;
+        }
+    }
+
     render() {
         trace();
         const pdfUrl = window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf";
         let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
         return (
-            <div style={{ overflow: "scroll", height: `${NumCast(this.props.Document.height)}px` }} onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}>
-                <PDFViewer url={pdfUrl} />
+            <div onScroll={this.onScroll} style={{ overflow: "scroll", height: `${NumCast(this.props.Document.nativeWidth ? this.props.Document.nativeWidth : 300)}px` }} onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}>
+                <PDFViewer url={pdfUrl} loaded={this.loaded} scrollY={this._scrollY} />
             </div>
         );
     }
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index d8ff06406..3a6045317 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -16,4 +16,8 @@
     border-radius: 5px;
 }
 
+.textLayer {
+    user-select: auto;
+}
+
 .viewer {}
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 26becebf1..6201f6330 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,14 +1,18 @@
 import { observer } from "mobx-react";
 import React = require("react");
-import { observable, action, runInAction } from "mobx";
+import { observable, action, runInAction, computed } from "mobx";
 import { RouteStore } from "../../../server/RouteStore";
 import * as Pdfjs from "pdfjs-dist";
 import { Opt } from "../../../new_fields/Doc";
 import "./PDFViewer.scss";
 import "pdfjs-dist/web/pdf_viewer.css";
+import { number } from "prop-types";
+import { JSXElement } from "babel-types";
 
 interface IPDFViewerProps {
     url: string;
+    loaded: (nw: number, nh: number) => void;
+    scrollY: number;
 }
 
 @observer
@@ -27,11 +31,9 @@ export class PDFViewer extends React.Component<IPDFViewerProps> {
     }
 
     render() {
-        console.log("PDFVIEWER");
-        console.log(this._pdf);
         return (
             <div>
-                <Viewer pdf={this._pdf} />
+                <Viewer pdf={this._pdf} loaded={this.props.loaded} scrollY={this.props.scrollY} />
             </div>
         );
     }
@@ -39,25 +41,166 @@ export class PDFViewer extends React.Component<IPDFViewerProps> {
 
 interface IViewerProps {
     pdf: Opt<Pdfjs.PDFDocumentProxy>;
+    loaded: (nw: number, nh: number) => void;
+    scrollY: number;
 }
 
+type PDFItem = React.Component<IPageProps> | HTMLDivElement;
+
+@observer
 class Viewer extends React.Component<IViewerProps> {
+    @observable.shallow private _visibleElements: JSX.Element[] = [];
+    @observable private _isPage: boolean[] = [];
+    @observable private _pageSizes: { width: number, height: number }[] = [];
+    @observable private _startIndex: number = 0;
+    @observable private _endIndex: number = 1;
+    @observable private _loaded: boolean = false;
+    @observable private _pdf: Opt<Pdfjs.PDFDocumentProxy>;
+
+    private _pageBuffer: number = 1;
+
+    @computed get scrollY(): number {
+        return this.props.scrollY;
+    }
+
+    @computed get startIndex(): number {
+        return Math.max(0, this.getIndex(this.scrollY) - this._pageBuffer);
+    }
+
+    @computed get endIndex(): number {
+        let width = this._pageSizes.map(i => i.width);
+        return Math.min(this.props.pdf ? this.props.pdf.numPages - 1 : 0, this.getIndex(this.scrollY + Math.max(...width)) + this._pageBuffer);
+    }
+
+    componentDidMount = () => {
+        let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
+        this.renderPages(0, numPages - 1, true);
+    }
+
+    componentDidUpdate = (prevProps: IViewerProps) => {
+        if (this.scrollY !== prevProps.scrollY || this._pdf !== this.props.pdf) {
+            this._pdf = this.props.pdf;
+            this.renderPages(this.startIndex, this.endIndex);
+        }
+    }
+
+    @action
+    renderPages = (startIndex: number, endIndex: number, forceRender: boolean = false) => {
+        let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
+
+        if (this._visibleElements.length !== numPages) {
+            let divs = Array.from(Array(numPages).keys()).map(i => (
+                <Page
+                    pdf={this.props.pdf}
+                    page={i}
+                    numPages={numPages}
+                    key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
+                    name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
+                    pageLoaded={this.pageLoaded}
+                    {...this.props} />
+            ));
+            let arr = Array.from(Array(numPages).keys()).map(i => false);
+            this._visibleElements.push(...divs);
+            this._isPage.push(...arr);
+        }
+
+        if (startIndex === this._startIndex && endIndex === this._endIndex && !forceRender) {
+            return;
+        }
+
+        for (let i = startIndex; i <= endIndex; i++) {
+            if (this._isPage[i] && forceRender) {
+                this._visibleElements[i] = (
+                    <Page
+                        pdf={this.props.pdf}
+                        page={i}
+                        numPages={numPages}
+                        key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
+                        name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
+                        pageLoaded={this.pageLoaded}
+                        {...this.props} />
+                );
+                this._isPage[i] = true;
+            }
+            else if (!this._isPage[i]) {
+                this._visibleElements[i] = (
+                    <Page
+                        pdf={this.props.pdf}
+                        page={i}
+                        numPages={numPages}
+                        key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
+                        name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
+                        pageLoaded={this.pageLoaded}
+                        {...this.props} />
+                );
+                this._isPage[i] = true;
+            }
+        }
+
+        for (let i = 0; i < numPages; i++) {
+            if (i < startIndex || i > endIndex) {
+                if (this._isPage[i]) {
+                    this._visibleElements[i] = (
+                        <div key={`pdfviewer-placeholder-${i}`} className="pdfviewer-placeholder" style={{ width: this._pageSizes[i] ? this._pageSizes[i].width : 0, height: this._pageSizes[i] ? this._pageSizes[i].height : 0 }} />
+                    );
+                    this._isPage[i] = false;
+                }
+            }
+        }
+
+        return;
+    }
+
+    getIndex = (vOffset: number) => {
+        if (this._loaded) {
+            let index = 0;
+            let currOffset = vOffset;
+            while (currOffset - this._pageSizes[index].height > 0) {
+                currOffset -= this._pageSizes[index].height;
+                index++;
+            }
+            return index;
+        }
+        return 0;
+    }
+
+    @action
+    pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => {
+        let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
+        this.props.loaded(page.width, page.height);
+        if (index > this._pageSizes.length) {
+            this._pageSizes.push({ width: page.width, height: page.height });
+        }
+        else {
+            this._pageSizes[index - 1] = { width: page.width, height: page.height };
+        }
+        if (index === numPages) {
+            this._loaded = true;
+            let divs = Array.from(Array(numPages).keys()).map(i => (
+                <div key={`pdfviewer-placeholder-${i}`} className="pdfviewer-placeholder" style={{ width: this._pageSizes[i] ? this._pageSizes[i].width : 0, height: this._pageSizes[i] ? this._pageSizes[i].height : 0 }} />
+            ));
+            this._visibleElements = new Array<JSX.Element>(...divs);
+        }
+    }
+
     render() {
-        console.log("VIEWER");
+        console.log(`START: ${this.startIndex}`);
+        console.log(`END: ${this.endIndex}`)
         let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
-        console.log(numPages);
         return (
             <div className="viewer">
-                {Array.from(Array(numPages).keys()).map((i) => (
+                {/* {Array.from(Array(numPages).keys()).map((i) => (
                     <Page
                         pdf={this.props.pdf}
                         page={i}
                         numPages={numPages}
                         key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
                         name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
+                        pageLoaded={this.pageLoaded}
                         {...this.props}
                     />
-                ))} }
+                ))} */}
+                {this._visibleElements}
             </div>
         );
     }
@@ -68,6 +211,7 @@ interface IPageProps {
     name: string;
     numPages: number;
     page: number;
+    pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void;
 }
 
 @observer
@@ -113,12 +257,10 @@ class Page extends React.Component<IPageProps> {
 
         pdf.getPage(this._currPage).then(
             (page: Pdfjs.PDFPageProxy) => {
-                console.log("PAGE");
-                console.log(page);
                 this._state = "rendering";
                 this.renderPage(page);
             }
-        )
+        );
     }
 
     @action
@@ -132,6 +274,7 @@ class Page extends React.Component<IPageProps> {
             this._width = viewport.width;
             canvas.height = viewport.height;
             this._height = viewport.height;
+            this.props.pageLoaded(this._currPage, viewport);
             if (context) {
                 page.render({ canvasContext: context, viewport: viewport });
                 page.getTextContent().then((res: Pdfjs.TextContent) => {
-- 
cgit v1.2.3-70-g09d2


From 1fb7a7bc185c1ba9bbe0f21ad5e16cf19235b2da Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Tue, 4 Jun 2019 13:59:04 -0400
Subject: updatesss

---
 src/client/views/nodes/PDFBox.tsx   |  2 +-
 src/client/views/pdf/PDFViewer.scss |  6 +++-
 src/client/views/pdf/PDFViewer.tsx  | 59 +++++++++++++++++++++++++++++--------
 3 files changed, 53 insertions(+), 14 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 62c8e1c99..dd945b030 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -86,7 +86,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
         let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
         return (
             <div onScroll={this.onScroll} style={{ overflow: "scroll", height: `${NumCast(this.props.Document.nativeWidth ? this.props.Document.nativeWidth : 300)}px` }} onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}>
-                <PDFViewer url={pdfUrl} loaded={this.loaded} scrollY={this._scrollY} />
+                <PDFViewer url={pdfUrl} loaded={this.loaded} scrollY={this._scrollY} parent={this} />
             </div>
         );
     }
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 3a6045317..9d41a1bb0 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -1,4 +1,8 @@
-.canvasContainer {}
+.textLayer {
+    div {
+        user-select: text;
+    }
+}
 
 .viewer-button-cont {
     position: absolute;
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 6201f6330..d510ba91c 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,18 +1,24 @@
 import { observer } from "mobx-react";
 import React = require("react");
-import { observable, action, runInAction, computed } from "mobx";
+import { observable, action, runInAction, computed, IReactionDisposer, reaction } from "mobx";
 import { RouteStore } from "../../../server/RouteStore";
 import * as Pdfjs from "pdfjs-dist";
+import * as htmlToImage from "html-to-image";
 import { Opt } from "../../../new_fields/Doc";
 import "./PDFViewer.scss";
 import "pdfjs-dist/web/pdf_viewer.css";
 import { number } from "prop-types";
 import { JSXElement } from "babel-types";
+import { PDFBox } from "../nodes/PDFBox";
+import { NumCast, FieldValue } from "../../../new_fields/Types";
+import { SearchBox } from "../SearchBox";
+import { Utils } from "../../../Utils";
 
 interface IPDFViewerProps {
     url: string;
     loaded: (nw: number, nh: number) => void;
     scrollY: number;
+    parent: PDFBox;
 }
 
 @observer
@@ -33,7 +39,7 @@ export class PDFViewer extends React.Component<IPDFViewerProps> {
     render() {
         return (
             <div>
-                <Viewer pdf={this._pdf} loaded={this.props.loaded} scrollY={this.props.scrollY} />
+                <Viewer pdf={this._pdf} loaded={this.props.loaded} scrollY={this.props.scrollY} parent={this.props.parent} />
             </div>
         );
     }
@@ -43,10 +49,9 @@ interface IViewerProps {
     pdf: Opt<Pdfjs.PDFDocumentProxy>;
     loaded: (nw: number, nh: number) => void;
     scrollY: number;
+    parent: PDFBox;
 }
 
-type PDFItem = React.Component<IPageProps> | HTMLDivElement;
-
 @observer
 class Viewer extends React.Component<IViewerProps> {
     @observable.shallow private _visibleElements: JSX.Element[] = [];
@@ -56,8 +61,41 @@ class Viewer extends React.Component<IViewerProps> {
     @observable private _endIndex: number = 1;
     @observable private _loaded: boolean = false;
     @observable private _pdf: Opt<Pdfjs.PDFDocumentProxy>;
+    @observable private _renderAsSvg = true;
 
     private _pageBuffer: number = 1;
+    private _reactionDisposer?: IReactionDisposer;
+    private _mainDiv = React.createRef<HTMLDivElement>();
+
+    @computed private get thumbnailPage() { return NumCast(this.props.parent.Document.thumbnailPage, -1); }
+
+    componentDidMount() {
+        let wasSelected = this.props.parent.props.isSelected();
+        this._reactionDisposer = reaction(
+            () => [this.props.parent.props.isSelected(), this.startIndex],
+            () => {
+                if (this.startIndex > 0 && !this.props.parent.props.isTopMost && this.startIndex !== this.thumbnailPage && wasSelected && !this.props.parent.props.isSelected()) {
+                    this.saveThumbnail();
+                }
+                wasSelected = this.props.parent.props.isSelected();
+            },
+            { fireImmediately: true }
+        );
+        let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
+        this.renderPages(0, numPages - 1, true);
+    }
+
+    saveThumbnail = () => {
+        this.props.parent.props.Document.thumbnailPage = FieldValue(this.props.parent.Document.curPage, -1);
+        this._renderAsSvg = false;
+        setTimeout(() => {
+            let nwidth = FieldValue(this.props.parent.Document.nativeWidth, 0);
+            let nheight = FieldValue(this.props.parent.Document.nativeHeight, 0);
+            htmlToImage.toPng(this._mainDiv.current!, { width: nwidth, height: nheight, quality: 0.8 })
+                .then(action((dataUrl: string) => {
+                }));
+        }, 1250);
+    }
 
     @computed get scrollY(): number {
         return this.props.scrollY;
@@ -72,11 +110,6 @@ class Viewer extends React.Component<IViewerProps> {
         return Math.min(this.props.pdf ? this.props.pdf.numPages - 1 : 0, this.getIndex(this.scrollY + Math.max(...width)) + this._pageBuffer);
     }
 
-    componentDidMount = () => {
-        let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
-        this.renderPages(0, numPages - 1, true);
-    }
-
     componentDidUpdate = (prevProps: IViewerProps) => {
         if (this.scrollY !== prevProps.scrollY || this._pdf !== this.props.pdf) {
             this._pdf = this.props.pdf;
@@ -188,7 +221,7 @@ class Viewer extends React.Component<IViewerProps> {
         console.log(`END: ${this.endIndex}`)
         let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
         return (
-            <div className="viewer">
+            <div className="viewer" ref={this._mainDiv}>
                 {/* {Array.from(Array(numPages).keys()).map((i) => (
                     <Page
                         pdf={this.props.pdf}
@@ -294,20 +327,22 @@ class Page extends React.Component<IPageProps> {
     }
 
     onPointerDown = (e: React.PointerEvent) => {
+        console.log("down");
         e.stopPropagation();
     }
 
     onPointerMove = (e: React.PointerEvent) => {
+        console.log("move")
         e.stopPropagation();
     }
 
     render() {
         return (
-            <div className={this.props.name} style={{ "width": this._width, "height": this._height }}>
+            <div onPointerDown={this.onPointerDown} onPointerMove={this.onPointerMove} className={this.props.name} style={{ "width": this._width, "height": this._height }}>
                 <div className="canvasContainer">
                     <canvas ref={this.canvas} />
                 </div>
-                <div onPointerDown={this.onPointerDown} onPointerMove={this.onPointerMove} className="textlayer" ref={this.textLayer} style={{ "position": "relative", "top": `-${this._height}px`, "height": `${this._height}px` }} />
+                <div className="textlayer" ref={this.textLayer} style={{ "position": "relative", "top": `-${this._height}px`, "height": `${this._height}px` }} />
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From cffe1d2d45e241a21fb071573399200567738163 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Tue, 4 Jun 2019 15:45:33 -0400
Subject: additionsss

---
 src/client/views/nodes/PDFBox.tsx  | 10 +-----
 src/client/views/pdf/PDFViewer.tsx | 63 ++++++++++++++++++++++++++++++--------
 2 files changed, 51 insertions(+), 22 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 83f69f7f9..217d7b5e1 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -7,23 +7,15 @@ import Measure from "react-measure";
 // import { Document, Page } from "react-pdf";
 // import 'react-pdf/dist/Page/AnnotationLayer.css';
 import { RouteStore } from "../../../server/RouteStore";
-import { Utils } from '../../../Utils';
-import { DocServer } from "../../DocServer";
 import { DocComponent } from "../DocComponent";
 import { InkingControl } from "../InkingControl";
-import { SearchBox } from "../SearchBox";
-import { Annotation } from './Annotation';
 import { positionSchema } from "./DocumentView";
 import { FieldView, FieldViewProps } from './FieldView';
 import { pageSchema } from "./ImageBox";
 import "./PDFBox.scss";
-var path = require('path');
 import React = require("react");
-import { SelectionManager } from "../../util/SelectionManager";
-import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
-import { Opt, HeightSym, Doc } from "../../../new_fields/Doc";
+import { NumCast } from "../../../new_fields/Types";
 import { makeInterface } from "../../../new_fields/Schema";
-import { ImageField, PdfField } from "../../../new_fields/URLField";
 import { PDFViewer } from "../pdf/PDFViewer";
 
 /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index d510ba91c..999cb6378 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -13,6 +13,10 @@ import { PDFBox } from "../nodes/PDFBox";
 import { NumCast, FieldValue } from "../../../new_fields/Types";
 import { SearchBox } from "../SearchBox";
 import { Utils } from "../../../Utils";
+import { Id } from "../../../new_fields/FieldSymbols";
+import { DocServer } from "../../DocServer";
+import { ImageField } from "../../../new_fields/URLField";
+var path = require("path");
 
 interface IPDFViewerProps {
     url: string;
@@ -24,6 +28,7 @@ interface IPDFViewerProps {
 @observer
 export class PDFViewer extends React.Component<IPDFViewerProps> {
     @observable _pdf: Opt<Pdfjs.PDFDocumentProxy>;
+    private _mainDiv = React.createRef<HTMLDivElement>();
 
     @action
     componentDidMount() {
@@ -38,8 +43,8 @@ export class PDFViewer extends React.Component<IPDFViewerProps> {
 
     render() {
         return (
-            <div>
-                <Viewer pdf={this._pdf} loaded={this.props.loaded} scrollY={this.props.scrollY} parent={this.props.parent} />
+            <div ref={this._mainDiv}>
+                <Viewer pdf={this._pdf} loaded={this.props.loaded} scrollY={this.props.scrollY} parent={this.props.parent} mainCont={this._mainDiv} />
             </div>
         );
     }
@@ -50,6 +55,7 @@ interface IViewerProps {
     loaded: (nw: number, nh: number) => void;
     scrollY: number;
     parent: PDFBox;
+    mainCont: React.RefObject<HTMLDivElement>;
 }
 
 @observer
@@ -61,20 +67,19 @@ class Viewer extends React.Component<IViewerProps> {
     @observable private _endIndex: number = 1;
     @observable private _loaded: boolean = false;
     @observable private _pdf: Opt<Pdfjs.PDFDocumentProxy>;
-    @observable private _renderAsSvg = true;
+    @observable private _renderAsSvg = false;
 
     private _pageBuffer: number = 1;
     private _reactionDisposer?: IReactionDisposer;
-    private _mainDiv = React.createRef<HTMLDivElement>();
 
-    @computed private get thumbnailPage() { return NumCast(this.props.parent.Document.thumbnailPage, -1); }
+    @computed private get thumbnailY() { return NumCast(this.props.parent.Document.thumbnailY, -1); }
 
-    componentDidMount() {
+    componentDidMount = () => {
         let wasSelected = this.props.parent.props.isSelected();
         this._reactionDisposer = reaction(
             () => [this.props.parent.props.isSelected(), this.startIndex],
             () => {
-                if (this.startIndex > 0 && !this.props.parent.props.isTopMost && this.startIndex !== this.thumbnailPage && wasSelected && !this.props.parent.props.isSelected()) {
+                if (this.startIndex >= 0 && !this.props.parent.props.isTopMost && this.scrollY !== this.thumbnailY && wasSelected && !this.props.parent.props.isSelected()) {
                     this.saveThumbnail();
                 }
                 wasSelected = this.props.parent.props.isSelected();
@@ -86,14 +91,23 @@ class Viewer extends React.Component<IViewerProps> {
     }
 
     saveThumbnail = () => {
-        this.props.parent.props.Document.thumbnailPage = FieldValue(this.props.parent.Document.curPage, -1);
+        this.props.parent.props.Document.thumbnailY = FieldValue(this.scrollY, 0);
         this._renderAsSvg = false;
         setTimeout(() => {
             let nwidth = FieldValue(this.props.parent.Document.nativeWidth, 0);
-            let nheight = FieldValue(this.props.parent.Document.nativeHeight, 0);
-            htmlToImage.toPng(this._mainDiv.current!, { width: nwidth, height: nheight, quality: 0.8 })
+            htmlToImage.toPng(this.props.mainCont.current!, { width: nwidth, height: nwidth, quality: 0.8, })
                 .then(action((dataUrl: string) => {
-                }));
+                    SearchBox.convertDataUri(dataUrl, `icon${this.props.parent.Document[Id]}_${this.startIndex}`).then((returnedFilename) => {
+                        if (returnedFilename) {
+                            let url = DocServer.prepend(returnedFilename);
+                            this.props.parent.props.Document.thumbnail = new ImageField(new URL(url));
+                        }
+                        runInAction(() => this._renderAsSvg = true);
+                    });
+                }))
+                .catch(function (error: any) {
+                    console.error("Oops, something went wrong!", error);
+                });
         }, 1250);
     }
 
@@ -101,6 +115,29 @@ class Viewer extends React.Component<IViewerProps> {
         return this.props.scrollY;
     }
 
+    @computed get imageProxyRenderer() {
+        let thumbField = this.props.parent.props.Document.thumbnail;
+        if (thumbField && this._renderAsSvg && NumCast(this.props.parent.props.Document.startY, 0) === this.scrollY) {
+            let pw = typeof this.props.parent.props.PanelWidth === "function" ? this.props.parent.props.PanelWidth() : typeof this.props.parent.props.PanelWidth === "number" ? (this.props.parent.props.PanelWidth as any) as number : 50;
+            let path = thumbField instanceof ImageField ? thumbField.url.href : "http://cs.brown.edu/people/bcz/prairie.jpg";
+            let field = thumbField;
+            if (field instanceof ImageField) path = this.choosePath(field.url);
+            return <img className="pdfBox-thumbnail" key={path} src={path} onError={this.onError} />;
+        }
+    }
+
+    @action onError = () => {
+    }
+
+    choosePath(url: URL) {
+        if (url.protocol === "data" || url.href.indexOf(window.location.origin) === -1) {
+            return url.href;
+        }
+        let ext = path.extname(url.href);
+        ///TODO: Not done lol - syip2
+        return url.href;
+    }
+
     @computed get startIndex(): number {
         return Math.max(0, this.getIndex(this.scrollY) - this._pageBuffer);
     }
@@ -221,7 +258,7 @@ class Viewer extends React.Component<IViewerProps> {
         console.log(`END: ${this.endIndex}`)
         let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
         return (
-            <div className="viewer" ref={this._mainDiv}>
+            <div className="viewer">
                 {/* {Array.from(Array(numPages).keys()).map((i) => (
                     <Page
                         pdf={this.props.pdf}
@@ -233,7 +270,7 @@ class Viewer extends React.Component<IViewerProps> {
                         {...this.props}
                     />
                 ))} */}
-                {this._visibleElements}
+                {this._renderAsSvg ? this.imageProxyRenderer : this._visibleElements}
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From 1708f2b2a19d3d4efc081bcc4ee82b4d5149da08 Mon Sep 17 00:00:00 2001
From: loudonclear <loudon_cohen@brown.edu>
Date: Tue, 4 Jun 2019 19:08:01 -0400
Subject: YES

---
 package.json                       |  1 +
 src/client/views/nodes/PDFBox.tsx  |  4 +--
 src/client/views/pdf/PDFViewer.tsx | 50 ++++++++++++++++++--------------
 src/server/Search.ts               |  3 +-
 src/server/index.ts                | 58 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 92 insertions(+), 24 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/package.json b/package.json
index 58b2cb049..648ebdbf9 100644
--- a/package.json
+++ b/package.json
@@ -101,6 +101,7 @@
         "bluebird": "^3.5.3",
         "body-parser": "^1.18.3",
         "bootstrap": "^4.3.1",
+        "canvas": "^2.5.0",
         "class-transformer": "^0.2.0",
         "connect-flash": "^0.1.1",
         "connect-mongo": "^2.0.3",
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 217d7b5e1..7e335e75e 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -14,7 +14,7 @@ import { FieldView, FieldViewProps } from './FieldView';
 import { pageSchema } from "./ImageBox";
 import "./PDFBox.scss";
 import React = require("react");
-import { NumCast } from "../../../new_fields/Types";
+import { NumCast, StrCast } from "../../../new_fields/Types";
 import { makeInterface } from "../../../new_fields/Schema";
 import { PDFViewer } from "../pdf/PDFViewer";
 
@@ -77,7 +77,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
 
     render() {
         trace();
-        const pdfUrl = window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf";
+        const pdfUrl = window.origin + RouteStore.corsProxy + "/" + StrCast(this.props.Document.title);
         let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
         return (
             <div onScroll={this.onScroll} style={{ overflow: "scroll", height: `${NumCast(this.props.Document.nativeWidth ? this.props.Document.nativeWidth : 300)}px` }} onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 999cb6378..374e598a4 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -91,24 +91,24 @@ class Viewer extends React.Component<IViewerProps> {
     }
 
     saveThumbnail = () => {
-        this.props.parent.props.Document.thumbnailY = FieldValue(this.scrollY, 0);
-        this._renderAsSvg = false;
-        setTimeout(() => {
-            let nwidth = FieldValue(this.props.parent.Document.nativeWidth, 0);
-            htmlToImage.toPng(this.props.mainCont.current!, { width: nwidth, height: nwidth, quality: 0.8, })
-                .then(action((dataUrl: string) => {
-                    SearchBox.convertDataUri(dataUrl, `icon${this.props.parent.Document[Id]}_${this.startIndex}`).then((returnedFilename) => {
-                        if (returnedFilename) {
-                            let url = DocServer.prepend(returnedFilename);
-                            this.props.parent.props.Document.thumbnail = new ImageField(new URL(url));
-                        }
-                        runInAction(() => this._renderAsSvg = true);
-                    });
-                }))
-                .catch(function (error: any) {
-                    console.error("Oops, something went wrong!", error);
-                });
-        }, 1250);
+        // this.props.parent.props.Document.thumbnailY = FieldValue(this.scrollY, 0);
+        // this._renderAsSvg = false;
+        // setTimeout(() => {
+        //     let nwidth = FieldValue(this.props.parent.Document.nativeWidth, 0);
+        //     htmlToImage.toPng(this.props.mainCont.current!, { width: nwidth, height: nwidth, quality: 0.8, })
+        //         .then(action((dataUrl: string) => {
+        //             SearchBox.convertDataUri(dataUrl, `icon${this.props.parent.Document[Id]}_${this.startIndex}`).then((returnedFilename) => {
+        //                 if (returnedFilename) {
+        //                     let url = DocServer.prepend(returnedFilename);
+        //                     this.props.parent.props.Document.thumbnail = new ImageField(new URL(url));
+        //                 }
+        //                 runInAction(() => this._renderAsSvg = true);
+        //             });
+        //         }))
+        //         .catch(function (error: any) {
+        //             console.error("Oops, something went wrong!", error);
+        //         });
+        // }, 1250);
     }
 
     @computed get scrollY(): number {
@@ -338,15 +338,23 @@ class Page extends React.Component<IPageProps> {
         let scale = 1;
         let viewport = page.getViewport(scale);
         let canvas = this.canvas.current;
-        if (canvas) {
-            let context = canvas.getContext("2d");
+        let tempCanvas = document.createElement('canvas')
+        if (tempCanvas && canvas) {
+            let ctx = canvas.getContext("2d");
+            let context = tempCanvas.getContext("2d");
+            tempCanvas.width = viewport.width;
             canvas.width = viewport.width;
             this._width = viewport.width;
+            tempCanvas.height = viewport.height;
             canvas.height = viewport.height;
             this._height = viewport.height;
             this.props.pageLoaded(this._currPage, viewport);
             if (context) {
-                page.render({ canvasContext: context, viewport: viewport });
+                page.render({ canvasContext: context, viewport: viewport }).promise.then(() => {
+                    if (context && ctx) {
+                        ctx.putImageData(context.getImageData(0, 0, tempCanvas.width, tempCanvas.height), 0, 0);
+                    }
+                });
                 page.getTextContent().then((res: Pdfjs.TextContent) => {
                     //@ts-ignore
                     let textLayer = Pdfjs.renderTextLayer({
diff --git a/src/server/Search.ts b/src/server/Search.ts
index 5ca5578a7..fd6ef36a6 100644
--- a/src/server/Search.ts
+++ b/src/server/Search.ts
@@ -7,6 +7,7 @@ export class Search {
     private url = 'http://localhost:8983/solr/';
 
     public async updateDocument(document: any) {
+        return;
         try {
             const res = await rp.post(this.url + "dash/update", {
                 headers: { 'content-type': 'application/json' },
@@ -14,7 +15,7 @@ export class Search {
             });
             return res;
         } catch (e) {
-            console.warn("Search error: " + e + document);
+            // console.warn("Search error: " + e + document);
         }
     }
 
diff --git a/src/server/index.ts b/src/server/index.ts
index fd66c90b4..9f95df4e0 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -7,6 +7,7 @@ import * as expressValidator from 'express-validator';
 import * as formidable from 'formidable';
 import * as fs from 'fs';
 import * as sharp from 'sharp';
+import * as Pdfjs from 'pdfjs-dist';
 const imageDataUri = require('image-data-uri');
 import * as mobileDetect from 'mobile-detect';
 import { ObservableMap } from 'mobx';
@@ -28,6 +29,7 @@ import { MessageStore, Transferable, Types, Diff } from "./Message";
 import { RouteStore } from './RouteStore';
 const app = express();
 const config = require('../../webpack.config');
+import { createCanvas, loadImage, Canvas } from "canvas";
 const compiler = webpack(config);
 const port = 1050; // default port to listen
 const serverPort = 4321;
@@ -168,7 +170,31 @@ addSecureRoute(
     RouteStore.getCurrUser
 );
 
+class NodeCanvasFactory {
+    create = (width: number, height: number) => {
+        var canvas = createCanvas(width, height);
+        var context = canvas.getContext('2d');
+        return {
+            canvas: canvas,
+            context: context,
+        };
+    }
+
+    reset = (canvasAndContext: any, width: number, height: number) => {
+        canvasAndContext.canvas.width = width;
+        canvasAndContext.canvas.height = height;
+    }
+
+    destroy = (canvasAndContext: any) => {
+        canvasAndContext.canvas.width = 0;
+        canvasAndContext.canvas.height = 0;
+        canvasAndContext.canvas = null;
+        canvasAndContext.context = null;
+    }
+}
+
 const pngTypes = [".png", ".PNG"];
+const pdfTypes = [".pdf", ".PDF"];
 const jpgTypes = [".jpg", ".JPG", ".jpeg", ".JPEG"];
 const uploadDir = __dirname + "/public/files/";
 // SETTERS
@@ -203,6 +229,38 @@ app.post(
                     });
                     isImage = true;
                 }
+                else if (pdfTypes.includes(ext)) {
+                    Pdfjs.getDocument(uploadDir + file).promise
+                        .then((pdf: Pdfjs.PDFDocumentProxy) => {
+                            let numPages = pdf.numPages;
+                            let factory = new NodeCanvasFactory();
+                            for (let pageNum = 0; pageNum < numPages; pageNum++) {
+                                console.log(pageNum);
+                                pdf.getPage(pageNum + 1).then((page: Pdfjs.PDFPageProxy) => {
+                                    console.log("reading " + pageNum);
+                                    let viewport = page.getViewport(1);
+                                    let canvasAndContext = factory.create(viewport.width, viewport.height);
+                                    let renderContext = {
+                                        canvasContext: canvasAndContext.context,
+                                        viewport: viewport,
+                                        canvasFactory: factory
+                                    }
+                                    console.log("read " + pageNum);
+
+                                    page.render(renderContext).promise
+                                        .then(() => {
+                                            console.log("saving " + pageNum);
+                                            let stream = canvasAndContext.canvas.createPNGStream();
+                                            let out = fs.createWriteStream(uploadDir + file.substring(0, file.length - ext.length) + `-${pageNum + 1}.PNG`);
+                                            stream.pipe(out);
+                                            out.on("finish", () => console.log(`Success! Saved to ${uploadDir + file.substring(0, file.length - ext.length) + `-${pageNum + 1}.PNG`}`));
+                                        }, (reason: string) => {
+                                            console.error(reason + ` ${pageNum}`);
+                                        });
+                                });
+                            }
+                        });
+                }
                 if (isImage) {
                     resizers.forEach(resizer => {
                         fs.createReadStream(uploadDir + file).pipe(resizer.resizer).pipe(fs.createWriteStream(uploadDir + file.substring(0, file.length - ext.length) + resizer.suffix + ext));
-- 
cgit v1.2.3-70-g09d2


From f774314ddae00f2d2ceda886f0b6699f01f80718 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Wed, 5 Jun 2019 16:33:57 -0400
Subject: better virtualization and thumbnails git status

---
 src/client/views/nodes/PDFBox.tsx   |  12 ++-
 src/client/views/pdf/PDFViewer.scss |   4 +
 src/client/views/pdf/PDFViewer.tsx  | 158 ++++++++++++++++--------------------
 3 files changed, 82 insertions(+), 92 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 7e335e75e..6d604ec75 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -14,9 +14,11 @@ import { FieldView, FieldViewProps } from './FieldView';
 import { pageSchema } from "./ImageBox";
 import "./PDFBox.scss";
 import React = require("react");
-import { NumCast, StrCast } from "../../../new_fields/Types";
+import { NumCast, StrCast, Cast } from "../../../new_fields/Types";
 import { makeInterface } from "../../../new_fields/Schema";
 import { PDFViewer } from "../pdf/PDFViewer";
+import { PdfField } from "../../../new_fields/URLField";
+import { HeightSym, WidthSym } from "../../../new_fields/Doc";
 
 /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx
  * This method renders PDF and puts all kinds of functionalities such as annotation, highlighting, 
@@ -65,6 +67,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
             let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
             doc.nativeWidth = nw;
             doc.nativeHeight = nh;
+            doc.height = nh * (doc[WidthSym]() / nw);
         }
     }
 
@@ -77,11 +80,12 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
 
     render() {
         trace();
-        const pdfUrl = window.origin + RouteStore.corsProxy + "/" + StrCast(this.props.Document.title);
+        const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"));
+        console.log(pdfUrl);
         let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
         return (
-            <div onScroll={this.onScroll} style={{ overflow: "scroll", height: `${NumCast(this.props.Document.nativeWidth ? this.props.Document.nativeWidth : 300)}px` }} onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}>
-                <PDFViewer url={pdfUrl} loaded={this.loaded} scrollY={this._scrollY} parent={this} />
+            <div onScroll={this.onScroll} style={{ overflow: "scroll", height: `${NumCast(this.props.Document.nativeHeight ? this.props.Document.nativeHeight : 300)}px` }} onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}>
+                <PDFViewer url={pdfUrl.url.href} loaded={this.loaded} scrollY={this._scrollY} parent={this} />
             </div>
         );
     }
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 9d41a1bb0..42bf023d6 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -24,4 +24,8 @@
     user-select: auto;
 }
 
+.pdfBox-cont-interactive {
+    overflow-x: visible;
+}
+
 .viewer {}
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 374e598a4..84d9f1ad0 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -4,18 +4,18 @@ import { observable, action, runInAction, computed, IReactionDisposer, reaction
 import { RouteStore } from "../../../server/RouteStore";
 import * as Pdfjs from "pdfjs-dist";
 import * as htmlToImage from "html-to-image";
-import { Opt } from "../../../new_fields/Doc";
+import { Opt, WidthSym } from "../../../new_fields/Doc";
 import "./PDFViewer.scss";
 import "pdfjs-dist/web/pdf_viewer.css";
 import { number } from "prop-types";
 import { JSXElement } from "babel-types";
 import { PDFBox } from "../nodes/PDFBox";
-import { NumCast, FieldValue } from "../../../new_fields/Types";
+import { NumCast, FieldValue, Cast } from "../../../new_fields/Types";
 import { SearchBox } from "../SearchBox";
 import { Utils } from "../../../Utils";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { DocServer } from "../../DocServer";
-import { ImageField } from "../../../new_fields/URLField";
+import { ImageField, PdfField } from "../../../new_fields/URLField";
 var path = require("path");
 
 interface IPDFViewerProps {
@@ -44,7 +44,7 @@ export class PDFViewer extends React.Component<IPDFViewerProps> {
     render() {
         return (
             <div ref={this._mainDiv}>
-                <Viewer pdf={this._pdf} loaded={this.props.loaded} scrollY={this.props.scrollY} parent={this.props.parent} mainCont={this._mainDiv} />
+                <Viewer pdf={this._pdf} loaded={this.props.loaded} scrollY={this.props.scrollY} parent={this.props.parent} mainCont={this._mainDiv} url={this.props.url} />
             </div>
         );
     }
@@ -56,6 +56,7 @@ interface IViewerProps {
     scrollY: number;
     parent: PDFBox;
     mainCont: React.RefObject<HTMLDivElement>;
+    url: string;
 }
 
 @observer
@@ -71,71 +72,63 @@ class Viewer extends React.Component<IViewerProps> {
 
     private _pageBuffer: number = 1;
     private _reactionDisposer?: IReactionDisposer;
-
-    @computed private get thumbnailY() { return NumCast(this.props.parent.Document.thumbnailY, -1); }
+    private _widthReactionDisposer?: IReactionDisposer;
+    private _width: number = 0;
 
     componentDidMount = () => {
         let wasSelected = this.props.parent.props.isSelected();
         this._reactionDisposer = reaction(
             () => [this.props.parent.props.isSelected(), this.startIndex],
             () => {
-                if (this.startIndex >= 0 && !this.props.parent.props.isTopMost && this.scrollY !== this.thumbnailY && wasSelected && !this.props.parent.props.isSelected()) {
+                if (wasSelected && !this.props.parent.props.isSelected()) {
                     this.saveThumbnail();
                 }
+                else if (!wasSelected && this.props.parent.props.isSelected()) {
+                    this.renderPages(this.startIndex, this.endIndex, true);
+                }
                 wasSelected = this.props.parent.props.isSelected();
             },
             { fireImmediately: true }
         );
-        let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
-        this.renderPages(0, numPages - 1, true);
-    }
 
-    saveThumbnail = () => {
-        // this.props.parent.props.Document.thumbnailY = FieldValue(this.scrollY, 0);
-        // this._renderAsSvg = false;
-        // setTimeout(() => {
-        //     let nwidth = FieldValue(this.props.parent.Document.nativeWidth, 0);
-        //     htmlToImage.toPng(this.props.mainCont.current!, { width: nwidth, height: nwidth, quality: 0.8, })
-        //         .then(action((dataUrl: string) => {
-        //             SearchBox.convertDataUri(dataUrl, `icon${this.props.parent.Document[Id]}_${this.startIndex}`).then((returnedFilename) => {
-        //                 if (returnedFilename) {
-        //                     let url = DocServer.prepend(returnedFilename);
-        //                     this.props.parent.props.Document.thumbnail = new ImageField(new URL(url));
-        //                 }
-        //                 runInAction(() => this._renderAsSvg = true);
-        //             });
-        //         }))
-        //         .catch(function (error: any) {
-        //             console.error("Oops, something went wrong!", error);
-        //         });
-        // }, 1250);
-    }
+        // this._widthReactionDisposer = reaction(
+        //     () => [this._docWidth],
+        //     () => {
+        //         if (this._width !== this._docWidth) {
+        //             this._width = this._docWidth;
+        //             this.renderPages(this.startIndex, this.endIndex, true);
+        //             console.log(this._width);
+        //         }
+        //     },
+        //     { fireImmediately: true }
+        // )
 
-    @computed get scrollY(): number {
-        return this.props.scrollY;
+        let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
+        this.renderPages(0, numPages - 1, true);
     }
 
-    @computed get imageProxyRenderer() {
-        let thumbField = this.props.parent.props.Document.thumbnail;
-        if (thumbField && this._renderAsSvg && NumCast(this.props.parent.props.Document.startY, 0) === this.scrollY) {
-            let pw = typeof this.props.parent.props.PanelWidth === "function" ? this.props.parent.props.PanelWidth() : typeof this.props.parent.props.PanelWidth === "number" ? (this.props.parent.props.PanelWidth as any) as number : 50;
-            let path = thumbField instanceof ImageField ? thumbField.url.href : "http://cs.brown.edu/people/bcz/prairie.jpg";
-            let field = thumbField;
-            if (field instanceof ImageField) path = this.choosePath(field.url);
-            return <img className="pdfBox-thumbnail" key={path} src={path} onError={this.onError} />;
+    componentWillUnmount = () => {
+        if (this._reactionDisposer) {
+            this._reactionDisposer();
         }
     }
 
-    @action onError = () => {
+    @action
+    saveThumbnail = () => {
+        const address: string = this.props.url;
+        console.log(address);
+        for (let i = 0; i < this._visibleElements.length; i++) {
+            if (this._isPage[i]) {
+                let thisAddress = `${address.substring(0, address.length - ".pdf".length)}-${i + 1}.PNG`;
+                let nWidth = this._pageSizes[i].width;
+                let nHeight = this._pageSizes[i].height;
+                this._visibleElements[i] = <img key={thisAddress} style={{ width: `${nWidth}px`, height: `${nHeight}px` }} src={thisAddress} />;
+            }
+        }
     }
 
-    choosePath(url: URL) {
-        if (url.protocol === "data" || url.href.indexOf(window.location.origin) === -1) {
-            return url.href;
-        }
-        let ext = path.extname(url.href);
-        ///TODO: Not done lol - syip2
-        return url.href;
+    @computed get scrollY(): number {
+        return this.props.scrollY;
     }
 
     @computed get startIndex(): number {
@@ -157,6 +150,9 @@ class Viewer extends React.Component<IViewerProps> {
     @action
     renderPages = (startIndex: number, endIndex: number, forceRender: boolean = false) => {
         let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
+        if (!this.props.pdf) {
+            return;
+        }
 
         if (this._visibleElements.length !== numPages) {
             let divs = Array.from(Array(numPages).keys()).map(i => (
@@ -178,21 +174,19 @@ class Viewer extends React.Component<IViewerProps> {
             return;
         }
 
-        for (let i = startIndex; i <= endIndex; i++) {
-            if (this._isPage[i] && forceRender) {
-                this._visibleElements[i] = (
-                    <Page
-                        pdf={this.props.pdf}
-                        page={i}
-                        numPages={numPages}
-                        key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
-                        name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
-                        pageLoaded={this.pageLoaded}
-                        {...this.props} />
-                );
-                this._isPage[i] = true;
+        for (let i = 0; i < numPages; i++) {
+            if (i < startIndex || i > endIndex) {
+                if (this._isPage[i]) {
+                    this._visibleElements[i] = (
+                        <div key={`pdfviewer-placeholder-${i}`} className="pdfviewer-placeholder" style={{ width: this._pageSizes[i] ? this._pageSizes[i].width : 0, height: this._pageSizes[i] ? this._pageSizes[i].height : 0 }} />
+                    );
+                }
+                this._isPage[i] = false;
             }
-            else if (!this._isPage[i]) {
+        }
+
+        for (let i = startIndex; i <= endIndex; i++) {
+            if (!this._isPage[i] || forceRender) {
                 this._visibleElements[i] = (
                     <Page
                         pdf={this.props.pdf}
@@ -207,25 +201,18 @@ class Viewer extends React.Component<IViewerProps> {
             }
         }
 
-        for (let i = 0; i < numPages; i++) {
-            if (i < startIndex || i > endIndex) {
-                if (this._isPage[i]) {
-                    this._visibleElements[i] = (
-                        <div key={`pdfviewer-placeholder-${i}`} className="pdfviewer-placeholder" style={{ width: this._pageSizes[i] ? this._pageSizes[i].width : 0, height: this._pageSizes[i] ? this._pageSizes[i].height : 0 }} />
-                    );
-                    this._isPage[i] = false;
-                }
-            }
-        }
+        this._startIndex = startIndex;
+        this._endIndex = endIndex;
 
         return;
     }
 
     getIndex = (vOffset: number) => {
         if (this._loaded) {
+            let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
             let index = 0;
             let currOffset = vOffset;
-            while (currOffset - this._pageSizes[index].height > 0) {
+            while (index < numPages && currOffset - this._pageSizes[index].height > 0) {
                 currOffset -= this._pageSizes[index].height;
                 index++;
             }
@@ -236,6 +223,9 @@ class Viewer extends React.Component<IViewerProps> {
 
     @action
     pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => {
+        if (this._loaded) {
+            return;
+        }
         let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
         this.props.loaded(page.width, page.height);
         if (index > this._pageSizes.length) {
@@ -270,7 +260,7 @@ class Viewer extends React.Component<IViewerProps> {
                         {...this.props}
                     />
                 ))} */}
-                {this._renderAsSvg ? this.imageProxyRenderer : this._visibleElements}
+                {this._visibleElements}
             </div>
         );
     }
@@ -338,32 +328,24 @@ class Page extends React.Component<IPageProps> {
         let scale = 1;
         let viewport = page.getViewport(scale);
         let canvas = this.canvas.current;
-        let tempCanvas = document.createElement('canvas')
-        if (tempCanvas && canvas) {
+        let textLayer = this.textLayer.current;
+        if (canvas && textLayer) {
             let ctx = canvas.getContext("2d");
-            let context = tempCanvas.getContext("2d");
-            tempCanvas.width = viewport.width;
             canvas.width = viewport.width;
             this._width = viewport.width;
-            tempCanvas.height = viewport.height;
             canvas.height = viewport.height;
             this._height = viewport.height;
             this.props.pageLoaded(this._currPage, viewport);
-            if (context) {
-                page.render({ canvasContext: context, viewport: viewport }).promise.then(() => {
-                    if (context && ctx) {
-                        ctx.putImageData(context.getImageData(0, 0, tempCanvas.width, tempCanvas.height), 0, 0);
-                    }
-                });
+            if (ctx) {
+                page.render({ canvasContext: ctx, viewport: viewport })
                 page.getTextContent().then((res: Pdfjs.TextContent) => {
                     //@ts-ignore
-                    let textLayer = Pdfjs.renderTextLayer({
+                    Pdfjs.renderTextLayer({
                         textContent: res,
-                        container: this.textLayer.current,
+                        container: textLayer,
                         viewport: viewport
                     });
                     // textLayer._render();
-                    this._state = "rendered";
                 });
 
                 this._page = page;
-- 
cgit v1.2.3-70-g09d2


From c4180929255db3bc7a75938e07afd86522f0a15e Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Wed, 5 Jun 2019 18:09:39 -0400
Subject: bug fixess

---
 src/client/views/nodes/PDFBox.tsx   | 2 +-
 src/client/views/pdf/PDFViewer.scss | 4 ----
 src/client/views/pdf/PDFViewer.tsx  | 2 +-
 3 files changed, 2 insertions(+), 6 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 6d604ec75..4ee3ae098 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -84,7 +84,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
         console.log(pdfUrl);
         let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
         return (
-            <div onScroll={this.onScroll} style={{ overflow: "scroll", height: `${NumCast(this.props.Document.nativeHeight ? this.props.Document.nativeHeight : 300)}px` }} onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}>
+            <div onScroll={this.onScroll} style={{ overflowY: "scroll", overflowX: "hidden", height: `${NumCast(this.props.Document.nativeHeight ? this.props.Document.nativeHeight : 300)}px` }} onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}>
                 <PDFViewer url={pdfUrl.url.href} loaded={this.loaded} scrollY={this._scrollY} parent={this} />
             </div>
         );
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 42bf023d6..9d41a1bb0 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -24,8 +24,4 @@
     user-select: auto;
 }
 
-.pdfBox-cont-interactive {
-    overflow-x: visible;
-}
-
 .viewer {}
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 84d9f1ad0..95f31bb89 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -104,7 +104,7 @@ class Viewer extends React.Component<IViewerProps> {
         // )
 
         let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
-        this.renderPages(0, numPages - 1, true);
+        setTimeout(() => this.renderPages(this.startIndex, this.endIndex, true), 1000);
     }
 
     componentWillUnmount = () => {
-- 
cgit v1.2.3-70-g09d2


From a37629f55ef279167a5ef2fec88dc548f36f4938 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Thu, 6 Jun 2019 14:13:55 -0400
Subject: commentss

---
 src/client/views/nodes/PDFBox.tsx  |  38 +---------
 src/client/views/pdf/PDFViewer.tsx | 147 ++++++++++++++++++-------------------
 2 files changed, 77 insertions(+), 108 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 4ee3ae098..de75c67cf 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -20,29 +20,6 @@ import { PDFViewer } from "../pdf/PDFViewer";
 import { PdfField } from "../../../new_fields/URLField";
 import { HeightSym, WidthSym } from "../../../new_fields/Doc";
 
-/** ALSO LOOK AT: Annotation.tsx, Sticky.tsx
- * This method renders PDF and puts all kinds of functionalities such as annotation, highlighting, 
- * area selection (I call it stickies), embedded ink node for directly annotating using a pen or 
- * mouse, and pagination. 
- *
- * 
- * HOW TO USE: 
- * AREA selection: 
- *          1) Click on Area button. 
- *          2) click on any part of the PDF, and drag to get desired sized area shape
- *          3) You can write on the area (hence the reason why it's called sticky)
- *          4) to make another area, you need to click on area button AGAIN. 
- * 
- * HIGHLIGHT: (Buggy. No multiline/multidiv text highlighting for now...)
- *          1) just click and drag on a text
- *          2) click highlight
- *          3) for annotation, just pull your cursor over to that text
- *          4) another method: click on highlight first and then drag on your desired text
- *          5) To make another highlight, you need to reclick on the button 
- * 
- * written by: Andrew Kim 
- */
-
 type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
 const PdfDocument = makeInterface(positionSchema, pageSchema);
 
@@ -53,15 +30,6 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     @observable private _alt = false;
     @observable private _scrollY: number = 0;
 
-    getHeight = (): number => {
-        if (this.props.Document) {
-            let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
-            console.log(doc);
-            return NumCast(doc.height);
-        }
-        return 0;
-    }
-
     loaded = (nw: number, nh: number) => {
         if (this.props.Document) {
             let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
@@ -80,11 +48,13 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
 
     render() {
         trace();
+        // uses mozilla pdf as default
         const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"));
-        console.log(pdfUrl);
         let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
         return (
-            <div onScroll={this.onScroll} style={{ overflowY: "scroll", overflowX: "hidden", height: `${NumCast(this.props.Document.nativeHeight ? this.props.Document.nativeHeight : 300)}px` }} onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}>
+            <div onScroll={this.onScroll}
+                style={{ overflowY: "scroll", overflowX: "hidden", height: `${NumCast(this.props.Document.nativeHeight ? this.props.Document.nativeHeight : 300)}px` }}
+                onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}>
                 <PDFViewer url={pdfUrl.url.href} loaded={this.loaded} scrollY={this._scrollY} parent={this} />
             </div>
         );
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index a76527618..d5a0a7aa1 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,22 +1,11 @@
 import { observer } from "mobx-react";
 import React = require("react");
 import { observable, action, runInAction, computed, IReactionDisposer, reaction } from "mobx";
-import { RouteStore } from "../../../server/RouteStore";
 import * as Pdfjs from "pdfjs-dist";
-import * as htmlToImage from "html-to-image";
-import { Opt, WidthSym } from "../../../new_fields/Doc";
+import { Opt } from "../../../new_fields/Doc";
 import "./PDFViewer.scss";
 import "pdfjs-dist/web/pdf_viewer.css";
-import { number } from "prop-types";
-import { JSXElement } from "babel-types";
 import { PDFBox } from "../nodes/PDFBox";
-import { NumCast, FieldValue, Cast } from "../../../new_fields/Types";
-import { SearchBox } from "../SearchBox";
-import { Utils } from "../../../Utils";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { DocServer } from "../../DocServer";
-import { ImageField, PdfField } from "../../../new_fields/URLField";
-var path = require("path");
 
 interface IPDFViewerProps {
     url: string;
@@ -25,6 +14,9 @@ interface IPDFViewerProps {
     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>;
@@ -32,7 +24,6 @@ export class PDFViewer extends React.Component<IPDFViewerProps> {
 
     @action
     componentDidMount() {
-        // const pdfUrl = window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf";
         const pdfUrl = this.props.url;
         let promise = Pdfjs.getDocument(pdfUrl).promise;
 
@@ -59,30 +50,35 @@ interface IViewerProps {
     url: string;
 }
 
+/**
+ * Handles rendering and virtualization of the pdf
+ */
 @observer
 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: boolean[] = [];
     @observable private _pageSizes: { width: number, height: number }[] = [];
     @observable private _startIndex: number = 0;
     @observable private _endIndex: number = 1;
     @observable private _loaded: boolean = false;
     @observable private _pdf: Opt<Pdfjs.PDFDocumentProxy>;
-    @observable private _renderAsSvg = false;
 
     private _pageBuffer: number = 1;
     private _reactionDisposer?: IReactionDisposer;
-    private _widthReactionDisposer?: IReactionDisposer;
-    private _width: number = 0;
 
     componentDidMount = () => {
         let wasSelected = this.props.parent.props.isSelected();
+        // reaction for when document gets (de)selected
         this._reactionDisposer = reaction(
             () => [this.props.parent.props.isSelected(), this.startIndex],
             () => {
+                // if deselected, render images in place of pdf
                 if (wasSelected && !this.props.parent.props.isSelected()) {
                     this.saveThumbnail();
                 }
+                // if selected, render pdf
                 else if (!wasSelected && this.props.parent.props.isSelected()) {
                     this.renderPages(this.startIndex, this.endIndex, true);
                 }
@@ -91,19 +87,7 @@ class Viewer extends React.Component<IViewerProps> {
             { fireImmediately: true }
         );
 
-        // this._widthReactionDisposer = reaction(
-        //     () => [this._docWidth],
-        //     () => {
-        //         if (this._width !== this._docWidth) {
-        //             this._width = this._docWidth;
-        //             this.renderPages(this.startIndex, this.endIndex, true);
-        //             console.log(this._width);
-        //         }
-        //     },
-        //     { fireImmediately: true }
-        // )
-
-        let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
+        // On load, render pdf
         setTimeout(() => this.renderPages(this.startIndex, this.endIndex, true), 1000);
     }
 
@@ -115,13 +99,15 @@ class Viewer extends React.Component<IViewerProps> {
 
     @action
     saveThumbnail = () => {
+        // file address of the pdf
         const address: string = this.props.url;
-        console.log(address);
         for (let i = 0; i < this._visibleElements.length; i++) {
             if (this._isPage[i]) {
+                // change the address to be the file address of the PNG version of each page
                 let thisAddress = `${address.substring(0, address.length - ".pdf".length)}-${i + 1}.PNG`;
                 let nWidth = this._pageSizes[i].width;
                 let nHeight = this._pageSizes[i].height;
+                // replace page with image
                 this._visibleElements[i] = <img key={thisAddress} style={{ width: `${nWidth}px`, height: `${nHeight}px` }} src={thisAddress} />;
             }
         }
@@ -143,10 +129,16 @@ class Viewer extends React.Component<IViewerProps> {
     componentDidUpdate = (prevProps: IViewerProps) => {
         if (this.scrollY !== prevProps.scrollY || this._pdf !== this.props.pdf) {
             this._pdf = this.props.pdf;
+            // render pages if the scorll position changes
             this.renderPages(this.startIndex, this.endIndex);
         }
     }
 
+    /**
+     * @param startIndex: where to start rendering pages
+     * @param endIndex: where to end rendering pages
+     * @param forceRender: (optional), force pdfs to re-render, even if the page already exists
+     */
     @action
     renderPages = (startIndex: number, endIndex: number, forceRender: boolean = false) => {
         let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
@@ -154,6 +146,7 @@ class Viewer extends React.Component<IViewerProps> {
             return;
         }
 
+        // this is only for an initial render to get all of the pages rendered
         if (this._visibleElements.length !== numPages) {
             let divs = Array.from(Array(numPages).keys()).map(i => (
                 <Page
@@ -170,10 +163,12 @@ class Viewer extends React.Component<IViewerProps> {
             this._isPage.push(...arr);
         }
 
+        // if nothing changed, return
         if (startIndex === this._startIndex && endIndex === this._endIndex && !forceRender) {
             return;
         }
 
+        // unrender pages outside of the pdf by replacing them with empty stand-in divs
         for (let i = 0; i < numPages; i++) {
             if (i < startIndex || i > endIndex) {
                 if (this._isPage[i]) {
@@ -185,6 +180,7 @@ class Viewer extends React.Component<IViewerProps> {
             }
         }
 
+        // render pages for any indices that don't already have pages (force rerender will make these render regardless)
         for (let i = startIndex; i <= endIndex; i++) {
             if (!this._isPage[i] || forceRender) {
                 this._visibleElements[i] = (
@@ -207,6 +203,7 @@ class Viewer extends React.Component<IViewerProps> {
         return;
     }
 
+    // get the page index that the vertical offset passed in is on
     getIndex = (vOffset: number) => {
         if (this._loaded) {
             let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
@@ -221,6 +218,10 @@ class Viewer extends React.Component<IViewerProps> {
         return 0;
     }
 
+    /**
+     * 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 => {
         if (this._loaded) {
@@ -244,22 +245,8 @@ class Viewer extends React.Component<IViewerProps> {
     }
 
     render() {
-        console.log(`START: ${this.startIndex}`);
-        console.log(`END: ${this.endIndex}`)
-        let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
         return (
             <div className="viewer">
-                {/* {Array.from(Array(numPages).keys()).map((i) => (
-                    <Page
-                        pdf={this.props.pdf}
-                        page={i}
-                        numPages={numPages}
-                        key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
-                        name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
-                        pageLoaded={this.pageLoaded}
-                        {...this.props}
-                    />
-                ))} */}
                 {this._visibleElements}
             </div>
         );
@@ -276,22 +263,23 @@ interface IPageProps {
 
 @observer
 class Page extends React.Component<IPageProps> {
-    @observable _state: string = "N/A";
-    @observable _width: number = 0;
-    @observable _height: number = 0;
-    @observable _page: Opt<Pdfjs.PDFPageProxy>;
-    canvas: React.RefObject<HTMLCanvasElement>;
-    textLayer: React.RefObject<HTMLDivElement>;
-    @observable _currPage: number = this.props.page + 1;
+    @observable private _state: string = "N/A";
+    @observable private _width: number = 0;
+    @observable private _height: number = 0;
+    @observable private _page: Opt<Pdfjs.PDFPageProxy>;
+    @observable private _currPage: number = this.props.page + 1;
+
+    private _canvas: React.RefObject<HTMLCanvasElement>;
+    private _currentAnnotations: HTMLDivElement[] = [];
+    private _textLayer: React.RefObject<HTMLDivElement>;
 
     constructor(props: IPageProps) {
         super(props);
-        this.canvas = React.createRef();
-        this.textLayer = React.createRef();
+        this._canvas = React.createRef();
+        this._textLayer = React.createRef();
     }
 
     componentDidMount() {
-        console.log(this.props.pdf);
         if (this.props.pdf) {
             this.update(this.props.pdf);
         }
@@ -327,8 +315,8 @@ class Page extends React.Component<IPageProps> {
     private renderPage = (page: Pdfjs.PDFPageProxy) => {
         let scale = 1;
         let viewport = page.getViewport(scale);
-        let canvas = this.canvas.current;
-        let textLayer = this.textLayer.current;
+        let canvas = this._canvas.current;
+        let textLayer = this._textLayer.current;
         if (canvas && textLayer) {
             let ctx = canvas.getContext("2d");
             canvas.width = viewport.width;
@@ -337,7 +325,9 @@ class Page extends React.Component<IPageProps> {
             this._height = viewport.height;
             this.props.pageLoaded(this._currPage, viewport);
             if (ctx) {
+                // renders the page onto the canvas context
                 page.render({ canvasContext: ctx, viewport: viewport })
+                // renders text onto the text container
                 page.getTextContent().then((res: Pdfjs.TextContent) => {
                     //@ts-ignore
                     Pdfjs.renderTextLayer({
@@ -345,7 +335,6 @@ class Page extends React.Component<IPageProps> {
                         container: textLayer,
                         viewport: viewport
                     });
-                    // textLayer._render();
                 });
 
                 this._page = page;
@@ -358,6 +347,11 @@ class Page extends React.Component<IPageProps> {
             e.stopPropagation();
             document.addEventListener("pointermove", this.onPointerMove);
             document.addEventListener("pointerup", this.onPointerUp);
+            if (!e.ctrlKey) {
+                for (let anno of this._currentAnnotations) {
+                    anno.remove();
+                }
+            }
         }
     }
 
@@ -367,30 +361,35 @@ class Page extends React.Component<IPageProps> {
         }
     }
 
+    startAnnotation = (e: DragEvent) => {
+        console.log("drag starting");
+    }
+
+    pointerDownCancel = (e: PointerEvent) => {
+        e.stopPropagation();
+    }
+
     onPointerUp = (e: PointerEvent) => {
         let sel = window.getSelection();
+        // if selecting over a range of things
         if (sel && sel.type === "Range") {
-            // console.log(sel.getRangeAt(0));
-            let commonContainer = sel.getRangeAt(0).commonAncestorContainer;
-            let startContainer = sel.getRangeAt(0).startContainer;
-            let endContainer = sel.getRangeAt(0).endContainer;
             let clientRects = sel.getRangeAt(0).getClientRects();
-            console.log(sel.getRangeAt(0));
-            let annoBoxes = [];
-            if (this.textLayer.current) {
-                // let transform = Utils.GetScreenTransform(this.textLayer.current);
-                console.log(transform);
+            if (this._textLayer.current) {
+                let boundingRect = this._textLayer.current.getBoundingClientRect();
                 for (let i = 0; i < clientRects.length; i++) {
                     let rect = clientRects.item(i);
                     if (rect) {
                         let annoBox = document.createElement("div");
                         annoBox.className = "pdfViewer-annotationBox";
-                        annoBox.style.top = rect.top.toString();
-                        annoBox.style.left = rect.left.toString();
-                        annoBox.style.width = rect.width.toString();
-                        annoBox.style.height = rect.height.toString();
-                        // annoBox.style.transform = `scale(${1 / transform.scale}) translate(-${transform.translateX * transform.scale}px, -${transform.translateY * transform.scale}px)`;
-                        this.textLayer.current.appendChild(annoBox);
+                        // transforms the positions from screen onto the pdf div
+                        annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString();
+                        annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString();
+                        annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString();
+                        annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString();
+                        annoBox.ondragstart = this.startAnnotation;
+                        annoBox.onpointerdown = this.pointerDownCancel;
+                        this._textLayer.current.appendChild(annoBox);
+                        this._currentAnnotations.push(annoBox);
                     }
                 }
             }
@@ -403,9 +402,9 @@ class Page extends React.Component<IPageProps> {
         return (
             <div onPointerDown={this.onPointerDown} className={this.props.name} style={{ "width": this._width, "height": this._height }}>
                 <div className="canvasContainer">
-                    <canvas ref={this.canvas} />
+                    <canvas ref={this._canvas} />
                 </div>
-                <div className="textlayer" ref={this.textLayer} style={{ "position": "relative", "top": `-${this._height}px`, "height": `${this._height}px` }} />
+                <div className="textlayer" ref={this._textLayer} style={{ "position": "relative", "top": `-${this._height}px`, "height": `${this._height}px` }} />
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From b96281d18a9c4ca0ea7f8360d7f69d12c325fada Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Tue, 11 Jun 2019 19:02:00 -0400
Subject: pin annotations

---
 src/client/views/nodes/PDFBox.tsx  |   1 +
 src/client/views/pdf/PDFViewer.tsx | 136 +++++++++++++++++++++++++++++--------
 src/client/views/pdf/Page.tsx      |   4 +-
 3 files changed, 110 insertions(+), 31 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index de75c67cf..5b118185b 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -50,6 +50,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
         trace();
         // uses mozilla pdf as default
         const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"));
+        console.log(pdfUrl);
         let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
         return (
             <div onScroll={this.onScroll}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index bc773ebef..6823f640e 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,6 +1,6 @@
 import { observer } from "mobx-react";
 import React = require("react");
-import { observable, action, runInAction, computed, IReactionDisposer, reaction } from "mobx";
+import { observable, action, runInAction, computed, IReactionDisposer, reaction, trace } from "mobx";
 import * as Pdfjs from "pdfjs-dist";
 import { Opt, HeightSym, WidthSym, Doc, DocListCast } from "../../../new_fields/Doc";
 import "./PDFViewer.scss";
@@ -14,6 +14,10 @@ import { DocumentManager } from "../../util/DocumentManager";
 import { SelectionManager } from "../../util/SelectionManager";
 import { List } from "../../../new_fields/List";
 import { DocumentContentsView } from "../nodes/DocumentContentsView";
+import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
+import { Transform } from "../../util/Transform";
+import { emptyFunction, returnTrue, returnFalse } from "../../../Utils";
+import { DocumentView } from "../nodes/DocumentView";
 
 interface IPDFViewerProps {
     url: string;
@@ -33,10 +37,14 @@ export class PDFViewer extends React.Component<IPDFViewerProps> {
     @action
     componentDidMount() {
         const pdfUrl = this.props.url;
+        console.log("pdf starting to load")
         let promise = Pdfjs.getDocument(pdfUrl).promise;
 
         promise.then((pdf: Pdfjs.PDFDocumentProxy) => {
-            runInAction(() => this._pdf = pdf);
+            runInAction(() => {
+                console.log("pdf url received");
+                this._pdf = pdf;
+            });
         });
     }
 
@@ -77,8 +85,16 @@ class Viewer extends React.Component<IViewerProps> {
     @observable private _annotations: Doc[] = [];
 
     private _pageBuffer: number = 1;
+    private _annotationLayer: React.RefObject<HTMLDivElement>;
     private _reactionDisposer?: IReactionDisposer;
     private _annotationReactionDisposer?: IReactionDisposer;
+    private _pagesLoaded: number = 0;
+
+    constructor(props: IViewerProps) {
+        super(props);
+
+        this._annotationLayer = React.createRef();
+    }
 
     componentDidMount = () => {
         let wasSelected = this.props.parent.props.isSelected();
@@ -112,8 +128,9 @@ class Viewer extends React.Component<IViewerProps> {
             );
         }
 
-        // On load, render pdf
-        setTimeout(() => this.renderPages(this.startIndex, this.endIndex, true), 1000);
+        setTimeout(() => {
+            this.renderPages(this.startIndex, this.endIndex, true);
+        }, 1000);
     }
 
     componentWillUnmount = () => {
@@ -150,7 +167,7 @@ class Viewer extends React.Component<IViewerProps> {
     }
 
     @computed get endIndex(): number {
-        let width = this._pageSizes.map(i => i.width);
+        let width = this._pageSizes.map(i => i ? i.width : 0);
         return Math.min(this.props.pdf ? this.props.pdf.numPages - 1 : 0, this.getIndex(this.scrollY + Math.max(...width)) + this._pageBuffer);
     }
 
@@ -185,6 +202,10 @@ class Viewer extends React.Component<IViewerProps> {
             return;
         }
 
+        if (this._pageSizes.length !== numPages) {
+            this._pageSizes = new Array(numPages).map(i => ({ width: 0, height: 0 }));
+        }
+
         // this is only for an initial render to get all of the pages rendered
         if (this._visibleElements.length !== numPages) {
             let divs = Array.from(Array(numPages).keys()).map(i => (
@@ -248,14 +269,14 @@ class Viewer extends React.Component<IViewerProps> {
         return;
     }
 
-    createPinAnnotation = (x: number, y: number): void => {
-        let targetDoc = Docs.TextDocument({ title: "New Pin Annotation" });
+    createPinAnnotation = (x: number, y: number, page: number): void => {
+        let targetDoc = Docs.TextDocument({ width: 100, height: 50, title: "New Pin Annotation" });
 
         let pinAnno = new Doc();
         pinAnno.x = x;
         pinAnno.y = y;
         pinAnno.width = pinAnno.height = PinRadius;
-        pinAnno.page = this.getIndex(y);
+        pinAnno.page = page;
         pinAnno.target = targetDoc;
         pinAnno.type = AnnotationTypes.Pin;
         // this._annotations.push(pinAnno);
@@ -301,18 +322,18 @@ class Viewer extends React.Component<IViewerProps> {
         }
         let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
         this.props.loaded(page.width, page.height);
-        if (index > this._pageSizes.length) {
-            this._pageSizes.push({ width: page.width, height: page.height });
-        }
-        else {
-            this._pageSizes[index - 1] = { width: page.width, height: page.height };
-        }
-        if (index === numPages) {
+        this._pageSizes[index - 1] = { width: page.width, height: page.height };
+        this._pagesLoaded++;
+        if (this._pagesLoaded === numPages) {
             this._loaded = true;
             let divs = Array.from(Array(numPages).keys()).map(i => (
                 <div key={`pdfviewer-placeholder-${i}`} className="pdfviewer-placeholder" style={{ width: this._pageSizes[i] ? this._pageSizes[i].width : 0, height: this._pageSizes[i] ? this._pageSizes[i].height : 0 }} />
             ));
             this._visibleElements = new Array<JSX.Element>(...divs);
+            // On load, render pdf
+            // setTimeout(() => {
+            this.renderPages(this.startIndex, this.endIndex, true);
+            // }, 1000);
         }
     }
 
@@ -332,21 +353,38 @@ class Viewer extends React.Component<IViewerProps> {
         let type = NumCast(anno.type);
         switch (type) {
             case AnnotationTypes.Pin:
-                return <PinAnnotation document={anno} x={NumCast(anno.x)} y={NumCast(anno.y) + this.getPageHeight(NumCast(anno.page))} width={anno[WidthSym]()} height={anno[HeightSym]()} key={anno[Id]} />;
+                return <PinAnnotation parent={this} document={anno} x={NumCast(anno.x)} y={NumCast(anno.y) + this.getPageHeight(NumCast(anno.page))} width={anno[WidthSym]()} height={anno[HeightSym]()} key={anno[Id]} />;
             case AnnotationTypes.Region:
-                return <RegionAnnotation document={anno} x={NumCast(anno.x)} y={NumCast(anno.y) + this.getPageHeight(NumCast(anno.page))} width={anno[WidthSym]()} height={anno[HeightSym]()} key={anno[Id]} />;
+                return <RegionAnnotation parent={this} document={anno} x={NumCast(anno.x)} y={NumCast(anno.y) + this.getPageHeight(NumCast(anno.page))} width={anno[WidthSym]()} height={anno[HeightSym]()} key={anno[Id]} />;
             default:
                 return <div></div>;
         }
     }
 
+    onDrop = (e: React.DragEvent) => {
+        console.log("Dropped!");
+    }
+
+    // ScreenToLocalTransform = (): Transform => {
+    //     if (this._annotationLayer.current) {
+    //         let boundingRect = this._annotationLayer.current.getBoundingClientRect();
+    //         let x = boundingRect.left;
+    //         let y = boundingRect.top;
+    //         let scale = NumCast(this.props.parent.Document.nativeWidth) / boundingRect.width;
+    //         let t = new Transform(x, y, scale);
+    //         return t;
+    //     }
+    //     return Transform.Identity();
+    // }
+
     render() {
+        trace();
         return (
-            <div>
+            <div onDrop={this.onDrop}>
                 <div className="viewer">
                     {this._visibleElements}
                 </div>
-                <div className="pdfViewer-annotationLayer" style={{ height: this.props.parent.Document.nativeHeight, width: `100%`, pointerEvents: "none" }}>
+                <div className="pdfViewer-annotationLayer" ref={this._annotationLayer} style={{ height: this.props.parent.Document.nativeHeight, width: `100%`, pointerEvents: "none" }}>
                     <div className="pdfViewer-annotationLayer-subCont" style={{ transform: `translateY(${-this.scrollY}px)` }}>
                         {this._annotations.map(anno => this.renderAnnotation(anno))}
                     </div>
@@ -365,25 +403,65 @@ interface IAnnotationProps {
     y: number;
     width: number;
     height: number;
+    parent: Viewer;
     document: Doc;
 }
 
+@observer
 class PinAnnotation extends React.Component<IAnnotationProps> {
-    @observable private _backgroundColor: string = "red";
+    @observable private _backgroundColor: string = "green";
+    @observable private _display: string = "initial";
 
-    pointerDown = (e: React.PointerEvent) => {
+    private _selected: boolean = true;
 
+    @action
+    pointerDown = (e: React.PointerEvent) => {
+        if (this._selected) {
+            this._backgroundColor = "red";
+            this._display = "none";
+            this._selected = false;
+        }
+        else {
+            this._backgroundColor = "green";
+            this._display = "initial";
+            this._selected = true;
+        }
+        e.preventDefault();
+        e.stopPropagation();
     }
 
     render() {
-        let targetDoc = Cast(this.props.document.targetDoc, Doc, Docs.TextDocument({ title: "New Pin Annotation" }));
-        return (
-            <div className="pdfViewer-pinAnnotation" onPointerDown={this.pointerDown}
-                style={{ top: this.props.y - PinRadius / 2, left: this.props.x - PinRadius / 2, width: PinRadius, height: PinRadius, pointerEvents: "all", backgroundColor: this._backgroundColor }}>
-                {/* <DocumentContentsView Document={targetDoc}
-                        isSelected={} /> */}
-            </div>
-        );
+        let targetDoc = Cast(this.props.document.target, Doc);
+        if (targetDoc instanceof Doc) {
+            return (
+                <div className="pdfViewer-pinAnnotation" onPointerDown={this.pointerDown}
+                    style={{
+                        top: this.props.y - PinRadius / 2, left: this.props.x - PinRadius / 2, width: PinRadius,
+                        height: PinRadius, pointerEvents: "all", backgroundColor: this._backgroundColor
+                    }}>
+                    <div style={{
+                        position: "absolute", top: "25px", left: "25px", transform: "scale(3)", transformOrigin: "top left",
+                        display: this._display, width: targetDoc[WidthSym](), height: targetDoc[HeightSym]()
+                    }}>
+                        <DocumentView Document={targetDoc}
+                            ContainingCollectionView={undefined}
+                            ScreenToLocalTransform={this.props.parent.props.parent.props.ScreenToLocalTransform}
+                            isTopMost={false}
+                            ContentScaling={() => 1}
+                            PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)}
+                            PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)}
+                            focus={emptyFunction}
+                            selectOnLoad={false}
+                            parentActive={this.props.parent.props.parent.props.active}
+                            whenActiveChanged={this.props.parent.props.parent.props.whenActiveChanged}
+                            bringToFront={emptyFunction}
+                            addDocTab={this.props.parent.props.parent.props.addDocTab}
+                        />
+                    </div>
+                </div >
+            );
+        }
+        return null;
     }
 }
 
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index fe7369aeb..73a7a93a0 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -23,7 +23,7 @@ interface IPageProps {
     pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void;
     parent: PDFBox;
     renderAnnotations: (annotations: Doc[], removeOld: boolean) => void;
-    makePin: (x: number, y: number) => void;
+    makePin: (x: number, y: number, page: number) => void;
 }
 
 @observer
@@ -378,7 +378,7 @@ export default class Page extends React.Component<IPageProps> {
             let boundingRect = current.getBoundingClientRect();
             let x = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width);
             let y = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height);
-            this.props.makePin(x, y);
+            this.props.makePin(x, y, this.props.page);
         }
     }
 
-- 
cgit v1.2.3-70-g09d2


From eaa66ece6340534ad09cf83134b344ef43816cd9 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Wed, 12 Jun 2019 18:30:53 -0400
Subject: deleting pins and better pin saving

---
 src/client/views/nodes/PDFBox.scss | 23 +++++++++++----
 src/client/views/nodes/PDFBox.tsx  |  3 ++
 src/client/views/pdf/PDFViewer.tsx | 57 ++++++++++++++++++++++++++++++++------
 3 files changed, 70 insertions(+), 13 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 449408a61..f4d455be7 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -2,39 +2,52 @@
     transform-origin: left top;
     position: absolute;
     top: 0;
-    left:0;
+    left: 0;
 }
+
 .react-pdf__Page__textContent span {
     user-select: text;
 }
+
 .react-pdf__Document {
     position: absolute;
 }
+
 .pdfBox-buttonTray {
-    position:absolute;
+    position: absolute;
     top: 0;
-    left:0;
+    left: 0;
     z-index: 25;
     pointer-events: all;
 }
+
 .pdfBox-thumbnail {
     position: absolute;
     width: 100%;
 }
+
 .pdfButton {
     pointer-events: all;
     width: 100px;
-    height:100px;
+    height: 100px;
 }
+
 .pdfBox-cont {
-    pointer-events: none ;
+    pointer-events: none;
+    display: flex;
+    flex-direction: row;
+
     span {
         pointer-events: none !important;
     }
 }
+
 .pdfBox-cont-interactive {
     pointer-events: all;
+    display: flex;
+    flex-direction: row;
 }
+
 .pdfBox-contentContainer {
     position: absolute;
     transform-origin: left top;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 5b118185b..4214a6777 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -19,6 +19,7 @@ import { makeInterface } from "../../../new_fields/Schema";
 import { PDFViewer } from "../pdf/PDFViewer";
 import { PdfField } from "../../../new_fields/URLField";
 import { HeightSym, WidthSym } from "../../../new_fields/Doc";
+import { CollectionStackingView } from "../collections/CollectionStackingView";
 
 type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
 const PdfDocument = makeInterface(positionSchema, pageSchema);
@@ -43,6 +44,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     onScroll = (e: React.UIEvent<HTMLDivElement>) => {
         if (e.currentTarget) {
             this._scrollY = e.currentTarget.scrollTop;
+            // e.currentTarget.scrollTo({ top: 1000, behavior: "smooth" });
         }
     }
 
@@ -57,6 +59,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
                 style={{ overflowY: "scroll", overflowX: "hidden", height: `${NumCast(this.props.Document.nativeHeight ? this.props.Document.nativeHeight : 300)}px` }}
                 onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}>
                 <PDFViewer url={pdfUrl.url.href} loaded={this.loaded} scrollY={this._scrollY} parent={this} />
+                {/* <div style={{ width: "100px", height: "300px" }}></div> */}
             </div>
         );
     }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index e54dfea6f..fe442c906 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -152,7 +152,7 @@ class Viewer extends React.Component<IViewerProps> {
         }
     }
 
-    makeAnnotationDocuments = (sourceDoc: Doc): Doc => {
+    makeAnnotationDocument = (sourceDoc: Doc): Doc => {
         let annoDocs: Doc[] = [];
         this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => {
             for (let anno of value) {
@@ -179,7 +179,7 @@ class Viewer extends React.Component<IViewerProps> {
     drop = async (e: Event, de: DragManager.DropEvent) => {
         if (de.data instanceof DragManager.LinkDragData) {
             let sourceDoc = de.data.linkSourceDocument;
-            let destDoc = this.makeAnnotationDocuments(sourceDoc);
+            let destDoc = this.makeAnnotationDocument(sourceDoc);
             let targetAnnotations = DocListCast(this.props.parent.Document.annotations);
             if (targetAnnotations) {
                 targetAnnotations.push(destDoc);
@@ -280,7 +280,7 @@ class Viewer extends React.Component<IViewerProps> {
                     makePin={this.createPinAnnotation}
                     createAnnotation={this.createAnnotation}
                     sendAnnotations={this.receiveAnnotations}
-                    makeAnnotationDocuments={this.makeAnnotationDocuments}
+                    makeAnnotationDocuments={this.makeAnnotationDocument}
                     receiveAnnotations={this.sendAnnotations}
                     {...this.props} />
             ));
@@ -322,7 +322,7 @@ class Viewer extends React.Component<IViewerProps> {
                         renderAnnotations={this.renderAnnotations}
                         createAnnotation={this.createAnnotation}
                         sendAnnotations={this.receiveAnnotations}
-                        makeAnnotationDocuments={this.makeAnnotationDocuments}
+                        makeAnnotationDocuments={this.makeAnnotationDocument}
                         receiveAnnotations={this.sendAnnotations}
                         {...this.props} />
                 );
@@ -492,29 +492,70 @@ class PinAnnotation extends React.Component<IAnnotationProps> {
     @observable private _backgroundColor: string = "green";
     @observable private _display: string = "initial";
 
-    private _selected: boolean = true;
+    private _mainCont: React.RefObject<HTMLDivElement>;
+
+    constructor(props: IAnnotationProps) {
+        super(props);
+        this._mainCont = React.createRef();
+    }
+
+    componentDidMount = () => {
+        let selected = this.props.document.selected;
+        if (selected && BoolCast(selected)) {
+            runInAction(() => {
+                this._backgroundColor = "green";
+                this._display = "initial";
+            })
+        }
+        else {
+            runInAction(() => {
+                this._backgroundColor = "red";
+                this._display = "none";
+            })
+        }
+    }
 
     @action
     pointerDown = (e: React.PointerEvent) => {
-        if (this._selected) {
+        let selected = this.props.document.selected;
+        if (selected && BoolCast(selected)) {
             this._backgroundColor = "red";
             this._display = "none";
-            this._selected = false;
+            this.props.document.selected = false;
         }
         else {
             this._backgroundColor = "green";
             this._display = "initial";
-            this._selected = true;
+            this.props.document.selected = true;
         }
         e.preventDefault();
         e.stopPropagation();
     }
 
+    @action
+    doubleClick = (e: React.MouseEvent) => {
+        if (this._mainCont.current) {
+            let annotations = DocListCast(this.props.parent.props.parent.Document.annotations);
+            if (annotations && annotations.length) {
+                let index = annotations.indexOf(this.props.document);
+                annotations.splice(index, 1);
+                this.props.parent.props.parent.Document.annotations = new List<Doc>(annotations);
+            }
+            // this._mainCont.current.childNodes.forEach(e => e.remove());
+            this._mainCont.current.style.display = "none";
+            // if (this._mainCont.current.parentElement) {
+            //     this._mainCont.current.remove();
+            // }
+        }
+        e.stopPropagation();
+    }
+
     render() {
         let targetDoc = Cast(this.props.document.target, Doc);
         if (targetDoc instanceof Doc) {
             return (
                 <div className="pdfViewer-pinAnnotation" onPointerDown={this.pointerDown}
+                    onDoubleClick={this.doubleClick} ref={this._mainCont}
                     style={{
                         top: this.props.y - PinRadius / 2, left: this.props.x - PinRadius / 2, width: PinRadius,
                         height: PinRadius, pointerEvents: "all", backgroundColor: this._backgroundColor
-- 
cgit v1.2.3-70-g09d2


From b631beaf11db59549e75cc38ae7288a0ba8845cf Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Thu, 13 Jun 2019 22:00:33 -0400
Subject: added collection back

---
 src/client/documents/Documents.ts                  |  2 +-
 src/client/views/collections/CollectionPDFView.tsx | 32 +++++++++++++++++++---
 src/client/views/nodes/PDFBox.tsx                  | 13 ++++++++-
 src/client/views/pdf/PDFViewer.scss                |  1 -
 src/client/views/pdf/PDFViewer.tsx                 | 30 ++++++++++++++------
 src/client/views/pdf/Page.tsx                      |  2 +-
 6 files changed, 64 insertions(+), 16 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index dfbe2e136..91d3707f6 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -170,7 +170,7 @@ export namespace Docs {
         return textProto;
     }
     function CreatePdfPrototype(): Doc {
-        let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", PDFBox.LayoutString(),
+        let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("annotations"),
             { x: 0, y: 0, width: 300, height: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1 });
         return pdfProto;
     }
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 62e8adbec..4af89d780 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -1,4 +1,4 @@
-import { action, observable } from "mobx";
+import { action, observable, IReactionDisposer, reaction } from "mobx";
 import { observer } from "mobx-react";
 import { ContextMenu } from "../ContextMenu";
 import "./CollectionPDFView.scss";
@@ -9,12 +9,36 @@ import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from ".
 import { emptyFunction } from "../../../Utils";
 import { NumCast } from "../../../new_fields/Types";
 import { Id } from "../../../new_fields/FieldSymbols";
+import { HeightSym, WidthSym } from "../../../new_fields/Doc";
 
 
 @observer
 export class CollectionPDFView extends React.Component<FieldViewProps> {
+    private _reactionDisposer?: IReactionDisposer;
+    private _buttonTray: React.RefObject<HTMLDivElement>;
+
     constructor(props: FieldViewProps) {
         super(props);
+
+        this._buttonTray = React.createRef();
+    }
+
+    componentDidMount() {
+        this._reactionDisposer = reaction(
+            () => NumCast(this.props.Document.scrollY),
+            () => {
+                // let transform = this.props.ScreenToLocalTransform();
+                if (this._buttonTray.current) {
+                    // console.log(this._buttonTray.current.offsetHeight);
+                    // console.log(NumCast(this.props.Document.scrollY));
+                    let scale = this.nativeWidth() / this.props.Document[WidthSym]();
+                    this.props.Document.panY = NumCast(this.props.Document.scrollY);
+                    // console.log(scale);
+                }
+                // console.log(this.props.Document[HeightSym]());
+            },
+            { fireImmediately: true }
+        )
     }
 
     public static LayoutString(fieldKey: string = "data") {
@@ -52,12 +76,12 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
     private get uIButtons() {
         let ratio = (this.curPage - 1) / this.numPages * 100;
         return (
-            <div className="collectionPdfView-buttonTray" key="tray" style={{ height: "100%" }}>
+            <div className="collectionPdfView-buttonTray" ref={this._buttonTray} key="tray" style={{ height: "100%" }}>
                 <button className="collectionPdfView-backward" onClick={this.onPageBack}>{"<"}</button>
                 <button className="collectionPdfView-forward" onClick={this.onPageForward}>{">"}</button>
-                <div className="collectionPdfView-slider" onPointerDown={this.onThumbDown} style={{ top: 60, left: -20, width: 50, height: `calc(100% - 80px)` }} >
+                {/* <div className="collectionPdfView-slider" onPointerDown={this.onThumbDown} style={{ top: 60, left: -20, width: 50, height: `calc(100% - 80px)` }} >
                     <div className="collectionPdfView-thumb" onPointerDown={this.onThumbDown} style={{ top: `${ratio}%`, width: 50, height: 50 }} />
-                </div>
+                </div> */}
             </div>
         );
     }
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 4214a6777..acb430deb 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -36,6 +36,10 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
             let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
             doc.nativeWidth = nw;
             doc.nativeHeight = nh;
+            let ccv = this.props.ContainingCollectionView;
+            if (ccv) {
+                ccv.props.Document.pdfHeight = nh;
+            }
             doc.height = nh * (doc[WidthSym]() / nw);
         }
     }
@@ -45,6 +49,10 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
         if (e.currentTarget) {
             this._scrollY = e.currentTarget.scrollTop;
             // e.currentTarget.scrollTo({ top: 1000, behavior: "smooth" });
+            let ccv = this.props.ContainingCollectionView;
+            if (ccv) {
+                ccv.props.Document.scrollY = this._scrollY;
+            }
         }
     }
 
@@ -56,7 +64,10 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
         let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
         return (
             <div onScroll={this.onScroll}
-                style={{ overflowY: "scroll", overflowX: "hidden", height: `${NumCast(this.props.Document.nativeHeight ? this.props.Document.nativeHeight : 300)}px` }}
+                style={{
+                    overflowY: "scroll", overflowX: "hidden", height: `${NumCast(this.props.Document.nativeHeight ? this.props.Document.nativeHeight : 300)}px`,
+                    marginTop: `${NumCast(this.props.ContainingCollectionView!.props.Document.panY)}px`
+                }}
                 onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}>
                 <PDFViewer url={pdfUrl.url.href} loaded={this.loaded} scrollY={this._scrollY} parent={this} />
                 {/* <div style={{ width: "100px", height: "300px" }}></div> */}
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 57be04b93..a73df2d58 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -33,7 +33,6 @@
 .pdfViewer-annotationLayer {
     position: absolute;
     top: 0;
-    overflow: visible hidden;
 }
 
 .pdfViewer-pinAnnotation {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index fe442c906..144fca9e0 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -188,8 +188,8 @@ class Viewer extends React.Component<IViewerProps> {
             else {
                 this.props.parent.Document.annotations = new List<Doc>([destDoc]);
             }
+            e.stopPropagation();
         }
-        e.stopPropagation();
     }
 
     componentWillUnmount = () => {
@@ -465,7 +465,7 @@ class Viewer extends React.Component<IViewerProps> {
                     {this._visibleElements}
                 </div>
                 <div className="pdfViewer-annotationLayer" style={{ height: this.props.parent.Document.nativeHeight, width: `100%`, pointerEvents: this._pointerEvents }}>
-                    <div className="pdfViewer-annotationLayer-subCont" style={{ transform: `translateY(${-this.scrollY}px)` }} ref={this._annotationLayer}>
+                    <div className="pdfViewer-annotationLayer-subCont" ref={this._annotationLayer}>
                         {this._annotations.map(anno => this.renderAnnotation(anno))}
                     </div>
                 </div>
@@ -501,17 +501,31 @@ class PinAnnotation extends React.Component<IAnnotationProps> {
 
     componentDidMount = () => {
         let selected = this.props.document.selected;
-        if (selected && BoolCast(selected)) {
+        if (!BoolCast(selected)) {
             runInAction(() => {
-                this._backgroundColor = "green";
-                this._display = "initial";
-            })
+                this._backgroundColor = "red";
+                this._display = "none";
+            });
+        }
+        if (selected) {
+            if (BoolCast(selected)) {
+                runInAction(() => {
+                    this._backgroundColor = "green";
+                    this._display = "initial";
+                });
+            }
+            else {
+                runInAction(() => {
+                    this._backgroundColor = "red";
+                    this._display = "none";
+                });
+            }
         }
         else {
             runInAction(() => {
                 this._backgroundColor = "red";
                 this._display = "none";
-            })
+            });
         }
     }
 
@@ -572,7 +586,7 @@ class PinAnnotation extends React.Component<IAnnotationProps> {
                             PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)}
                             PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)}
                             focus={emptyFunction}
-                            selectOnLoad={false}
+                            selectOnLoad={true}
                             parentActive={this.props.parent.props.parent.props.active}
                             whenActiveChanged={this.props.parent.props.parent.props.whenActiveChanged}
                             bringToFront={emptyFunction}
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 9e3bf4954..1c305caa3 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -257,7 +257,7 @@ export default class Page extends React.Component<IPageProps> {
             let ratio = this._marqueeWidth / this._marqueeHeight;
             if (ratio > 1.5) {
                 // vertical
-                transform = "rotate(90deg) scale(1, 2)";
+                transform = "rotate(90deg) scale(1, 5)";
             }
             else if (ratio < 0.5) {
                 // horizontal
-- 
cgit v1.2.3-70-g09d2


From 0b4f3c25471e51d27ddb33dc5ddafafb2c0c03e5 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 14 Jun 2019 13:09:15 -0400
Subject: fixes for full screen.

---
 src/client/views/nodes/PDFBox.tsx  | 3 ++-
 src/client/views/pdf/PDFViewer.tsx | 3 +--
 2 files changed, 3 insertions(+), 3 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index acb430deb..cce7b2631 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -65,7 +65,8 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
         return (
             <div onScroll={this.onScroll}
                 style={{
-                    overflowY: "scroll", overflowX: "hidden", height: `${NumCast(this.props.Document.nativeHeight ? this.props.Document.nativeHeight : 300)}px`,
+                    height: "100%",
+                    overflowY: "scroll", overflowX: "hidden",
                     marginTop: `${NumCast(this.props.ContainingCollectionView!.props.Document.panY)}px`
                 }}
                 onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index d6081142a..440a20e8e 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -111,14 +111,13 @@ class Viewer extends React.Component<IViewerProps> {
                 // if deselected, render images in place of pdf
                 if (wasSelected && !this.props.parent.props.active()) {
                     this.saveThumbnail();
-                    this._pointerEvents = "all";
                 }
                 // if selected, render pdf
                 else if (!wasSelected && this.props.parent.props.active()) {
                     this.renderPages(this.startIndex, this.endIndex, true);
-                    this._pointerEvents = "none";
                 }
                 wasSelected = this.props.parent.props.active();
+                this._pointerEvents = wasSelected ? "none" : "all";
             },
             { fireImmediately: true }
         );
-- 
cgit v1.2.3-70-g09d2


From 2ef1dd2089ad991f1d4897022f2d28c2eb130837 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Fri, 14 Jun 2019 15:47:43 -0400
Subject: highlighting

---
 src/client/views/nodes/PDFBox.tsx  | 10 +++++-
 src/client/views/pdf/PDFMenu.tsx   | 25 +++++++++++--
 src/client/views/pdf/PDFViewer.tsx |  6 ++--
 src/client/views/pdf/Page.tsx      | 74 ++++++++++++++++++++++++++++----------
 4 files changed, 91 insertions(+), 24 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index acb430deb..6ee62bbad 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -34,8 +34,16 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     loaded = (nw: number, nh: number) => {
         if (this.props.Document) {
             let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
+            let oldnw = NumCast(doc.nativeWidth);
             doc.nativeWidth = nw;
-            doc.nativeHeight = nh;
+            if (!doc.nativeHeight) {
+                doc.nativeHeight = nh;
+            }
+            else {
+                let oldnh = NumCast(doc.nativeHeight);
+                let aspect = oldnh / oldnw;
+                doc.nativeHeight = nw * aspect;
+            }
             let ccv = this.props.ContainingCollectionView;
             if (ccv) {
                 ccv.props.Document.pdfHeight = nh;
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index cc5c0b77b..a0230113b 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -3,6 +3,8 @@ import "./PDFMenu.scss";
 import { observable } from "mobx";
 import { observer } from "mobx-react";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { emptyFunction } from "../../../Utils";
+import { Doc } from "../../../new_fields/Doc";
 
 @observer
 export default class PDFMenu extends React.Component {
@@ -10,6 +12,8 @@ export default class PDFMenu extends React.Component {
 
     @observable Top: number = 0;
     @observable Left: number = 0;
+    StartDrag: (e: PointerEvent) => void = emptyFunction;
+    Highlight: (d: Doc | undefined) => void = emptyFunction;
 
     constructor(props: Readonly<{}>) {
         super(props);
@@ -17,11 +21,28 @@ export default class PDFMenu extends React.Component {
         PDFMenu.Instance = this;
     }
 
+    pointerDown = (e: React.PointerEvent) => {
+        document.removeEventListener("pointermove", this.StartDrag);
+        document.addEventListener("pointermove", this.StartDrag);
+        document.removeEventListener("pointerup", this.pointerUp)
+        document.addEventListener("pointerup", this.pointerUp)
+
+        e.stopPropagation();
+        e.preventDefault();
+    }
+
+    pointerUp = (e: PointerEvent) => {
+        document.removeEventListener("pointermove", this.StartDrag);
+        document.removeEventListener("pointerup", this.pointerUp);
+        e.stopPropagation();
+        e.preventDefault();
+    }
+
     render() {
         return (
             <div className="pdfMenu-cont" style={{ left: this.Left, top: this.Top }}>
-                <button className="pdfMenu-button" title="Highlight"><FontAwesomeIcon icon="highlighter" size="sm" /></button>
-                <button className="pdfMenu-button" title="Annotate"><FontAwesomeIcon icon="comment-alt" size="sm" /></button>
+                <button className="pdfMenu-button" title="Highlight" onClick={() => this.Highlight(undefined)}><FontAwesomeIcon icon="highlighter" size="sm" /></button>
+                <button className="pdfMenu-button" title="Annotate" onPointerDown={this.pointerDown}><FontAwesomeIcon icon="comment-alt" size="sm" /></button>
             </div>
         )
     }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index d6081142a..fbad39880 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -152,7 +152,7 @@ class Viewer extends React.Component<IViewerProps> {
         }
     }
 
-    makeAnnotationDocument = (sourceDoc: Doc): Doc => {
+    makeAnnotationDocument = (sourceDoc: Doc | undefined): Doc => {
         let annoDocs: Doc[] = [];
         this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => {
             for (let anno of value) {
@@ -171,7 +171,9 @@ class Viewer extends React.Component<IViewerProps> {
 
         let annoDoc = new Doc();
         annoDoc.annotations = new List<Doc>(annoDocs);
-        DocUtils.MakeLink(sourceDoc, annoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
+        if (sourceDoc) {
+            DocUtils.MakeLink(sourceDoc, annoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
+        }
         this._savedAnnotations.clear();
         return annoDoc;
     }
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index bdb6952cc..44c502a04 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -28,7 +28,7 @@ interface IPageProps {
     sendAnnotations: (annotations: HTMLDivElement[], page: number) => void;
     receiveAnnotations: (page: number) => HTMLDivElement[] | undefined;
     createAnnotation: (div: HTMLDivElement, page: number) => void;
-    makeAnnotationDocuments: (doc: Doc) => Doc;
+    makeAnnotationDocuments: (doc: Doc | undefined) => Doc;
 }
 
 @observer
@@ -132,6 +132,20 @@ export default class Page extends React.Component<IPageProps> {
         }
     }
 
+    highlight = (targetDoc: Doc | undefined) => {
+        // creates annotation documents for current highlights
+        let annotationDoc = this.props.makeAnnotationDocuments(targetDoc);
+        let targetAnnotations = DocListCast(this.props.parent.Document.annotations);
+        if (targetAnnotations) {
+            targetAnnotations.push(annotationDoc);
+            this.props.parent.Document.annotations = new List<Doc>(targetAnnotations);
+        }
+        else {
+            this.props.parent.Document.annotations = new List<Doc>([annotationDoc]);
+        }
+        return annotationDoc;
+    }
+
     /**
      * This is temporary for creating annotations from highlights. It will
      * start a drag event and create or put the necessary info into the drag event.
@@ -149,16 +163,7 @@ export default class Page extends React.Component<IPageProps> {
         // document that this annotation is linked to
         let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" });
         targetDoc.targetPage = this.props.page;
-        // creates annotation documents for current highlights
-        let annotationDoc = this.props.makeAnnotationDocuments(targetDoc);
-        let targetAnnotations = DocListCast(this.props.parent.Document.annotations);
-        if (targetAnnotations) {
-            targetAnnotations.push(annotationDoc);
-            this.props.parent.Document.annotations = new List<Doc>(targetAnnotations);
-        }
-        else {
-            this.props.parent.Document.annotations = new List<Doc>([annotationDoc]);
-        }
+        let annotationDoc = this.highlight(targetDoc);
         // create dragData and star tdrag
         let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc);
         if (this._textLayer.current) {
@@ -173,8 +178,8 @@ export default class Page extends React.Component<IPageProps> {
 
     // cleans up events and boolean
     endDrag = (e: PointerEvent): void => {
-        document.removeEventListener("pointermove", this.startDrag);
-        document.removeEventListener("pointerup", this.endDrag);
+        // document.removeEventListener("pointermove", this.startDrag);
+        // document.removeEventListener("pointerup", this.endDrag);
         this._dragging = false;
         e.stopPropagation();
     }
@@ -185,10 +190,10 @@ export default class Page extends React.Component<IPageProps> {
         if (e.altKey && e.button === 0) {
             e.stopPropagation();
 
-            document.removeEventListener("pointermove", this.startDrag);
-            document.addEventListener("pointermove", this.startDrag);
-            document.removeEventListener("pointerup", this.endDrag);
-            document.addEventListener("pointerup", this.endDrag);
+            // document.removeEventListener("pointermove", this.startDrag);
+            // document.addEventListener("pointermove", this.startDrag);
+            // document.removeEventListener("pointerup", this.endDrag);
+            // document.addEventListener("pointerup", this.endDrag);
         }
         else if (e.button === 0) {
             let target: any = e.target;
@@ -299,6 +304,8 @@ export default class Page extends React.Component<IPageProps> {
                 }
                 copy.className = this._marquee.current.className;
                 this.props.createAnnotation(copy, this.props.page);
+                PDFMenu.Instance.StartDrag = this.startDrag;
+                PDFMenu.Instance.Highlight = this.highlight;
                 this._marquee.current.style.opacity = "0";
             }
 
@@ -308,8 +315,10 @@ export default class Page extends React.Component<IPageProps> {
         }
         else {
             let sel = window.getSelection();
-            if (sel && sel.type === "range") {
-
+            if (sel && sel.type === "Range") {
+                PDFMenu.Instance.StartDrag = this.startDrag;
+                PDFMenu.Instance.Highlight = this.highlight;
+                this.createTextAnnotation(sel);
                 PDFMenu.Instance.Left = e.clientX;
                 PDFMenu.Instance.Top = e.clientY;
             }
@@ -380,6 +389,33 @@ export default class Page extends React.Component<IPageProps> {
         document.removeEventListener("pointerup", this.onSelectEnd);
     }
 
+    @action
+    createTextAnnotation = (sel: Selection) => {
+        let clientRects = sel.getRangeAt(0).getClientRects();
+        if (this._textLayer.current) {
+            let boundingRect = this._textLayer.current.getBoundingClientRect();
+            for (let i = 0; i < clientRects.length; i++) {
+                let rect = clientRects.item(i);
+                if (rect) {
+                    let annoBox = document.createElement("div");
+                    annoBox.className = "pdfViewer-annotationBox";
+                    // transforms the positions from screen onto the pdf div
+                    annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString();
+                    annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString();
+                    annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString();
+                    annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString();
+                    this.props.createAnnotation(annoBox, this.props.page);
+                }
+            }
+        }
+        // clear selection
+        if (sel.empty) {  // Chrome
+            sel.empty();
+        } else if (sel.removeAllRanges) {  // Firefox
+            sel.removeAllRanges();
+        }
+    }
+
     doubleClick = (e: React.MouseEvent) => {
         let target: any = e.target;
         // if double clicking text
-- 
cgit v1.2.3-70-g09d2


From 15e8341334419142f4a54db23cc643f18ba0e0f1 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 14 Jun 2019 15:53:13 -0400
Subject: a few tweaks to fix unfreezing documents to give them a margin

---
 src/client/views/collections/CollectionDockingView.tsx | 8 ++++++--
 src/client/views/nodes/DocumentView.tsx                | 3 ++-
 src/client/views/nodes/PDFBox.tsx                      | 5 ++++-
 3 files changed, 12 insertions(+), 4 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 51e29cb54..235bf5ae4 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -9,7 +9,7 @@ import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc";
 import { Id } from '../../../new_fields/FieldSymbols';
 import { FieldId } from "../../../new_fields/RefField";
 import { listSpec } from "../../../new_fields/Schema";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types";
 import { emptyFunction, returnTrue, Utils } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { DocumentManager } from '../../util/DocumentManager';
@@ -429,7 +429,11 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     }
 
     nativeWidth = () => NumCast(this._document!.nativeWidth, this._panelWidth);
-    nativeHeight = () => NumCast(this._document!.nativeHeight, this._panelHeight);
+    nativeHeight = () => {
+        let nh = NumCast(this._document!.nativeHeight, this._panelHeight);
+        let res = BoolCast(this._document!.ignoreAspect) ? this._panelHeight : nh;
+        return res;
+    }
     contentScaling = () => {
         const nativeH = this.nativeHeight();
         const nativeW = this.nativeWidth();
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6fe01963a..583fa3e1a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -452,8 +452,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     render() {
         var scaling = this.props.ContentScaling();
-        var nativeHeight = this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
         var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%";
+        let ph = this.props.PanelHeight();
+        var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
         return (
             <div className={`documentView-node${this.props.isTopMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index cce7b2631..899caa68a 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -33,9 +33,12 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
 
     loaded = (nw: number, nh: number) => {
         if (this.props.Document) {
+            if (this.props.Document.nativeWidth && this.props.Document.nativeHeight) return;
             let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
+            let oldaspect = NumCast(doc.nativeHeight) / NumCast(doc.nativeWidth, 1);
             doc.nativeWidth = nw;
-            doc.nativeHeight = nh;
+            if (doc.nativeHeight) doc.nativeHeight = nw * oldaspect;
+            else doc.nativeHeight = nh;
             let ccv = this.props.ContainingCollectionView;
             if (ccv) {
                 ccv.props.Document.pdfHeight = nh;
-- 
cgit v1.2.3-70-g09d2


From f6e8b7a0f8a13ddf059cf701e46b8cbb8d9228f7 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 14 Jun 2019 17:27:49 -0400
Subject: added page fwd/back/goto

---
 src/client/views/collections/CollectionPDFView.tsx | 48 ++++++-----------
 src/client/views/nodes/DocumentView.tsx            |  1 -
 src/client/views/nodes/FieldView.tsx               |  2 +
 src/client/views/nodes/PDFBox.tsx                  | 62 ++++++++++++++++------
 src/client/views/pdf/PDFViewer.tsx                 |  6 +--
 src/client/views/pdf/Page.tsx                      | 12 ++---
 6 files changed, 72 insertions(+), 59 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 4af89d780..b62d3f7bb 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -1,19 +1,21 @@
-import { action, observable, IReactionDisposer, reaction } from "mobx";
+import { action, IReactionDisposer, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
+import { WidthSym } from "../../../new_fields/Doc";
+import { Id } from "../../../new_fields/FieldSymbols";
+import { NumCast } from "../../../new_fields/Types";
+import { emptyFunction } from "../../../Utils";
 import { ContextMenu } from "../ContextMenu";
+import { FieldView, FieldViewProps } from "../nodes/FieldView";
+import { CollectionBaseView, CollectionRenderProps, CollectionViewType } from "./CollectionBaseView";
+import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
 import "./CollectionPDFView.scss";
 import React = require("react");
-import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
-import { FieldView, FieldViewProps } from "../nodes/FieldView";
-import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from "./CollectionBaseView";
-import { emptyFunction } from "../../../Utils";
-import { NumCast } from "../../../new_fields/Types";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { HeightSym, WidthSym } from "../../../new_fields/Doc";
+import { PDFBox } from "../nodes/PDFBox";
 
 
 @observer
 export class CollectionPDFView extends React.Component<FieldViewProps> {
+    private _pdfBox?: PDFBox;
     private _reactionDisposer?: IReactionDisposer;
     private _buttonTray: React.RefObject<HTMLDivElement>;
 
@@ -46,31 +48,12 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
     }
     @observable _inThumb = false;
 
-    private set curPage(value: number) { this.props.Document.curPage = value; }
+    private set curPage(value: number) { this._pdfBox && this._pdfBox.GotoPage(value); }
     private get curPage() { return NumCast(this.props.Document.curPage, -1); }
     private get numPages() { return NumCast(this.props.Document.numPages); }
-    @action onPageBack = () => this.curPage > 1 ? (this.props.Document.curPage = this.curPage - 1) : -1;
-    @action onPageForward = () => this.curPage < this.numPages ? (this.props.Document.curPage = this.curPage + 1) : -1;
+    @action onPageBack = () => this._pdfBox && this._pdfBox.BackPage();
+    @action onPageForward = () => this._pdfBox && this._pdfBox.ForwardPage();
 
-    @action
-    onThumbDown = (e: React.PointerEvent) => {
-        document.addEventListener("pointermove", this.onThumbMove, false);
-        document.addEventListener("pointerup", this.onThumbUp, false);
-        e.stopPropagation();
-        this._inThumb = true;
-    }
-    @action
-    onThumbMove = (e: PointerEvent) => {
-        let pso = (e.clientY - (e as any).target.parentElement.getBoundingClientRect().top) / (e as any).target.parentElement.getBoundingClientRect().height;
-        this.curPage = Math.trunc(Math.min(this.numPages, pso * this.numPages + 1));
-        e.stopPropagation();
-    }
-    @action
-    onThumbUp = (e: PointerEvent) => {
-        this._inThumb = false;
-        document.removeEventListener("pointermove", this.onThumbMove);
-        document.removeEventListener("pointerup", this.onThumbUp);
-    }
     nativeWidth = () => NumCast(this.props.Document.nativeWidth);
     nativeHeight = () => NumCast(this.props.Document.nativeHeight);
     private get uIButtons() {
@@ -92,11 +75,14 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
         }
     }
 
+    setPdfBox = (pdfBox: PDFBox) => { this._pdfBox = pdfBox; };
+
+
     private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => {
         let props = { ...this.props, ...renderProps };
         return (
             <>
-                <CollectionFreeFormView {...props} CollectionView={this} />
+                <CollectionFreeFormView {...props} setPdfBox={this.setPdfBox} CollectionView={this} />
                 {renderProps.active() ? this.uIButtons : (null)}
             </>
         );
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 583fa3e1a..0d5df550a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -453,7 +453,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     render() {
         var scaling = this.props.ContentScaling();
         var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%";
-        let ph = this.props.PanelHeight();
         var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
         return (
             <div className={`documentView-node${this.props.isTopMost ? "-topmost" : ""}`}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 5a83de8e3..cf6d2012f 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -18,6 +18,7 @@ import { FormattedTextBox } from "./FormattedTextBox";
 import { IconBox } from "./IconBox";
 import { ImageBox } from "./ImageBox";
 import { VideoBox } from "./VideoBox";
+import { PDFBox } from "./PDFBox";
 
 
 //
@@ -44,6 +45,7 @@ export interface FieldViewProps {
     PanelWidth: () => number;
     PanelHeight: () => number;
     setVideoBox?: (player: VideoBox) => void;
+    setPdfBox?: (player: PDFBox) => void;
 }
 
 @observer
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index b9ccd79e4..243982a3b 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,25 +1,22 @@
-import * as htmlToImage from "html-to-image";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx';
+import { action, IReactionDisposer, observable, reaction, trace, untracked } from 'mobx';
 import { observer } from "mobx-react";
 import 'react-image-lightbox/style.css';
-import Measure from "react-measure";
+import { WidthSym } from "../../../new_fields/Doc";
+import { makeInterface } from "../../../new_fields/Schema";
+import { Cast, NumCast } from "../../../new_fields/Types";
+import { PdfField } from "../../../new_fields/URLField";
 //@ts-ignore
 // import { Document, Page } from "react-pdf";
 // import 'react-pdf/dist/Page/AnnotationLayer.css';
 import { RouteStore } from "../../../server/RouteStore";
 import { DocComponent } from "../DocComponent";
 import { InkingControl } from "../InkingControl";
+import { PDFViewer } from "../pdf/PDFViewer";
 import { positionSchema } from "./DocumentView";
 import { FieldView, FieldViewProps } from './FieldView';
 import { pageSchema } from "./ImageBox";
 import "./PDFBox.scss";
 import React = require("react");
-import { NumCast, StrCast, Cast } from "../../../new_fields/Types";
-import { makeInterface } from "../../../new_fields/Schema";
-import { PDFViewer } from "../pdf/PDFViewer";
-import { PdfField } from "../../../new_fields/URLField";
-import { HeightSym, WidthSym } from "../../../new_fields/Doc";
-import { CollectionStackingView } from "../collections/CollectionStackingView";
 
 type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
 const PdfDocument = makeInterface(positionSchema, pageSchema);
@@ -31,19 +28,50 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     @observable private _alt = false;
     @observable private _scrollY: number = 0;
     private _reactionDisposer?: IReactionDisposer;
-    _targetDiv: any = undefined;
 
-    componentDidMount: () => void = () => {
+    componentDidMount() {
+        if (this.props.setPdfBox) this.props.setPdfBox(this);
+    }
+
+    public GetPage() {
+        return Math.floor(NumCast(this.props.Document.scrollY) / NumCast(this.Document.pdfHeight)) + 1;
+    }
+    public BackPage() {
+        let cp = Math.ceil(NumCast(this.props.Document.scrollY) / NumCast(this.Document.pdfHeight)) + 1;
+        cp = cp - 1;
+        if (cp > 0) {
+            this.props.Document.curPage = cp;
+            this.props.Document.scrollY = (cp - 1) * NumCast(this.Document.pdfHeight);
+        }
+    }
+    public GotoPage(p: number) {
+        if (p > 0 && p <= NumCast(this.props.Document.numPages)) {
+            this.props.Document.curPage = p;
+            this.props.Document.scrollY = (p - 1) * NumCast(this.Document.pdfHeight);
+        }
+    }
+
+    public ForwardPage() {
+        let cp = this.GetPage() + 1;
+        if (cp <= NumCast(this.props.Document.numPages)) {
+            this.props.Document.curPage = cp;
+            this.props.Document.scrollY = (cp - 1) * NumCast(this.Document.pdfHeight);
+        }
+    }
+
+    createRef = (ele: HTMLDivElement | null) => {
         if (this._reactionDisposer) this._reactionDisposer();
-        this._reactionDisposer = reaction(() => this.props.Document.scrollY, () =>
-            this._targetDiv && this._targetDiv.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "smooth" })
-        );
+        this._reactionDisposer = reaction(() => this.props.Document.scrollY, () => {
+            ele && ele.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "smooth" });
+        });
     }
 
-    loaded = (nw: number, nh: number) => {
+    loaded = (nw: number, nh: number, np: number) => {
         if (this.props.Document) {
-            if (this.props.Document.nativeWidth && this.props.Document.nativeHeight) return;
             let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
+            console.log("pages = " + np);
+            doc.numPages = np;
+            if (doc.nativeWidth && doc.nativeHeight) return;
             let oldaspect = NumCast(doc.nativeHeight) / NumCast(doc.nativeWidth, 1);
             doc.nativeWidth = nw;
             if (doc.nativeHeight) doc.nativeHeight = nw * oldaspect;
@@ -59,7 +87,6 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     @action
     onScroll = (e: React.UIEvent<HTMLDivElement>) => {
         if (e.currentTarget) {
-            this._targetDiv = e.currentTarget;
             this._scrollY = e.currentTarget.scrollTop;
             // e.currentTarget.scrollTo({ top: 1000, behavior: "smooth" });
             let ccv = this.props.ContainingCollectionView;
@@ -82,6 +109,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
                     overflowY: "scroll", overflowX: "hidden",
                     marginTop: `${NumCast(this.props.ContainingCollectionView!.props.Document.panY)}px`
                 }}
+                ref={this.createRef}
                 onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}>
                 <PDFViewer url={pdfUrl.url.href} loaded={this.loaded} scrollY={this._scrollY} parent={this} />
                 {/* <div style={{ width: "100px", height: "300px" }}></div> */}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 17f65c7a6..dee891ba6 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -23,7 +23,7 @@ import { Dictionary } from "typescript-collections";
 
 interface IPDFViewerProps {
     url: string;
-    loaded: (nw: number, nh: number) => void;
+    loaded: (nw: number, nh: number, np: number) => void;
     scrollY: number;
     parent: PDFBox;
 }
@@ -61,7 +61,7 @@ export class PDFViewer extends React.Component<IPDFViewerProps> {
 
 interface IViewerProps {
     pdf: Opt<Pdfjs.PDFDocumentProxy>;
-    loaded: (nw: number, nh: number) => void;
+    loaded: (nw: number, nh: number, np: number) => void;
     scrollY: number;
     parent: PDFBox;
     mainCont: React.RefObject<HTMLDivElement>;
@@ -400,7 +400,7 @@ class Viewer extends React.Component<IViewerProps> {
             return;
         }
         let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
-        this.props.loaded(page.width, page.height);
+        this.props.loaded(page.width, page.height, numPages);
         this._pageSizes[index - 1] = { width: page.width, height: page.height };
         this._pagesLoaded++;
         if (this._pagesLoaded === numPages) {
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 44c502a04..e3dbeaebe 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -132,16 +132,14 @@ export default class Page extends React.Component<IPageProps> {
         }
     }
 
-    highlight = (targetDoc: Doc | undefined) => {
+    highlight = (targetDoc?: Doc) => {
         // creates annotation documents for current highlights
         let annotationDoc = this.props.makeAnnotationDocuments(targetDoc);
-        let targetAnnotations = DocListCast(this.props.parent.Document.annotations);
-        if (targetAnnotations) {
+        let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc));
+        if (targetAnnotations === undefined) {
+            Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]);
+        } else {
             targetAnnotations.push(annotationDoc);
-            this.props.parent.Document.annotations = new List<Doc>(targetAnnotations);
-        }
-        else {
-            this.props.parent.Document.annotations = new List<Doc>([annotationDoc]);
         }
         return annotationDoc;
     }
-- 
cgit v1.2.3-70-g09d2


From 96f30f9beda568cf4d8687d07cbdd5467ab05b1b Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Sun, 16 Jun 2019 19:08:08 -0400
Subject: better virtualization

---
 package.json                       |   1 +
 src/client/views/nodes/PDFBox.tsx  |   2 +-
 src/client/views/pdf/PDFViewer.tsx |  47 ++++++++------
 src/server/index.ts                | 122 ++++++++++++++++++++++++++++---------
 4 files changed, 122 insertions(+), 50 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/package.json b/package.json
index 0fe26f16d..7fd6c4ba9 100644
--- a/package.json
+++ b/package.json
@@ -142,6 +142,7 @@
         "passport": "^0.4.0",
         "passport-local": "^1.0.0",
         "pdfjs-dist": "^2.0.943",
+        "probe-image-size": "^4.0.0",
         "prosemirror-commands": "^1.0.7",
         "prosemirror-example-setup": "^1.0.1",
         "prosemirror-history": "^1.0.4",
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 243982a3b..655c12ab3 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -111,7 +111,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
                 }}
                 ref={this.createRef}
                 onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}>
-                <PDFViewer url={pdfUrl.url.href} loaded={this.loaded} scrollY={this._scrollY} parent={this} />
+                <PDFViewer url={pdfUrl.url.pathname} loaded={this.loaded} scrollY={this._scrollY} parent={this} />
                 {/* <div style={{ width: "100px", height: "300px" }}></div> */}
             </div>
         );
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 55d15893a..75c298f55 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -20,6 +20,9 @@ import { emptyFunction, returnTrue, returnFalse } from "../../../Utils";
 import { DocumentView } from "../nodes/DocumentView";
 import { DragManager } from "../../util/DragManager";
 import { Dictionary } from "typescript-collections";
+import * as rp from "request-promise";
+import { restProperty } from "babel-types";
+import { DocServer } from "../../DocServer";
 
 export const scale = 2;
 interface IPDFViewerProps {
@@ -137,7 +140,8 @@ class Viewer extends React.Component<IViewerProps> {
         }
 
         setTimeout(() => {
-            this.renderPages(this.startIndex, this.endIndex, true);
+            // this.renderPages(this.startIndex, this.endIndex, true);
+            this.saveThumbnail();
         }, 1000);
     }
 
@@ -204,17 +208,19 @@ class Viewer extends React.Component<IViewerProps> {
     }
 
     @action
-    saveThumbnail = () => {
+    saveThumbnail = async () => {
         // file address of the pdf
         const address: string = this.props.url;
         for (let i = 0; i < this._visibleElements.length; i++) {
             if (this._isPage[i]) {
                 // change the address to be the file address of the PNG version of each page
-                let thisAddress = `${address.substring(0, address.length - ".pdf".length)}-${i + 1}.PNG`;
-                let nWidth = this._pageSizes[i].width;
-                let nHeight = this._pageSizes[i].height;
+                let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${i + 1}.PNG`)));
+                let thisAddress = res.path;
+                let nWidth = parseInt(res.width);
+                let nHeight = parseInt(res.height);
                 // replace page with image
-                this._visibleElements[i] = <img key={thisAddress} style={{ width: `${nWidth}px`, height: `${nHeight}px` }} src={thisAddress} />;
+                runInAction(() =>
+                    this._visibleElements[i] = <img key={thisAddress} style={{ width: `${nWidth * scale}px`, height: `${nHeight * scale}px` }} src={thisAddress} />);
             }
         }
     }
@@ -236,6 +242,7 @@ class Viewer extends React.Component<IViewerProps> {
         if (this.scrollY !== prevProps.scrollY || this._pdf !== this.props.pdf) {
             this._pdf = this.props.pdf;
             // render pages if the scorll position changes
+            console.log(`START: ${this.startIndex}, END: ${this.endIndex}`);
             this.renderPages(this.startIndex, this.endIndex);
         }
     }
@@ -269,7 +276,7 @@ class Viewer extends React.Component<IViewerProps> {
 
         // this is only for an initial render to get all of the pages rendered
         if (this._visibleElements.length !== numPages) {
-            let divs = Array.from(Array(numPages).keys()).map(i => (
+            let divs = Array.from(Array(numPages).keys()).map(i => i < 5 ? (
                 <Page
                     pdf={this.props.pdf}
                     page={i}
@@ -285,8 +292,10 @@ class Viewer extends React.Component<IViewerProps> {
                     makeAnnotationDocuments={this.makeAnnotationDocument}
                     receiveAnnotations={this.sendAnnotations}
                     {...this.props} />
-            ));
-            let arr = Array.from(Array(numPages).keys()).map(() => false);
+            ) :
+                (<div key={`pdfviewer-placeholder-${i}`} className="pdfviewer-placeholder" style={{ width: this._pageSizes[i] ? this._pageSizes[i].width : 612 * scale, height: this._pageSizes[i] ? this._pageSizes[i].height : 792 * scale }} />)
+            );
+            let arr = Array.from(Array(numPages).keys()).map(i => i < 5);
             this._visibleElements.push(...divs);
             this._isPage.push(...arr);
         }
@@ -378,16 +387,16 @@ class Viewer extends React.Component<IViewerProps> {
 
     // get the page index that the vertical offset passed in is on
     getIndex = (vOffset: number) => {
-        if (this._loaded) {
-            let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
-            let index = 0;
-            let currOffset = vOffset;
-            while (index < numPages && currOffset - this._pageSizes[index].height > 0) {
-                currOffset -= this._pageSizes[index].height;
-                index++;
-            }
-            return index;
-        }
+        // if (this._loaded) {
+        let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
+        let index = 0;
+        let currOffset = vOffset;
+        while (index < numPages && currOffset - (this._pageSizes[index] ? this._pageSizes[index].height : 792 * scale) > 0) {
+            currOffset -= this._pageSizes[index].height;
+            index++;
+        }
+        return index;
+        // }
         return 0;
     }
 
diff --git a/src/server/index.ts b/src/server/index.ts
index e22794df1..3e51eb4ff 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -38,8 +38,10 @@ import c = require("crypto");
 import { Search } from './Search';
 import { debug } from 'util';
 import _ = require('lodash');
+import { Response } from 'express-serve-static-core';
 const MongoStore = require('connect-mongo')(session);
 const mongoose = require('mongoose');
+const probe = require("probe-image-size");
 
 const download = (url: string, dest: fs.PathLike) => request.get(url).pipe(fs.createWriteStream(dest));
 
@@ -134,6 +136,66 @@ app.get("/search", async (req, res) => {
     res.send(results);
 });
 
+app.get("/thumbnail/:filename", (req, res) => {
+    let filename = req.params.filename;
+    let noExt = filename.substring(0, filename.length - ".png".length);
+    let pagenumber = parseInt(noExt[noExt.length - 1]);
+    fs.exists(uploadDir + filename, (exists: boolean) => {
+        console.log(`${uploadDir + filename} ${exists ? "exists" : "does not exist"}`);
+        if (exists) {
+            let input = fs.createReadStream(uploadDir + filename);
+            probe(input, (err: any, result: any) => {
+                if (err) {
+                    console.log(err);
+                    return;
+                }
+                console.log(result.width);
+                console.log(result.height);
+                res.send({ path: "/files/" + filename, width: result.width, height: result.height });
+            });
+        }
+        else {
+            LoadPage(uploadDir + filename.substring(0, filename.length - "-n.png".length) + ".pdf", pagenumber, res);
+        }
+    });
+});
+
+function LoadPage(file: string, pageNumber: number, res: Response) {
+    console.log(file);
+    Pdfjs.getDocument(file).promise
+        .then((pdf: Pdfjs.PDFDocumentProxy) => {
+            let factory = new NodeCanvasFactory();
+            console.log(pageNumber);
+            pdf.getPage(pageNumber).then((page: Pdfjs.PDFPageProxy) => {
+                console.log("reading " + page);
+                let viewport = page.getViewport(1);
+                let canvasAndContext = factory.create(viewport.width, viewport.height);
+                let renderContext = {
+                    canvasContext: canvasAndContext.context,
+                    viewport: viewport,
+                    canvasFactory: factory
+                }
+                console.log("read " + pageNumber);
+
+                page.render(renderContext).promise
+                    .then(() => {
+                        console.log("saving " + pageNumber);
+                        let stream = canvasAndContext.canvas.createPNGStream();
+                        let pngFile = `${file.substring(0, file.length - ".pdf".length)}-${pageNumber}.PNG`;
+                        let out = fs.createWriteStream(pngFile);
+                        stream.pipe(out);
+                        out.on("finish", () => {
+                            console.log(`Success! Saved to ${pngFile}`);
+                            let name = path.basename(pngFile);
+                            res.send({ path: "/files/" + name, width: viewport.width, height: viewport.height });
+                        });
+                    }, (reason: string) => {
+                        console.error(reason + ` ${pageNumber}`);
+                    });
+            });
+        });
+}
+
 // anyone attempting to navigate to localhost at this port will
 // first have to login
 addSecureRoute(
@@ -229,36 +291,36 @@ app.post(
                     isImage = true;
                 }
                 else if (pdfTypes.includes(ext)) {
-                    Pdfjs.getDocument(uploadDir + file).promise
-                        .then((pdf: Pdfjs.PDFDocumentProxy) => {
-                            let numPages = pdf.numPages;
-                            let factory = new NodeCanvasFactory();
-                            for (let pageNum = 0; pageNum < numPages; pageNum++) {
-                                console.log(pageNum);
-                                pdf.getPage(pageNum + 1).then((page: Pdfjs.PDFPageProxy) => {
-                                    console.log("reading " + pageNum);
-                                    let viewport = page.getViewport(1);
-                                    let canvasAndContext = factory.create(viewport.width, viewport.height);
-                                    let renderContext = {
-                                        canvasContext: canvasAndContext.context,
-                                        viewport: viewport,
-                                        canvasFactory: factory
-                                    }
-                                    console.log("read " + pageNum);
-
-                                    page.render(renderContext).promise
-                                        .then(() => {
-                                            console.log("saving " + pageNum);
-                                            let stream = canvasAndContext.canvas.createPNGStream();
-                                            let out = fs.createWriteStream(uploadDir + file.substring(0, file.length - ext.length) + `-${pageNum + 1}.PNG`);
-                                            stream.pipe(out);
-                                            out.on("finish", () => console.log(`Success! Saved to ${uploadDir + file.substring(0, file.length - ext.length) + `-${pageNum + 1}.PNG`}`));
-                                        }, (reason: string) => {
-                                            console.error(reason + ` ${pageNum}`);
-                                        });
-                                });
-                            }
-                        });
+                    // Pdfjs.getDocument(uploadDir + file).promise
+                    //     .then((pdf: Pdfjs.PDFDocumentProxy) => {
+                    //         let numPages = pdf.numPages;
+                    //         let factory = new NodeCanvasFactory();
+                    //         for (let pageNum = 0; pageNum < numPages; pageNum++) {
+                    //             console.log(pageNum);
+                    //             pdf.getPage(pageNum + 1).then((page: Pdfjs.PDFPageProxy) => {
+                    //                 console.log("reading " + pageNum);
+                    //                 let viewport = page.getViewport(1);
+                    //                 let canvasAndContext = factory.create(viewport.width, viewport.height);
+                    //                 let renderContext = {
+                    //                     canvasContext: canvasAndContext.context,
+                    //                     viewport: viewport,
+                    //                     canvasFactory: factory
+                    //                 }
+                    //                 console.log("read " + pageNum);
+
+                    //                 page.render(renderContext).promise
+                    //                     .then(() => {
+                    //                         console.log("saving " + pageNum);
+                    //                         let stream = canvasAndContext.canvas.createPNGStream();
+                    //                         let out = fs.createWriteStream(uploadDir + file.substring(0, file.length - ext.length) + `-${pageNum + 1}.PNG`);
+                    //                         stream.pipe(out);
+                    //                         out.on("finish", () => console.log(`Success! Saved to ${uploadDir + file.substring(0, file.length - ext.length) + `-${pageNum + 1}.PNG`}`));
+                    //                     }, (reason: string) => {
+                    //                         console.error(reason + ` ${pageNum}`);
+                    //                     });
+                    //             });
+                    //         }
+                    //     });
                 }
                 if (isImage) {
                     resizers.forEach(resizer => {
-- 
cgit v1.2.3-70-g09d2


From 62c781c0c79ac395c5e117d208a90485ff1ba599 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 18 Jun 2019 02:19:07 -0400
Subject: faster loading of PDFs

---
 src/client/views/nodes/PDFBox.tsx  |   1 -
 src/client/views/pdf/PDFViewer.tsx | 340 +++++++++++++------------------------
 2 files changed, 116 insertions(+), 225 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 655c12ab3..3aaa5749d 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -69,7 +69,6 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     loaded = (nw: number, nh: number, np: number) => {
         if (this.props.Document) {
             let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
-            console.log("pages = " + np);
             doc.numPages = np;
             if (doc.nativeWidth && doc.nativeHeight) return;
             let oldaspect = NumCast(doc.nativeHeight) / NumCast(doc.nativeWidth, 1);
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index d74a16f3f..5149e48fd 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -43,16 +43,7 @@ export class PDFViewer extends React.Component<IPDFViewerProps> {
 
     @action
     componentDidMount() {
-        const pdfUrl = this.props.url;
-        console.log("pdf starting to load")
-        let promise = Pdfjs.getDocument(pdfUrl).promise;
-
-        promise.then((pdf: Pdfjs.PDFDocumentProxy) => {
-            runInAction(() => {
-                console.log("pdf url received");
-                this._pdf = pdf;
-            });
-        });
+        Pdfjs.getDocument(this.props.url).promise.then(pdf => runInAction(() => this._pdf = pdf));
     }
 
     render() {
@@ -83,12 +74,8 @@ 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: boolean[] = [];
+    @observable private _isPage: string[] = [];
     @observable private _pageSizes: { width: number, height: number }[] = [];
-    @observable private _startIndex: number = 0;
-    @observable private _endIndex: number = 1;
-    @observable private _loaded: boolean = false;
-    @observable private _pdf: Opt<Pdfjs.PDFDocumentProxy>;
     @observable private _annotations: Doc[] = [];
     @observable private _pointerEvents: "all" | "none" = "all";
     @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
@@ -97,7 +84,6 @@ class Viewer extends React.Component<IViewerProps> {
     private _annotationLayer: React.RefObject<HTMLDivElement>;
     private _reactionDisposer?: IReactionDisposer;
     private _annotationReactionDisposer?: IReactionDisposer;
-    private _pagesLoaded: number = 0;
     private _dropDisposer?: DragManager.DragDropDisposer;
 
     constructor(props: IViewerProps) {
@@ -106,77 +92,62 @@ class Viewer extends React.Component<IViewerProps> {
         this._annotationLayer = React.createRef();
     }
 
+    componentDidUpdate = (prevProps: IViewerProps) => {
+        if (this.scrollY !== prevProps.scrollY && this._visibleElements.length) {
+            this.renderPages(this.startIndex, this.endIndex, false);
+        }
+    }
+
     @action
     componentDidMount = () => {
         let wasSelected = this.props.parent.props.active();
-        // reaction for when document gets (de)selected
         this._reactionDisposer = reaction(
-            () => [this.props.parent.props.active(), this.startIndex],
-            () => {
-                // if deselected, render images in place of pdf
-                if (wasSelected && !this.props.parent.props.active()) {
-                    this.saveThumbnail();
-                }
-                // if selected, render pdf
-                else if (!wasSelected && this.props.parent.props.active()) {
-                    this.renderPages(this.startIndex, this.endIndex, true);
-                }
+            () => [this.props.parent.props.active(), this.startIndex, this.props.pdf],
+            async () => {
+                await this.initialLoad();
                 wasSelected = this.props.parent.props.active();
-                this._pointerEvents = wasSelected ? "none" : "all";
-            },
-            { fireImmediately: true }
-        );
+                runInAction(() => this._pointerEvents = wasSelected ? "none" : "all");
+                this.renderPages(this.startIndex, this.endIndex, false);
+            }, { fireImmediately: true });
 
-        if (this.props.parent.Document) {
-            this._annotationReactionDisposer = reaction(
-                () => DocListCast(this.props.parent.Document.annotations),
-                () => {
-                    let annotations = DocListCast(this.props.parent.Document.annotations);
-                    if (annotations && annotations.length) {
-                        this.renderAnnotations(annotations, true);
-                    }
-                },
-                { fireImmediately: true }
-            );
-        }
+        this._annotationReactionDisposer = reaction(
+            () => this.props.parent.Document && DocListCast(this.props.parent.Document.annotations),
+            (annotations: Doc[]) =>
+                annotations && annotations.length && this.renderAnnotations(annotations, true),
+            { fireImmediately: true });
+    }
 
-        setTimeout(() => {
-            // this.renderPages(this.startIndex, this.endIndex, true);
-            this.initialLoad();
-        }, 1000);
+    componentWillUnmount = () => {
+        this._reactionDisposer && this._reactionDisposer();
+        this._annotationReactionDisposer && this._annotationReactionDisposer();
     }
 
     @action
-    initialLoad = () => {
-        let pdf = this.props.pdf;
-        if (pdf) {
-            this._pageSizes = Array<{ width: number, height: number }>(pdf.numPages);
-            let rendered = 0;
-            for (let i = 0; i < pdf.numPages; i++) {
-                pdf.getPage(i + 1).then(
-                    (page: Pdfjs.PDFPageProxy) => {
-                        runInAction(() => {
-                            this._pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale };
-                        });
-                        console.log(`page ${i} size retreieved`);
-                        rendered++;
-                        if (rendered === pdf!.numPages - 1) {
-                            this.saveThumbnail();
-                        }
-                    }
-                );
+    initialLoad = async () => {
+        if (this.props.pdf && this._pageSizes.length === 0) {
+            let pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages);
+            for (let i = 0; i < this.props.pdf.numPages; i++) {
+                await this.props.pdf.getPage(i + 1).then(page => runInAction(() => {
+                    pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale };
+                    if (i === 0) this.props.loaded(pageSizes[i].width, pageSizes[i].height, this.props.pdf!.numPages);
+                }));
             }
+            runInAction(() => {
+                this._pageSizes = pageSizes;
+                let divs = Array.from(Array(this._pageSizes.length).keys()).map(i => (
+                    <div key={`pdfviewer-placeholder-${i}`} className="pdfviewer-placeholder"
+                        style={{ width: this._pageSizes[i] ? this._pageSizes[i].width : 0, height: this._pageSizes[i] ? this._pageSizes[i].height : 0 }} />
+                ));
+                this._isPage = Array.from(Array(this._pageSizes.length).map(p => "none"));
+                this._visibleElements = new Array<JSX.Element>(...divs);
+            })
         }
     }
 
     private mainCont = (div: HTMLDivElement | null) => {
-        if (this._dropDisposer) {
-            this._dropDisposer();
-        }
+        this._dropDisposer && this._dropDisposer();
         if (div) {
-            this._dropDisposer = DragManager.MakeDropTarget(div, {
-                handlers: { drop: this.drop.bind(this) }
-            });
+            this._dropDisposer = div && DragManager.MakeDropTarget(div, { handlers: { drop: this.drop.bind(this) } });
         }
     }
 
@@ -222,154 +193,102 @@ class Viewer extends React.Component<IViewerProps> {
             e.stopPropagation();
         }
     }
-
-    componentWillUnmount = () => {
-        if (this._reactionDisposer) {
-            this._reactionDisposer();
-        }
-        if (this._annotationReactionDisposer) {
-            this._annotationReactionDisposer();
-        }
-    }
-
+    /**
+     * 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
-    saveThumbnail = async () => {
-        // file address of the pdf
-        const address: string = this.props.url;
-        for (let i = 0; i < this._visibleElements.length; i++) {
-            if (this._isPage[i]) {
-                // change the address to be the file address of the PNG version of each page
-                let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${i + 1}.PNG`)));
-                let thisAddress = res.path;
-                let nWidth = parseInt(res.width);
-                let nHeight = parseInt(res.height);
-                // replace page with image
-                runInAction(() =>
-                    this._visibleElements[i] = <img key={thisAddress} style={{ width: `${nWidth * scale}px`, height: `${nHeight * scale}px` }} src={thisAddress} />);
-            }
-        }
-    }
-
-    @computed get scrollY(): number {
-        return this.props.scrollY;
-    }
-
-    @computed get startIndex(): number {
-        return Math.max(0, this.getIndex(this.scrollY) - this._pageBuffer);
-    }
-
-    @computed get endIndex(): number {
-        let width = this._pageSizes.map(i => i ? i.width : 0);
-        return Math.min(this.props.pdf ? this.props.pdf.numPages - 1 : 0, this.getIndex(this.scrollY + Math.max(...width)) + this._pageBuffer);
-    }
-
-    componentDidUpdate = (prevProps: IViewerProps) => {
-        if (this.scrollY !== prevProps.scrollY || this._pdf !== this.props.pdf) {
-            this._pdf = this.props.pdf;
-            // render pages if the scorll position changes
-            console.log(`START: ${this.startIndex}, END: ${this.endIndex}`);
-            this.renderPages(this.startIndex, this.endIndex);
-        }
+    pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => {
+        this.props.pdf && this.props.loaded && this.props.loaded(page.width, page.height, this.props.pdf.numPages);
     }
-
     @action
-    private renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => {
-        if (removeOldAnnotations) {
-            this._annotations = annotations;
-        }
-        else {
-            this._annotations.push(...annotations);
-            this._annotations = new Array<Doc>(...this._annotations);
+    getPlaceholderPage = (page: number) => {
+        if (this._isPage[page] !== "none") {
+            this._isPage[page] = "none";
+            this._visibleElements[page] = (
+                <div key={`pdfviewer-placeholder-${page}`} className="pdfviewer-placeholder" style={{ width: this._pageSizes[page] ? this._pageSizes[page].width : 0, height: this._pageSizes[page] ? this._pageSizes[page].height : 0 }} />
+            );
         }
     }
-
-    /**
-     * @param startIndex: where to start rendering pages
-     * @param endIndex: where to end rendering pages
-     * @param forceRender: (optional), force pdfs to re-render, even if the page already exists
-     */
     @action
-    renderPages = (startIndex: number, endIndex: number, forceRender: boolean = false) => {
-        let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
-        if (!this.props.pdf) {
-            return;
-        }
-
-        if (this._pageSizes.length !== numPages) {
-            this._pageSizes = new Array(numPages).map(i => ({ width: 0, height: 0 }));
-        }
-
-        // this is only for an initial render to get all of the pages rendered
-        if (this._visibleElements.length !== numPages) {
-            let divs = Array.from(Array(numPages).keys()).map(i => i < 5 ? (
+    getRenderedPage = (page: number) => {
+        if (this._isPage[page] !== "page") {
+            this._isPage[page] = "page";
+            this._visibleElements[page] = (
                 <Page
                     pdf={this.props.pdf}
-                    page={i}
-                    numPages={numPages}
-                    key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
-                    name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
+                    page={page}
+                    numPages={this.props.pdf!.numPages}
+                    key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${page + 1}` : "undefined"}`}
+                    name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${page + 1}` : "undefined"}`}
                     pageLoaded={this.pageLoaded}
                     parent={this.props.parent}
-                    renderAnnotations={this.renderAnnotations}
                     makePin={this.createPinAnnotation}
+                    renderAnnotations={this.renderAnnotations}
                     createAnnotation={this.createAnnotation}
                     sendAnnotations={this.receiveAnnotations}
                     makeAnnotationDocuments={this.makeAnnotationDocument}
                     receiveAnnotations={this.sendAnnotations}
                     {...this.props} />
-            ) :
-                (<div key={`pdfviewer-placeholder-${i}`} className="pdfviewer-placeholder" style={{ width: this._pageSizes[i] ? this._pageSizes[i].width : 612 * scale, height: this._pageSizes[i] ? this._pageSizes[i].height : 792 * scale }} />)
             );
-            let arr = Array.from(Array(numPages).keys()).map(i => i < 5);
-            this._visibleElements.push(...divs);
-            this._isPage.push(...arr);
         }
+    }
 
-        // if nothing changed, return
-        if (startIndex === this._startIndex && endIndex === this._endIndex && !forceRender) {
-            return;
+    // change the address to be the file address of the PNG version of each page
+    // 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;
+            let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`)));
+            runInAction(() => this._visibleElements[page] = <img key={res.path} src={res.path} onError={handleError}
+                style={{ width: `${parseInt(res.width) * scale}px`, height: `${parseInt(res.height) * scale}px` }} />);
         }
+    }
 
-        // unrender pages outside of the pdf by replacing them with empty stand-in divs
-        for (let i = 0; i < numPages; i++) {
-            if (i < startIndex || i > endIndex) {
-                if (this._isPage[i]) {
-                    this._visibleElements[i] = (
-                        <div key={`pdfviewer-placeholder-${i}`} className="pdfviewer-placeholder" style={{ width: this._pageSizes[i] ? this._pageSizes[i].width : 0, height: this._pageSizes[i] ? this._pageSizes[i].height : 0 }} />
-                    );
+    @computed get scrollY(): number { return this.props.scrollY; }
+
+    @computed get startIndex(): number { return Math.max(0, this.getPageFromScroll(this.scrollY) - this._pageBuffer); }
+
+    @computed get endIndex(): number {
+        let width = this._pageSizes.map(i => i ? i.width : 0);
+        return Math.min(this.props.pdf ? this.props.pdf.numPages - 1 : 0, this.getPageFromScroll(this.scrollY + Math.max(...width)) + this._pageBuffer);
+    }
+
+    /**
+     * @param startIndex: where to start rendering pages
+     * @param endIndex: where to end rendering pages
+     * @param forceRender: (optional), force pdfs to re-render, even if the page already exists
+     */
+    @action
+    renderPages = (startIndex: number, endIndex: number, forceRender: boolean = false) => {
+        if (this.props.pdf) {
+            // unrender pages outside of the pdf by replacing them with empty stand-in divs
+            for (let i = 0; i < this.props.pdf.numPages; i++) {
+                if (i < startIndex || i > endIndex) {
+                    this.getPlaceholderPage(i);
+                } else {
+                    if (this.props.parent.props.active()) {
+                        this.getRenderedPage(i);
+                    } else {
+                        this.getPageImage(i);
+                    }
                 }
-                this._isPage[i] = false;
             }
         }
+    }
 
-        // render pages for any indices that don't already have pages (force rerender will make these render regardless)
-        for (let i = startIndex; i <= endIndex; i++) {
-            if (!this._isPage[i] || (this._isPage[i] && forceRender)) {
-                this._visibleElements[i] = (
-                    <Page
-                        pdf={this.props.pdf}
-                        page={i}
-                        numPages={numPages}
-                        key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
-                        name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
-                        pageLoaded={this.pageLoaded}
-                        parent={this.props.parent}
-                        makePin={this.createPinAnnotation}
-                        renderAnnotations={this.renderAnnotations}
-                        createAnnotation={this.createAnnotation}
-                        sendAnnotations={this.receiveAnnotations}
-                        makeAnnotationDocuments={this.makeAnnotationDocument}
-                        receiveAnnotations={this.sendAnnotations}
-                        {...this.props} />
-                );
-                this._isPage[i] = true;
-            }
+    @action
+    private renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => {
+        if (removeOldAnnotations) {
+            this._annotations = annotations;
+        }
+        else {
+            this._annotations.push(...annotations);
+            this._annotations = new Array<Doc>(...this._annotations);
         }
-
-        this._startIndex = startIndex;
-        this._endIndex = endIndex;
-
-        return;
     }
 
     @action
@@ -392,7 +311,7 @@ class Viewer extends React.Component<IViewerProps> {
 
         let pinAnno = new Doc();
         pinAnno.x = x;
-        pinAnno.y = y + this.getPageHeight(page);
+        pinAnno.y = y + this.getScrollFromPage(page);
         pinAnno.width = pinAnno.height = PinRadius;
         pinAnno.page = page;
         pinAnno.target = targetDoc;
@@ -411,9 +330,7 @@ class Viewer extends React.Component<IViewerProps> {
     }
 
     // get the page index that the vertical offset passed in is on
-    getIndex = (vOffset: number) => {
-        // if (this._loaded) {
-        let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
+    getPageFromScroll = (vOffset: number) => {
         let index = 0;
         let currOffset = vOffset;
         while (index < this._pageSizes.length && currOffset - (this._pageSizes[index] ? this._pageSizes[index].height : 792 * scale) > 0) {
@@ -421,34 +338,10 @@ class Viewer extends React.Component<IViewerProps> {
             index++;
         }
         return index;
-        // }
-        return 0;
     }
 
-    /**
-     * 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 => {
-        if (this._loaded) {
-            return;
-        }
-        let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
-        this.props.loaded(page.width, page.height, numPages);
-        this._pageSizes[index - 1] = { width: page.width, height: page.height };
-        this._pagesLoaded++;
-        if (this._pagesLoaded === numPages) {
-            this._loaded = true;
-            let divs = Array.from(Array(numPages).keys()).map(i => (
-                <div key={`pdfviewer-placeholder-${i}`} className="pdfviewer-placeholder" style={{ width: this._pageSizes[i] ? this._pageSizes[i].width : 0, height: this._pageSizes[i] ? this._pageSizes[i].height : 0 }} />
-            ));
-            this._visibleElements = new Array<JSX.Element>(...divs);
-            this.renderPages(this.startIndex, this.endIndex, true);
-        }
-    }
 
-    getPageHeight = (index: number): number => {
+    getScrollFromPage = (index: number): number => {
         let counter = 0;
         if (this.props.pdf && index < this.props.pdf.numPages) {
             for (let i = 0; i < index; i++) {
@@ -463,7 +356,7 @@ class Viewer extends React.Component<IViewerProps> {
     createAnnotation = (div: HTMLDivElement, page: number) => {
         if (this._annotationLayer.current) {
             if (div.style.top) {
-                div.style.top = (parseInt(div.style.top) + this.getPageHeight(page)).toString();
+                div.style.top = (parseInt(div.style.top) + this.getScrollFromPage(page)).toString();
             }
             this._annotationLayer.current.append(div);
             let savedPage = this._savedAnnotations.getValue(page);
@@ -494,7 +387,6 @@ class Viewer extends React.Component<IViewerProps> {
     }
 
     render() {
-        trace();
         return (
             <div ref={this.mainCont} style={{ pointerEvents: "all" }}>
                 <div className="viewer">
-- 
cgit v1.2.3-70-g09d2


From 4b8324fcf44c5d3c3a4b3f6e98a4d1dfce84811b Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 18 Jun 2019 08:53:01 -0400
Subject: removed trace

---
 src/client/views/nodes/PDFBox.tsx | 1 -
 1 file changed, 1 deletion(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 3aaa5749d..e79182e9b 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -96,7 +96,6 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     }
 
     render() {
-        trace();
         // uses mozilla pdf as default
         const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"));
         console.log(pdfUrl);
-- 
cgit v1.2.3-70-g09d2


From 64e6a941639aab8d7109178aa151a50909547309 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 18 Jun 2019 09:05:41 -0400
Subject: fixed index out of range

---
 src/client/views/nodes/PDFBox.tsx  | 1 -
 src/client/views/pdf/PDFViewer.tsx | 5 +++--
 2 files changed, 3 insertions(+), 3 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index e79182e9b..38e91ac12 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -98,7 +98,6 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     render() {
         // uses mozilla pdf as default
         const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"));
-        console.log(pdfUrl);
         let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
         return (
             <div onScroll={this.onScroll}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index e5fb32e53..89f4fd778 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -111,13 +111,14 @@ class Viewer extends React.Component<IViewerProps> {
     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);
             for (let i = 0; i < this.props.pdf.numPages; i++) {
                 await this.props.pdf.getPage(i + 1).then(page => runInAction(() =>
                     pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale }));
             }
-            this.props.loaded(pageSizes[0].width, pageSizes[0].height, this.props.pdf.numPages);
             runInAction(() =>
-                Array.from(Array((this._pageSizes = pageSizes).length).keys()).map(this.getPlaceholderPage))
+                Array.from(Array((this._pageSizes = pageSizes).length).keys()).map(this.getPlaceholderPage));
+            this.props.loaded(pageSizes[0].width, pageSizes[0].height, this.props.pdf.numPages);
         }
     }
 
-- 
cgit v1.2.3-70-g09d2


From cc032e2f60015728f64f46ef009c9306e36746a0 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Tue, 18 Jun 2019 10:05:49 -0400
Subject: fixes

---
 src/client/views/nodes/PDFBox.tsx  |  2 +-
 src/client/views/pdf/PDFMenu.tsx   | 24 +++++++++++++++++-------
 src/client/views/pdf/PDFViewer.tsx | 21 +++++++++++++++++++++
 3 files changed, 39 insertions(+), 8 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 655c12ab3..ae68a530e 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -62,7 +62,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     createRef = (ele: HTMLDivElement | null) => {
         if (this._reactionDisposer) this._reactionDisposer();
         this._reactionDisposer = reaction(() => this.props.Document.scrollY, () => {
-            ele && ele.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "smooth" });
+            ele && ele.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "auto" });
         });
     }
 
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index b44370e3d..7817e8c26 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -10,8 +10,8 @@ import { Doc } from "../../../new_fields/Doc";
 export default class PDFMenu extends React.Component {
     static Instance: PDFMenu;
 
-    @observable private _top: number = 0;
-    @observable private _left: number = 0;
+    @observable private _top: number = -300;
+    @observable private _left: number = -300;
     @observable private _opacity: number = 1;
     @observable private _transition: string = "opacity 0.5s";
     @observable private _transitionDelay: string = "";
@@ -22,18 +22,25 @@ export default class PDFMenu extends React.Component {
     @observable Highlighting: boolean = false;
 
     private _timeout: NodeJS.Timeout | undefined;
+    private _offsetY: number = 0;
+    private _offsetX: number = 0;
+    private _mainCont: React.RefObject<HTMLDivElement>;
 
     constructor(props: Readonly<{}>) {
         super(props);
 
         PDFMenu.Instance = this;
+
+        this._mainCont = React.createRef();
     }
 
     pointerDown = (e: React.PointerEvent) => {
         document.removeEventListener("pointermove", this.StartDrag);
         document.addEventListener("pointermove", this.StartDrag);
-        document.removeEventListener("pointerup", this.pointerUp)
-        document.addEventListener("pointerup", this.pointerUp)
+        document.removeEventListener("pointerup", this.pointerUp);
+        document.addEventListener("pointerup", this.pointerUp);
+
+        console.log(this.StartDrag);
 
         e.stopPropagation();
         e.preventDefault();
@@ -102,8 +109,8 @@ export default class PDFMenu extends React.Component {
 
     @action
     dragging = (e: PointerEvent) => {
-        this._left += e.movementX;
-        this._top += e.movementY;
+        this._left = e.pageX - this._offsetX;
+        this._top = e.pageY - this._offsetY;
 
         e.stopPropagation();
         e.preventDefault();
@@ -122,6 +129,9 @@ export default class PDFMenu extends React.Component {
         document.removeEventListener("pointerup", this.dragEnd);
         document.addEventListener("pointerup", this.dragEnd);
 
+        this._offsetX = this._mainCont.current!.getBoundingClientRect().width - e.nativeEvent.offsetX;
+        this._offsetY = e.nativeEvent.offsetY;
+
         e.stopPropagation();
         e.preventDefault();
     }
@@ -139,7 +149,7 @@ export default class PDFMenu extends React.Component {
 
     render() {
         return (
-            <div className="pdfMenu-cont" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered}
+            <div className="pdfMenu-cont" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont}
                 style={{ left: this._left, top: this._top, opacity: this._opacity, transition: this._transition, transitionDelay: this._transitionDelay }}>
                 <button className="pdfMenu-button" title="Highlight" onClick={this.highlightClicked}
                     style={this.Highlighting ? { backgroundColor: "#121212" } : {}}>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index d74a16f3f..e13d11fe6 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -24,6 +24,7 @@ import * as rp from "request-promise";
 import { restProperty } from "babel-types";
 import { DocServer } from "../../DocServer";
 import { number } from "prop-types";
+import PDFMenu from "./PDFMenu";
 
 export const scale = 2;
 interface IPDFViewerProps {
@@ -474,6 +475,26 @@ class Viewer extends React.Component<IViewerProps> {
             else {
                 this._savedAnnotations.setValue(page, [div]);
             }
+            PDFMenu.Instance.StartDrag = this.startDrag;
+        }
+    }
+
+    startDrag = (e: PointerEvent) => {
+        e.preventDefault();
+        e.stopPropagation();
+        let thisDoc = this.props.parent.Document;
+        // document that this annotation is linked to
+        let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" });
+        targetDoc.targetPage = Math.min(...this._savedAnnotations.keys());
+        let annotationDoc = this.makeAnnotationDocument(targetDoc, 1, "red");
+        let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc);
+        if (this._annotationLayer.current) {
+            DragManager.StartAnnotationDrag([this._annotationLayer.current], dragData, e.pageX, e.pageY, {
+                handlers: {
+                    dragComplete: action(emptyFunction),
+                },
+                hideSource: false
+            });
         }
     }
 
-- 
cgit v1.2.3-70-g09d2


From 66d4cc94bcc69f590d90dd35823f93b8e2fb90d8 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Tue, 18 Jun 2019 10:52:10 -0400
Subject: selection fixes

---
 src/client/views/nodes/PDFBox.tsx  | 5 +++--
 src/client/views/pdf/PDFViewer.tsx | 6 +++---
 src/client/views/pdf/Page.tsx      | 3 ++-
 3 files changed, 8 insertions(+), 6 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 80d274c6d..7dd2b1dc5 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -87,7 +87,6 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     onScroll = (e: React.UIEvent<HTMLDivElement>) => {
         if (e.currentTarget) {
             this._scrollY = e.currentTarget.scrollTop;
-            // e.currentTarget.scrollTo({ top: 1000, behavior: "smooth" });
             let ccv = this.props.ContainingCollectionView;
             if (ccv) {
                 ccv.props.Document.scrollY = this._scrollY;
@@ -107,7 +106,9 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
                     marginTop: `${NumCast(this.props.ContainingCollectionView!.props.Document.panY)}px`
                 }}
                 ref={this.createRef}
-                onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}>
+                onWheel={(e: React.WheelEvent) => {
+                    e.stopPropagation();
+                }} className={classname}>
                 <PDFViewer url={pdfUrl.url.pathname} loaded={this.loaded} scrollY={this._scrollY} parent={this} />
                 {/* <div style={{ width: "100px", height: "300px" }}></div> */}
             </div>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 86a17c0a6..69372f43b 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -388,7 +388,7 @@ class Viewer extends React.Component<IViewerProps> {
                         {this._annotations.map(anno => this.renderAnnotation(anno))}
                     </div>
                 </div>
-            </div>
+            </div >
         );
     }
 }
@@ -522,7 +522,7 @@ class PinAnnotation extends React.Component<IAnnotationProps> {
 class RegionAnnotation extends React.Component<IAnnotationProps> {
     @observable private _backgroundColor: string = "red";
 
-    onPointerDown = (e: React.PointerEvent) => {
+    onPointerDown = (e: React.MouseEvent) => {
         let targetDoc = Cast(this.props.document.target, Doc, null);
         if (targetDoc) {
             DocumentManager.Instance.jumpToDocument(targetDoc);
@@ -531,7 +531,7 @@ class RegionAnnotation extends React.Component<IAnnotationProps> {
 
     render() {
         return (
-            <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown}
+            <div className="pdfViewer-annotationBox" onClick={this.onPointerDown}
                 style={{ top: this.props.y * scale, left: this.props.x * scale, width: this.props.width * scale, height: this.props.height * scale, pointerEvents: "all", backgroundColor: StrCast(this.props.document.color) }}></div>
         );
     }
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index bb87ec9d4..a19b64eda 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -402,7 +402,8 @@ export default class Page extends React.Component<IPageProps> {
             let boundingRect = this._textLayer.current.getBoundingClientRect();
             for (let i = 0; i < clientRects.length; i++) {
                 let rect = clientRects.item(i);
-                if (rect) {
+                if (rect && rect.width !== this._textLayer.current.getBoundingClientRect().width && rect.height !== this._textLayer.current.getBoundingClientRect().height) {
+                    console.log(rect);
                     let annoBox = document.createElement("div");
                     annoBox.className = "pdfViewer-annotationBox";
                     // transforms the positions from screen onto the pdf div
-- 
cgit v1.2.3-70-g09d2


From 37f327ab659e6fa1221f9f4ed7649402c5dedc00 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Wed, 19 Jun 2019 11:24:32 -0400
Subject: aspect ratio, dragging, and full screen scrolling fixed

---
 src/client/views/nodes/PDFBox.tsx  |  2 +-
 src/client/views/pdf/PDFMenu.tsx   | 20 ++++++++++++++++----
 src/client/views/pdf/PDFViewer.tsx | 15 ++++++++++-----
 src/client/views/pdf/Page.tsx      | 10 +++-------
 4 files changed, 30 insertions(+), 17 deletions(-)

(limited to 'src/client/views/nodes/PDFBox.tsx')

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 7dd2b1dc5..d2de1cb1c 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -97,7 +97,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     render() {
         // uses mozilla pdf as default
         const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"));
-        let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
+        let classname = "pdfBox-cont" + (this.props.active() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
         return (
             <div onScroll={this.onScroll}
                 style={{
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index 2ed891131..2462a5f94 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -25,6 +25,7 @@ export default class PDFMenu extends React.Component {
     private _offsetY: number = 0;
     private _offsetX: number = 0;
     private _mainCont: React.RefObject<HTMLDivElement>;
+    private _dragging: boolean = false;
 
     constructor(props: Readonly<{}>) {
         super(props);
@@ -35,19 +36,30 @@ export default class PDFMenu extends React.Component {
     }
 
     pointerDown = (e: React.PointerEvent) => {
-        document.removeEventListener("pointermove", this.StartDrag);
-        document.addEventListener("pointermove", this.StartDrag);
+        document.removeEventListener("pointermove", this.pointerMove);
+        document.addEventListener("pointermove", this.pointerMove);
         document.removeEventListener("pointerup", this.pointerUp);
         document.addEventListener("pointerup", this.pointerUp);
 
-        console.log(this.StartDrag);
+        e.stopPropagation();
+        e.preventDefault();
+    }
 
+    pointerMove = (e: PointerEvent) => {
         e.stopPropagation();
         e.preventDefault();
+
+        if (this._dragging) {
+            return;
+        }
+
+        this.StartDrag(e);
+        this._dragging = true;
     }
 
     pointerUp = (e: PointerEvent) => {
-        document.removeEventListener("pointermove", this.StartDrag);
+        this._dragging = false;
+        document.removeEventListener("pointermove", this.pointerMove);
         document.removeEventListener("pointerup", this.pointerUp);
         e.stopPropagation();
         e.preventDefault();
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 8c0aaea00..67278b1eb 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -90,7 +90,8 @@ class Viewer extends React.Component<IViewerProps> {
     @action
     componentDidMount = () => {
         this._reactionDisposer = reaction(
-            () => [this.props.parent.props.active(), this.startIndex, this.endIndex],
+
+            () => [this.props.parent.props.active(), this.startIndex, this._pageSizes.length ? this.endIndex : 0],
             async () => {
                 await this.initialLoad();
                 this.renderPages();
@@ -114,12 +115,16 @@ class Viewer extends React.Component<IViewerProps> {
             let pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages);
             this._isPage = Array<string>(this.props.pdf.numPages);
             for (let i = 0; i < this.props.pdf.numPages; i++) {
-                await this.props.pdf.getPage(i + 1).then(page => runInAction(() =>
-                    pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale }));
+                await this.props.pdf.getPage(i + 1).then(page => runInAction(() => {
+                    // pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale };
+                    let x = page.getViewport(scale);
+                    pageSizes[i] = { width: x.width, height: x.height };
+                }));
             }
             runInAction(() =>
                 Array.from(Array((this._pageSizes = pageSizes).length).keys()).map(this.getPlaceholderPage));
-            this.props.loaded(pageSizes[0].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);
+            // this.props.loaded(Math.max(...pageSizes.map(i => i.width)), pageSizes[0].height, this.props.pdf.numPages);
         }
     }
 
@@ -236,7 +241,7 @@ class Viewer extends React.Component<IViewerProps> {
 
     // endIndex: where to end rendering pages
     @computed get endIndex(): number {
-        return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.scrollY) + this._pageBuffer);
+        return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.scrollY + this._pageSizes[0].height) + this._pageBuffer);
     }
 
     @action
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index a19b64eda..2697c9eee 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -51,7 +51,6 @@ export default class Page extends React.Component<IPageProps> {
     private _marquee: React.RefObject<HTMLDivElement>;
     private _curly: React.RefObject<HTMLImageElement>;
     private _marqueeing: boolean = false;
-    private _dragging: boolean = false;
     private _reactionDisposer?: IReactionDisposer;
 
     constructor(props: IPageProps) {
@@ -151,13 +150,9 @@ export default class Page extends React.Component<IPageProps> {
      */
     @action
     startDrag = (e: PointerEvent): void => {
-        // the first 5 lines is a hack to prevent text selection while dragging
         e.preventDefault();
         e.stopPropagation();
-        if (this._dragging) {
-            return;
-        }
-        this._dragging = true;
+        console.log("dragging");
         let thisDoc = this.props.parent.Document;
         // document that this annotation is linked to
         let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" });
@@ -179,7 +174,6 @@ export default class Page extends React.Component<IPageProps> {
     endDrag = (e: PointerEvent): void => {
         // document.removeEventListener("pointermove", this.startDrag);
         // document.removeEventListener("pointerup", this.endDrag);
-        this._dragging = false;
         e.stopPropagation();
     }
 
@@ -195,6 +189,8 @@ export default class Page extends React.Component<IPageProps> {
             // document.addEventListener("pointerup", this.endDrag);
         }
         else if (e.button === 0) {
+            PDFMenu.Instance.StartDrag = this.startDrag;
+            PDFMenu.Instance.Highlight = this.highlight;
             PDFMenu.Instance.fadeOut(true);
             let target: any = e.target;
             if (target && target.parentElement === this._textLayer.current) {
-- 
cgit v1.2.3-70-g09d2

