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

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

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 92cdb8dae6162b1aa7e41f3392c670e7c5d377eb Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Fri, 7 Jun 2019 19:02:05 -0400
Subject: cleanup

---
 src/client/views/pdf/Page.tsx | 116 +++++++++++++++++++++++-------------------
 1 file changed, 63 insertions(+), 53 deletions(-)

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

diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 7cd72b27f..940f31f41 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -211,19 +211,19 @@ export default class Page extends React.Component<IPageProps> {
             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);
-                    }
+                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);
+            document.removeEventListener("pointermove", this.onSelectStart);
+            document.addEventListener("pointermove", this.onSelectStart);
+            document.removeEventListener("pointerup", this.onSelectEnd);
+            document.addEventListener("pointerup", this.onSelectEnd);
             if (!e.ctrlKey) {
                 for (let anno of this._currentAnnotations) {
                     anno.remove();
@@ -234,37 +234,20 @@ export default class Page extends React.Component<IPageProps> {
     }
 
     @action
-    onPointerMove = (e: PointerEvent) => {
+    onSelectStart = (e: PointerEvent) => {
         let target: any = e.target;
         if (this._marqueeing) {
             let current = this._textLayer.current;
             if (current) {
+                // transform positions and find the width and height to set the marquee to
                 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;
+                let { background, opacity, transform: transform } = this.getCurlyTransform();
                 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)";
-                    }
+                    this._marquee.current.style.background = background;
+                    this._curly.current.style.opacity = opacity;
+                    this._rotate = transform;
                 }
             }
             e.stopPropagation();
@@ -275,25 +258,62 @@ export default class Page extends React.Component<IPageProps> {
         }
     }
 
-    startAnnotation = () => {
-        console.log("drag starting");
-    }
+    getCurlyTransform = (): { background: string, opacity: string, transform: string } => {
+        let background = "", opacity = "", transform = "";
+        if (this._marquee.current && this._curly.current) {
+            if (this._marqueeWidth > 100 && this._marqueeHeight > 100) {
+                background = "red";
+                opacity = "0";
+            }
+            else {
+                background = "transparent";
+                opacity = "1";
+            }
 
-    pointerDownCancel = (e: PointerEvent) => {
-        e.stopPropagation();
+            // split up for simplicity, could be done in a nested ternary. please do not. -syip2
+            let ratio = this._marqueeWidth / this._marqueeHeight;
+            if (ratio > 1.5) {
+                // vertical
+                transform = "rotate(90deg) scale(1, 2)";
+            }
+            else if (ratio < 0.5) {
+                // horizontal
+                transform = "scale(2, 1)";
+            }
+            else {
+                // diagonal
+                transform = "rotate(45deg) scale(1.5, 1.5)";
+            }
+        }
+        return { background: background, opacity: opacity, transform: transform };
     }
 
     @action
-    onPointerUp = () => {
+    onSelectEnd = () => {
         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;
-                copy.style.opacity = this._marquee.current.style.opacity;
+
+                // 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;
                 if (this._annotationLayer.current) {
                     this._annotationLayer.current.append(copy);
@@ -321,13 +341,12 @@ 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();
-                            annoBox.ondragstart = this.startAnnotation;
-                            annoBox.onpointerdown = this.pointerDownCancel;
                             if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox);
                             this._currentAnnotations.push(annoBox);
                         }
                     }
                 }
+                // clear selection
                 if (sel.empty) {  // Chrome
                     sel.empty();
                 } else if (sel.removeAllRanges) {  // Firefox
@@ -335,8 +354,8 @@ export default class Page extends React.Component<IPageProps> {
                 }
             }
         }
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.removeEventListener("pointerup", this.onPointerUp);
+        document.removeEventListener("pointermove", this.onSelectStart);
+        document.removeEventListener("pointerup", this.onSelectEnd);
     }
 
     renderAnnotation = (anno: Doc | undefined): HTMLDivElement => {
@@ -348,19 +367,10 @@ export default class Page extends React.Component<IPageProps> {
             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 (
-- 
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/Page.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 bcd6960fe91008bf31d364c48b5c8765eec28701 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Mon, 10 Jun 2019 11:33:36 -0400
Subject: oops

---
 src/client/views/pdf/Page.tsx | 86 ++++++++++++++++++++++++++++++-------------
 1 file changed, 61 insertions(+), 25 deletions(-)

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

diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 0d86f22c1..7125c820c 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -1,8 +1,8 @@
 import { observer } from "mobx-react";
 import React = require("react");
-import { observable, action, runInAction } from "mobx";
+import { observable, action, runInAction, IReactionDisposer, reaction } from "mobx";
 import * as Pdfjs from "pdfjs-dist";
-import { Opt, Doc, FieldResult, Field } from "../../../new_fields/Doc";
+import { Opt, Doc, FieldResult, Field, DocListCast, WidthSym, HeightSym } from "../../../new_fields/Doc";
 import "./PDFViewer.scss";
 import "pdfjs-dist/web/pdf_viewer.css";
 import { PDFBox } from "../nodes/PDFBox";
@@ -12,6 +12,7 @@ import { List } from "../../../new_fields/List";
 import { emptyFunction } from "../../../Utils";
 import { Cast, NumCast } from "../../../new_fields/Types";
 import { listSpec } from "../../../new_fields/Schema";
+import { menuBar } from "prosemirror-menu";
 
 interface IPageProps {
     pdf: Opt<Pdfjs.PDFDocumentProxy>;
@@ -34,7 +35,7 @@ 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: List<Doc> = new List<Doc>();
+    @observable private _annotations: Doc[] = [];
 
     private _canvas: React.RefObject<HTMLCanvasElement>;
     private _textLayer: React.RefObject<HTMLDivElement>;
@@ -44,6 +45,7 @@ export default class Page extends React.Component<IPageProps> {
     private _currentAnnotations: HTMLDivElement[] = [];
     private _marqueeing: boolean = false;
     private _dragging: boolean = false;
+    private _reactionDisposer?: IReactionDisposer;
 
     constructor(props: IPageProps) {
         super(props);
@@ -54,19 +56,38 @@ export default class Page extends React.Component<IPageProps> {
         this._curly = React.createRef();
     }
 
-    componentDidMount() {
+    componentDidMount = (): void => {
         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) {
+                            this.renderAnnotations(annotations, true);
+                        }
+                    },
+                    { fireImmediately: true }
+                );
+            }
+        }
+    }
+
+    componentWillUnmount = (): void => {
+        if (this._reactionDisposer) {
+            this._reactionDisposer();
         }
     }
 
-    componentDidUpdate() {
+    componentDidUpdate = (): void => {
         if (this.props.pdf) {
             this.update(this.props.pdf);
         }
     }
 
-    private update = (pdf: Pdfjs.PDFDocumentProxy) => {
+    private update = (pdf: Pdfjs.PDFDocumentProxy): void => {
         if (pdf) {
             this.loadPage(pdf);
         }
@@ -75,11 +96,11 @@ export default class Page extends React.Component<IPageProps> {
         }
     }
 
-    private loadPage = (pdf: Pdfjs.PDFDocumentProxy) => {
+    private loadPage = (pdf: Pdfjs.PDFDocumentProxy): void => {
         if (this._state === "rendering" || this._page) return;
 
         pdf.getPage(this._currPage).then(
-            (page: Pdfjs.PDFPageProxy) => {
+            (page: Pdfjs.PDFPageProxy): void => {
                 this._state = "rendering";
                 this.renderPage(page);
             }
@@ -87,7 +108,18 @@ export default class Page extends React.Component<IPageProps> {
     }
 
     @action
-    private renderPage = (page: Pdfjs.PDFPageProxy) => {
+    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
         let scale = 2;
         let viewport = page.getViewport(scale);
@@ -104,7 +136,7 @@ export default class Page extends React.Component<IPageProps> {
                 // 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) => {
+                page.getTextContent().then((res: Pdfjs.TextContent): void => {
                     //@ts-ignore
                     Pdfjs.renderTextLayer({
                         textContent: res,
@@ -144,7 +176,7 @@ export default class Page extends React.Component<IPageProps> {
      * start a drag event and create or put the necessary info into the drag event.
      */
     @action
-    startDrag = (e: PointerEvent) => {
+    startDrag = (e: PointerEvent): void => {
         // the first 5 lines is a hack to prevent text selection while dragging
         e.preventDefault();
         e.stopPropagation();
@@ -166,15 +198,6 @@ export default class Page extends React.Component<IPageProps> {
         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) {
-            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) {
@@ -196,7 +219,7 @@ export default class Page extends React.Component<IPageProps> {
     }
 
     @action
-    onPointerDown = (e: React.PointerEvent) => {
+    onPointerDown = (e: React.PointerEvent): void => {
         // if alt+left click, drag and annotate
         if (e.altKey && e.button === 0) {
             e.stopPropagation();
@@ -237,7 +260,7 @@ export default class Page extends React.Component<IPageProps> {
     }
 
     @action
-    onSelectStart = (e: PointerEvent) => {
+    onSelectStart = (e: PointerEvent): void => {
         let target: any = e.target;
         if (this._marqueeing) {
             let current = this._textLayer.current;
@@ -292,7 +315,7 @@ export default class Page extends React.Component<IPageProps> {
     }
 
     @action
-    onSelectEnd = () => {
+    onSelectEnd = (): void => {
         if (this._marqueeing) {
             this._marqueeing = false;
             if (this._marquee.current) {
@@ -375,7 +398,6 @@ export default class Page extends React.Component<IPageProps> {
     }
 
     render() {
-        let annotations = this._annotations;
         return (
             <div onPointerDown={this.onPointerDown} className={this.props.name} style={{ "width": this._width, "height": this._height }}>
                 <div className="canvasContainer">
@@ -386,10 +408,24 @@ export default class Page extends React.Component<IPageProps> {
                         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))}
+                    {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.x, left: this.props.y, width: this.props.width, height: this.props.height }}></div>
+        );
+    }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 2ecd0c3486ec37d1061468cda12aa64e38d0d3d5 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Mon, 10 Jun 2019 14:07:36 -0400
Subject: annotation improvements

---
 src/client/documents/Documents.ts                  |  8 +--
 src/client/views/collections/CollectionSubView.tsx |  2 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 64 ++++++++++++++--------
 src/client/views/pdf/Page.tsx                      | 17 ++++--
 4 files changed, 57 insertions(+), 34 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 973c90ff4..fa6d19a5e 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -69,15 +69,15 @@ export interface DocumentOptions {
 const delegateKeys = ["x", "y", "width", "height", "panX", "panY"];
 
 export namespace DocUtils {
-    export function MakeLink(source: Doc, target: Doc) {
+    export function MakeLink(source: Doc, target: Doc, title: string = "-link name-", description: string = "", tags: string = "Default") {
         let protoSrc = source.proto ? source.proto : source;
         let protoTarg = target.proto ? target.proto : target;
         UndoManager.RunInBatch(() => {
             let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 });
             //let linkDoc = new Doc;
-            linkDoc.proto!.title = "-link name-";
-            linkDoc.proto!.linkDescription = "";
-            linkDoc.proto!.linkTags = "Default";
+            linkDoc.proto!.title = title;
+            linkDoc.proto!.linkDescription = description;
+            linkDoc.proto!.linkTags = tags;
 
             linkDoc.proto!.linkedTo = target;
             linkDoc.proto!.linkedToPage = target.curPage;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 1ced6a426..d25d07c3d 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -110,6 +110,8 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
             else if (de.data instanceof DragManager.AnnotationDragData) {
                 console.log("dropped!");
                 console.log(de.data);
+                // de.data.dropDocument.x = de.x;
+                // de.data.dropDocument.y = de.y;
                 return this.props.addDocument(de.data.dropDocument);
             }
             return false;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 9d19df540..d8e197cbe 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -81,31 +81,47 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
-        if (super.drop(e, de) && de.data instanceof DragManager.DocumentDragData) {
-            if (de.data.droppedDocuments.length) {
-                let dragDoc = de.data.droppedDocuments[0];
-                let zoom = NumCast(dragDoc.zoomBasis, 1);
-                let [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
-                let x = xp - de.data.xOffset / zoom;
-                let y = yp - de.data.yOffset / zoom;
-                let dropX = NumCast(de.data.droppedDocuments[0].x);
-                let dropY = NumCast(de.data.droppedDocuments[0].y);
-                de.data.droppedDocuments.forEach(d => {
-                    d.x = x + NumCast(d.x) - dropX;
-                    d.y = y + NumCast(d.y) - dropY;
-                    if (!NumCast(d.width)) {
-                        d.width = 300;
-                    }
-                    if (!NumCast(d.height)) {
-                        let nw = NumCast(d.nativeWidth);
-                        let nh = NumCast(d.nativeHeight);
-                        d.height = nw && nh ? nh / nw * NumCast(d.width) : 300;
-                    }
-                    this.bringToFront(d);
-                });
-                SelectionManager.ReselectAll();
+        if (super.drop(e, de)) {
+            if (de.data instanceof DragManager.DocumentDragData) {
+                if (de.data.droppedDocuments.length) {
+                    let dragDoc = de.data.droppedDocuments[0];
+                    let zoom = NumCast(dragDoc.zoomBasis, 1);
+                    let [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
+                    let x = xp - de.data.xOffset / zoom;
+                    let y = yp - de.data.yOffset / zoom;
+                    let dropX = NumCast(de.data.droppedDocuments[0].x);
+                    let dropY = NumCast(de.data.droppedDocuments[0].y);
+                    de.data.droppedDocuments.forEach(d => {
+                        d.x = x + NumCast(d.x) - dropX;
+                        d.y = y + NumCast(d.y) - dropY;
+                        if (!NumCast(d.width)) {
+                            d.width = 300;
+                        }
+                        if (!NumCast(d.height)) {
+                            let nw = NumCast(d.nativeWidth);
+                            let nh = NumCast(d.nativeHeight);
+                            d.height = nw && nh ? nh / nw * NumCast(d.width) : 300;
+                        }
+                        this.bringToFront(d);
+                    });
+                    SelectionManager.ReselectAll();
+                }
+                return true;
+            }
+            else if (de.data instanceof DragManager.AnnotationDragData) {
+                if (de.data.dropDocument) {
+                    let dragDoc = de.data.dropDocument;
+                    let zoom = NumCast(dragDoc.zoomBasis, 1);
+                    let [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
+                    let x = xp - de.data.xOffset / zoom;
+                    let y = yp - de.data.yOffset / zoom;
+                    let dropX = NumCast(de.data.dropDocument.x);
+                    let dropY = NumCast(de.data.dropDocument.y);
+                    dragDoc.x = x + NumCast(dragDoc.x) - dropX;
+                    dragDoc.y = y + NumCast(dragDoc.y) - dropY;
+                    this.bringToFront(dragDoc);
+                }
             }
-            return true;
         }
         return false;
     }
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 7125c820c..5269f448b 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -7,10 +7,10 @@ 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 { Docs, DocUtils } from "../../documents/Documents";
 import { List } from "../../../new_fields/List";
 import { emptyFunction } from "../../../Utils";
-import { Cast, NumCast } from "../../../new_fields/Types";
+import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { listSpec } from "../../../new_fields/Schema";
 import { menuBar } from "prosemirror-menu";
 
@@ -66,6 +66,7 @@ export default class Page extends React.Component<IPageProps> {
                     () => {
                         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);
                         }
                     },
@@ -163,8 +164,10 @@ export default class Page extends React.Component<IPageProps> {
             annoDoc.y = anno.offsetTop;
             annoDoc.height = anno.offsetHeight;
             annoDoc.width = anno.offsetWidth;
+            annoDoc.page = this.props.page;
             annoDoc.target = targetDoc;
             annoDocs.push(annoDoc);
+            DocUtils.MakeLink(annoDoc, targetDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
             anno.remove();
         }
         this._currentAnnotations = [];
@@ -190,13 +193,14 @@ export default class Page extends React.Component<IPageProps> {
         targetDoc.targetPage = this.props.page;
         // creates annotation documents for current highlights
         let annotationDocs = this.makeAnnotationDocuments(targetDoc);
-        let targetAnnotations = Cast(targetDoc.annotations, listSpec(Doc));
+        console.log(annotationDocs);
+        let targetAnnotations = DocListCast(this.props.parent.Document.annotations);
         if (targetAnnotations) {
             targetAnnotations.push(...annotationDocs);
-            targetDoc.annotations = targetAnnotations;
+            this.props.parent.Document.annotations = new List<Doc>(targetAnnotations);
         }
         else {
-            targetDoc.annotations = new List<Doc>(annotationDocs);
+            this.props.parent.Document.annotations = new List<Doc>(annotationDocs);
         }
         // create dragData and star tdrag
         let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc);
@@ -422,10 +426,11 @@ interface IAnnotation {
     width: number;
     height: number;
 }
+
 class RegionAnnotation extends React.Component<IAnnotation> {
     render() {
         return (
-            <div className="pdfViewer-annotationBox" style={{ top: this.props.x, left: this.props.y, width: this.props.width, height: this.props.height }}></div>
+            <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 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/Page.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 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/Page.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/Page.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/Page.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 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/Page.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 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/Page.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/Page.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 94ed67966e7fdc7aa36b1a8b045153d0d661ce57 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Fri, 14 Jun 2019 12:50:18 -0400
Subject: early impl of pdf menu

---
 src/client/views/MainView.tsx |  5 ++-
 src/client/views/pdf/Page.tsx | 96 +++++++++++++++++++++++++++++++------------
 2 files changed, 74 insertions(+), 27 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 42d5929bf..0779e1471 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,5 +1,5 @@
 import { IconName, library } from '@fortawesome/fontawesome-svg-core';
-import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt, faBell } from '@fortawesome/free-solid-svg-icons';
+import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, configure, observable, runInAction, trace } from 'mobx';
 import { observer } from 'mobx-react';
@@ -32,6 +32,7 @@ import { listSpec } from '../../new_fields/Schema';
 import { Id } from '../../new_fields/FieldSymbols';
 import { HistoryUtil } from '../util/History';
 import { CollectionBaseView } from './collections/CollectionBaseView';
+import PDFMenu from './pdf/PDFMenu';
 
 
 @observer
@@ -88,6 +89,7 @@ export class MainView extends React.Component {
         library.add(faFilm);
         library.add(faMusic);
         library.add(faTree);
+        library.add(faCommentAlt);
         this.initEventListeners();
         this.initAuthenticationRouters();
     }
@@ -315,6 +317,7 @@ export class MainView extends React.Component {
                 {this.nodesMenu()}
                 {this.miscButtons}
                 <InkingControl />
+                <PDFMenu />
                 <MainOverlayTextBox />
             </div>
         );
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 1c305caa3..2c237740c 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -14,6 +14,7 @@ import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { listSpec } from "../../../new_fields/Schema";
 import { menuBar } from "prosemirror-menu";
 import { AnnotationTypes } from "./PDFViewer";
+import PDFMenu from "./PDFMenu";
 
 interface IPageProps {
     pdf: Opt<Pdfjs.PDFDocumentProxy>;
@@ -272,7 +273,7 @@ export default class Page extends React.Component<IPageProps> {
     }
 
     @action
-    onSelectEnd = (): void => {
+    onSelectEnd = (e: PointerEvent): void => {
         if (this._marqueeing) {
             this._marqueeing = false;
             if (this._marquee.current) {
@@ -303,36 +304,79 @@ export default class Page extends React.Component<IPageProps> {
             }
 
             this._marqueeHeight = this._marqueeWidth = 0;
+            PDFMenu.Instance.Left = e.clientX;
+            PDFMenu.Instance.Top = e.clientY;
         }
         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();
-                }
+            if (sel && sel.type === "range") {
+
+                PDFMenu.Instance.Left = e.clientX;
+                PDFMenu.Instance.Top = e.clientY;
             }
         }
+        // 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);
     }
-- 
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/Page.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/Page.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 b987e2edbf7befbe90fafbdee476ee3b6513cc50 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Fri, 14 Jun 2019 17:44:17 -0400
Subject: pinning things

---
 src/client/views/MainView.tsx     |   3 +-
 src/client/views/pdf/PDFMenu.scss |  13 ++++-
 src/client/views/pdf/PDFMenu.tsx  | 119 +++++++++++++++++++++++++++++++++++---
 src/client/views/pdf/Page.tsx     |  21 ++++---
 4 files changed, 137 insertions(+), 19 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 384cd2860..29015995f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,5 +1,5 @@
 import { IconName, library } from '@fortawesome/fontawesome-svg-core';
-import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt } from '@fortawesome/free-solid-svg-icons';
+import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faThumbtack, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, configure, observable, runInAction, trace } from 'mobx';
 import { observer } from 'mobx-react';
@@ -92,6 +92,7 @@ export class MainView extends React.Component {
         library.add(faMusic);
         library.add(faTree);
         library.add(faCommentAlt);
+        library.add(faThumbtack);
         this.initEventListeners();
         this.initAuthenticationRouters();
     }
diff --git a/src/client/views/pdf/PDFMenu.scss b/src/client/views/pdf/PDFMenu.scss
index b84ebc12d..22868082a 100644
--- a/src/client/views/pdf/PDFMenu.scss
+++ b/src/client/views/pdf/PDFMenu.scss
@@ -1,18 +1,25 @@
 .pdfMenu-cont {
     position: absolute;
     z-index: 10000;
-    width: 100px;
-    height: 30px;
+    height: 35px;
     background: #323232;
     box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
-    border-radius: 0px 4px 4px 4px;
+    border-radius: 0px 6px 6px 6px;
     overflow: hidden;
+    display: flex;
 
     .pdfMenu-button {
         background-color: transparent;
+        width: 35px;
+        height: 35px;
     }
 
     .pdfMenu-button:hover {
         background-color: #121212;
     }
+
+    .pdfMenu-dragger {
+        height: 100%;
+        transition: width .2s;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index a0230113b..d2a20fb6e 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -1,6 +1,6 @@
 import React = require("react");
 import "./PDFMenu.scss";
-import { observable } from "mobx";
+import { observable, action } from "mobx";
 import { observer } from "mobx-react";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { emptyFunction } from "../../../Utils";
@@ -10,10 +10,18 @@ import { Doc } from "../../../new_fields/Doc";
 export default class PDFMenu extends React.Component {
     static Instance: PDFMenu;
 
-    @observable Top: number = 0;
-    @observable Left: number = 0;
+    @observable private _top: number = 0;
+    @observable private _left: number = 0;
+    @observable private _opacity: number = 1;
+    @observable private _transition: string = "opacity 0.5s";
+    @observable private _transitionDelay: string = "";
+    @observable private _pinned: boolean = false;
+
     StartDrag: (e: PointerEvent) => void = emptyFunction;
     Highlight: (d: Doc | undefined) => void = emptyFunction;
+    @observable Highlighting: boolean = false;
+
+    private _timeout: NodeJS.Timeout | undefined;
 
     constructor(props: Readonly<{}>) {
         super(props);
@@ -38,12 +46,109 @@ export default class PDFMenu extends React.Component {
         e.preventDefault();
     }
 
+    @action
+    jumpTo = (x: number, y: number) => {
+        if (!this._pinned) {
+            this._transition = this._transitionDelay = "";
+            this._opacity = 1;
+            this._left = x;
+            this._top = y;
+        }
+    }
+
+    @action
+    fadeOut = (forceOut: boolean) => {
+        if (!this._pinned) {
+            if (this._opacity === 0.2) {
+                this._transition = "opacity 0.1s";
+                this._transitionDelay = "";
+                this._opacity = 0;
+                this._left = this._top = -300;
+            }
+
+            if (forceOut) {
+                this._transition = "";
+                this._transitionDelay = "";
+                this._opacity = 0;
+                this._left = this._top = -300;
+            }
+        }
+    }
+
+    @action
+    pointerLeave = (e: React.PointerEvent) => {
+        if (!this._pinned) {
+            this._transition = "opacity 0.5s";
+            this._transitionDelay = "1s";
+            this._opacity = 0.2;
+            setTimeout(() => this.fadeOut(false), 3000);
+        }
+    }
+
+    @action
+    pointerEntered = (e: React.PointerEvent) => {
+        this._transition = "opacity 0.1s";
+        this._transitionDelay = "";
+        this._opacity = 1;
+    }
+
+    @action
+    togglePin = (e: React.MouseEvent) => {
+        this._pinned = !this._pinned;
+    }
+
+    @action
+    dragging = (e: PointerEvent) => {
+        this._left += e.movementX;
+        this._top += e.movementY;
+
+        e.stopPropagation();
+        e.preventDefault();
+    }
+
+    dragEnd = (e: PointerEvent) => {
+        document.removeEventListener("pointermove", this.dragging);
+        document.removeEventListener("pointerup", this.dragEnd);
+        e.stopPropagation();
+        e.preventDefault();
+    }
+
+    dragStart = (e: React.PointerEvent) => {
+        document.removeEventListener("pointermove", this.dragging);
+        document.addEventListener("pointermove", this.dragging);
+        document.removeEventListener("pointerup", this.dragEnd);
+        document.addEventListener("pointerup", this.dragEnd);
+
+        e.stopPropagation();
+        e.preventDefault();
+    }
+
+    @action
+    highlightClicked = (e: React.MouseEvent) => {
+        if (!this._pinned) {
+            this.Highlight(undefined);
+        }
+        else {
+            this.Highlighting = !this.Highlighting;
+            this.Highlight(undefined);
+        }
+    }
+
     render() {
         return (
-            <div className="pdfMenu-cont" style={{ left: this.Left, top: this.Top }}>
-                <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>
+            <div className="pdfMenu-cont" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered}
+                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" } : {}}>
+                    <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} />
+                </button>
+                <button className="pdfMenu-button" title="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>
+                <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/Page.tsx b/src/client/views/pdf/Page.tsx
index 44c502a04..3bc3ac33c 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -132,6 +132,7 @@ export default class Page extends React.Component<IPageProps> {
         }
     }
 
+    @action
     highlight = (targetDoc: Doc | undefined) => {
         // creates annotation documents for current highlights
         let annotationDoc = this.props.makeAnnotationDocuments(targetDoc);
@@ -196,6 +197,7 @@ export default class Page extends React.Component<IPageProps> {
             // document.addEventListener("pointerup", this.endDrag);
         }
         else if (e.button === 0) {
+            PDFMenu.Instance.fadeOut(true);
             let target: any = e.target;
             if (target && target.parentElement === this._textLayer.current) {
                 e.stopPropagation();
@@ -304,25 +306,28 @@ 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";
             }
 
             this._marqueeHeight = this._marqueeWidth = 0;
-            PDFMenu.Instance.Left = e.clientX;
-            PDFMenu.Instance.Top = e.clientY;
+            PDFMenu.Instance.jumpTo(e.clientX, e.clientY);
         }
         else {
             let sel = window.getSelection();
             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;
+                PDFMenu.Instance.jumpTo(e.clientX, e.clientY);
             }
         }
+
+
+        if (PDFMenu.Instance.Highlighting) {
+            this.highlight(undefined);
+        }
+        else {
+            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) {
-- 
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/Page.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 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/Page.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/Page.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

