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/pdf/PDFViewer.tsx | 183 +++++++++++++++++++++++++++++++++++++
 1 file changed, 183 insertions(+)
 create mode 100644 src/client/views/pdf/PDFViewer.tsx

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
new file mode 100644
index 000000000..558a4fab6
--- /dev/null
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -0,0 +1,183 @@
+import { observer } from "mobx-react";
+import React = require("react");
+import { observable, action, runInAction } from "mobx";
+import { RouteStore } from "../../../server/RouteStore";
+import * as Pdfjs from "pdfjs-dist";
+import { Opt } from "../../../new_fields/Doc";
+
+interface IPDFViewerProps {
+    url: string;
+}
+
+@observer
+export class PDFViewer extends React.Component<IPDFViewerProps> {
+    @observable _pdf: Opt<Pdfjs.PDFDocumentProxy>;
+
+    @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;
+
+        promise.then((pdf: Pdfjs.PDFDocumentProxy) => {
+            runInAction(() => this._pdf = pdf);
+        });
+    }
+
+    render() {
+        console.log("PDFVIEWER");
+        console.log(this._pdf);
+        return (
+            <div>
+                <Viewer pdf={this._pdf} />
+            </div>
+        );
+    }
+}
+
+interface IViewerProps {
+    pdf: Opt<Pdfjs.PDFDocumentProxy>;
+}
+
+class Viewer extends React.Component<IViewerProps> {
+    render() {
+        console.log("VIEWER");
+        let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
+        console.log(numPages);
+        return (
+            <div className="viewer">
+                {/* {Array.from(Array(numPages).keys()).map((i) => ( */}
+                <Page
+                    pdf={this.props.pdf}
+                    numPages={numPages}
+                    key={`${this.props.pdf ? this.props.pdf.fingerprint : "undefined"}`}
+                    name={`${this.props.pdf ? this.props.pdf.fingerprint : "undefined"}`}
+                    {...this.props}
+                />
+                {/* ))} */}
+            </div>
+        );
+    }
+}
+
+interface IPageProps {
+    pdf: Opt<Pdfjs.PDFDocumentProxy>;
+    name: string;
+    numPages: number;
+}
+
+@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 = 1;
+
+    constructor(props: IPageProps) {
+        super(props);
+        this.canvas = React.createRef();
+        this.textLayer = React.createRef();
+    }
+
+    componentDidMount() {
+        console.log(this.props.pdf);
+        if (this.props.pdf) {
+            this.update(this.props.pdf);
+        }
+    }
+
+    componentDidUpdate() {
+        if (this.props.pdf) {
+            this.update(this.props.pdf);
+        }
+    }
+
+    private update = (pdf: Pdfjs.PDFDocumentProxy) => {
+        if (pdf) {
+            this.loadPage(pdf);
+        }
+        else {
+            this._state = "loading";
+        }
+    }
+
+    private loadPage = (pdf: Pdfjs.PDFDocumentProxy) => {
+        if (this._state === "rendering" || this._page) return;
+
+        pdf.getPage(this._currPage).then(
+            (page: Pdfjs.PDFPageProxy) => {
+                console.log("PAGE");
+                console.log(page);
+                this._state = "rendering";
+                this.renderPage(page);
+            }
+        )
+    }
+
+    @action
+    private renderPage = (page: Pdfjs.PDFPageProxy) => {
+        let scale = 1;
+        let viewport = page.getViewport(scale);
+        let canvas = this.canvas.current;
+        if (canvas) {
+            let context = canvas.getContext("2d");
+            canvas.width = viewport.width;
+            this._width = viewport.width;
+            canvas.height = viewport.height;
+            this._height = viewport.height;
+            if (context) {
+                page.render({ canvasContext: context, viewport: viewport });
+                page.getTextContent().then((res: Pdfjs.TextContent) => {
+                    //@ts-ignore
+                    let textLayer = Pdfjs.renderTextLayer({
+                        textContent: res,
+                        container: this.textLayer.current,
+                        viewport: viewport
+                    });
+                    // textLayer._render();
+                    this._state = "rendered";
+                });
+
+                this._page = page;
+            }
+        }
+    }
+
+    @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";
+        }
+    }
+
+    @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";
+        }
+    }
+
+    render() {
+        return (
+            <div className={this.props.name} style={{ "width": this._width, "height": this._height }}>
+                <div className="canvasContainer">
+                    <canvas ref={this.canvas} />
+                </div>
+                <div className="textlayer" ref={this.textLayer} />
+                <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>
+        );
+    }
+}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From f043c60ff537667021a76e716b6e6465aec44525 Mon Sep 17 00:00:00 2001
From: _stanleyyip <33562077+yipstanley@users.noreply.github.com>
Date: Sun, 19 May 2019 12:30:09 -0700
Subject: scrolling ?

---
 src/client/views/pdf/PDFViewer.scss | 19 +++++++++++++++++++
 src/client/views/pdf/PDFViewer.tsx  | 29 ++++++++++++++++-------------
 2 files changed, 35 insertions(+), 13 deletions(-)
 create mode 100644 src/client/views/pdf/PDFViewer.scss

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
new file mode 100644
index 000000000..d8ff06406
--- /dev/null
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -0,0 +1,19 @@
+.canvasContainer {}
+
+.viewer-button-cont {
+    position: absolute;
+    display: flex;
+    justify-content: space-evenly;
+    align-items: center;
+}
+
+.viewer-previousPage,
+.viewer-nextPage {
+    background: grey;
+    font-weight: bold;
+    opacity: 0.5;
+    padding: 0 10px;
+    border-radius: 5px;
+}
+
+.viewer {}
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 558a4fab6..1b445eae4 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -4,6 +4,7 @@ import { observable, action, runInAction } from "mobx";
 import { RouteStore } from "../../../server/RouteStore";
 import * as Pdfjs from "pdfjs-dist";
 import { Opt } from "../../../new_fields/Doc";
+import "./PDFViewer.scss";
 
 interface IPDFViewerProps {
     url: string;
@@ -46,15 +47,16 @@ class Viewer extends React.Component<IViewerProps> {
         console.log(numPages);
         return (
             <div className="viewer">
-                {/* {Array.from(Array(numPages).keys()).map((i) => ( */}
-                <Page
-                    pdf={this.props.pdf}
-                    numPages={numPages}
-                    key={`${this.props.pdf ? this.props.pdf.fingerprint : "undefined"}`}
-                    name={`${this.props.pdf ? this.props.pdf.fingerprint : "undefined"}`}
-                    {...this.props}
-                />
-                {/* ))} */}
+                {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"}`}
+                        {...this.props}
+                    />
+                ))} }
             </div>
         );
     }
@@ -64,6 +66,7 @@ interface IPageProps {
     pdf: Opt<Pdfjs.PDFDocumentProxy>;
     name: string;
     numPages: number;
+    page: number;
 }
 
 @observer
@@ -74,7 +77,7 @@ class Page extends React.Component<IPageProps> {
     @observable _page: Opt<Pdfjs.PDFPageProxy>;
     canvas: React.RefObject<HTMLCanvasElement>;
     textLayer: React.RefObject<HTMLDivElement>;
-    @observable _currPage: number = 1;
+    @observable _currPage: number = this.props.page + 1;
 
     constructor(props: IPageProps) {
         super(props);
@@ -172,11 +175,11 @@ class Page extends React.Component<IPageProps> {
                 <div className="canvasContainer">
                     <canvas ref={this.canvas} />
                 </div>
-                <div className="textlayer" ref={this.textLayer} />
-                <div className="viewer-button-cont" style={{ "width": this._width / 10, "height": this._height / 20, "left": this._width * .9, "top": this._height * .95 }}>
+                <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> */}
             </div>
         );
     }
-- 
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/pdf/PDFViewer.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/pdf/PDFViewer.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/pdf/PDFViewer.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/pdf/PDFViewer.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/pdf/PDFViewer.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/pdf/PDFViewer.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/pdf/PDFViewer.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 727c8be8d51766f483a90edf07366b5840f5a4f6 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Wed, 5 Jun 2019 23:00:10 -0400
Subject: screen transform remaining

---
 src/client/views/pdf/PDFViewer.scss |  6 ++++-
 src/client/views/pdf/PDFViewer.tsx  | 48 ++++++++++++++++++++++++++++++++-----
 2 files changed, 47 insertions(+), 7 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 9d41a1bb0..484d9dc04 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -24,4 +24,8 @@
     user-select: auto;
 }
 
-.viewer {}
\ No newline at end of file
+.pdfViewer-annotationBox {
+    position: absolute;
+    background-color: red;
+    opacity: 0.5;
+}
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 95f31bb89..a76527618 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -354,18 +354,54 @@ class Page extends React.Component<IPageProps> {
     }
 
     onPointerDown = (e: React.PointerEvent) => {
-        console.log("down");
-        e.stopPropagation();
+        if (e.button === 0) {
+            e.stopPropagation();
+            document.addEventListener("pointermove", this.onPointerMove);
+            document.addEventListener("pointerup", this.onPointerUp);
+        }
+    }
+
+    onPointerMove = (e: PointerEvent) => {
+        if (e.button === 0) {
+            e.stopPropagation();
+        }
     }
 
-    onPointerMove = (e: React.PointerEvent) => {
-        console.log("move")
-        e.stopPropagation();
+    onPointerUp = (e: PointerEvent) => {
+        let sel = window.getSelection();
+        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);
+                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);
+                    }
+                }
+            }
+        }
+        document.removeEventListener("pointermove", this.onPointerMove);
+        document.removeEventListener("pointerup", this.onPointerUp);
     }
 
     render() {
         return (
-            <div onPointerDown={this.onPointerDown} onPointerMove={this.onPointerMove} className={this.props.name} style={{ "width": this._width, "height": this._height }}>
+            <div onPointerDown={this.onPointerDown} className={this.props.name} style={{ "width": this._width, "height": this._height }}>
                 <div className="canvasContainer">
                     <canvas ref={this.canvas} />
                 </div>
-- 
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/pdf/PDFViewer.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 0a14ecf9b9fa5e15bc3e5373c8f042f9cd876c8a Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Thu, 6 Jun 2019 18:26:57 -0400
Subject: region annotation basics

---
 src/client/views/collections/CollectionPDFView.tsx |   3 +
 src/client/views/pdf/PDFAnnotationLayer.tsx        |  24 ++++
 src/client/views/pdf/PDFViewer.scss                |   2 +-
 src/client/views/pdf/PDFViewer.tsx                 | 125 +++++++++++++++++----
 4 files changed, 129 insertions(+), 25 deletions(-)
 create mode 100644 src/client/views/pdf/PDFAnnotationLayer.tsx

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 5e51437a4..773e4ae60 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -13,6 +13,9 @@ import { Id } from "../../../new_fields/FieldSymbols";
 
 @observer
 export class CollectionPDFView extends React.Component<FieldViewProps> {
+    constructor(props: FieldViewProps) {
+        super(props);
+    }
 
     public static LayoutString(fieldKey: string = "data") {
         return FieldView.LayoutString(CollectionPDFView, fieldKey);
diff --git a/src/client/views/pdf/PDFAnnotationLayer.tsx b/src/client/views/pdf/PDFAnnotationLayer.tsx
new file mode 100644
index 000000000..e92dcacbf
--- /dev/null
+++ b/src/client/views/pdf/PDFAnnotationLayer.tsx
@@ -0,0 +1,24 @@
+import React = require("react");
+import { observer } from "mobx-react";
+
+interface IAnnotationProps {
+
+}
+
+@observer
+export class PDFAnnotationLayer extends React.Component {
+    onPointerDown = (e: React.PointerEvent) => {
+        if (e.ctrlKey) {
+            console.log("annotating");
+            e.stopPropagation();
+        }
+    }
+
+    render() {
+        return (
+            <div className="pdfAnnotationLayer-cont" style={{ width: "100%", height: "100%", position: "relative", top: "-200%" }} onPointerDown={this.onPointerDown}>
+
+            </div>
+        )
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 484d9dc04..cb1aef410 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -27,5 +27,5 @@
 .pdfViewer-annotationBox {
     position: absolute;
     background-color: red;
-    opacity: 0.5;
+    opacity: 0.1;
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index d5a0a7aa1..5c480090c 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -6,6 +6,9 @@ import { Opt } from "../../../new_fields/Doc";
 import "./PDFViewer.scss";
 import "pdfjs-dist/web/pdf_viewer.css";
 import { PDFBox } from "../nodes/PDFBox";
+import { PDFAnnotationLayer } from "./PDFAnnotationLayer";
+import { TSMethodSignature } from "babel-types";
+import { checkPropTypes } from "prop-types";
 
 interface IPDFViewerProps {
     url: string;
@@ -268,15 +271,24 @@ class Page extends React.Component<IPageProps> {
     @observable private _height: number = 0;
     @observable private _page: Opt<Pdfjs.PDFPageProxy>;
     @observable private _currPage: number = this.props.page + 1;
+    @observable private _marqueeX: number = 0;
+    @observable private _marqueeY: number = 0;
+    @observable private _marqueeWidth: number = 0;
+    @observable private _marqueeHeight: number = 0;
 
     private _canvas: React.RefObject<HTMLCanvasElement>;
-    private _currentAnnotations: HTMLDivElement[] = [];
     private _textLayer: React.RefObject<HTMLDivElement>;
+    private _annotationLayer: React.RefObject<HTMLDivElement>;
+    private _marquee: React.RefObject<HTMLDivElement>;
+    private _currentAnnotations: HTMLDivElement[] = [];
+    private _marqueeing: boolean = false;
 
     constructor(props: IPageProps) {
         super(props);
         this._canvas = React.createRef();
         this._textLayer = React.createRef();
+        this._annotationLayer = React.createRef();
+        this._marquee = React.createRef();
     }
 
     componentDidMount() {
@@ -342,9 +354,26 @@ class Page extends React.Component<IPageProps> {
         }
     }
 
+    @action
     onPointerDown = (e: React.PointerEvent) => {
         if (e.button === 0) {
-            e.stopPropagation();
+            let target: any = e.target;
+            if (target && target.parentElement === this._textLayer.current) {
+                e.stopPropagation();
+            }
+            else {
+                e.stopPropagation();
+                runInAction(() => {
+                    let current = this._textLayer.current;
+                    if (current) {
+                        let boundingRect = current.getBoundingClientRect();
+                        this._marqueeX = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width);
+                        this._marqueeY = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height);
+                    }
+                });
+                this._marqueeing = true;
+                if (this._marquee.current) this._marquee.current.style.opacity = "0.2";
+            }
             document.addEventListener("pointermove", this.onPointerMove);
             document.addEventListener("pointerup", this.onPointerUp);
             if (!e.ctrlKey) {
@@ -355,8 +384,22 @@ class Page extends React.Component<IPageProps> {
         }
     }
 
+    @action
     onPointerMove = (e: PointerEvent) => {
-        if (e.button === 0) {
+        let target: any = e.target;
+        if (this._marqueeing) {
+            let current = this._textLayer.current;
+            if (current) {
+                let boundingRect = current.getBoundingClientRect();
+                this._marqueeWidth = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width) - this._marqueeX;
+                this._marqueeHeight = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height) - this._marqueeY;
+                console.log(this._marqueeWidth);
+                console.log(this._marqueeHeight);
+            }
+            e.stopPropagation();
+            e.preventDefault();
+        }
+        else if (target && target.parentElement === this._textLayer.current) {
             e.stopPropagation();
         }
     }
@@ -369,42 +412,76 @@ class Page extends React.Component<IPageProps> {
         e.stopPropagation();
     }
 
+    @action
     onPointerUp = (e: PointerEvent) => {
-        let sel = window.getSelection();
-        // if selecting over a range of things
-        if (sel && sel.type === "Range") {
-            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();
-                        annoBox.ondragstart = this.startAnnotation;
-                        annoBox.onpointerdown = this.pointerDownCancel;
-                        this._textLayer.current.appendChild(annoBox);
-                        this._currentAnnotations.push(annoBox);
+        if (this._marqueeing) {
+            this._marqueeing = false;
+            if (this._marquee.current) {
+                let copy = document.createElement("div");
+                copy.style.left = this._marquee.current.style.left;
+                copy.style.top = this._marquee.current.style.top;
+                copy.style.width = this._marquee.current.style.width;
+                copy.style.height = this._marquee.current.style.height;
+                copy.style.opacity = this._marquee.current.style.opacity;
+                copy.className = this._marquee.current.className;
+                if (this._annotationLayer.current) {
+                    this._annotationLayer.current.append(copy);
+                    this._currentAnnotations.push(copy);
+                }
+                this._marquee.current.style.opacity = "0";
+            }
+
+            this._marqueeHeight = this._marqueeWidth = 0;
+        }
+        else {
+            let sel = window.getSelection();
+            // if selecting over a range of things
+            if (sel && sel.type === "Range") {
+                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();
+                            annoBox.ondragstart = this.startAnnotation;
+                            annoBox.onpointerdown = this.pointerDownCancel;
+                            if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox);
+                            this._currentAnnotations.push(annoBox);
+                        }
                     }
                 }
+                if (sel.empty) {  // Chrome
+                    sel.empty();
+                } else if (sel.removeAllRanges) {  // Firefox
+                    sel.removeAllRanges();
+                }
             }
         }
         document.removeEventListener("pointermove", this.onPointerMove);
         document.removeEventListener("pointerup", this.onPointerUp);
     }
 
+    annotationPointerDown = (e: React.PointerEvent) => {
+        console.log("annotation");
+    }
+
     render() {
         return (
             <div onPointerDown={this.onPointerDown} className={this.props.name} style={{ "width": this._width, "height": this._height }}>
                 <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="pdfAnnotationLayer-cont" ref={this._annotationLayer} style={{ width: "100%", height: "100%", position: "relative", top: "-100%" }}>
+                    <div className="pdfViewer-annotationBox" ref={this._marquee} style={{ left: `${this._marqueeX}px`, top: `${this._marqueeY}px`, width: `${this._marqueeWidth}px`, height: `${this._marqueeHeight}px` }}></div>
+                </div>
+                <div className="textlayer" ref={this._textLayer} style={{ "position": "relative", "top": `-${2 * this._height}px`, "height": `${this._height}px` }} />
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From e96d0be82b19a4e108447ba5afc6cdc5deb07110 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Fri, 7 Jun 2019 15:28:25 -0400
Subject: starting annotation dragss

---
 src/client/util/DragManager.ts     |  20 ++++++++
 src/client/views/pdf/PDFViewer.tsx | 102 ++++++++++++++++++++++++++++++++++---
 2 files changed, 116 insertions(+), 6 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 1e84a0db0..a6c3dfa7a 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -153,6 +153,22 @@ export namespace DragManager {
         [id: string]: any;
     }
 
+    export class AnnotationDragData {
+        constructor(dragDoc: Doc, annotationDocs: Doc[], dropDoc: Doc) {
+            this.dragDocument = dragDoc;
+            this.dropDocument = dropDoc;
+            this.annotationDocuments = annotationDocs;
+            this.xOffset = this.yOffset = 0;
+        }
+        dragDocument: Doc;
+        annotationDocuments: Doc[];
+        dropDocument: Doc;
+        xOffset: number;
+        yOffset: number;
+        dropAction: dropActionType;
+        userDropAction: dropActionType;
+    }
+
     export let StartDragFunctions: (() => void)[] = [];
 
     export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) {
@@ -166,6 +182,10 @@ export namespace DragManager {
                         dragData.draggedDocuments));
     }
 
+    export function StartAnnotationDrag(eles: HTMLElement[], dragData: AnnotationDragData, downX: number, downY: number, options?: DragOptions) {
+        StartDrag(eles, dragData, downX, downY, options);
+    }
+
     export class LinkDragData {
         constructor(linkSourceDoc: Doc, blacklist: Doc[] = []) {
             this.linkSourceDocument = linkSourceDoc;
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 5c480090c..b0a48ac84 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -2,13 +2,19 @@ import { observer } from "mobx-react";
 import React = require("react");
 import { observable, action, runInAction, computed, IReactionDisposer, reaction } from "mobx";
 import * as Pdfjs from "pdfjs-dist";
-import { Opt } from "../../../new_fields/Doc";
+import { Opt, Doc, HeightSym, WidthSym, Field, DocListCast } from "../../../new_fields/Doc";
 import "./PDFViewer.scss";
 import "pdfjs-dist/web/pdf_viewer.css";
 import { PDFBox } from "../nodes/PDFBox";
 import { PDFAnnotationLayer } from "./PDFAnnotationLayer";
 import { TSMethodSignature } from "babel-types";
 import { checkPropTypes } from "prop-types";
+import { DragManager } from "../../util/DragManager";
+import { Docs } from "../../documents/Documents";
+import { List } from "../../../new_fields/List";
+import { Cast } from "../../../new_fields/Types";
+import { emptyFunction } from "../../../Utils";
+const Curly = require("./curly.png");
 
 interface IPDFViewerProps {
     url: string;
@@ -159,6 +165,7 @@ class Viewer extends React.Component<IViewerProps> {
                     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}
                     {...this.props} />
             ));
             let arr = Array.from(Array(numPages).keys()).map(i => false);
@@ -194,6 +201,7 @@ class Viewer extends React.Component<IViewerProps> {
                         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}
                         {...this.props} />
                 );
                 this._isPage[i] = true;
@@ -262,6 +270,7 @@ interface IPageProps {
     numPages: number;
     page: number;
     pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void;
+    parent: PDFBox;
 }
 
 @observer
@@ -275,11 +284,13 @@ class Page extends React.Component<IPageProps> {
     @observable private _marqueeY: number = 0;
     @observable private _marqueeWidth: number = 0;
     @observable private _marqueeHeight: number = 0;
+    @observable private _rotate: string = "";
 
     private _canvas: React.RefObject<HTMLCanvasElement>;
     private _textLayer: React.RefObject<HTMLDivElement>;
     private _annotationLayer: React.RefObject<HTMLDivElement>;
     private _marquee: React.RefObject<HTMLDivElement>;
+    private _curly: React.RefObject<HTMLImageElement>;
     private _currentAnnotations: HTMLDivElement[] = [];
     private _marqueeing: boolean = false;
 
@@ -289,6 +300,7 @@ class Page extends React.Component<IPageProps> {
         this._textLayer = React.createRef();
         this._annotationLayer = React.createRef();
         this._marquee = React.createRef();
+        this._curly = React.createRef();
     }
 
     componentDidMount() {
@@ -338,7 +350,7 @@ class Page extends React.Component<IPageProps> {
             this.props.pageLoaded(this._currPage, viewport);
             if (ctx) {
                 // renders the page onto the canvas context
-                page.render({ canvasContext: ctx, viewport: viewport })
+                page.render({ canvasContext: ctx, viewport: viewport });
                 // renders text onto the text container
                 page.getTextContent().then((res: Pdfjs.TextContent) => {
                     //@ts-ignore
@@ -354,9 +366,56 @@ class Page extends React.Component<IPageProps> {
         }
     }
 
+    makeAnnotationDocuments = (targetDoc: Doc): Doc[] => {
+        let annoDocs: Doc[] = [];
+        for (let anno of this._currentAnnotations) {
+            let annoDoc = new Doc();
+            annoDoc.x = anno.offsetLeft;
+            annoDoc.y = anno.offsetTop;
+            annoDoc.height = anno.offsetHeight;
+            annoDoc.width = anno.offsetWidth;
+            annoDoc.target = targetDoc;
+            annoDocs.push(annoDoc);
+        }
+        return annoDocs;
+    }
+
+    startDrag = (e: PointerEvent) => {
+        console.log("start drag");
+        e.preventDefault();
+        document.removeEventListener("pointermove", this.startDrag);
+        document.removeEventListener("pointerup", this.endDrag);
+        let thisDoc = this.props.parent.Document;
+        let targetDoc = Docs.TextDocument();
+        let annotationDocs = this.makeAnnotationDocuments(targetDoc);
+        targetDoc.annotations = new List(annotationDocs);
+        let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc);
+        DragManager.StartAnnotationDrag(this._currentAnnotations, dragData, e.pageX, e.pageY, {
+            handlers: {
+                dragComplete: action(emptyFunction),
+            },
+            hideSource: false
+        });
+        e.stopPropagation();
+    }
+
+    endDrag = (e: PointerEvent): void => {
+        document.removeEventListener("pointermove", this.startDrag);
+        document.removeEventListener("pointerup", this.endDrag);
+        e.stopPropagation();
+    }
+
     @action
     onPointerDown = (e: React.PointerEvent) => {
-        if (e.button === 0) {
+        if (e.shiftKey && 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);
+        }
+        else if (e.button === 0) {
             let target: any = e.target;
             if (target && target.parentElement === this._textLayer.current) {
                 e.stopPropagation();
@@ -374,7 +433,9 @@ class Page extends React.Component<IPageProps> {
                 this._marqueeing = true;
                 if (this._marquee.current) this._marquee.current.style.opacity = "0.2";
             }
+            document.removeEventListener("pointermove", this.onPointerMove);
             document.addEventListener("pointermove", this.onPointerMove);
+            document.removeEventListener("pointerup", this.onPointerUp);
             document.addEventListener("pointerup", this.onPointerUp);
             if (!e.ctrlKey) {
                 for (let anno of this._currentAnnotations) {
@@ -393,8 +454,30 @@ class Page extends React.Component<IPageProps> {
                 let boundingRect = current.getBoundingClientRect();
                 this._marqueeWidth = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width) - this._marqueeX;
                 this._marqueeHeight = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height) - this._marqueeY;
-                console.log(this._marqueeWidth);
-                console.log(this._marqueeHeight);
+                if (this._marquee.current && this._curly.current) {
+                    if (this._marqueeWidth > 100 && this._marqueeHeight > 100) {
+                        this._marquee.current.style.background = "red";
+                        this._curly.current.style.opacity = "0";
+                    }
+                    else {
+                        this._marquee.current.style.background = "transparent";
+                        this._curly.current.style.opacity = "1";
+                    }
+
+                    let ratio = this._marqueeWidth / this._marqueeHeight;
+                    if (ratio > 1.5) {
+                        // vertical
+                        this._rotate = "rotate(90deg) scale(1, 2)";
+                    }
+                    else if (ratio < 0.5) {
+                        // horizontal
+                        this._rotate = "scale(2, 1)";
+                    }
+                    else {
+                        // diagonal
+                        this._rotate = "rotate(45deg) scale(1.5, 1.5)";
+                    }
+                }
             }
             e.stopPropagation();
             e.preventDefault();
@@ -472,6 +555,10 @@ class Page extends React.Component<IPageProps> {
         console.log("annotation");
     }
 
+    // imgVisible = () => {
+    //     return this._marqueeWidth < 100 && this._marqueeHeight < 100 ? { opacity: "1" } : { opacity: "0" }
+    // }
+
     render() {
         return (
             <div onPointerDown={this.onPointerDown} className={this.props.name} style={{ "width": this._width, "height": this._height }}>
@@ -479,7 +566,10 @@ class Page extends React.Component<IPageProps> {
                     <canvas ref={this._canvas} />
                 </div>
                 <div className="pdfAnnotationLayer-cont" ref={this._annotationLayer} style={{ width: "100%", height: "100%", position: "relative", top: "-100%" }}>
-                    <div className="pdfViewer-annotationBox" ref={this._marquee} style={{ left: `${this._marqueeX}px`, top: `${this._marqueeY}px`, width: `${this._marqueeWidth}px`, height: `${this._marqueeHeight}px` }}></div>
+                    <div className="pdfViewer-annotationBox" ref={this._marquee}
+                        style={{ left: `${this._marqueeX}px`, top: `${this._marqueeY}px`, width: `${this._marqueeWidth}px`, height: `${this._marqueeHeight}px`, background: "transparent" }}>
+                        <img ref={this._curly} src="https://static.thenounproject.com/png/331760-200.png" style={{ width: "100%", height: "100%", transform: `${this._rotate}` }} />
+                    </div>
                 </div>
                 <div className="textlayer" ref={this._textLayer} style={{ "position": "relative", "top": `-${2 * this._height}px`, "height": `${this._height}px` }} />
             </div>
-- 
cgit v1.2.3-70-g09d2


From 5d799d1a26fa12d666b11b80776151edcbbd67f8 Mon Sep 17 00:00:00 2001
From: loudonclear <loudon_cohen@brown.edu>
Date: Fri, 7 Jun 2019 18:14:39 -0400
Subject: some comments, switching computers

---
 src/client/util/DragManager.ts                     |   2 +-
 src/client/views/collections/CollectionSubView.tsx |   5 +
 src/client/views/pdf/PDFBox2.tsx                   |  28 --
 src/client/views/pdf/PDFViewer.tsx                 | 327 +-----------------
 src/client/views/pdf/Page.tsx                      | 382 +++++++++++++++++++++
 5 files changed, 391 insertions(+), 353 deletions(-)
 delete mode 100644 src/client/views/pdf/PDFBox2.tsx
 create mode 100644 src/client/views/pdf/Page.tsx

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index a6c3dfa7a..e92ed9b4a 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -218,7 +218,7 @@ export namespace DragManager {
         let ys: number[] = [];
 
         const docs: Doc[] =
-            dragData instanceof DocumentDragData ? dragData.draggedDocuments : [];
+            dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof AnnotationDragData ? [dragData.dragDocument] : [];
         let dragElements = eles.map(ele => {
             const w = ele.offsetWidth,
                 h = ele.offsetHeight;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index be37efd3d..1ced6a426 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -107,6 +107,11 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                 e.stopPropagation();
                 return added;
             }
+            else if (de.data instanceof DragManager.AnnotationDragData) {
+                console.log("dropped!");
+                console.log(de.data);
+                return this.props.addDocument(de.data.dropDocument);
+            }
             return false;
         }
 
diff --git a/src/client/views/pdf/PDFBox2.tsx b/src/client/views/pdf/PDFBox2.tsx
deleted file mode 100644
index 71825c260..000000000
--- a/src/client/views/pdf/PDFBox2.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import React = require("react");
-import { FieldViewProps, FieldView } from "../nodes/FieldView";
-import { DocComponent } from "../DocComponent";
-import { makeInterface } from "../../../new_fields/Schema";
-import { positionSchema } from "../nodes/DocumentView";
-import { pageSchema } from "../nodes/ImageBox";
-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);
-
-@observer
-export class PDFBox2 extends DocComponent<FieldViewProps, PdfDocument>(PdfDocument) {
-    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 (
-            <PDFViewer url={pdfUrl} />
-        )
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index b0a48ac84..0711ead23 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -2,19 +2,11 @@ import { observer } from "mobx-react";
 import React = require("react");
 import { observable, action, runInAction, computed, IReactionDisposer, reaction } from "mobx";
 import * as Pdfjs from "pdfjs-dist";
-import { Opt, Doc, HeightSym, WidthSym, Field, DocListCast } from "../../../new_fields/Doc";
+import { Opt } from "../../../new_fields/Doc";
 import "./PDFViewer.scss";
 import "pdfjs-dist/web/pdf_viewer.css";
 import { PDFBox } from "../nodes/PDFBox";
-import { PDFAnnotationLayer } from "./PDFAnnotationLayer";
-import { TSMethodSignature } from "babel-types";
-import { checkPropTypes } from "prop-types";
-import { DragManager } from "../../util/DragManager";
-import { Docs } from "../../documents/Documents";
-import { List } from "../../../new_fields/List";
-import { Cast } from "../../../new_fields/Types";
-import { emptyFunction } from "../../../Utils";
-const Curly = require("./curly.png");
+import Page from "./Page";
 
 interface IPDFViewerProps {
     url: string;
@@ -168,7 +160,7 @@ class Viewer extends React.Component<IViewerProps> {
                     parent={this.props.parent}
                     {...this.props} />
             ));
-            let arr = Array.from(Array(numPages).keys()).map(i => false);
+            let arr = Array.from(Array(numPages).keys()).map(() => false);
             this._visibleElements.push(...divs);
             this._isPage.push(...arr);
         }
@@ -263,316 +255,3 @@ class Viewer extends React.Component<IViewerProps> {
         );
     }
 }
-
-interface IPageProps {
-    pdf: Opt<Pdfjs.PDFDocumentProxy>;
-    name: string;
-    numPages: number;
-    page: number;
-    pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void;
-    parent: PDFBox;
-}
-
-@observer
-class Page extends React.Component<IPageProps> {
-    @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;
-    @observable private _marqueeX: number = 0;
-    @observable private _marqueeY: number = 0;
-    @observable private _marqueeWidth: number = 0;
-    @observable private _marqueeHeight: number = 0;
-    @observable private _rotate: string = "";
-
-    private _canvas: React.RefObject<HTMLCanvasElement>;
-    private _textLayer: React.RefObject<HTMLDivElement>;
-    private _annotationLayer: React.RefObject<HTMLDivElement>;
-    private _marquee: React.RefObject<HTMLDivElement>;
-    private _curly: React.RefObject<HTMLImageElement>;
-    private _currentAnnotations: HTMLDivElement[] = [];
-    private _marqueeing: boolean = false;
-
-    constructor(props: IPageProps) {
-        super(props);
-        this._canvas = React.createRef();
-        this._textLayer = React.createRef();
-        this._annotationLayer = React.createRef();
-        this._marquee = React.createRef();
-        this._curly = React.createRef();
-    }
-
-    componentDidMount() {
-        if (this.props.pdf) {
-            this.update(this.props.pdf);
-        }
-    }
-
-    componentDidUpdate() {
-        if (this.props.pdf) {
-            this.update(this.props.pdf);
-        }
-    }
-
-    private update = (pdf: Pdfjs.PDFDocumentProxy) => {
-        if (pdf) {
-            this.loadPage(pdf);
-        }
-        else {
-            this._state = "loading";
-        }
-    }
-
-    private loadPage = (pdf: Pdfjs.PDFDocumentProxy) => {
-        if (this._state === "rendering" || this._page) return;
-
-        pdf.getPage(this._currPage).then(
-            (page: Pdfjs.PDFPageProxy) => {
-                this._state = "rendering";
-                this.renderPage(page);
-            }
-        );
-    }
-
-    @action
-    private renderPage = (page: Pdfjs.PDFPageProxy) => {
-        let scale = 1;
-        let viewport = page.getViewport(scale);
-        let canvas = this._canvas.current;
-        let textLayer = this._textLayer.current;
-        if (canvas && textLayer) {
-            let ctx = canvas.getContext("2d");
-            canvas.width = viewport.width;
-            this._width = viewport.width;
-            canvas.height = viewport.height;
-            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({
-                        textContent: res,
-                        container: textLayer,
-                        viewport: viewport
-                    });
-                });
-
-                this._page = page;
-            }
-        }
-    }
-
-    makeAnnotationDocuments = (targetDoc: Doc): Doc[] => {
-        let annoDocs: Doc[] = [];
-        for (let anno of this._currentAnnotations) {
-            let annoDoc = new Doc();
-            annoDoc.x = anno.offsetLeft;
-            annoDoc.y = anno.offsetTop;
-            annoDoc.height = anno.offsetHeight;
-            annoDoc.width = anno.offsetWidth;
-            annoDoc.target = targetDoc;
-            annoDocs.push(annoDoc);
-        }
-        return annoDocs;
-    }
-
-    startDrag = (e: PointerEvent) => {
-        console.log("start drag");
-        e.preventDefault();
-        document.removeEventListener("pointermove", this.startDrag);
-        document.removeEventListener("pointerup", this.endDrag);
-        let thisDoc = this.props.parent.Document;
-        let targetDoc = Docs.TextDocument();
-        let annotationDocs = this.makeAnnotationDocuments(targetDoc);
-        targetDoc.annotations = new List(annotationDocs);
-        let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc);
-        DragManager.StartAnnotationDrag(this._currentAnnotations, dragData, e.pageX, e.pageY, {
-            handlers: {
-                dragComplete: action(emptyFunction),
-            },
-            hideSource: false
-        });
-        e.stopPropagation();
-    }
-
-    endDrag = (e: PointerEvent): void => {
-        document.removeEventListener("pointermove", this.startDrag);
-        document.removeEventListener("pointerup", this.endDrag);
-        e.stopPropagation();
-    }
-
-    @action
-    onPointerDown = (e: React.PointerEvent) => {
-        if (e.shiftKey && 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);
-        }
-        else if (e.button === 0) {
-            let target: any = e.target;
-            if (target && target.parentElement === this._textLayer.current) {
-                e.stopPropagation();
-            }
-            else {
-                e.stopPropagation();
-                runInAction(() => {
-                    let current = this._textLayer.current;
-                    if (current) {
-                        let boundingRect = current.getBoundingClientRect();
-                        this._marqueeX = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width);
-                        this._marqueeY = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height);
-                    }
-                });
-                this._marqueeing = true;
-                if (this._marquee.current) this._marquee.current.style.opacity = "0.2";
-            }
-            document.removeEventListener("pointermove", this.onPointerMove);
-            document.addEventListener("pointermove", this.onPointerMove);
-            document.removeEventListener("pointerup", this.onPointerUp);
-            document.addEventListener("pointerup", this.onPointerUp);
-            if (!e.ctrlKey) {
-                for (let anno of this._currentAnnotations) {
-                    anno.remove();
-                }
-            }
-        }
-    }
-
-    @action
-    onPointerMove = (e: PointerEvent) => {
-        let target: any = e.target;
-        if (this._marqueeing) {
-            let current = this._textLayer.current;
-            if (current) {
-                let boundingRect = current.getBoundingClientRect();
-                this._marqueeWidth = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width) - this._marqueeX;
-                this._marqueeHeight = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height) - this._marqueeY;
-                if (this._marquee.current && this._curly.current) {
-                    if (this._marqueeWidth > 100 && this._marqueeHeight > 100) {
-                        this._marquee.current.style.background = "red";
-                        this._curly.current.style.opacity = "0";
-                    }
-                    else {
-                        this._marquee.current.style.background = "transparent";
-                        this._curly.current.style.opacity = "1";
-                    }
-
-                    let ratio = this._marqueeWidth / this._marqueeHeight;
-                    if (ratio > 1.5) {
-                        // vertical
-                        this._rotate = "rotate(90deg) scale(1, 2)";
-                    }
-                    else if (ratio < 0.5) {
-                        // horizontal
-                        this._rotate = "scale(2, 1)";
-                    }
-                    else {
-                        // diagonal
-                        this._rotate = "rotate(45deg) scale(1.5, 1.5)";
-                    }
-                }
-            }
-            e.stopPropagation();
-            e.preventDefault();
-        }
-        else if (target && target.parentElement === this._textLayer.current) {
-            e.stopPropagation();
-        }
-    }
-
-    startAnnotation = (e: DragEvent) => {
-        console.log("drag starting");
-    }
-
-    pointerDownCancel = (e: PointerEvent) => {
-        e.stopPropagation();
-    }
-
-    @action
-    onPointerUp = (e: PointerEvent) => {
-        if (this._marqueeing) {
-            this._marqueeing = false;
-            if (this._marquee.current) {
-                let copy = document.createElement("div");
-                copy.style.left = this._marquee.current.style.left;
-                copy.style.top = this._marquee.current.style.top;
-                copy.style.width = this._marquee.current.style.width;
-                copy.style.height = this._marquee.current.style.height;
-                copy.style.opacity = this._marquee.current.style.opacity;
-                copy.className = this._marquee.current.className;
-                if (this._annotationLayer.current) {
-                    this._annotationLayer.current.append(copy);
-                    this._currentAnnotations.push(copy);
-                }
-                this._marquee.current.style.opacity = "0";
-            }
-
-            this._marqueeHeight = this._marqueeWidth = 0;
-        }
-        else {
-            let sel = window.getSelection();
-            // if selecting over a range of things
-            if (sel && sel.type === "Range") {
-                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();
-                            annoBox.ondragstart = this.startAnnotation;
-                            annoBox.onpointerdown = this.pointerDownCancel;
-                            if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox);
-                            this._currentAnnotations.push(annoBox);
-                        }
-                    }
-                }
-                if (sel.empty) {  // Chrome
-                    sel.empty();
-                } else if (sel.removeAllRanges) {  // Firefox
-                    sel.removeAllRanges();
-                }
-            }
-        }
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.removeEventListener("pointerup", this.onPointerUp);
-    }
-
-    annotationPointerDown = (e: React.PointerEvent) => {
-        console.log("annotation");
-    }
-
-    // imgVisible = () => {
-    //     return this._marqueeWidth < 100 && this._marqueeHeight < 100 ? { opacity: "1" } : { opacity: "0" }
-    // }
-
-    render() {
-        return (
-            <div onPointerDown={this.onPointerDown} className={this.props.name} style={{ "width": this._width, "height": this._height }}>
-                <div className="canvasContainer">
-                    <canvas ref={this._canvas} />
-                </div>
-                <div className="pdfAnnotationLayer-cont" ref={this._annotationLayer} style={{ width: "100%", height: "100%", position: "relative", top: "-100%" }}>
-                    <div className="pdfViewer-annotationBox" ref={this._marquee}
-                        style={{ left: `${this._marqueeX}px`, top: `${this._marqueeY}px`, width: `${this._marqueeWidth}px`, height: `${this._marqueeHeight}px`, background: "transparent" }}>
-                        <img ref={this._curly} src="https://static.thenounproject.com/png/331760-200.png" style={{ width: "100%", height: "100%", transform: `${this._rotate}` }} />
-                    </div>
-                </div>
-                <div className="textlayer" ref={this._textLayer} style={{ "position": "relative", "top": `-${2 * this._height}px`, "height": `${this._height}px` }} />
-            </div>
-        );
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
new file mode 100644
index 000000000..7cd72b27f
--- /dev/null
+++ b/src/client/views/pdf/Page.tsx
@@ -0,0 +1,382 @@
+import { observer } from "mobx-react";
+import React = require("react");
+import { observable, action, runInAction } from "mobx";
+import * as Pdfjs from "pdfjs-dist";
+import { Opt, Doc, FieldResult, Field } from "../../../new_fields/Doc";
+import "./PDFViewer.scss";
+import "pdfjs-dist/web/pdf_viewer.css";
+import { PDFBox } from "../nodes/PDFBox";
+import { DragManager } from "../../util/DragManager";
+import { Docs } from "../../documents/Documents";
+import { List } from "../../../new_fields/List";
+import { emptyFunction } from "../../../Utils";
+import { Cast, NumCast } from "../../../new_fields/Types";
+import { listSpec } from "../../../new_fields/Schema";
+
+interface IPageProps {
+    pdf: Opt<Pdfjs.PDFDocumentProxy>;
+    name: string;
+    numPages: number;
+    page: number;
+    pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void;
+    parent: PDFBox;
+}
+
+@observer
+export default class Page extends React.Component<IPageProps> {
+    @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;
+    @observable private _marqueeX: number = 0;
+    @observable private _marqueeY: number = 0;
+    @observable private _marqueeWidth: number = 0;
+    @observable private _marqueeHeight: number = 0;
+    @observable private _rotate: string = "";
+    @observable private _annotations: List<Doc> = new List<Doc>();
+
+    private _canvas: React.RefObject<HTMLCanvasElement>;
+    private _textLayer: React.RefObject<HTMLDivElement>;
+    private _annotationLayer: React.RefObject<HTMLDivElement>;
+    private _marquee: React.RefObject<HTMLDivElement>;
+    private _curly: React.RefObject<HTMLImageElement>;
+    private _currentAnnotations: HTMLDivElement[] = [];
+    private _marqueeing: boolean = false;
+    private _dragging: boolean = false;
+
+    constructor(props: IPageProps) {
+        super(props);
+        this._canvas = React.createRef();
+        this._textLayer = React.createRef();
+        this._annotationLayer = React.createRef();
+        this._marquee = React.createRef();
+        this._curly = React.createRef();
+    }
+
+    componentDidMount() {
+        if (this.props.pdf) {
+            this.update(this.props.pdf);
+        }
+    }
+
+    componentDidUpdate() {
+        if (this.props.pdf) {
+            this.update(this.props.pdf);
+        }
+    }
+
+    private update = (pdf: Pdfjs.PDFDocumentProxy) => {
+        if (pdf) {
+            this.loadPage(pdf);
+        }
+        else {
+            this._state = "loading";
+        }
+    }
+
+    private loadPage = (pdf: Pdfjs.PDFDocumentProxy) => {
+        if (this._state === "rendering" || this._page) return;
+
+        pdf.getPage(this._currPage).then(
+            (page: Pdfjs.PDFPageProxy) => {
+                this._state = "rendering";
+                this.renderPage(page);
+            }
+        );
+    }
+
+    @action
+    private renderPage = (page: Pdfjs.PDFPageProxy) => {
+        // lower scale = easier to read at small sizes, higher scale = easier to read at large sizes
+        let scale = 2;
+        let viewport = page.getViewport(scale);
+        let canvas = this._canvas.current;
+        let textLayer = this._textLayer.current;
+        if (canvas && textLayer) {
+            let ctx = canvas.getContext("2d");
+            canvas.width = viewport.width;
+            this._width = viewport.width;
+            canvas.height = viewport.height;
+            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({
+                        textContent: res,
+                        container: textLayer,
+                        viewport: viewport
+                    });
+                });
+
+                this._page = page;
+            }
+        }
+    }
+
+    /**
+     * @param targetDoc: Document that annotations are linked to
+     * This method makes the list of current annotations into documents linked to
+     * the parameter passed in.
+     */
+    makeAnnotationDocuments = (targetDoc: Doc): Doc[] => {
+        let annoDocs: Doc[] = [];
+        for (let anno of this._currentAnnotations) {
+            let annoDoc = new Doc();
+            annoDoc.x = anno.offsetLeft;
+            annoDoc.y = anno.offsetTop;
+            annoDoc.height = anno.offsetHeight;
+            annoDoc.width = anno.offsetWidth;
+            annoDoc.target = targetDoc;
+            annoDocs.push(annoDoc);
+            anno.remove();
+        }
+        this._currentAnnotations = [];
+        return annoDocs;
+    }
+
+    /**
+     * 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.
+     */
+    @action
+    startDrag = (e: PointerEvent) => {
+        // the first 5 lines is a hack to prevent text selection while dragging
+        e.preventDefault();
+        e.stopPropagation();
+        if (this._dragging) {
+            return;
+        }
+        this._dragging = true;
+        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 = this.props.page;
+        // creates annotation documents for current highlights
+        let annotationDocs = this.makeAnnotationDocuments(targetDoc);
+        let targetAnnotations = Cast(targetDoc.annotations, listSpec(Doc));
+        if (targetAnnotations) {
+            targetAnnotations.push(...annotationDocs);
+            targetDoc.annotations = targetAnnotations;
+        }
+        // temporary code (currently broken ? 6/7/19) to get annotations to rerender on pdf
+        let thisAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc));
+        if (thisAnnotations) {
+            thisAnnotations.push(...annotationDocs);
+            this.props.parent.Document.annotations = thisAnnotations;
+            let temp = new List<Doc>(thisAnnotations);
+            temp.push(...this._annotations);
+            this._annotations = temp;
+        }
+        // create dragData and star tdrag
+        let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc);
+        if (this._textLayer.current) {
+            DragManager.StartAnnotationDrag([this._textLayer.current], dragData, e.pageX, e.pageY, {
+                handlers: {
+                    dragComplete: action(emptyFunction),
+                },
+                hideSource: false
+            });
+        }
+    }
+
+    // cleans up events and boolean
+    endDrag = (e: PointerEvent): void => {
+        document.removeEventListener("pointermove", this.startDrag);
+        document.removeEventListener("pointerup", this.endDrag);
+        this._dragging = false;
+        e.stopPropagation();
+    }
+
+    @action
+    onPointerDown = (e: React.PointerEvent) => {
+        // if alt+left click, drag and annotate
+        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);
+        }
+        else if (e.button === 0) {
+            let target: any = e.target;
+            if (target && target.parentElement === this._textLayer.current) {
+                e.stopPropagation();
+            }
+            else {
+                e.stopPropagation();
+                // set marquee x and y positions to the spatially transformed position
+                    let current = this._textLayer.current;
+                    if (current) {
+                        let boundingRect = current.getBoundingClientRect();
+                        this._marqueeX = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width);
+                        this._marqueeY = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height);
+                    }
+                this._marqueeing = true;
+                if (this._marquee.current) this._marquee.current.style.opacity = "0.2";
+            }
+            document.removeEventListener("pointermove", this.onPointerMove);
+            document.addEventListener("pointermove", this.onPointerMove);
+            document.removeEventListener("pointerup", this.onPointerUp);
+            document.addEventListener("pointerup", this.onPointerUp);
+            if (!e.ctrlKey) {
+                for (let anno of this._currentAnnotations) {
+                    anno.remove();
+                }
+                this._currentAnnotations = [];
+            }
+        }
+    }
+
+    @action
+    onPointerMove = (e: PointerEvent) => {
+        let target: any = e.target;
+        if (this._marqueeing) {
+            let current = this._textLayer.current;
+            if (current) {
+                let boundingRect = current.getBoundingClientRect();
+                this._marqueeWidth = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width) - this._marqueeX;
+                this._marqueeHeight = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height) - this._marqueeY;
+                if (this._marquee.current && this._curly.current) {
+                    if (this._marqueeWidth > 100 && this._marqueeHeight > 100) {
+                        this._marquee.current.style.background = "red";
+                        this._curly.current.style.opacity = "0";
+                    }
+                    else {
+                        this._marquee.current.style.background = "transparent";
+                        this._curly.current.style.opacity = "1";
+                    }
+
+                    let ratio = this._marqueeWidth / this._marqueeHeight;
+                    if (ratio > 1.5) {
+                        // vertical
+                        this._rotate = "rotate(90deg) scale(1, 2)";
+                    }
+                    else if (ratio < 0.5) {
+                        // horizontal
+                        this._rotate = "scale(2, 1)";
+                    }
+                    else {
+                        // diagonal
+                        this._rotate = "rotate(45deg) scale(1.5, 1.5)";
+                    }
+                }
+            }
+            e.stopPropagation();
+            e.preventDefault();
+        }
+        else if (target && target.parentElement === this._textLayer.current) {
+            e.stopPropagation();
+        }
+    }
+
+    startAnnotation = () => {
+        console.log("drag starting");
+    }
+
+    pointerDownCancel = (e: PointerEvent) => {
+        e.stopPropagation();
+    }
+
+    @action
+    onPointerUp = () => {
+        if (this._marqueeing) {
+            this._marqueeing = false;
+            if (this._marquee.current) {
+                let copy = document.createElement("div");
+                copy.style.left = this._marquee.current.style.left;
+                copy.style.top = this._marquee.current.style.top;
+                copy.style.width = this._marquee.current.style.width;
+                copy.style.height = this._marquee.current.style.height;
+                copy.style.opacity = this._marquee.current.style.opacity;
+                copy.className = this._marquee.current.className;
+                if (this._annotationLayer.current) {
+                    this._annotationLayer.current.append(copy);
+                    this._currentAnnotations.push(copy);
+                }
+                this._marquee.current.style.opacity = "0";
+            }
+
+            this._marqueeHeight = this._marqueeWidth = 0;
+        }
+        else {
+            let sel = window.getSelection();
+            // if selecting over a range of things
+            if (sel && sel.type === "Range") {
+                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();
+                            annoBox.ondragstart = this.startAnnotation;
+                            annoBox.onpointerdown = this.pointerDownCancel;
+                            if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox);
+                            this._currentAnnotations.push(annoBox);
+                        }
+                    }
+                }
+                if (sel.empty) {  // Chrome
+                    sel.empty();
+                } else if (sel.removeAllRanges) {  // Firefox
+                    sel.removeAllRanges();
+                }
+            }
+        }
+        document.removeEventListener("pointermove", this.onPointerMove);
+        document.removeEventListener("pointerup", this.onPointerUp);
+    }
+
+    renderAnnotation = (anno: Doc | undefined): HTMLDivElement => {
+        let annoBox = document.createElement("div");
+        if (anno) {
+            annoBox.className = "pdfViewer-annotationBox";
+            // transforms the positions from screen onto the pdf div
+            annoBox.style.top = NumCast(anno.x).toString();
+            annoBox.style.left = NumCast(anno.y).toString();
+            annoBox.style.width = NumCast(anno.width).toString();
+            annoBox.style.height = NumCast(anno.height).toString()
+            annoBox.onpointerdown = this.pointerDownCancel;
+        }
+        return annoBox;
+    }
+
+    annotationPointerDown = () => {
+        console.log("annotation");
+    }
+
+    // imgVisible = () => {
+    //     return this._marqueeWidth < 100 && this._marqueeHeight < 100 ? { opacity: "1" } : { opacity: "0" }
+    // }
+
+    render() {
+        let annotations = this._annotations;
+        return (
+            <div onPointerDown={this.onPointerDown} className={this.props.name} style={{ "width": this._width, "height": this._height }}>
+                <div className="canvasContainer">
+                    <canvas ref={this._canvas} />
+                </div>
+                <div className="pdfAnnotationLayer-cont" ref={this._annotationLayer} style={{ width: "100%", height: "100%", position: "relative", top: "-100%" }}>
+                    <div className="pdfViewer-annotationBox" ref={this._marquee}
+                        style={{ left: `${this._marqueeX}px`, top: `${this._marqueeY}px`, width: `${this._marqueeWidth}px`, height: `${this._marqueeHeight}px`, background: "transparent" }}>
+                        <img ref={this._curly} src="https://static.thenounproject.com/png/331760-200.png" style={{ width: "100%", height: "100%", transform: `${this._rotate}` }} />
+                    </div>
+                    {annotations.map(anno => this.renderAnnotation(anno instanceof Doc ? anno : undefined))}
+                </div>
+                <div className="textlayer" ref={this._textLayer} style={{ "position": "relative", "top": `-${2 * this._height}px`, "height": `${this._height}px` }} />
+            </div>
+        );
+    }
+}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 81a502f81073e7fb15c75563cd557a6a2c5a31cd Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Mon, 10 Jun 2019 10:58:00 -0400
Subject: annotation loading maybe idk not workign rn

---
 src/client/views/pdf/PDFViewer.tsx | 2 +-
 src/client/views/pdf/Page.tsx      | 3 +++
 2 files changed, 4 insertions(+), 1 deletion(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 0711ead23..0a6886878 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -184,7 +184,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) {
+            if (!this._isPage[i] || (this._isPage[i] && forceRender)) {
                 this._visibleElements[i] = (
                     <Page
                         pdf={this.props.pdf}
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 940f31f41..0d86f22c1 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -163,6 +163,9 @@ export default class Page extends React.Component<IPageProps> {
             targetAnnotations.push(...annotationDocs);
             targetDoc.annotations = targetAnnotations;
         }
+        else {
+            targetDoc.annotations = new List<Doc>(annotationDocs);
+        }
         // temporary code (currently broken ? 6/7/19) to get annotations to rerender on pdf
         let thisAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc));
         if (thisAnnotations) {
-- 
cgit v1.2.3-70-g09d2


From 82a6a258422666fbe3db2a2cf1934f8673912b67 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Mon, 10 Jun 2019 15:49:34 -0400
Subject: annotation updates blehh

---
 src/client/views/pdf/PDFViewer.scss |  6 +++
 src/client/views/pdf/PDFViewer.tsx  | 76 +++++++++++++++++++++++++++++++++++--
 src/client/views/pdf/Page.tsx       | 44 +--------------------
 3 files changed, 81 insertions(+), 45 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index cb1aef410..831e6add1 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -28,4 +28,10 @@
     position: absolute;
     background-color: red;
     opacity: 0.1;
+}
+
+.pdfViewer-annotationLayer {
+    position: absolute;
+    top: 0;
+    overflow: visible hidden;
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 0a6886878..1152587a4 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -2,11 +2,12 @@ import { observer } from "mobx-react";
 import React = require("react");
 import { observable, action, runInAction, computed, IReactionDisposer, reaction } from "mobx";
 import * as Pdfjs from "pdfjs-dist";
-import { Opt } from "../../../new_fields/Doc";
+import { Opt, HeightSym, WidthSym, Doc, DocListCast } from "../../../new_fields/Doc";
 import "./PDFViewer.scss";
 import "pdfjs-dist/web/pdf_viewer.css";
 import { PDFBox } from "../nodes/PDFBox";
 import Page from "./Page";
+import { NumCast } from "../../../new_fields/Types";
 
 interface IPDFViewerProps {
     url: string;
@@ -65,9 +66,11 @@ class Viewer extends React.Component<IViewerProps> {
     @observable private _endIndex: number = 1;
     @observable private _loaded: boolean = false;
     @observable private _pdf: Opt<Pdfjs.PDFDocumentProxy>;
+    @observable private _annotations: Doc[] = [];
 
     private _pageBuffer: number = 1;
     private _reactionDisposer?: IReactionDisposer;
+    private _annotationReactionDisposer?: IReactionDisposer;
 
     componentDidMount = () => {
         let wasSelected = this.props.parent.props.isSelected();
@@ -88,6 +91,19 @@ class Viewer extends React.Component<IViewerProps> {
             { 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 }
+            );
+        }
+
         // On load, render pdf
         setTimeout(() => this.renderPages(this.startIndex, this.endIndex, true), 1000);
     }
@@ -96,6 +112,9 @@ class Viewer extends React.Component<IViewerProps> {
         if (this._reactionDisposer) {
             this._reactionDisposer();
         }
+        if (this._annotationReactionDisposer) {
+            this._annotationReactionDisposer();
+        }
     }
 
     @action
@@ -135,6 +154,17 @@ class Viewer extends React.Component<IViewerProps> {
         }
     }
 
+    @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);
+        }
+    }
+
     /**
      * @param startIndex: where to start rendering pages
      * @param endIndex: where to end rendering pages
@@ -158,6 +188,7 @@ class Viewer extends React.Component<IViewerProps> {
                     name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
                     pageLoaded={this.pageLoaded}
                     parent={this.props.parent}
+                    renderAnnotations={this.renderAnnotations}
                     {...this.props} />
             ));
             let arr = Array.from(Array(numPages).keys()).map(() => false);
@@ -194,6 +225,7 @@ class Viewer extends React.Component<IViewerProps> {
                         name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
                         pageLoaded={this.pageLoaded}
                         parent={this.props.parent}
+                        renderAnnotations={this.renderAnnotations}
                         {...this.props} />
                 );
                 this._isPage[i] = true;
@@ -247,11 +279,49 @@ class Viewer extends React.Component<IViewerProps> {
         }
     }
 
+    getPageHeight = (index: number): number => {
+        let counter = 0;
+        if (this.props.pdf && index < this.props.pdf.numPages) {
+            for (let i = 0; i < index; i++) {
+                if (this._pageSizes[i]) {
+                    counter += this._pageSizes[i].height;
+                }
+            }
+        }
+        return counter;
+    }
+
     render() {
         return (
-            <div className="viewer">
-                {this._visibleElements}
+            <div>
+                <div className="viewer">
+                    {this._visibleElements}
+                </div>
+                <div className="pdfViewer-annotationLayer" style={{ height: this.props.parent.Document.nativeHeight }}>
+                    <div className="pdfViewer-annotationLayer-subCont" style={{ transform: `translateY(${-this.scrollY}px)` }}>
+                        {this._annotations.map(anno => <RegionAnnotation x={NumCast(anno.x)} y={NumCast(anno.y) + this.getPageHeight(NumCast(anno.page))} width={anno[WidthSym]()} height={anno[HeightSym]()} />)}
+                    </div>
+                </div>
             </div>
         );
     }
 }
+
+interface IAnnotation {
+    x: number;
+    y: number;
+    width: number;
+    height: number;
+}
+
+class RegionAnnotation extends React.Component<IAnnotation> {
+    onPointerDown = (e: React.PointerEvent) => {
+        console.log("clicked!");
+    }
+
+    render() {
+        return (
+            <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown} style={{ top: this.props.y, left: this.props.x, width: this.props.width, height: this.props.height }}></div>
+        );
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 5269f448b..5738889c4 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -21,6 +21,7 @@ interface IPageProps {
     page: number;
     pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void;
     parent: PDFBox;
+    renderAnnotations: (annotations: Doc[], removeOld: boolean) => void;
 }
 
 @observer
@@ -35,7 +36,6 @@ export default class Page extends React.Component<IPageProps> {
     @observable private _marqueeWidth: number = 0;
     @observable private _marqueeHeight: number = 0;
     @observable private _rotate: string = "";
-    @observable private _annotations: Doc[] = [];
 
     private _canvas: React.RefObject<HTMLCanvasElement>;
     private _textLayer: React.RefObject<HTMLDivElement>;
@@ -60,19 +60,6 @@ export default class Page extends React.Component<IPageProps> {
         if (this.props.pdf) {
             this.update(this.props.pdf);
 
-            if (this.props.parent.Document) {
-                this._reactionDisposer = reaction(
-                    () => DocListCast(this.props.parent.Document.annotations),
-                    () => {
-                        let annotations = DocListCast(this.props.parent.Document.annotations);
-                        if (annotations && annotations.length) {
-                            annotations = annotations.filter(anno => NumCast(anno.page) === this.props.page);
-                            this.renderAnnotations(annotations, true);
-                        }
-                    },
-                    { fireImmediately: true }
-                );
-            }
         }
     }
 
@@ -108,17 +95,6 @@ export default class Page extends React.Component<IPageProps> {
         );
     }
 
-    @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);
-        }
-    }
-
     @action
     private renderPage = (page: Pdfjs.PDFPageProxy): void => {
         // lower scale = easier to read at small sizes, higher scale = easier to read at large sizes
@@ -407,30 +383,14 @@ export default class Page extends React.Component<IPageProps> {
                 <div className="canvasContainer">
                     <canvas ref={this._canvas} />
                 </div>
-                <div className="pdfAnnotationLayer-cont" ref={this._annotationLayer} style={{ width: "100%", height: "100%", position: "relative", top: "-100%" }}>
+                <div className="pdfInkingLayer-cont" ref={this._annotationLayer} style={{ width: "100%", height: "100%", position: "relative", top: "-100%" }}>
                     <div className="pdfViewer-annotationBox" ref={this._marquee}
                         style={{ left: `${this._marqueeX}px`, top: `${this._marqueeY}px`, width: `${this._marqueeWidth}px`, height: `${this._marqueeHeight}px`, background: "transparent" }}>
                         <img ref={this._curly} src="https://static.thenounproject.com/png/331760-200.png" style={{ width: "100%", height: "100%", transform: `${this._rotate}` }} />
                     </div>
-                    {this._annotations.map(anno => <RegionAnnotation x={NumCast(anno.x)} y={NumCast(anno.y)} width={anno[WidthSym]()} height={anno[HeightSym]()} />)}
                 </div>
                 <div className="textlayer" ref={this._textLayer} style={{ "position": "relative", "top": `-${2 * this._height}px`, "height": `${this._height}px` }} />
             </div>
         );
     }
 }
-
-interface IAnnotation {
-    x: number;
-    y: number;
-    width: number;
-    height: number;
-}
-
-class RegionAnnotation extends React.Component<IAnnotation> {
-    render() {
-        return (
-            <div className="pdfViewer-annotationBox" style={{ top: this.props.y, left: this.props.x, width: this.props.width, height: this.props.height }}></div>
-        );
-    }
-}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 46b9a18bf278ab17845d1082a1152d16c07b1240 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Tue, 11 Jun 2019 11:30:53 -0400
Subject: annotation navigationnn

---
 src/client/views/pdf/PDFViewer.tsx | 41 +++++++++++++++++++++++++++++++-------
 1 file changed, 34 insertions(+), 7 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 1152587a4..092765324 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -7,7 +7,11 @@ import "./PDFViewer.scss";
 import "pdfjs-dist/web/pdf_viewer.css";
 import { PDFBox } from "../nodes/PDFBox";
 import Page from "./Page";
-import { NumCast } from "../../../new_fields/Types";
+import { NumCast, Cast, BoolCast } from "../../../new_fields/Types";
+import { Id } from "../../../new_fields/FieldSymbols";
+import { DocUtils } from "../../documents/Documents";
+import { DocumentManager } from "../../util/DocumentManager";
+import { SelectionManager } from "../../util/SelectionManager";
 
 interface IPDFViewerProps {
     url: string;
@@ -297,9 +301,9 @@ class Viewer extends React.Component<IViewerProps> {
                 <div className="viewer">
                     {this._visibleElements}
                 </div>
-                <div className="pdfViewer-annotationLayer" style={{ height: this.props.parent.Document.nativeHeight }}>
+                <div className="pdfViewer-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 => <RegionAnnotation x={NumCast(anno.x)} y={NumCast(anno.y) + this.getPageHeight(NumCast(anno.page))} width={anno[WidthSym]()} height={anno[HeightSym]()} />)}
+                        {this._annotations.map(anno => <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]} />)}
                     </div>
                 </div>
             </div>
@@ -307,21 +311,44 @@ class Viewer extends React.Component<IViewerProps> {
     }
 }
 
-interface IAnnotation {
+interface IAnnotationProps {
     x: number;
     y: number;
     width: number;
     height: number;
+    document: Doc;
 }
 
-class RegionAnnotation extends React.Component<IAnnotation> {
+class RegionAnnotation extends React.Component<IAnnotationProps> {
+    @observable private _backgroundColor: string = "red";
+
+    // private _reactionDisposer?: IReactionDisposer;
+
+    constructor(props: IAnnotationProps) {
+        super(props);
+
+        // this._reactionDisposer = reaction(
+        //     () => { BoolCast(this.props.document.selected); },
+        //     () => { this._backgroundColor = BoolCast(this.props.document.selected) ? "yellow" : "red"; },
+        //     { fireImmediately: true }
+        // )
+    }
+
     onPointerDown = (e: React.PointerEvent) => {
-        console.log("clicked!");
+        let targetDoc = Cast(this.props.document.target, Doc, null);
+        if (targetDoc) {
+            DocumentManager.Instance.jumpToDocument(targetDoc);
+            // let annotations = DocListCast(targetDoc.proto!.linkedFromDocs);
+            // if (annotations && annotations.length) {
+            //     annotations.forEach(anno => anno.selected = true);
+            // }
+        }
     }
 
     render() {
         return (
-            <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown} style={{ top: this.props.y, left: this.props.x, width: this.props.width, height: this.props.height }}></div>
+            <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown}
+                style={{ top: this.props.y, left: this.props.x, width: this.props.width, height: this.props.height, pointerEvents: "all", backgroundColor: this._backgroundColor }}></div>
         );
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 64f6a0d54656ded133af113746003d61eaa1d651 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Tue, 11 Jun 2019 15:04:47 -0400
Subject: pin annotations base

---
 src/client/views/pdf/PDFViewer.scss |  6 ++++
 src/client/views/pdf/PDFViewer.tsx  | 72 +++++++++++++++++++++++++++++++++++--
 src/client/views/pdf/Page.tsx       | 22 ++++++++++--
 3 files changed, 96 insertions(+), 4 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 831e6add1..57be04b93 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -34,4 +34,10 @@
     position: absolute;
     top: 0;
     overflow: visible hidden;
+}
+
+.pdfViewer-pinAnnotation {
+    background-color: red;
+    position: absolute;
+    border-radius: 100%;
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 092765324..bc773ebef 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -9,9 +9,11 @@ import { PDFBox } from "../nodes/PDFBox";
 import Page from "./Page";
 import { NumCast, Cast, BoolCast } from "../../../new_fields/Types";
 import { Id } from "../../../new_fields/FieldSymbols";
-import { DocUtils } from "../../documents/Documents";
+import { DocUtils, Docs } from "../../documents/Documents";
 import { DocumentManager } from "../../util/DocumentManager";
 import { SelectionManager } from "../../util/SelectionManager";
+import { List } from "../../../new_fields/List";
+import { DocumentContentsView } from "../nodes/DocumentContentsView";
 
 interface IPDFViewerProps {
     url: string;
@@ -56,6 +58,8 @@ interface IViewerProps {
     url: string;
 }
 
+const PinRadius = 25;
+
 /**
  * Handles rendering and virtualization of the pdf
  */
@@ -193,6 +197,7 @@ class Viewer extends React.Component<IViewerProps> {
                     pageLoaded={this.pageLoaded}
                     parent={this.props.parent}
                     renderAnnotations={this.renderAnnotations}
+                    makePin={this.createPinAnnotation}
                     {...this.props} />
             ));
             let arr = Array.from(Array(numPages).keys()).map(() => false);
@@ -229,6 +234,7 @@ class Viewer extends React.Component<IViewerProps> {
                         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}
                         {...this.props} />
                 );
@@ -242,6 +248,33 @@ class Viewer extends React.Component<IViewerProps> {
         return;
     }
 
+    createPinAnnotation = (x: number, y: number): void => {
+        let targetDoc = Docs.TextDocument({ 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.target = targetDoc;
+        pinAnno.type = AnnotationTypes.Pin;
+        // this._annotations.push(pinAnno);
+        let annotations = DocListCast(this.props.parent.Document.annotations);
+        if (annotations && annotations.length) {
+            annotations.push(pinAnno);
+            this.props.parent.Document.annotations = new List<Doc>(annotations);
+        }
+        else {
+            this.props.parent.Document.annotations = new List<Doc>([pinAnno]);
+        }
+        // let pinAnno = document.createElement("div");
+        // pinAnno.className = "pdfViewer-pinAnnotation";
+        // pinAnno.style.top = (y - (radius / 2)).toString();
+        // pinAnno.style.left = (x - (radius / 2)).toString();
+        // pinAnno.style.height = pinAnno.style.width = radius.toString();
+        // if (this._annotationLayer.current) this._annotationLayer.current.append(pinAnno);
+    }
+
     // get the page index that the vertical offset passed in is on
     getIndex = (vOffset: number) => {
         if (this._loaded) {
@@ -295,6 +328,18 @@ class Viewer extends React.Component<IViewerProps> {
         return counter;
     }
 
+    renderAnnotation = (anno: Doc): JSX.Element => {
+        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]} />;
+            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]} />;
+            default:
+                return <div></div>;
+        }
+    }
+
     render() {
         return (
             <div>
@@ -303,7 +348,7 @@ class Viewer extends React.Component<IViewerProps> {
                 </div>
                 <div className="pdfViewer-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 => <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]} />)}
+                        {this._annotations.map(anno => this.renderAnnotation(anno))}
                     </div>
                 </div>
             </div>
@@ -311,6 +356,10 @@ class Viewer extends React.Component<IViewerProps> {
     }
 }
 
+export enum AnnotationTypes {
+    Region, Pin
+}
+
 interface IAnnotationProps {
     x: number;
     y: number;
@@ -319,6 +368,25 @@ interface IAnnotationProps {
     document: Doc;
 }
 
+class PinAnnotation extends React.Component<IAnnotationProps> {
+    @observable private _backgroundColor: string = "red";
+
+    pointerDown = (e: React.PointerEvent) => {
+
+    }
+
+    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>
+        );
+    }
+}
+
 class RegionAnnotation extends React.Component<IAnnotationProps> {
     @observable private _backgroundColor: string = "red";
 
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 5738889c4..fe7369aeb 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -13,6 +13,7 @@ import { emptyFunction } from "../../../Utils";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { listSpec } from "../../../new_fields/Schema";
 import { menuBar } from "prosemirror-menu";
+import { AnnotationTypes } from "./PDFViewer";
 
 interface IPageProps {
     pdf: Opt<Pdfjs.PDFDocumentProxy>;
@@ -22,6 +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;
 }
 
 @observer
@@ -142,6 +144,7 @@ export default class Page extends React.Component<IPageProps> {
             annoDoc.width = anno.offsetWidth;
             annoDoc.page = this.props.page;
             annoDoc.target = targetDoc;
+            annoDoc.type = AnnotationTypes.Region;
             annoDocs.push(annoDoc);
             DocUtils.MakeLink(annoDoc, targetDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
             anno.remove();
@@ -169,7 +172,6 @@ export default class Page extends React.Component<IPageProps> {
         targetDoc.targetPage = this.props.page;
         // creates annotation documents for current highlights
         let annotationDocs = this.makeAnnotationDocuments(targetDoc);
-        console.log(annotationDocs);
         let targetAnnotations = DocListCast(this.props.parent.Document.annotations);
         if (targetAnnotations) {
             targetAnnotations.push(...annotationDocs);
@@ -364,6 +366,22 @@ export default class Page extends React.Component<IPageProps> {
         document.removeEventListener("pointerup", this.onSelectEnd);
     }
 
+    doubleClick = (e: React.MouseEvent) => {
+        let target: any = e.target;
+        // if double clicking text
+        if (target && target.parentElement === this._textLayer.current) {
+            // do something to select the paragraph ideally
+        }
+
+        let current = this._textLayer.current;
+        if (current) {
+            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);
+        }
+    }
+
     renderAnnotation = (anno: Doc | undefined): HTMLDivElement => {
         let annoBox = document.createElement("div");
         if (anno) {
@@ -379,7 +397,7 @@ export default class Page extends React.Component<IPageProps> {
 
     render() {
         return (
-            <div onPointerDown={this.onPointerDown} className={this.props.name} style={{ "width": this._width, "height": this._height }}>
+            <div onPointerDown={this.onPointerDown} onDoubleClick={this.doubleClick} className={this.props.name} style={{ "width": this._width, "height": this._height }}>
                 <div className="canvasContainer">
                     <canvas ref={this._canvas} />
                 </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/pdf/PDFViewer.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 9ec4a529dc886acca8f147cfe913e60f938f3bda Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Wed, 12 Jun 2019 14:35:24 -0400
Subject: reverse annotating

---
 src/client/util/DragManager.ts     |   6 +-
 src/client/views/pdf/PDFViewer.tsx | 112 ++++++++++++++++++++++++++++++-------
 src/client/views/pdf/Page.tsx      |  27 ++++++---
 3 files changed, 116 insertions(+), 29 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index e92ed9b4a..2ffb77e44 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -154,14 +154,14 @@ export namespace DragManager {
     }
 
     export class AnnotationDragData {
-        constructor(dragDoc: Doc, annotationDocs: Doc[], dropDoc: Doc) {
+        constructor(dragDoc: Doc, annotationDoc: Doc, dropDoc: Doc) {
             this.dragDocument = dragDoc;
             this.dropDocument = dropDoc;
-            this.annotationDocuments = annotationDocs;
+            this.annotationDocument = annotationDoc;
             this.xOffset = this.yOffset = 0;
         }
         dragDocument: Doc;
-        annotationDocuments: Doc[];
+        annotationDocument: Doc;
         dropDocument: Doc;
         xOffset: number;
         yOffset: number;
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 6823f640e..bfd0e036f 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -7,7 +7,7 @@ import "./PDFViewer.scss";
 import "pdfjs-dist/web/pdf_viewer.css";
 import { PDFBox } from "../nodes/PDFBox";
 import Page from "./Page";
-import { NumCast, Cast, BoolCast } from "../../../new_fields/Types";
+import { NumCast, Cast, BoolCast, StrCast } from "../../../new_fields/Types";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { DocUtils, Docs } from "../../documents/Documents";
 import { DocumentManager } from "../../util/DocumentManager";
@@ -18,6 +18,8 @@ import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocum
 import { Transform } from "../../util/Transform";
 import { emptyFunction, returnTrue, returnFalse } from "../../../Utils";
 import { DocumentView } from "../nodes/DocumentView";
+import { DragManager } from "../../util/DragManager";
+import { Dictionary } from "typescript-collections";
 
 interface IPDFViewerProps {
     url: string;
@@ -83,12 +85,15 @@ class Viewer extends React.Component<IViewerProps> {
     @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[]>();
 
     private _pageBuffer: number = 1;
     private _annotationLayer: React.RefObject<HTMLDivElement>;
     private _reactionDisposer?: IReactionDisposer;
     private _annotationReactionDisposer?: IReactionDisposer;
     private _pagesLoaded: number = 0;
+    private _dropDisposer?: DragManager.DragDropDisposer;
 
     constructor(props: IViewerProps) {
         super(props);
@@ -96,6 +101,7 @@ class Viewer extends React.Component<IViewerProps> {
         this._annotationLayer = React.createRef();
     }
 
+    @action
     componentDidMount = () => {
         let wasSelected = this.props.parent.props.isSelected();
         // reaction for when document gets (de)selected
@@ -105,10 +111,12 @@ class Viewer extends React.Component<IViewerProps> {
                 // if deselected, render images in place of pdf
                 if (wasSelected && !this.props.parent.props.isSelected()) {
                     this.saveThumbnail();
+                    this._pointerEvents = "all";
                 }
                 // if selected, render pdf
                 else if (!wasSelected && this.props.parent.props.isSelected()) {
                     this.renderPages(this.startIndex, this.endIndex, true);
+                    this._pointerEvents = "none";
                 }
                 wasSelected = this.props.parent.props.isSelected();
             },
@@ -133,6 +141,57 @@ class Viewer extends React.Component<IViewerProps> {
         }, 1000);
     }
 
+    private mainCont = (div: HTMLDivElement | null) => {
+        if (this._dropDisposer) {
+            this._dropDisposer();
+        }
+        if (div) {
+            this._dropDisposer = DragManager.MakeDropTarget(div, {
+                handlers: { drop: this.drop.bind(this) }
+            });
+        }
+    }
+
+    makeAnnotationDocuments = (sourceDoc: Doc): Doc => {
+        let annoDocs: Doc[] = [];
+        this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => {
+            for (let anno of value) {
+                let annoDoc = new Doc();
+                if (anno.style.left) annoDoc.x = parseInt(anno.style.left);
+                if (anno.style.top) annoDoc.y = parseInt(anno.style.top);
+                if (anno.style.height) annoDoc.height = parseInt(anno.style.height);
+                if (anno.style.width) annoDoc.width = parseInt(anno.style.width);
+                annoDoc.page = key;
+                annoDoc.target = sourceDoc;
+                annoDoc.type = AnnotationTypes.Region;
+                annoDocs.push(annoDoc);
+                anno.remove();
+            }
+        });
+
+        let annoDoc = new Doc();
+        annoDoc.annotations = new List<Doc>(annoDocs);
+        DocUtils.MakeLink(sourceDoc, annoDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
+        this._savedAnnotations.clear();
+        return annoDoc;
+    }
+
+    drop = async (e: Event, de: DragManager.DropEvent) => {
+        if (de.data instanceof DragManager.LinkDragData) {
+            let sourceDoc = de.data.linkSourceDocument;
+            let destDoc = this.makeAnnotationDocuments(sourceDoc);
+            let targetAnnotations = DocListCast(this.props.parent.Document.annotations);
+            if (targetAnnotations) {
+                targetAnnotations.push(destDoc);
+                this.props.parent.Document.annotations = new List<Doc>(targetAnnotations);
+            }
+            else {
+                this.props.parent.Document.annotations = new List<Doc>([destDoc]);
+            }
+        }
+        e.stopPropagation();
+    }
+
     componentWillUnmount = () => {
         if (this._reactionDisposer) {
             this._reactionDisposer();
@@ -219,6 +278,8 @@ class Viewer extends React.Component<IViewerProps> {
                     parent={this.props.parent}
                     renderAnnotations={this.renderAnnotations}
                     makePin={this.createPinAnnotation}
+                    sendAnnotations={this.receiveAnnotations}
+                    receiveAnnotations={this.sendAnnotations}
                     {...this.props} />
             ));
             let arr = Array.from(Array(numPages).keys()).map(() => false);
@@ -257,6 +318,8 @@ class Viewer extends React.Component<IViewerProps> {
                         parent={this.props.parent}
                         makePin={this.createPinAnnotation}
                         renderAnnotations={this.renderAnnotations}
+                        sendAnnotations={this.receiveAnnotations}
+                        receiveAnnotations={this.sendAnnotations}
                         {...this.props} />
                 );
                 this._isPage[i] = true;
@@ -269,6 +332,15 @@ class Viewer extends React.Component<IViewerProps> {
         return;
     }
 
+    @action
+    receiveAnnotations = (annotations: HTMLDivElement[], page: number) => {
+        this._savedAnnotations.setValue(page, annotations);
+    }
+
+    sendAnnotations = (page: number): HTMLDivElement[] | undefined => {
+        return this._savedAnnotations.getValue(page);
+    }
+
     createPinAnnotation = (x: number, y: number, page: number): void => {
         let targetDoc = Docs.TextDocument({ width: 100, height: 50, title: "New Pin Annotation" });
 
@@ -280,13 +352,15 @@ class Viewer extends React.Component<IViewerProps> {
         pinAnno.target = targetDoc;
         pinAnno.type = AnnotationTypes.Pin;
         // this._annotations.push(pinAnno);
+        let annoDoc = new Doc();
+        annoDoc.annotations = new List<Doc>([pinAnno]);
         let annotations = DocListCast(this.props.parent.Document.annotations);
         if (annotations && annotations.length) {
-            annotations.push(pinAnno);
+            annotations.push(annoDoc);
             this.props.parent.Document.annotations = new List<Doc>(annotations);
         }
         else {
-            this.props.parent.Document.annotations = new List<Doc>([pinAnno]);
+            this.props.parent.Document.annotations = new List<Doc>([annoDoc]);
         }
         // let pinAnno = document.createElement("div");
         // pinAnno.className = "pdfViewer-pinAnnotation";
@@ -349,20 +423,20 @@ class Viewer extends React.Component<IViewerProps> {
         return counter;
     }
 
-    renderAnnotation = (anno: Doc): JSX.Element => {
-        let type = NumCast(anno.type);
-        switch (type) {
-            case AnnotationTypes.Pin:
-                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 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!");
+    renderAnnotation = (anno: Doc): JSX.Element[] => {
+        let annotationDocs = DocListCast(anno.annotations);
+        let res = annotationDocs.map(a => {
+            let type = NumCast(a.type);
+            switch (type) {
+                case AnnotationTypes.Pin:
+                    return <PinAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y) + this.getPageHeight(NumCast(a.page))} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
+                case AnnotationTypes.Region:
+                    return <RegionAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y) + this.getPageHeight(NumCast(a.page))} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
+                default:
+                    return <div></div>;
+            }
+        });
+        return res;
     }
 
     // ScreenToLocalTransform = (): Transform => {
@@ -380,11 +454,11 @@ class Viewer extends React.Component<IViewerProps> {
     render() {
         trace();
         return (
-            <div onDrop={this.onDrop}>
+            <div ref={this.mainCont} style={{ pointerEvents: "all" }}>
                 <div className="viewer">
                     {this._visibleElements}
                 </div>
-                <div className="pdfViewer-annotationLayer" ref={this._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: this._pointerEvents }}>
                     <div className="pdfViewer-annotationLayer-subCont" style={{ transform: `translateY(${-this.scrollY}px)` }}>
                         {this._annotations.map(anno => this.renderAnnotation(anno))}
                     </div>
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 73a7a93a0..908804605 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -24,6 +24,8 @@ interface IPageProps {
     parent: PDFBox;
     renderAnnotations: (annotations: Doc[], removeOld: boolean) => void;
     makePin: (x: number, y: number, page: number) => void;
+    sendAnnotations: (annotations: HTMLDivElement[], page: number) => void;
+    receiveAnnotations: (page: number) => HTMLDivElement[] | undefined;
 }
 
 @observer
@@ -61,11 +63,19 @@ export default class Page extends React.Component<IPageProps> {
     componentDidMount = (): void => {
         if (this.props.pdf) {
             this.update(this.props.pdf);
+        }
 
+        let received = this.props.receiveAnnotations(this.props.page);
+        this._currentAnnotations = received ? received : [];
+        if (this._annotationLayer.current) {
+            this._annotationLayer.current.append(...this._currentAnnotations);
         }
     }
 
     componentWillUnmount = (): void => {
+        console.log(this._currentAnnotations);
+        this.props.sendAnnotations(this._currentAnnotations, this.props.page);
+
         if (this._reactionDisposer) {
             this._reactionDisposer();
         }
@@ -134,8 +144,9 @@ export default class Page extends React.Component<IPageProps> {
      * This method makes the list of current annotations into documents linked to
      * the parameter passed in.
      */
-    makeAnnotationDocuments = (targetDoc: Doc): Doc[] => {
+    makeAnnotationDocuments = (targetDoc: Doc): Doc => {
         let annoDocs: Doc[] = [];
+
         for (let anno of this._currentAnnotations) {
             let annoDoc = new Doc();
             annoDoc.x = anno.offsetLeft;
@@ -146,11 +157,13 @@ export default class Page extends React.Component<IPageProps> {
             annoDoc.target = targetDoc;
             annoDoc.type = AnnotationTypes.Region;
             annoDocs.push(annoDoc);
-            DocUtils.MakeLink(annoDoc, targetDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
             anno.remove();
         }
+        let annoDoc = new Doc();
+        annoDoc.annotations = new List<Doc>(annoDocs);
+        DocUtils.MakeLink(annoDoc, targetDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
         this._currentAnnotations = [];
-        return annoDocs;
+        return annoDoc;
     }
 
     /**
@@ -171,17 +184,17 @@ export default class Page extends React.Component<IPageProps> {
         let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" });
         targetDoc.targetPage = this.props.page;
         // creates annotation documents for current highlights
-        let annotationDocs = this.makeAnnotationDocuments(targetDoc);
+        let annotationDoc = this.makeAnnotationDocuments(targetDoc);
         let targetAnnotations = DocListCast(this.props.parent.Document.annotations);
         if (targetAnnotations) {
-            targetAnnotations.push(...annotationDocs);
+            targetAnnotations.push(annotationDoc);
             this.props.parent.Document.annotations = new List<Doc>(targetAnnotations);
         }
         else {
-            this.props.parent.Document.annotations = new List<Doc>(annotationDocs);
+            this.props.parent.Document.annotations = new List<Doc>([annotationDoc]);
         }
         // create dragData and star tdrag
-        let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc);
+        let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc);
         if (this._textLayer.current) {
             DragManager.StartAnnotationDrag([this._textLayer.current], dragData, e.pageX, e.pageY, {
                 handlers: {
-- 
cgit v1.2.3-70-g09d2


From 2cec2be7f16cf8ef656def60659e5555b75f2ce7 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Wed, 12 Jun 2019 15:09:11 -0400
Subject: oops

---
 src/client/views/pdf/PDFViewer.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index bfd0e036f..deacdd623 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -171,7 +171,7 @@ class Viewer extends React.Component<IViewerProps> {
 
         let annoDoc = new Doc();
         annoDoc.annotations = new List<Doc>(annoDocs);
-        DocUtils.MakeLink(sourceDoc, annoDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
+        DocUtils.MakeLink(sourceDoc, annoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
         this._savedAnnotations.clear();
         return annoDoc;
     }
-- 
cgit v1.2.3-70-g09d2


From 0f85b1b7a6198013269c19a36dc8bcf14d2a4952 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Wed, 12 Jun 2019 16:02:25 -0400
Subject: Refactored some code

---
 src/client/views/pdf/PDFViewer.tsx | 76 +++++++++++++++++---------------------
 src/client/views/pdf/Page.tsx      | 67 +++------------------------------
 2 files changed, 39 insertions(+), 104 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index deacdd623..e54dfea6f 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -278,7 +278,9 @@ class Viewer extends React.Component<IViewerProps> {
                     parent={this.props.parent}
                     renderAnnotations={this.renderAnnotations}
                     makePin={this.createPinAnnotation}
+                    createAnnotation={this.createAnnotation}
                     sendAnnotations={this.receiveAnnotations}
+                    makeAnnotationDocuments={this.makeAnnotationDocuments}
                     receiveAnnotations={this.sendAnnotations}
                     {...this.props} />
             ));
@@ -318,7 +320,9 @@ class Viewer extends React.Component<IViewerProps> {
                         parent={this.props.parent}
                         makePin={this.createPinAnnotation}
                         renderAnnotations={this.renderAnnotations}
+                        createAnnotation={this.createAnnotation}
                         sendAnnotations={this.receiveAnnotations}
+                        makeAnnotationDocuments={this.makeAnnotationDocuments}
                         receiveAnnotations={this.sendAnnotations}
                         {...this.props} />
                 );
@@ -334,7 +338,13 @@ class Viewer extends React.Component<IViewerProps> {
 
     @action
     receiveAnnotations = (annotations: HTMLDivElement[], page: number) => {
-        this._savedAnnotations.setValue(page, annotations);
+        if (page === -1) {
+            this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
+            this._savedAnnotations.keys().forEach(k => this._savedAnnotations.setValue(k, annotations));
+        }
+        else {
+            this._savedAnnotations.setValue(page, annotations);
+        }
     }
 
     sendAnnotations = (page: number): HTMLDivElement[] | undefined => {
@@ -346,7 +356,7 @@ class Viewer extends React.Component<IViewerProps> {
 
         let pinAnno = new Doc();
         pinAnno.x = x;
-        pinAnno.y = y;
+        pinAnno.y = y + this.getPageHeight(page);
         pinAnno.width = pinAnno.height = PinRadius;
         pinAnno.page = page;
         pinAnno.target = targetDoc;
@@ -362,12 +372,6 @@ class Viewer extends React.Component<IViewerProps> {
         else {
             this.props.parent.Document.annotations = new List<Doc>([annoDoc]);
         }
-        // let pinAnno = document.createElement("div");
-        // pinAnno.className = "pdfViewer-pinAnnotation";
-        // pinAnno.style.top = (y - (radius / 2)).toString();
-        // pinAnno.style.left = (x - (radius / 2)).toString();
-        // pinAnno.style.height = pinAnno.style.width = radius.toString();
-        // if (this._annotationLayer.current) this._annotationLayer.current.append(pinAnno);
     }
 
     // get the page index that the vertical offset passed in is on
@@ -404,10 +408,7 @@ class Viewer extends React.Component<IViewerProps> {
                 <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);
         }
     }
 
@@ -423,15 +424,32 @@ class Viewer extends React.Component<IViewerProps> {
         return counter;
     }
 
+    createAnnotation = (div: HTMLDivElement, page: number) => {
+        if (this._annotationLayer.current) {
+            if (div.style.top) {
+                div.style.top = (parseInt(div.style.top) + this.getPageHeight(page)).toString();
+            }
+            this._annotationLayer.current.append(div);
+            let savedPage = this._savedAnnotations.getValue(page);
+            if (savedPage) {
+                savedPage.push(div);
+                this._savedAnnotations.setValue(page, savedPage);
+            }
+            else {
+                this._savedAnnotations.setValue(page, [div]);
+            }
+        }
+    }
+
     renderAnnotation = (anno: Doc): JSX.Element[] => {
         let annotationDocs = DocListCast(anno.annotations);
         let res = annotationDocs.map(a => {
             let type = NumCast(a.type);
             switch (type) {
                 case AnnotationTypes.Pin:
-                    return <PinAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y) + this.getPageHeight(NumCast(a.page))} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
+                    return <PinAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
                 case AnnotationTypes.Region:
-                    return <RegionAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y) + this.getPageHeight(NumCast(a.page))} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
+                    return <RegionAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
                 default:
                     return <div></div>;
             }
@@ -439,18 +457,6 @@ class Viewer extends React.Component<IViewerProps> {
         return res;
     }
 
-    // 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 (
@@ -458,8 +464,8 @@ class Viewer extends React.Component<IViewerProps> {
                 <div className="viewer">
                     {this._visibleElements}
                 </div>
-                <div className="pdfViewer-annotationLayer" ref={this._annotationLayer} style={{ height: this.props.parent.Document.nativeHeight, width: `100%`, pointerEvents: this._pointerEvents }}>
-                    <div className="pdfViewer-annotationLayer-subCont" style={{ transform: `translateY(${-this.scrollY}px)` }}>
+                <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}>
                         {this._annotations.map(anno => this.renderAnnotation(anno))}
                     </div>
                 </div>
@@ -542,26 +548,10 @@ class PinAnnotation extends React.Component<IAnnotationProps> {
 class RegionAnnotation extends React.Component<IAnnotationProps> {
     @observable private _backgroundColor: string = "red";
 
-    // private _reactionDisposer?: IReactionDisposer;
-
-    constructor(props: IAnnotationProps) {
-        super(props);
-
-        // this._reactionDisposer = reaction(
-        //     () => { BoolCast(this.props.document.selected); },
-        //     () => { this._backgroundColor = BoolCast(this.props.document.selected) ? "yellow" : "red"; },
-        //     { fireImmediately: true }
-        // )
-    }
-
     onPointerDown = (e: React.PointerEvent) => {
         let targetDoc = Cast(this.props.document.target, Doc, null);
         if (targetDoc) {
             DocumentManager.Instance.jumpToDocument(targetDoc);
-            // let annotations = DocListCast(targetDoc.proto!.linkedFromDocs);
-            // if (annotations && annotations.length) {
-            //     annotations.forEach(anno => anno.selected = true);
-            // }
         }
     }
 
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 998ac25fd..9e3bf4954 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -26,6 +26,8 @@ interface IPageProps {
     makePin: (x: number, y: number, page: number) => void;
     sendAnnotations: (annotations: HTMLDivElement[], page: number) => void;
     receiveAnnotations: (page: number) => HTMLDivElement[] | undefined;
+    createAnnotation: (div: HTMLDivElement, page: number) => void;
+    makeAnnotationDocuments: (doc: Doc) => Doc;
 }
 
 @observer
@@ -46,7 +48,6 @@ export default class Page extends React.Component<IPageProps> {
     private _annotationLayer: React.RefObject<HTMLDivElement>;
     private _marquee: React.RefObject<HTMLDivElement>;
     private _curly: React.RefObject<HTMLImageElement>;
-    private _currentAnnotations: HTMLDivElement[] = [];
     private _marqueeing: boolean = false;
     private _dragging: boolean = false;
     private _reactionDisposer?: IReactionDisposer;
@@ -64,18 +65,9 @@ export default class Page extends React.Component<IPageProps> {
         if (this.props.pdf) {
             this.update(this.props.pdf);
         }
-
-        let received = this.props.receiveAnnotations(this.props.page);
-        this._currentAnnotations = received ? received : [];
-        if (this._annotationLayer.current) {
-            this._annotationLayer.current.append(...this._currentAnnotations);
-        }
     }
 
     componentWillUnmount = (): void => {
-        console.log(this._currentAnnotations);
-        this.props.sendAnnotations(this._currentAnnotations, this.props.page);
-
         if (this._reactionDisposer) {
             this._reactionDisposer();
         }
@@ -139,33 +131,6 @@ export default class Page extends React.Component<IPageProps> {
         }
     }
 
-    /**
-     * @param targetDoc: Document that annotations are linked to
-     * This method makes the list of current annotations into documents linked to
-     * the parameter passed in.
-     */
-    makeAnnotationDocuments = (targetDoc: Doc): Doc => {
-        let annoDocs: Doc[] = [];
-
-        for (let anno of this._currentAnnotations) {
-            let annoDoc = new Doc();
-            annoDoc.x = anno.offsetLeft;
-            annoDoc.y = anno.offsetTop;
-            annoDoc.height = anno.offsetHeight;
-            annoDoc.width = anno.offsetWidth;
-            annoDoc.page = this.props.page;
-            annoDoc.target = targetDoc;
-            annoDoc.type = AnnotationTypes.Region;
-            annoDocs.push(annoDoc);
-            anno.remove();
-        }
-        let annoDoc = new Doc();
-        annoDoc.annotations = new List<Doc>(annoDocs);
-        DocUtils.MakeLink(annoDoc, targetDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
-        this._currentAnnotations = [];
-        return annoDoc;
-    }
-
     /**
      * 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.
@@ -184,7 +149,7 @@ export default class Page extends React.Component<IPageProps> {
         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.makeAnnotationDocuments(targetDoc);
+        let annotationDoc = this.props.makeAnnotationDocuments(targetDoc);
         let targetAnnotations = DocListCast(this.props.parent.Document.annotations);
         if (targetAnnotations) {
             targetAnnotations.push(annotationDoc);
@@ -246,10 +211,7 @@ export default class Page extends React.Component<IPageProps> {
             document.removeEventListener("pointerup", this.onSelectEnd);
             document.addEventListener("pointerup", this.onSelectEnd);
             if (!e.ctrlKey) {
-                for (let anno of this._currentAnnotations) {
-                    anno.remove();
-                }
-                this._currentAnnotations = [];
+                this.props.sendAnnotations([], -1);
             }
         }
     }
@@ -336,10 +298,7 @@ export default class Page extends React.Component<IPageProps> {
                     copy.style.opacity = this._marquee.current.style.opacity;
                 }
                 copy.className = this._marquee.current.className;
-                if (this._annotationLayer.current) {
-                    this._annotationLayer.current.append(copy);
-                    this._currentAnnotations.push(copy);
-                }
+                this.props.createAnnotation(copy, this.props.page);
                 this._marquee.current.style.opacity = "0";
             }
 
@@ -362,8 +321,7 @@ export default class Page extends React.Component<IPageProps> {
                             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();
-                            if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox);
-                            this._currentAnnotations.push(annoBox);
+                            this.props.createAnnotation(annoBox, this.props.page);
                         }
                     }
                 }
@@ -395,19 +353,6 @@ export default class Page extends React.Component<IPageProps> {
         }
     }
 
-    renderAnnotation = (anno: Doc | undefined): HTMLDivElement => {
-        let annoBox = document.createElement("div");
-        if (anno) {
-            annoBox.className = "pdfViewer-annotationBox";
-            // transforms the positions from screen onto the pdf div
-            annoBox.style.top = NumCast(anno.x).toString();
-            annoBox.style.left = NumCast(anno.y).toString();
-            annoBox.style.width = NumCast(anno.width).toString();
-            annoBox.style.height = NumCast(anno.height).toString()
-        }
-        return annoBox;
-    }
-
     render() {
         return (
             <div onPointerDown={this.onPointerDown} onDoubleClick={this.doubleClick} className={this.props.name} style={{ "width": this._width, "height": this._height }}>
-- 
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/pdf/PDFViewer.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/pdf/PDFViewer.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 6a2cb71af332d4c782c1678750ba955757eaab45 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 14 Jun 2019 11:56:36 -0400
Subject: fixes

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

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index f4d455be7..bb1f534c6 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -41,6 +41,12 @@
         pointer-events: none !important;
     }
 }
+.textlayer {
+    span {
+        pointer-events: all !important;
+        user-select: text;
+    }
+}
 
 .pdfBox-cont-interactive {
     pointer-events: all;
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index a73df2d58..53c33ce0b 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -35,6 +35,8 @@
     top: 0;
 }
 
+
+
 .pdfViewer-pinAnnotation {
     background-color: red;
     position: absolute;
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 144fca9e0..4b949aa3e 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -103,22 +103,22 @@ class Viewer extends React.Component<IViewerProps> {
 
     @action
     componentDidMount = () => {
-        let wasSelected = this.props.parent.props.isSelected();
+        let wasSelected = this.props.parent.props.active();
         // reaction for when document gets (de)selected
         this._reactionDisposer = reaction(
-            () => [this.props.parent.props.isSelected(), this.startIndex],
+            () => [this.props.parent.props.active(), this.startIndex],
             () => {
                 // if deselected, render images in place of pdf
-                if (wasSelected && !this.props.parent.props.isSelected()) {
+                if (wasSelected && !this.props.parent.props.active()) {
                     this.saveThumbnail();
                     this._pointerEvents = "all";
                 }
                 // if selected, render pdf
-                else if (!wasSelected && this.props.parent.props.isSelected()) {
+                else if (!wasSelected && this.props.parent.props.active()) {
                     this.renderPages(this.startIndex, this.endIndex, true);
                     this._pointerEvents = "none";
                 }
-                wasSelected = this.props.parent.props.isSelected();
+                wasSelected = this.props.parent.props.active();
             },
             { fireImmediately: true }
         );
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 1c305caa3..fa3f7baca 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -195,7 +195,6 @@ export default class Page extends React.Component<IPageProps> {
                 e.stopPropagation();
             }
             else {
-                e.stopPropagation();
                 // set marquee x and y positions to the spatially transformed position
                 let current = this._textLayer.current;
                 if (current) {
@@ -355,7 +354,7 @@ export default class Page extends React.Component<IPageProps> {
 
     render() {
         return (
-            <div onPointerDown={this.onPointerDown} onDoubleClick={this.doubleClick} className={this.props.name} style={{ "width": this._width, "height": this._height }}>
+            <div onPointerDown={this.onPointerDown} onDoubleClick={this.doubleClick} className={"page-cont"} style={{ "width": this._width, "height": this._height }}>
                 <div className="canvasContainer">
                     <canvas ref={this._canvas} />
                 </div>
-- 
cgit v1.2.3-70-g09d2


From 41c290677030cde827c438d9bfda0dbeac64aa14 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 14 Jun 2019 12:30:24 -0400
Subject: changed selectOnLoad

---
 src/client/views/pdf/PDFViewer.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 4b949aa3e..d6081142a 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -586,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={true}
+                            selectOnLoad={false}
                             parentActive={this.props.parent.props.parent.props.active}
                             whenActiveChanged={this.props.parent.props.parent.props.whenActiveChanged}
                             bringToFront={emptyFunction}
-- 
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/pdf/PDFViewer.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/pdf/PDFViewer.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 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/pdf/PDFViewer.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 747fdb6d26afabad067f3f8d98789bfc7ca44f8d Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Fri, 14 Jun 2019 18:11:41 -0400
Subject: small stuffs

---
 src/client/views/pdf/PDFMenu.tsx   |  1 +
 src/client/views/pdf/PDFViewer.tsx | 13 +++++++------
 src/client/views/pdf/Page.tsx      | 13 ++++++++-----
 3 files changed, 16 insertions(+), 11 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index d2a20fb6e..2ba875e42 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -95,6 +95,7 @@ export default class PDFMenu extends React.Component {
     @action
     togglePin = (e: React.MouseEvent) => {
         this._pinned = !this._pinned;
+        this.Highlighting = this._pinned === false;
     }
 
     @action
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index dee891ba6..55d15893a 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -21,6 +21,7 @@ import { DocumentView } from "../nodes/DocumentView";
 import { DragManager } from "../../util/DragManager";
 import { Dictionary } from "typescript-collections";
 
+export const scale = 2;
 interface IPDFViewerProps {
     url: string;
     loaded: (nw: number, nh: number, np: number) => void;
@@ -156,10 +157,10 @@ class Viewer extends React.Component<IViewerProps> {
         this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => {
             for (let anno of value) {
                 let annoDoc = new Doc();
-                if (anno.style.left) annoDoc.x = parseInt(anno.style.left);
-                if (anno.style.top) annoDoc.y = parseInt(anno.style.top);
-                if (anno.style.height) annoDoc.height = parseInt(anno.style.height);
-                if (anno.style.width) annoDoc.width = parseInt(anno.style.width);
+                if (anno.style.left) annoDoc.x = parseInt(anno.style.left) / scale;
+                if (anno.style.top) annoDoc.y = parseInt(anno.style.top) / scale;
+                if (anno.style.height) annoDoc.height = parseInt(anno.style.height) / scale;
+                if (anno.style.width) annoDoc.width = parseInt(anno.style.width) / scale;
                 annoDoc.page = key;
                 annoDoc.target = sourceDoc;
                 annoDoc.type = AnnotationTypes.Region;
@@ -572,7 +573,7 @@ class PinAnnotation extends React.Component<IAnnotationProps> {
                 <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,
+                        top: this.props.y * scale - PinRadius / 2, left: this.props.x * scale - PinRadius / 2, width: PinRadius,
                         height: PinRadius, pointerEvents: "all", backgroundColor: this._backgroundColor
                     }}>
                     <div style={{
@@ -614,7 +615,7 @@ class RegionAnnotation extends React.Component<IAnnotationProps> {
     render() {
         return (
             <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown}
-                style={{ top: this.props.y, left: this.props.x, width: this.props.width, height: this.props.height, pointerEvents: "all", backgroundColor: this._backgroundColor }}></div>
+                style={{ top: this.props.y * scale, left: this.props.x * scale, width: this.props.width * scale, height: this.props.height * scale, pointerEvents: "all", backgroundColor: this._backgroundColor }}></div>
         );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 455e1d831..bd2cae749 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -13,9 +13,10 @@ import { emptyFunction } from "../../../Utils";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { listSpec } from "../../../new_fields/Schema";
 import { menuBar } from "prosemirror-menu";
-import { AnnotationTypes } from "./PDFViewer";
+import { AnnotationTypes, PDFViewer, scale } from "./PDFViewer";
 import PDFMenu from "./PDFMenu";
 
+
 interface IPageProps {
     pdf: Opt<Pdfjs.PDFDocumentProxy>;
     name: string;
@@ -28,7 +29,7 @@ interface IPageProps {
     sendAnnotations: (annotations: HTMLDivElement[], page: number) => void;
     receiveAnnotations: (page: number) => HTMLDivElement[] | undefined;
     createAnnotation: (div: HTMLDivElement, page: number) => void;
-    makeAnnotationDocuments: (doc: Doc | undefined) => Doc;
+    makeAnnotationDocuments: (doc: Doc | undefined, scale: number) => Doc;
 }
 
 @observer
@@ -103,7 +104,6 @@ export default class Page extends React.Component<IPageProps> {
     @action
     private renderPage = (page: Pdfjs.PDFPageProxy): void => {
         // lower scale = easier to read at small sizes, higher scale = easier to read at large sizes
-        let scale = 2;
         let viewport = page.getViewport(scale);
         let canvas = this._canvas.current;
         let textLayer = this._textLayer.current;
@@ -135,7 +135,7 @@ export default class Page extends React.Component<IPageProps> {
     @action
     highlight = (targetDoc?: Doc) => {
         // creates annotation documents for current highlights
-        let annotationDoc = this.props.makeAnnotationDocuments(targetDoc);
+        let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale);
         let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc));
         if (targetAnnotations === undefined) {
             Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]);
@@ -307,8 +307,11 @@ export default class Page extends React.Component<IPageProps> {
                 this._marquee.current.style.opacity = "0";
             }
 
+            if (this._marqueeWidth > 10 || this._marqueeHeight > 10) {
+                PDFMenu.Instance.jumpTo(e.clientX, e.clientY);
+            }
+
             this._marqueeHeight = this._marqueeWidth = 0;
-            PDFMenu.Instance.jumpTo(e.clientX, e.clientY);
         }
         else {
             let sel = window.getSelection();
-- 
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/pdf/PDFViewer.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 ee1e6f815b76f9c0e076c8fecc89dd4fa1f5cf4b Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 16 Jun 2019 21:39:48 -0400
Subject: fixed access out-of-bounds error..  fixed event handling for
 unselected pdf.

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

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index bb1f534c6..8bcae4f1e 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -36,15 +36,14 @@
     pointer-events: none;
     display: flex;
     flex-direction: row;
-
-    span {
-        pointer-events: none !important;
+    .textlayer {
+        pointer-events: none;
+        span {
+            pointer-events: none !important;
+        }
     }
-}
-.textlayer {
-    span {
-        pointer-events: all !important;
-        user-select: text;
+    .page-cont {
+        pointer-events: none;
     }
 }
 
@@ -52,6 +51,12 @@
     pointer-events: all;
     display: flex;
     flex-direction: row;
+    .textlayer {
+        span {
+            pointer-events: all !important;
+            user-select: text;
+        }
+    }
 }
 
 .pdfBox-contentContainer {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 75c298f55..bc7cfecbb 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -392,7 +392,7 @@ class Viewer extends React.Component<IViewerProps> {
         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;
+            currOffset -= this._pageSizes[index] ? this._pageSizes[index].height : this._pageSizes[0].height;
             index++;
         }
         return index;
-- 
cgit v1.2.3-70-g09d2


From 90c14f3ad8d9e48698c516bc6549143912e19236 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Mon, 17 Jun 2019 10:47:40 -0400
Subject: highlights are a different color

---
 src/client/views/pdf/PDFViewer.tsx | 5 +++--
 src/client/views/pdf/Page.tsx      | 4 ++--
 2 files changed, 5 insertions(+), 4 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index bc7cfecbb..9becfb419 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -156,7 +156,7 @@ class Viewer extends React.Component<IViewerProps> {
         }
     }
 
-    makeAnnotationDocument = (sourceDoc: Doc | undefined): Doc => {
+    makeAnnotationDocument = (sourceDoc: Doc | undefined, s: number, color: string): Doc => {
         let annoDocs: Doc[] = [];
         this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => {
             for (let anno of value) {
@@ -167,6 +167,7 @@ class Viewer extends React.Component<IViewerProps> {
                 if (anno.style.width) annoDoc.width = parseInt(anno.style.width) / scale;
                 annoDoc.page = key;
                 annoDoc.target = sourceDoc;
+                annoDoc.color = color;
                 annoDoc.type = AnnotationTypes.Region;
                 annoDocs.push(annoDoc);
                 anno.remove();
@@ -624,7 +625,7 @@ class RegionAnnotation extends React.Component<IAnnotationProps> {
     render() {
         return (
             <div className="pdfViewer-annotationBox" onPointerDown={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: this._backgroundColor }}></div>
+                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>
         );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index bd2cae749..e706a0d5c 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -29,7 +29,7 @@ interface IPageProps {
     sendAnnotations: (annotations: HTMLDivElement[], page: number) => void;
     receiveAnnotations: (page: number) => HTMLDivElement[] | undefined;
     createAnnotation: (div: HTMLDivElement, page: number) => void;
-    makeAnnotationDocuments: (doc: Doc | undefined, scale: number) => Doc;
+    makeAnnotationDocuments: (doc: Doc | undefined, scale: number, color: string) => Doc;
 }
 
 @observer
@@ -135,7 +135,7 @@ export default class Page extends React.Component<IPageProps> {
     @action
     highlight = (targetDoc?: Doc) => {
         // creates annotation documents for current highlights
-        let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale);
+        let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, "#f4f442");
         let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc));
         if (targetAnnotations === undefined) {
             Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]);
-- 
cgit v1.2.3-70-g09d2


From 122607408882617235af255af84ce78828b7982f Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Mon, 17 Jun 2019 13:42:21 -0400
Subject: page sizes loaded

---
 src/client/views/pdf/PDFMenu.tsx   |  6 +++---
 src/client/views/pdf/PDFViewer.tsx | 30 +++++++++++++++++++++++++++---
 src/client/views/pdf/Page.tsx      |  8 ++++----
 3 files changed, 34 insertions(+), 10 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index b0735f63b..b44370e3d 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -18,7 +18,7 @@ export default class PDFMenu extends React.Component {
     @observable private _pinned: boolean = false;
 
     StartDrag: (e: PointerEvent) => void = emptyFunction;
-    Highlight: (d: Doc | undefined) => void = emptyFunction;
+    Highlight: (d: Doc | undefined, color: string | undefined) => void = emptyFunction;
     @observable Highlighting: boolean = false;
 
     private _timeout: NodeJS.Timeout | undefined;
@@ -129,11 +129,11 @@ export default class PDFMenu extends React.Component {
     @action
     highlightClicked = (e: React.MouseEvent) => {
         if (!this._pinned) {
-            this.Highlight(undefined);
+            this.Highlight(undefined, "#f4f442");
         }
         else {
             this.Highlighting = !this.Highlighting;
-            this.Highlight(undefined);
+            this.Highlight(undefined, "#f4f442");
         }
     }
 
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 9becfb419..d74a16f3f 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -23,6 +23,7 @@ import { Dictionary } from "typescript-collections";
 import * as rp from "request-promise";
 import { restProperty } from "babel-types";
 import { DocServer } from "../../DocServer";
+import { number } from "prop-types";
 
 export const scale = 2;
 interface IPDFViewerProps {
@@ -141,10 +142,33 @@ class Viewer extends React.Component<IViewerProps> {
 
         setTimeout(() => {
             // this.renderPages(this.startIndex, this.endIndex, true);
-            this.saveThumbnail();
+            this.initialLoad();
         }, 1000);
     }
 
+    @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();
+                        }
+                    }
+                );
+            }
+        }
+    }
+
     private mainCont = (div: HTMLDivElement | null) => {
         if (this._dropDisposer) {
             this._dropDisposer();
@@ -186,7 +210,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.makeAnnotationDocument(sourceDoc);
+            let destDoc = this.makeAnnotationDocument(sourceDoc, 1, "red");
             let targetAnnotations = DocListCast(this.props.parent.Document.annotations);
             if (targetAnnotations) {
                 targetAnnotations.push(destDoc);
@@ -392,7 +416,7 @@ class Viewer extends React.Component<IViewerProps> {
         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) {
+        while (index < this._pageSizes.length && currOffset - (this._pageSizes[index] ? this._pageSizes[index].height : 792 * scale) > 0) {
             currOffset -= this._pageSizes[index] ? this._pageSizes[index].height : this._pageSizes[0].height;
             index++;
         }
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index e706a0d5c..bb87ec9d4 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -133,9 +133,9 @@ export default class Page extends React.Component<IPageProps> {
     }
 
     @action
-    highlight = (targetDoc?: Doc) => {
+    highlight = (targetDoc?: Doc, color: string = "red") => {
         // creates annotation documents for current highlights
-        let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, "#f4f442");
+        let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, color);
         let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc));
         if (targetAnnotations === undefined) {
             Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]);
@@ -162,7 +162,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;
-        let annotationDoc = this.highlight(targetDoc);
+        let annotationDoc = this.highlight(targetDoc, "red");
         // create dragData and star tdrag
         let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc);
         if (this._textLayer.current) {
@@ -323,7 +323,7 @@ export default class Page extends React.Component<IPageProps> {
 
 
         if (PDFMenu.Instance.Highlighting) {
-            this.highlight(undefined);
+            this.highlight(undefined, "#f4f442");
         }
         else {
             PDFMenu.Instance.StartDrag = this.startDrag;
-- 
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/pdf/PDFViewer.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 2f5c38c6a0a5220c2a31931c34d94e199854d703 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 18 Jun 2019 08:36:37 -0400
Subject: more streamlining

---
 src/client/views/pdf/PDFViewer.tsx | 144 +++++++++++++++----------------------
 1 file changed, 57 insertions(+), 87 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 5149e48fd..645afc636 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,29 +1,23 @@
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import React = require("react");
-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";
 import "pdfjs-dist/web/pdf_viewer.css";
-import { PDFBox } from "../nodes/PDFBox";
-import Page from "./Page";
-import { NumCast, Cast, BoolCast, StrCast } from "../../../new_fields/Types";
+import * as rp from "request-promise";
+import { Dictionary } from "typescript-collections";
+import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
-import { DocUtils, Docs } from "../../documents/Documents";
-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";
-import { DragManager } from "../../util/DragManager";
-import { Dictionary } from "typescript-collections";
-import * as rp from "request-promise";
-import { restProperty } from "babel-types";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { emptyFunction } from "../../../Utils";
 import { DocServer } from "../../DocServer";
-import { number } from "prop-types";
+import { Docs, DocUtils } from "../../documents/Documents";
+import { DocumentManager } from "../../util/DocumentManager";
+import { DragManager } from "../../util/DragManager";
+import { DocumentView } from "../nodes/DocumentView";
+import { PDFBox } from "../nodes/PDFBox";
+import Page from "./Page";
+import "./PDFViewer.scss";
+import React = require("react");
 
 export const scale = 2;
 interface IPDFViewerProps {
@@ -49,14 +43,15 @@ 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} url={this.props.url} />
+                {!this._pdf ? (null) :
+                    <Viewer pdf={this._pdf} loaded={this.props.loaded} scrollY={this.props.scrollY} parent={this.props.parent} mainCont={this._mainDiv} url={this.props.url} />}
             </div>
         );
     }
 }
 
 interface IViewerProps {
-    pdf: Opt<Pdfjs.PDFDocumentProxy>;
+    pdf: Pdfjs.PDFDocumentProxy;
     loaded: (nw: number, nh: number, np: number) => void;
     scrollY: number;
     parent: PDFBox;
@@ -77,37 +72,27 @@ class Viewer extends React.Component<IViewerProps> {
     @observable private _isPage: string[] = [];
     @observable private _pageSizes: { width: number, height: number }[] = [];
     @observable private _annotations: Doc[] = [];
-    @observable private _pointerEvents: "all" | "none" = "all";
     @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
 
     private _pageBuffer: number = 1;
-    private _annotationLayer: React.RefObject<HTMLDivElement>;
+    private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     private _reactionDisposer?: IReactionDisposer;
     private _annotationReactionDisposer?: IReactionDisposer;
     private _dropDisposer?: DragManager.DragDropDisposer;
 
-    constructor(props: IViewerProps) {
-        super(props);
-
-        this._annotationLayer = React.createRef();
-    }
-
     componentDidUpdate = (prevProps: IViewerProps) => {
-        if (this.scrollY !== prevProps.scrollY && this._visibleElements.length) {
-            this.renderPages(this.startIndex, this.endIndex, false);
+        if (this.scrollY !== prevProps.scrollY) {
+            this.renderPages();
         }
     }
 
     @action
     componentDidMount = () => {
-        let wasSelected = this.props.parent.props.active();
         this._reactionDisposer = reaction(
-            () => [this.props.parent.props.active(), this.startIndex, this.props.pdf],
+            () => [this.props.parent.props.active(), this.startIndex, this.endIndex],
             async () => {
                 await this.initialLoad();
-                wasSelected = this.props.parent.props.active();
-                runInAction(() => this._pointerEvents = wasSelected ? "none" : "all");
-                this.renderPages(this.startIndex, this.endIndex, false);
+                this.renderPages();
             }, { fireImmediately: true });
 
         this._annotationReactionDisposer = reaction(
@@ -124,23 +109,15 @@ class Viewer extends React.Component<IViewerProps> {
 
     @action
     initialLoad = async () => {
-        if (this.props.pdf && this._pageSizes.length === 0) {
+        if (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);
-                }));
+                await this.props.pdf.getPage(i + 1).then(page => runInAction(() =>
+                    pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale }));
             }
-            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);
-            })
+            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))
         }
     }
 
@@ -199,14 +176,15 @@ class Viewer extends React.Component<IViewerProps> {
      */
     @action
     pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => {
-        this.props.pdf && this.props.loaded && this.props.loaded(page.width, page.height, this.props.pdf.numPages);
+        this.props.loaded(page.width, page.height, this.props.pdf.numPages);
     }
     @action
     getPlaceholderPage = (page: number) => {
         if (this._isPage[page] !== "none") {
             this._isPage[page] = "none";
             this._visibleElements[page] = (
-                <div key={`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 }} />
+                <div key={`placeholder-${page}`} className="pdfviewer-placeholder"
+                    style={{ width: this._pageSizes[page].width, height: this._pageSizes[page].height }} />
             );
         }
     }
@@ -219,8 +197,8 @@ class Viewer extends React.Component<IViewerProps> {
                     pdf={this.props.pdf}
                     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"}`}
+                    key={`rendered-${page + 1}`}
+                    name={`${this.props.pdf.fingerprint + `-page${page + 1}`}`}
                     pageLoaded={this.pageLoaded}
                     parent={this.props.parent}
                     makePin={this.createPinAnnotation}
@@ -243,38 +221,33 @@ class Viewer extends React.Component<IViewerProps> {
             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` }} />);
+            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` }} />);
         }
     }
 
     @computed get scrollY(): number { return this.props.scrollY; }
 
+    // startIndex: where to start rendering pages
     @computed get startIndex(): number { return Math.max(0, this.getPageFromScroll(this.scrollY) - this._pageBuffer); }
 
+    // endIndex: where to end rendering pages
     @computed get endIndex(): number {
-        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);
+        let width = this._pageSizes.map(i => i.width);
+        return Math.min(this.props.pdf.numPages - 1, 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);
+    renderPages = () => {
+        for (let i = 0; i < this.props.pdf.numPages; i++) {
+            if (i < this.startIndex || i > this.endIndex) {
+                this.getPlaceholderPage(i);  // pages outside of the pdf use empty stand-in divs
+            } else {
+                if (this.props.parent.props.active()) {
+                    this.getRenderedPage(i);
                 } else {
-                    if (this.props.parent.props.active()) {
-                        this.getRenderedPage(i);
-                    } else {
-                        this.getPageImage(i);
-                    }
+                    this.getPageImage(i);
                 }
             }
         }
@@ -308,7 +281,6 @@ class Viewer extends React.Component<IViewerProps> {
 
     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 + this.getScrollFromPage(page);
@@ -333,22 +305,16 @@ class Viewer extends React.Component<IViewerProps> {
     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) {
-            currOffset -= this._pageSizes[index] ? this._pageSizes[index].height : this._pageSizes[0].height;
-            index++;
+        while (index < this._pageSizes.length && currOffset - this._pageSizes[index].height > 0) {
+            currOffset -= this._pageSizes[index++].height;
         }
         return index;
     }
 
-
     getScrollFromPage = (index: number): number => {
         let counter = 0;
-        if (this.props.pdf && index < this.props.pdf.numPages) {
-            for (let i = 0; i < index; i++) {
-                if (this._pageSizes[i]) {
-                    counter += this._pageSizes[i].height;
-                }
-            }
+        for (let i = 0; i < Math.min(this.props.pdf.numPages, index); i++) {
+            counter += this._pageSizes[i].height;
         }
         return counter;
     }
@@ -392,7 +358,11 @@ class Viewer extends React.Component<IViewerProps> {
                 <div className="viewer">
                     {this._visibleElements}
                 </div>
-                <div className="pdfViewer-annotationLayer" style={{ height: this.props.parent.Document.nativeHeight, width: `100%`, pointerEvents: this._pointerEvents }}>
+                <div className="pdfViewer-annotationLayer"
+                    style={{
+                        height: this.props.parent.Document.nativeHeight, width: `100%`,
+                        pointerEvents: this.props.parent.props.active() ? "none" : "all"
+                    }}>
                     <div className="pdfViewer-annotationLayer-subCont" ref={this._annotationLayer}>
                         {this._annotations.map(anno => this.renderAnnotation(anno))}
                     </div>
-- 
cgit v1.2.3-70-g09d2


From a3b8a57027d7c45ea19d259e1ec18fa6a8648c24 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 18 Jun 2019 08:49:02 -0400
Subject: looked like wrong code...

---
 src/client/views/pdf/PDFViewer.tsx | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 645afc636..e5fb32e53 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -234,8 +234,7 @@ class Viewer extends React.Component<IViewerProps> {
 
     // endIndex: where to end rendering pages
     @computed get endIndex(): number {
-        let width = this._pageSizes.map(i => i.width);
-        return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.scrollY + Math.max(...width)) + this._pageBuffer);
+        return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.scrollY) + this._pageBuffer);
     }
 
     @action
-- 
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/pdf/PDFViewer.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/pdf/PDFViewer.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/pdf/PDFViewer.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 13e301dea2f537b67b338cc6a98d3f3b5a8e1f36 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Tue, 18 Jun 2019 20:58:32 -0400
Subject: Fixed linter errors

---
 src/client/northstar/dash-nodes/HistogramBox.tsx             |  3 +--
 src/client/util/DocumentManager.ts                           |  6 +++---
 src/client/util/DragManager.ts                               |  4 +++-
 src/client/util/RichTextSchema.tsx                           | 10 +++++-----
 src/client/util/TooltipTextMenu.tsx                          |  2 +-
 src/client/views/ContextMenu.tsx                             |  4 ++--
 src/client/views/DocumentDecorations.tsx                     |  2 +-
 src/client/views/MainOverlayTextBox.tsx                      |  2 +-
 src/client/views/MainView.tsx                                |  7 ++++---
 src/client/views/PresentationView.tsx                        |  4 ++--
 src/client/views/collections/CollectionBaseView.tsx          |  2 +-
 src/client/views/collections/CollectionDockingView.tsx       |  5 +++--
 src/client/views/collections/CollectionPDFView.tsx           |  2 +-
 src/client/views/collections/CollectionSchemaView.tsx        | 11 ++++++-----
 src/client/views/collections/CollectionStackingView.tsx      |  4 ++--
 src/client/views/collections/CollectionTreeView.tsx          | 10 +++++-----
 .../collectionFreeForm/CollectionFreeFormLinksView.tsx       |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx            |  2 +-
 .../views/collections/collectionFreeForm/MarqueeView.tsx     |  2 +-
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx    |  2 +-
 src/client/views/nodes/DocumentView.tsx                      | 12 ++++++------
 src/client/views/nodes/FormattedTextBox.tsx                  |  2 +-
 src/client/views/nodes/ImageBox.tsx                          |  3 ++-
 src/client/views/pdf/PDFAnnotationLayer.tsx                  |  2 +-
 src/client/views/pdf/PDFMenu.tsx                             |  2 +-
 src/client/views/pdf/PDFViewer.tsx                           |  4 ++--
 src/server/index.ts                                          |  2 +-
 27 files changed, 59 insertions(+), 54 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index d7732ee86..a60eaea85 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -125,8 +125,7 @@ export class HistogramBox extends React.Component<FieldViewProps> {
                                 let mapped = brushingDocs.map((brush, i) => {
                                     brush.backgroundColor = StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length];
                                     let brushed = DocListCast(brush.brushingDocs);
-                                    if (!brushed.length)
-                                        return null;
+                                    if (!brushed.length) return null;
                                     return { l: brush, b: brushed[0][Id] === proto[Id] ? brushed[1] : brushed[0] };
                                 });
                                 runInAction(() => this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...mapped.filter(m => m) as { l: Doc, b: Doc }[]));
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index ff0c1560b..862395d74 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -35,9 +35,9 @@ export class DocumentManager {
         let toReturn: DocumentView | null = null;
         let passes = preferredCollection ? [preferredCollection, undefined] : [undefined];
 
-        for (let i = 0; i < passes.length; i++) {
+        for (let pass of passes) {
             DocumentManager.Instance.DocumentViews.map(view => {
-                if (view.props.Document[Id] === id && (!passes[i] || view.props.ContainingCollectionView === preferredCollection)) {
+                if (view.props.Document[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) {
                     toReturn = view;
                     return;
                 }
@@ -45,7 +45,7 @@ export class DocumentManager {
             if (!toReturn) {
                 DocumentManager.Instance.DocumentViews.map(view => {
                     let doc = view.props.Document.proto;
-                    if (doc && doc[Id] === id && (!passes[i] || view.props.ContainingCollectionView === preferredCollection)) {
+                    if (doc && doc[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) {
                         toReturn = view;
                     }
                 });
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 89566e777..c3c92daa5 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -282,11 +282,13 @@ export namespace DragManager {
             // }
             let set = dragElement.getElementsByTagName('*');
             if (dragElement.hasAttribute("style")) (dragElement as any).style.pointerEvents = "none";
-            for (let i = 0; i < set.length; i++)
+            // tslint:disable-next-line: prefer-for-of
+            for (let i = 0; i < set.length; i++) {
                 if (set[i].hasAttribute("style")) {
                     let s = set[i];
                     (s as any).style.pointerEvents = "none";
                 }
+            }
 
 
             dragDiv.appendChild(dragElement);
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index e1e595925..943cdb4d1 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -156,12 +156,12 @@ export const nodes: { [index: string]: NodeSpec } = {
                     title: dom.getAttribute("title"),
                     alt: dom.getAttribute("alt"),
                     width: Math.min(100, Number(dom.getAttribute("width"))),
-                }
+                };
             }
         }],
         toDOM(node) {
-            const attrs = { style: `width: ${node.attrs.width}` }
-            return ["video", { ...node.attrs, ...attrs }]
+            const attrs = { style: `width: ${node.attrs.width}` };
+            return ["video", { ...node.attrs, ...attrs }];
         }
     },
 
@@ -285,7 +285,7 @@ export const marks: { [index: string]: MarkSpec } = {
         toDOM() {
             return ['span', {
                 style: 'color: blue'
-            }]
+            }];
         }
     },
 
@@ -536,4 +536,4 @@ schema.nodeFromJSON = (json: any) => {
         node.attrs.oldtext = Slice.fromJSON(schema, node.attrs.oldtextslice);
     }
     return node;
-}
\ No newline at end of file
+};
\ No newline at end of file
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 0a61b7e7d..f2559db74 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -256,7 +256,7 @@ export class TooltipTextMenu {
             starButton.onclick = () => {
                 let state = this.view.state;
                 this.insertStar(state, this.view.dispatch);
-            }
+            };
 
             this.tooltip.appendChild(starButton);
         }
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index da374455e..eb1937683 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -1,8 +1,8 @@
 import React = require("react");
 import { ContextMenuItem, ContextMenuProps } from "./ContextMenuItem";
 import { observable, action } from "mobx";
-import { observer } from "mobx-react"
-import "./ContextMenu.scss"
+import { observer } from "mobx-react";
+import "./ContextMenu.scss";
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { faSearch, faCircle } from '@fortawesome/free-solid-svg-icons';
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index ceca940b6..e60f8c86c 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -466,7 +466,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         SelectionManager.SelectedDocuments().forEach(element => {
             const rect = element.ContentDiv ? element.ContentDiv.getBoundingClientRect() : new DOMRect();
 
-            if (rect.width !== 0 && (dX != 0 || dY != 0 || dW != 0 || dH != 0)) {
+            if (rect.width !== 0 && (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0)) {
                 let doc = PositionDocument(element.props.Document);
                 let nwidth = doc.nativeWidth || 0;
                 let nheight = doc.nativeHeight || 0;
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 0de880175..b4ad5f4d7 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -40,7 +40,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
                     this.TextDoc = box.props.Document;
                     let sxf = Utils.GetScreenTransform(box ? box.CurrentDiv : undefined);
                     let xf = () => { box.props.ScreenToLocalTransform(); return new Transform(-sxf.translateX, -sxf.translateY, 1 / sxf.scale); };
-                    this.setTextDoc(box.props.fieldKey, box.CurrentDiv, xf, BoolCast(box.props.Document.autoHeight, false) || box.props.height === "min-content")
+                    this.setTextDoc(box.props.fieldKey, box.CurrentDiv, xf, BoolCast(box.props.Document.autoHeight, false) || box.props.height === "min-content");
                 }
                 else {
                     this.TextDoc = undefined;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index fd76cbbd3..7f979cd3b 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -237,10 +237,11 @@ export class MainView extends React.Component {
     }
 
     onColorClick = (e: React.MouseEvent) => {
-        let target = (e.nativeEvent! as any).path[0];
-        let parent = (e.nativeEvent! as any).path[1];
-        if (target.localName === "input" || parent.localName === "span")
+        let target = (e.nativeEvent as any).path[0];
+        let parent = (e.nativeEvent as any).path[1];
+        if (target.localName === "input" || parent.localName === "span") {
             e.stopPropagation();
+        }
     }
 
 
diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx
index d2d41a4ba..1dacbb663 100644
--- a/src/client/views/PresentationView.tsx
+++ b/src/client/views/PresentationView.tsx
@@ -40,8 +40,8 @@ class PresentationViewList extends React.Component<PresListProps> {
             //this doc is selected
             className += " presentationView-selected";
         }
-        let onEnter = (e: React.PointerEvent) => { document.libraryBrush = true; }
-        let onLeave = (e: React.PointerEvent) => { document.libraryBrush = false; }
+        let onEnter = (e: React.PointerEvent) => { document.libraryBrush = true; };
+        let onLeave = (e: React.PointerEvent) => { document.libraryBrush = false; };
         return (
             <div className={className} key={document[Id] + index}
                 onPointerEnter={onEnter} onPointerLeave={onLeave}
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 5238ad114..1e42593d1 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -130,7 +130,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
 
     @action.bound
     removeDocument(doc: Doc): boolean {
-        let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView)
+        let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
         docView && SelectionManager.DeselectDoc(docView);
         const props = this.props;
         //TODO This won't create the field if it doesn't already exist
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index f2b3528b8..5beb89315 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -347,7 +347,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                             tab.header.parent.setActiveContentItem(tab.contentItem);
                         }
                         tab.setActive(true);
-                    }
+                    };
                     ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={(doc, location) => CollectionDockingView.Instance.AddTab(stack, doc)} />, upDiv);
                     tab.reactComponents = [upDiv];
                     tab.element.append(upDiv);
@@ -432,8 +432,9 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     @observable private _document: Opt<Doc>;
     get _stack(): any {
         let parent = (this.props as any).glContainer.parent.parent;
-        if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1)
+        if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1) {
             return parent.parent.contentItems[1];
+        }
         return parent;
     }
     constructor(props: any) {
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index b62d3f7bb..b2d016934 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -40,7 +40,7 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
                 // console.log(this.props.Document[HeightSym]());
             },
             { fireImmediately: true }
-        )
+        );
     }
 
     public static LayoutString(fieldKey: string = "data") {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 14a7d19d0..faea8d44d 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -87,8 +87,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             let columnDocs = DocListCast(schemaDoc.data);
             if (columnDocs) {
                 let ddoc = columnDocs.find(doc => doc.title === columnName);
-                if (ddoc)
+                if (ddoc) {
                     return ddoc;
+                }
             }
         }
         return this.props.Document;
@@ -285,7 +286,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     }
 
     getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(
-        - this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth, - this.borderWidth);
+        - this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth, - this.borderWidth)
 
 
     get documentKeysCheckList() {
@@ -334,7 +335,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             columns={this.tableColumns}
             column={{ ...ReactTableDefaults.column, Cell: this.renderCell, }}
             getTrProps={this.getTrProps}
-        />
+        />;
     }
 
     @computed
@@ -360,7 +361,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             addDocTab={this.props.addDocTab}
             setPreviewScript={this.setPreviewScript}
             previewScript={this.previewScript}
-        />
+        />;
     }
     @action
     setPreviewScript = (script: string) => {
@@ -409,7 +410,7 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
     }
     private PanelWidth = () => this.nativeWidth * this.contentScaling();
     private PanelHeight = () => this.nativeHeight * this.contentScaling();
-    private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, 0).scale(1 / this.contentScaling())
+    private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, 0).scale(1 / this.contentScaling());
     get centeringOffset() { return (this.props.width() - this.nativeWidth * this.contentScaling()) / 2; }
     @action
     onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index e1ac3505b..f5ad4ee95 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -101,7 +101,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             let childFocus = (doc: Doc) => {
                 doc.libraryBrush = true;
                 this.props.focus(this.props.Document); // just focus on this collection, not the underlying document because the API doesn't support adding an offset to focus on and we can't pan zoom our contents to be centered.
-            }
+            };
             return (<div className="collectionStackingView-masonryDoc"
                 key={d[Id]}
                 ref={dref}
@@ -131,7 +131,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                     whenActiveChanged={this.props.whenActiveChanged}
                 />
             </div>);
-        })
+        });
     }
     onContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index b13694e9d..3e495b734 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -67,8 +67,8 @@ class TreeView extends React.Component<TreeViewProps> {
     @undoBatch delete = () => this.props.deleteDoc(this.props.document);
     @undoBatch openRight = async () => this.props.addDocTab(this.props.document, "openRight");
 
-    @action onMouseEnter = () => { this._isOver = true; }
-    @action onMouseLeave = () => { this._isOver = false; }
+    @action onMouseEnter = () => { this._isOver = true; };
+    @action onMouseLeave = () => { this._isOver = false; };
 
     onPointerEnter = (e: React.PointerEvent): void => {
         this.props.active() && (this.props.document.libraryBrush = true);
@@ -89,8 +89,8 @@ class TreeView extends React.Component<TreeViewProps> {
         let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
         let before = x[1] < bounds[1];
         let inside = x[0] > bounds[0] + 75 || (!before && this._bulletType === BulletType.Collapsible);
-        this._header!.current!.className = "treeViewItem-header"
-        if (inside && this._bulletType != BulletType.List) this._header!.current!.className += " treeViewItem-header-inside";
+        this._header!.current!.className = "treeViewItem-header";
+        if (inside && this._bulletType !== BulletType.List) this._header!.current!.className += " treeViewItem-header-inside";
         else if (before) this._header!.current!.className += " treeViewItem-header-above";
         else if (!before) this._header!.current!.className += " treeViewItem-header-below";
         e.stopPropagation();
@@ -192,7 +192,7 @@ class TreeView extends React.Component<TreeViewProps> {
             if (inside) {
                 let docList = Cast(this.props.document.data, listSpec(Doc));
                 if (docList !== undefined) {
-                    addDoc = (doc: Doc) => { docList && docList.push(doc); return true; }
+                    addDoc = (doc: Doc) => { docList && docList.push(doc); return true; };
                 }
             }
             let added = false;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index c4dd534ed..be75c6c5c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -109,7 +109,7 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
                     }
                     return match || found;
                 }, false)) {
-                    drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] })
+                    drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] });
                 }
             });
             return drawnPairs;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 250b6bf5d..84841e469 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -342,7 +342,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     private childViews = () => [
         <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />,
         ...this.views
-    ];
+    ]
     render() {
         const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
         const easing = () => this.props.Document.panTransformType === "Ease";
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index dedcc3172..05dc6f534 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -321,7 +321,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                             summary.imageSummary = imageSummary;
                             this.props.addDocument(imageSummary, false);
                         }
-                    })
+                    });
                     newCollection.proto!.summaryDoc = summary;
                     selected = [newCollection];
                     newCollection.x = bounds.left + bounds.width;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index f6b1c62ee..858959d81 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -48,7 +48,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     panelHeight = () => this.props.PanelHeight();
     getTransform = (): Transform => this.props.ScreenToLocalTransform()
         .translate(-this.X, -this.Y)
-        .scale(1 / this.contentScaling()).scale(1 / this.zoom);
+        .scale(1 / this.contentScaling()).scale(1 / this.zoom)
 
     animateBetweenIcon = (icon: number[], stime: number, maximizing: boolean) => {
         this.props.bringToFront(this.props.Document);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7c058d91c..4992669df 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -168,8 +168,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     public static animateBetweenIconFunc = (doc: Doc, width: number, height: number, stime: number, maximizing: boolean, cb?: (progress: number) => void) => {
         setTimeout(() => {
             let now = Date.now();
-            let progress = now < stime + 200 ? Math.min(1, (now - stime) / 200) : 1
-            doc.width = progress === 1 ? width : maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress
+            let progress = now < stime + 200 ? Math.min(1, (now - stime) / 200) : 1;
+            doc.width = progress === 1 ? width : maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress;
             doc.height = progress === 1 ? height : maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress;
             cb && cb(progress);
             if (now < stime + 200) {
@@ -229,7 +229,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (minimizedDoc) {
             let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(
                 NumCast(minimizedDoc.x) - NumCast(this.Document.x), NumCast(minimizedDoc.y) - NumCast(this.Document.y));
-            this.collapseTargetsToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs))
+            this.collapseTargetsToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs));
         }
     }
 
@@ -372,8 +372,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         this._lastTap = Date.now();
     }
 
-    deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); }
-    fieldsClicked = (): void => { this.props.addDocTab(Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }), "onRight") };
+    deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); };
+    fieldsClicked = (): void => { this.props.addDocTab(Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }), "onRight"); };
     makeBtnClicked = (): void => {
         let doc = Doc.GetProto(this.props.Document);
         doc.isButton = !BoolCast(doc.isButton, false);
@@ -527,7 +527,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = false; };
 
     isSelected = () => SelectionManager.IsSelected(this);
-    @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); }
+    @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); };
 
     @computed get nativeWidth() { return this.Document.nativeWidth || 0; }
     @computed get nativeHeight() { return this.Document.nativeHeight || 0; }
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 36d902c4f..df12f261b 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -161,7 +161,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                     de.data.moveDocument(de.data.draggedDocuments[0], stackDoc, (doc) => {
                         Cast(stackDoc.data, listSpec(Doc))!.push(doc);
                         return true;
-                    })
+                    });
                 }
             }
         }
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 6b8b64c5f..f208ce2ce 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -188,8 +188,9 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
     }
     @action onError = () => {
         let timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount;
-        if (timeout < 10)
+        if (timeout < 10) {
             setTimeout(this.retryPath, Math.min(10000, timeout * 5));
+        }
     }
     _curSuffix = "_m";
     render() {
diff --git a/src/client/views/pdf/PDFAnnotationLayer.tsx b/src/client/views/pdf/PDFAnnotationLayer.tsx
index e92dcacbf..1f49e0d2f 100644
--- a/src/client/views/pdf/PDFAnnotationLayer.tsx
+++ b/src/client/views/pdf/PDFAnnotationLayer.tsx
@@ -19,6 +19,6 @@ export class PDFAnnotationLayer extends React.Component {
             <div className="pdfAnnotationLayer-cont" style={{ width: "100%", height: "100%", position: "relative", top: "-200%" }} onPointerDown={this.onPointerDown}>
 
             </div>
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index 7817e8c26..2ed891131 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -162,6 +162,6 @@ export default class PDFMenu extends React.Component {
                 </button>
                 <div className="pdfMenu-dragger" onPointerDown={this.dragStart} style={{ width: this._pinned ? "20px" : "0px" }} />
             </div >
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 69372f43b..8c0aaea00 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -198,7 +198,7 @@ class Viewer extends React.Component<IViewerProps> {
                 <Page
                     pdf={this.props.pdf}
                     page={page}
-                    numPages={this.props.pdf!.numPages}
+                    numPages={this.props.pdf.numPages}
                     key={`rendered-${page + 1}`}
                     name={`${this.props.pdf.fingerprint + `-page${page + 1}`}`}
                     pageLoaded={this.pageLoaded}
@@ -219,7 +219,7 @@ class Viewer extends React.Component<IViewerProps> {
     @action
     getPageImage = async (page: number) => {
         let handleError = () => this.getRenderedPage(page);
-        if (this._isPage[page] != "image") {
+        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`)));
diff --git a/src/server/index.ts b/src/server/index.ts
index 7ef542b01..2901f61ed 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -172,7 +172,7 @@ function LoadPage(file: string, pageNumber: number, res: Response) {
                     canvasContext: canvasAndContext.context,
                     viewport: viewport,
                     canvasFactory: factory
-                }
+                };
                 console.log("read " + pageNumber);
 
                 page.render(renderContext).promise
-- 
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/pdf/PDFViewer.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


From c056adeca11f35972b5f75c6b1cc31292d5765d4 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Wed, 19 Jun 2019 11:47:20 -0400
Subject: push

---
 src/client/views/pdf/PDFViewer.tsx | 316 ++++++++++++++++++-------------------
 src/client/views/pdf/Page.tsx      |  67 +-------
 2 files changed, 154 insertions(+), 229 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 67278b1eb..f0e55705d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -19,6 +19,7 @@ import Page from "./Page";
 import "./PDFViewer.scss";
 import React = require("react");
 import PDFMenu from "./PDFMenu";
+import { UndoManager } from "../../util/UndoManager";
 
 export const scale = 2;
 interface IPDFViewerProps {
@@ -190,7 +191,7 @@ class Viewer extends React.Component<IViewerProps> {
         if (this._isPage[page] !== "none") {
             this._isPage[page] = "none";
             this._visibleElements[page] = (
-                <div key={`placeholder-${page}`} className="pdfviewer-placeholder"
+                <div key={`${this.props.url}-placeholder-${page + 1}`} className="pdfviewer-placeholder"
                     style={{ width: this._pageSizes[page].width, height: this._pageSizes[page].height }} />
             );
         }
@@ -204,11 +205,11 @@ class Viewer extends React.Component<IViewerProps> {
                     pdf={this.props.pdf}
                     page={page}
                     numPages={this.props.pdf.numPages}
-                    key={`rendered-${page + 1}`}
+                    key={`${this.props.url}-rendered-${page + 1}`}
                     name={`${this.props.pdf.fingerprint + `-page${page + 1}`}`}
                     pageLoaded={this.pageLoaded}
                     parent={this.props.parent}
-                    makePin={this.createPinAnnotation}
+                    makePin={emptyFunction}
                     renderAnnotations={this.renderAnnotations}
                     createAnnotation={this.createAnnotation}
                     sendAnnotations={this.receiveAnnotations}
@@ -285,27 +286,27 @@ class Viewer extends React.Component<IViewerProps> {
         return this._savedAnnotations.getValue(page);
     }
 
-    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 + this.getScrollFromPage(page);
-        pinAnno.width = pinAnno.height = PinRadius;
-        pinAnno.page = page;
-        pinAnno.target = targetDoc;
-        pinAnno.type = AnnotationTypes.Pin;
-        // this._annotations.push(pinAnno);
-        let annoDoc = new Doc();
-        annoDoc.annotations = new List<Doc>([pinAnno]);
-        let annotations = DocListCast(this.props.parent.Document.annotations);
-        if (annotations && annotations.length) {
-            annotations.push(annoDoc);
-            this.props.parent.Document.annotations = new List<Doc>(annotations);
-        }
-        else {
-            this.props.parent.Document.annotations = new List<Doc>([annoDoc]);
-        }
-    }
+    // 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 + this.getScrollFromPage(page);
+    //     pinAnno.width = pinAnno.height = PinRadius;
+    //     pinAnno.page = page;
+    //     pinAnno.target = targetDoc;
+    //     pinAnno.type = AnnotationTypes.Pin;
+    //     // this._annotations.push(pinAnno);
+    //     let annoDoc = new Doc();
+    //     annoDoc.annotations = new List<Doc>([pinAnno]);
+    //     let annotations = DocListCast(this.props.parent.Document.annotations);
+    //     if (annotations && annotations.length) {
+    //         annotations.push(annoDoc);
+    //         this.props.parent.Document.annotations = new List<Doc>(annotations);
+    //     }
+    //     else {
+    //         this.props.parent.Document.annotations = new List<Doc>([annoDoc]);
+    //     }
+    // }
 
     // get the page index that the vertical offset passed in is on
     getPageFromScroll = (vOffset: number) => {
@@ -339,26 +340,6 @@ 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
-            });
         }
     }
 
@@ -367,8 +348,8 @@ class Viewer extends React.Component<IViewerProps> {
         let res = annotationDocs.map(a => {
             let type = NumCast(a.type);
             switch (type) {
-                case AnnotationTypes.Pin:
-                    return <PinAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
+                // case AnnotationTypes.Pin:
+                //     return <PinAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
                 case AnnotationTypes.Region:
                     return <RegionAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
                 default:
@@ -399,7 +380,7 @@ class Viewer extends React.Component<IViewerProps> {
 }
 
 export enum AnnotationTypes {
-    Region, Pin
+    Region
 }
 
 interface IAnnotationProps {
@@ -411,132 +392,139 @@ interface IAnnotationProps {
     document: Doc;
 }
 
-@observer
-class PinAnnotation extends React.Component<IAnnotationProps> {
-    @observable private _backgroundColor: string = "green";
-    @observable private _display: string = "initial";
-
-    private _mainCont: React.RefObject<HTMLDivElement>;
-
-    constructor(props: IAnnotationProps) {
-        super(props);
-        this._mainCont = React.createRef();
-    }
-
-    componentDidMount = () => {
-        let selected = this.props.document.selected;
-        if (!BoolCast(selected)) {
-            runInAction(() => {
-                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";
-            });
-        }
-    }
-
-    @action
-    pointerDown = (e: React.PointerEvent) => {
-        let selected = this.props.document.selected;
-        if (selected && BoolCast(selected)) {
-            this._backgroundColor = "red";
-            this._display = "none";
-            this.props.document.selected = false;
-        }
-        else {
-            this._backgroundColor = "green";
-            this._display = "initial";
-            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 * scale - PinRadius / 2, left: this.props.x * scale - 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;
-    }
-}
+// @observer
+// class PinAnnotation extends React.Component<IAnnotationProps> {
+//     @observable private _backgroundColor: string = "green";
+//     @observable private _display: string = "initial";
+
+//     private _mainCont: React.RefObject<HTMLDivElement>;
+
+//     constructor(props: IAnnotationProps) {
+//         super(props);
+//         this._mainCont = React.createRef();
+//     }
+
+//     componentDidMount = () => {
+//         let selected = this.props.document.selected;
+//         if (!BoolCast(selected)) {
+//             runInAction(() => {
+//                 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";
+//             });
+//         }
+//     }
+
+//     @action
+//     pointerDown = (e: React.PointerEvent) => {
+//         let selected = this.props.document.selected;
+//         if (selected && BoolCast(selected)) {
+//             this._backgroundColor = "red";
+//             this._display = "none";
+//             this.props.document.selected = false;
+//         }
+//         else {
+//             this._backgroundColor = "green";
+//             this._display = "initial";
+//             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 * scale - PinRadius / 2, left: this.props.x * scale - 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;
+//     }
+// }
 
 class RegionAnnotation extends React.Component<IAnnotationProps> {
     @observable private _backgroundColor: string = "red";
 
-    onPointerDown = (e: React.MouseEvent) => {
-        let targetDoc = Cast(this.props.document.target, Doc, null);
-        if (targetDoc) {
-            DocumentManager.Instance.jumpToDocument(targetDoc);
+    onPointerDown = (e: React.PointerEvent) => {
+        if (e.button === 0) {
+            let targetDoc = Cast(this.props.document.target, Doc, null);
+            if (targetDoc) {
+                DocumentManager.Instance.jumpToDocument(targetDoc);
+            }
         }
+        // if (e.button === 2) {
+        //     console.log("right");
+        //     e.stopPropagation();
+        //     e.preventDefault();
+        // }
     }
 
     render() {
         return (
-            <div className="pdfViewer-annotationBox" onClick={this.onPointerDown}
+            <div className="pdfViewer-annotationBox" onPointerDown={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 2697c9eee..39e737c32 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -15,6 +15,7 @@ import { listSpec } from "../../../new_fields/Schema";
 import { menuBar } from "prosemirror-menu";
 import { AnnotationTypes, PDFViewer, scale } from "./PDFViewer";
 import PDFMenu from "./PDFMenu";
+import { UndoManager } from "../../util/UndoManager";
 
 
 interface IPageProps {
@@ -152,7 +153,6 @@ export default class Page extends React.Component<IPageProps> {
     startDrag = (e: PointerEvent): void => {
         e.preventDefault();
         e.stopPropagation();
-        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" });
@@ -163,7 +163,7 @@ export default class Page extends React.Component<IPageProps> {
         if (this._textLayer.current) {
             DragManager.StartAnnotationDrag([this._textLayer.current], dragData, e.pageX, e.pageY, {
                 handlers: {
-                    dragComplete: action(emptyFunction),
+                    dragComplete: emptyFunction,
                 },
                 hideSource: false
             });
@@ -325,68 +325,6 @@ export default class Page extends React.Component<IPageProps> {
             PDFMenu.Instance.StartDrag = this.startDrag;
             PDFMenu.Instance.Highlight = this.highlight;
         }
-        // let x = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width);
-        // let y = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height);
-        // if (this._marqueeing) {
-        //     this._marqueeing = false;
-        //     if (this._marquee.current) {
-        //         let copy = document.createElement("div");
-        //         // make a copy of the marquee
-        //         copy.style.left = this._marquee.current.style.left;
-        //         copy.style.top = this._marquee.current.style.top;
-        //         copy.style.width = this._marquee.current.style.width;
-        //         copy.style.height = this._marquee.current.style.height;
-
-        //         // apply the appropriate background, opacity, and transform
-        //         let { background, opacity, transform } = this.getCurlyTransform();
-        //         copy.style.background = background;
-        //         // if curly bracing, add a curly brace
-        //         if (opacity === "1" && this._curly.current) {
-        //             copy.style.opacity = opacity;
-        //             let img = this._curly.current.cloneNode();
-        //             (img as any).style.opacity = opacity;
-        //             (img as any).style.transform = transform;
-        //             copy.appendChild(img);
-        //         }
-        //         else {
-        //             copy.style.opacity = this._marquee.current.style.opacity;
-        //         }
-        //         copy.className = this._marquee.current.className;
-        //         this.props.createAnnotation(copy, this.props.page);
-        //         this._marquee.current.style.opacity = "0";
-        //     }
-
-        //     this._marqueeHeight = this._marqueeWidth = 0;
-        // }
-        // else {
-        //     let sel = window.getSelection();
-        //     // if selecting over a range of things
-        //     if (sel && sel.type === "Range") {
-        //         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();
-        //         }
-        //     }
-        // }
         document.removeEventListener("pointermove", this.onSelectStart);
         document.removeEventListener("pointerup", this.onSelectEnd);
     }
@@ -399,7 +337,6 @@ export default class Page extends React.Component<IPageProps> {
             for (let i = 0; i < clientRects.length; i++) {
                 let rect = clientRects.item(i);
                 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 46d57bc21cda4703855b85a4603bd471975d845b Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Wed, 19 Jun 2019 14:25:47 -0400
Subject: deleting annotations

---
 src/client/views/pdf/PDFMenu.tsx   | 40 +++++++++++++++++----
 src/client/views/pdf/PDFViewer.tsx | 74 ++++++++++++++++++++++++++++++++------
 src/client/views/pdf/Page.tsx      |  1 +
 3 files changed, 97 insertions(+), 18 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index 2462a5f94..5dd7b4bcd 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -19,9 +19,11 @@ export default class PDFMenu extends React.Component {
 
     StartDrag: (e: PointerEvent) => void = emptyFunction;
     Highlight: (d: Doc | undefined, color: string | undefined) => void = emptyFunction;
-    @observable Highlighting: boolean = false;
+    Delete: () => void = emptyFunction;
+
+    @observable public Highlighting: boolean = false;
+    @observable public Status: "pdf" | "annotation" | "" = "";
 
-    private _timeout: NodeJS.Timeout | undefined;
     private _offsetY: number = 0;
     private _offsetX: number = 0;
     private _mainCont: React.RefObject<HTMLDivElement>;
@@ -66,8 +68,8 @@ export default class PDFMenu extends React.Component {
     }
 
     @action
-    jumpTo = (x: number, y: number) => {
-        if (!this._pinned) {
+    jumpTo = (x: number, y: number, forceJump: boolean = false) => {
+        if (!this._pinned || forceJump) {
             this._transition = this._transitionDelay = "";
             this._opacity = 1;
             this._left = x;
@@ -159,11 +161,35 @@ export default class PDFMenu extends React.Component {
         }
     }
 
+    deleteClicked = (e: React.PointerEvent) => {
+        this.Delete();
+    }
+
+    handleContextMenu = (e: React.MouseEvent) => {
+        e.stopPropagation();
+        e.preventDefault();
+    }
+
     render() {
+        let buttons = this.Status === "pdf" ? [
+            <button className="pdfMenu-button" title="Click to Highlight" onClick={this.highlightClicked}
+                style={this.Highlighting ? { backgroundColor: "#121212" } : {}}>
+                <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} />
+            </button>,
+            <button className="pdfMenu-button" title="Drag to Annotate" onPointerDown={this.pointerDown}><FontAwesomeIcon icon="comment-alt" size="lg" /></button>,
+            <button className="pdfMenu-button" title="Pin Menu" onClick={this.togglePin}
+                style={this._pinned ? { backgroundColor: "#121212" } : {}}>
+                <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transition: "transform 0.1s", transform: this._pinned ? "rotate(45deg)" : "" }} />
+            </button>
+        ] : [
+                <button className="pdfMenu-button" title="Delete Anchor" onPointerDown={this.deleteClicked}><FontAwesomeIcon icon="trash-alt" size="lg" /></button>
+            ];
+
         return (
-            <div className="pdfMenu-cont" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont}
+            <div className="pdfMenu-cont" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont} onContextMenu={this.handleContextMenu}
                 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}
+                {buttons}
+                {/* <button className="pdfMenu-button" title="Highlight" onClick={this.highlightClicked}
                     style={this.Highlighting ? { backgroundColor: "#121212" } : {}}>
                     <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} />
                 </button>
@@ -171,7 +197,7 @@ export default class PDFMenu extends React.Component {
                 <button className="pdfMenu-button" title="Pin Menu" onClick={this.togglePin}
                     style={this._pinned ? { backgroundColor: "#121212" } : {}}>
                     <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transition: "transform 0.1s", transform: this._pinned ? "rotate(45deg)" : "" }} />
-                </button>
+                </button> */}
                 <div className="pdfMenu-dragger" onPointerDown={this.dragStart} style={{ width: this._pinned ? "20px" : "0px" }} />
             </div >
         );
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index f0e55705d..ea4c0bca2 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -7,7 +7,7 @@ import { Dictionary } from "typescript-collections";
 import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { BoolCast, Cast, NumCast, StrCast, FieldValue } from "../../../new_fields/Types";
 import { emptyFunction } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { Docs, DocUtils } from "../../documents/Documents";
@@ -138,6 +138,7 @@ class Viewer extends React.Component<IViewerProps> {
 
     makeAnnotationDocument = (sourceDoc: Doc | undefined, s: number, color: string): Doc => {
         let annoDocs: Doc[] = [];
+        let mainAnnoDoc = new Doc();
         this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => {
             for (let anno of value) {
                 let annoDoc = new Doc();
@@ -147,6 +148,7 @@ class Viewer extends React.Component<IViewerProps> {
                 if (anno.style.width) annoDoc.width = parseInt(anno.style.width) / scale;
                 annoDoc.page = key;
                 annoDoc.target = sourceDoc;
+                annoDoc.group = mainAnnoDoc;
                 annoDoc.color = color;
                 annoDoc.type = AnnotationTypes.Region;
                 annoDocs.push(annoDoc);
@@ -154,13 +156,12 @@ class Viewer extends React.Component<IViewerProps> {
             }
         });
 
-        let annoDoc = new Doc();
-        annoDoc.annotations = new List<Doc>(annoDocs);
+        mainAnnoDoc.annotations = new List<Doc>(annoDocs);
         if (sourceDoc) {
-            DocUtils.MakeLink(sourceDoc, annoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
+            DocUtils.MakeLink(sourceDoc, mainAnnoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
         }
         this._savedAnnotations.clear();
-        return annoDoc;
+        return mainAnnoDoc;
     }
 
     drop = async (e: Event, de: DragManager.DropEvent) => {
@@ -508,6 +509,57 @@ interface IAnnotationProps {
 class RegionAnnotation extends React.Component<IAnnotationProps> {
     @observable private _backgroundColor: string = "red";
 
+    private _reactionDisposer?: IReactionDisposer;
+    private _mainCont: React.RefObject<HTMLDivElement>;
+
+    constructor(props: IAnnotationProps) {
+        super(props);
+
+        this._mainCont = React.createRef();
+    }
+
+    componentDidMount() {
+        this._reactionDisposer = reaction(
+            () => BoolCast(this.props.document.delete),
+            () => {
+                if (BoolCast(this.props.document.delete)) {
+                    if (this._mainCont.current) {
+                        this._mainCont.current.style.display = "none";
+                    }
+                }
+            },
+            { fireImmediately: true }
+        );
+    }
+
+    componentWillUnmount() {
+        this._reactionDisposer && this._reactionDisposer();
+    }
+
+    deleteAnnotation = () => {
+        let annotation = DocListCast(this.props.parent.props.parent.Document.annotations);
+        let group = FieldValue(Cast(this.props.document.group, Doc));
+        if (group && annotation.indexOf(group) !== -1) {
+            let newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc)));
+            this.props.parent.props.parent.Document.annotations = new List<Doc>(newAnnotations);
+        }
+
+        if (group) {
+            let groupAnnotations = DocListCast(group.annotations);
+            groupAnnotations.forEach(anno => anno.delete = true);
+        }
+    }
+
+
+    // annotateThis = (e: PointerEvent) => {
+    //     e.preventDefault();
+    //     e.stopPropagation();
+    //     // document that this annotation is linked to
+    //     let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" });
+    //     let group = FieldValue(Cast(this.props.document.group, Doc));
+    // }
+
+    @action
     onPointerDown = (e: React.PointerEvent) => {
         if (e.button === 0) {
             let targetDoc = Cast(this.props.document.target, Doc, null);
@@ -515,16 +567,16 @@ class RegionAnnotation extends React.Component<IAnnotationProps> {
                 DocumentManager.Instance.jumpToDocument(targetDoc);
             }
         }
-        // if (e.button === 2) {
-        //     console.log("right");
-        //     e.stopPropagation();
-        //     e.preventDefault();
-        // }
+        if (e.button === 2) {
+            PDFMenu.Instance.Status = "annotation";
+            PDFMenu.Instance.Delete = this.deleteAnnotation;
+            PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true);
+        }
     }
 
     render() {
         return (
-            <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown}
+            <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown} ref={this._mainCont}
                 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 39e737c32..734dff7fc 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -191,6 +191,7 @@ export default class Page extends React.Component<IPageProps> {
         else if (e.button === 0) {
             PDFMenu.Instance.StartDrag = this.startDrag;
             PDFMenu.Instance.Highlight = this.highlight;
+            PDFMenu.Instance.Status = "pdf";
             PDFMenu.Instance.fadeOut(true);
             let target: any = e.target;
             if (target && target.parentElement === this._textLayer.current) {
-- 
cgit v1.2.3-70-g09d2


From b9849810231e540a5898a56012abd32c197b23b5 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Wed, 19 Jun 2019 14:39:15 -0400
Subject: anna

---
 src/client/views/pdf/PDFViewer.tsx | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index ea4c0bca2..f3aa2f5a0 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -570,6 +570,7 @@ class RegionAnnotation extends React.Component<IAnnotationProps> {
         if (e.button === 2) {
             PDFMenu.Instance.Status = "annotation";
             PDFMenu.Instance.Delete = this.deleteAnnotation;
+            PDFMenu.Instance.Pinned = false;
             PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true);
         }
     }
-- 
cgit v1.2.3-70-g09d2


From 9ab47393a2ce3d174ad3238422c2c310764be9af Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Wed, 19 Jun 2019 14:40:28 -0400
Subject: interaction improvements with delete button

---
 src/client/views/pdf/PDFViewer.tsx | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index f3aa2f5a0..7000352e7 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -548,6 +548,8 @@ class RegionAnnotation extends React.Component<IAnnotationProps> {
             let groupAnnotations = DocListCast(group.annotations);
             groupAnnotations.forEach(anno => anno.delete = true);
         }
+
+        PDFMenu.Instance.fadeOut(true);
     }
 
 
-- 
cgit v1.2.3-70-g09d2


From d2dfc0f9d35f0084a7c0dea73215f5d21055f2f3 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Thu, 20 Jun 2019 17:17:14 -0400
Subject: pdf page sizes loading error

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

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 7000352e7..6adead626 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -203,6 +203,7 @@ class Viewer extends React.Component<IViewerProps> {
             this._isPage[page] = "page";
             this._visibleElements[page] = (
                 <Page
+                    size={this._pageSizes[page]}
                     pdf={this.props.pdf}
                     page={page}
                     numPages={this.props.pdf.numPages}
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 734dff7fc..b6f362702 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -19,6 +19,7 @@ import { UndoManager } from "../../util/UndoManager";
 
 
 interface IPageProps {
+    size: { width: number, height: number };
     pdf: Opt<Pdfjs.PDFDocumentProxy>;
     name: string;
     numPages: number;
@@ -36,8 +37,8 @@ interface IPageProps {
 @observer
 export default class Page extends React.Component<IPageProps> {
     @observable private _state: string = "N/A";
-    @observable private _width: number = 0;
-    @observable private _height: number = 0;
+    @observable private _width: number = this.props.size.width;
+    @observable private _height: number = this.props.size.height;
     @observable private _page: Opt<Pdfjs.PDFPageProxy>;
     @observable private _currPage: number = this.props.page + 1;
     @observable private _marqueeX: number = 0;
-- 
cgit v1.2.3-70-g09d2

