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

---
 src/client/views/pdf/PDFViewer.tsx | 183 +++++++++++++++++++++++++++++++++++++
 1 file changed, 183 insertions(+)
 create mode 100644 src/client/views/pdf/PDFViewer.tsx

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

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
new file mode 100644
index 000000000..558a4fab6
--- /dev/null
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -0,0 +1,183 @@
+import { observer } from "mobx-react";
+import React = require("react");
+import { observable, action, runInAction } from "mobx";
+import { RouteStore } from "../../../server/RouteStore";
+import * as Pdfjs from "pdfjs-dist";
+import { Opt } from "../../../new_fields/Doc";
+
+interface IPDFViewerProps {
+    url: string;
+}
+
+@observer
+export class PDFViewer extends React.Component<IPDFViewerProps> {
+    @observable _pdf: Opt<Pdfjs.PDFDocumentProxy>;
+
+    @action
+    componentDidMount() {
+        // const pdfUrl = window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf";
+        const pdfUrl = this.props.url;
+        let promise = Pdfjs.getDocument(pdfUrl).promise;
+
+        promise.then((pdf: Pdfjs.PDFDocumentProxy) => {
+            runInAction(() => this._pdf = pdf);
+        });
+    }
+
+    render() {
+        console.log("PDFVIEWER");
+        console.log(this._pdf);
+        return (
+            <div>
+                <Viewer pdf={this._pdf} />
+            </div>
+        );
+    }
+}
+
+interface IViewerProps {
+    pdf: Opt<Pdfjs.PDFDocumentProxy>;
+}
+
+class Viewer extends React.Component<IViewerProps> {
+    render() {
+        console.log("VIEWER");
+        let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
+        console.log(numPages);
+        return (
+            <div className="viewer">
+                {/* {Array.from(Array(numPages).keys()).map((i) => ( */}
+                <Page
+                    pdf={this.props.pdf}
+                    numPages={numPages}
+                    key={`${this.props.pdf ? this.props.pdf.fingerprint : "undefined"}`}
+                    name={`${this.props.pdf ? this.props.pdf.fingerprint : "undefined"}`}
+                    {...this.props}
+                />
+                {/* ))} */}
+            </div>
+        );
+    }
+}
+
+interface IPageProps {
+    pdf: Opt<Pdfjs.PDFDocumentProxy>;
+    name: string;
+    numPages: number;
+}
+
+@observer
+class Page extends React.Component<IPageProps> {
+    @observable _state: string = "N/A";
+    @observable _width: number = 0;
+    @observable _height: number = 0;
+    @observable _page: Opt<Pdfjs.PDFPageProxy>;
+    canvas: React.RefObject<HTMLCanvasElement>;
+    textLayer: React.RefObject<HTMLDivElement>;
+    @observable _currPage: number = 1;
+
+    constructor(props: IPageProps) {
+        super(props);
+        this.canvas = React.createRef();
+        this.textLayer = React.createRef();
+    }
+
+    componentDidMount() {
+        console.log(this.props.pdf);
+        if (this.props.pdf) {
+            this.update(this.props.pdf);
+        }
+    }
+
+    componentDidUpdate() {
+        if (this.props.pdf) {
+            this.update(this.props.pdf);
+        }
+    }
+
+    private update = (pdf: Pdfjs.PDFDocumentProxy) => {
+        if (pdf) {
+            this.loadPage(pdf);
+        }
+        else {
+            this._state = "loading";
+        }
+    }
+
+    private loadPage = (pdf: Pdfjs.PDFDocumentProxy) => {
+        if (this._state === "rendering" || this._page) return;
+
+        pdf.getPage(this._currPage).then(
+            (page: Pdfjs.PDFPageProxy) => {
+                console.log("PAGE");
+                console.log(page);
+                this._state = "rendering";
+                this.renderPage(page);
+            }
+        )
+    }
+
+    @action
+    private renderPage = (page: Pdfjs.PDFPageProxy) => {
+        let scale = 1;
+        let viewport = page.getViewport(scale);
+        let canvas = this.canvas.current;
+        if (canvas) {
+            let context = canvas.getContext("2d");
+            canvas.width = viewport.width;
+            this._width = viewport.width;
+            canvas.height = viewport.height;
+            this._height = viewport.height;
+            if (context) {
+                page.render({ canvasContext: context, viewport: viewport });
+                page.getTextContent().then((res: Pdfjs.TextContent) => {
+                    //@ts-ignore
+                    let textLayer = Pdfjs.renderTextLayer({
+                        textContent: res,
+                        container: this.textLayer.current,
+                        viewport: viewport
+                    });
+                    // textLayer._render();
+                    this._state = "rendered";
+                });
+
+                this._page = page;
+            }
+        }
+    }
+
+    @action
+    prevPage = (e: React.MouseEvent) => {
+        if (this._currPage > 2 && this._state !== "rendering") {
+            this._currPage = Math.max(this._currPage - 1, 1);
+            this._page = undefined;
+            this.loadPage(this.props.pdf!);
+            this._state = "rendering";
+        }
+    }
+
+    @action
+    nextPage = (e: React.MouseEvent) => {
+        if (this._currPage < this.props.numPages - 1 && this._state !== "rendering") {
+            this._currPage = Math.min(this._currPage + 1, this.props.numPages)
+            this._page = undefined;
+            this.loadPage(this.props.pdf!);
+            this._state = "rendering";
+        }
+    }
+
+    render() {
+        return (
+            <div className={this.props.name} style={{ "width": this._width, "height": this._height }}>
+                <div className="canvasContainer">
+                    <canvas ref={this.canvas} />
+                </div>
+                <div className="textlayer" ref={this.textLayer} />
+                <div className="viewer-button-cont" style={{ "width": this._width / 10, "height": this._height / 20, "left": this._width * .9, "top": this._height * .95 }}>
+                    <div className="viewer-previousPage" onClick={this.prevPage}>&lt;</div>
+                    <div className="viewer-nextPage" onClick={this.nextPage}>&gt;</div>
+                </div>
+            </div>
+        );
+    }
+}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


From 727c8be8d51766f483a90edf07366b5840f5a4f6 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Wed, 5 Jun 2019 23:00:10 -0400
Subject: screen transform remaining

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

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

diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 9d41a1bb0..484d9dc04 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -24,4 +24,8 @@
     user-select: auto;
 }
 
-.viewer {}
\ No newline at end of file
+.pdfViewer-annotationBox {
+    position: absolute;
+    background-color: red;
+    opacity: 0.5;
+}
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 95f31bb89..a76527618 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -354,18 +354,54 @@ class Page extends React.Component<IPageProps> {
     }
 
     onPointerDown = (e: React.PointerEvent) => {
-        console.log("down");
-        e.stopPropagation();
+        if (e.button === 0) {
+            e.stopPropagation();
+            document.addEventListener("pointermove", this.onPointerMove);
+            document.addEventListener("pointerup", this.onPointerUp);
+        }
+    }
+
+    onPointerMove = (e: PointerEvent) => {
+        if (e.button === 0) {
+            e.stopPropagation();
+        }
     }
 
-    onPointerMove = (e: React.PointerEvent) => {
-        console.log("move")
-        e.stopPropagation();
+    onPointerUp = (e: PointerEvent) => {
+        let sel = window.getSelection();
+        if (sel && sel.type === "Range") {
+            // console.log(sel.getRangeAt(0));
+            let commonContainer = sel.getRangeAt(0).commonAncestorContainer;
+            let startContainer = sel.getRangeAt(0).startContainer;
+            let endContainer = sel.getRangeAt(0).endContainer;
+            let clientRects = sel.getRangeAt(0).getClientRects();
+            console.log(sel.getRangeAt(0));
+            let annoBoxes = [];
+            if (this.textLayer.current) {
+                // let transform = Utils.GetScreenTransform(this.textLayer.current);
+                console.log(transform);
+                for (let i = 0; i < clientRects.length; i++) {
+                    let rect = clientRects.item(i);
+                    if (rect) {
+                        let annoBox = document.createElement("div");
+                        annoBox.className = "pdfViewer-annotationBox";
+                        annoBox.style.top = rect.top.toString();
+                        annoBox.style.left = rect.left.toString();
+                        annoBox.style.width = rect.width.toString();
+                        annoBox.style.height = rect.height.toString();
+                        // annoBox.style.transform = `scale(${1 / transform.scale}) translate(-${transform.translateX * transform.scale}px, -${transform.translateY * transform.scale}px)`;
+                        this.textLayer.current.appendChild(annoBox);
+                    }
+                }
+            }
+        }
+        document.removeEventListener("pointermove", this.onPointerMove);
+        document.removeEventListener("pointerup", this.onPointerUp);
     }
 
     render() {
         return (
-            <div onPointerDown={this.onPointerDown} onPointerMove={this.onPointerMove} className={this.props.name} style={{ "width": this._width, "height": this._height }}>
+            <div onPointerDown={this.onPointerDown} className={this.props.name} style={{ "width": this._width, "height": this._height }}>
                 <div className="canvasContainer">
                     <canvas ref={this.canvas} />
                 </div>
-- 
cgit v1.2.3-70-g09d2


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

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

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

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


From 0a14ecf9b9fa5e15bc3e5373c8f042f9cd876c8a Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Thu, 6 Jun 2019 18:26:57 -0400
Subject: region annotation basics

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

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

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


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

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

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

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


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

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

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

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


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

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

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

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 0711ead23..0a6886878 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -184,7 +184,7 @@ class Viewer extends React.Component<IViewerProps> {
 
         // render pages for any indices that don't already have pages (force rerender will make these render regardless)
         for (let i = startIndex; i <= endIndex; i++) {
-            if (!this._isPage[i] || forceRender) {
+            if (!this._isPage[i] || (this._isPage[i] && forceRender)) {
                 this._visibleElements[i] = (
                     <Page
                         pdf={this.props.pdf}
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 940f31f41..0d86f22c1 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -163,6 +163,9 @@ export default class Page extends React.Component<IPageProps> {
             targetAnnotations.push(...annotationDocs);
             targetDoc.annotations = targetAnnotations;
         }
+        else {
+            targetDoc.annotations = new List<Doc>(annotationDocs);
+        }
         // temporary code (currently broken ? 6/7/19) to get annotations to rerender on pdf
         let thisAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc));
         if (thisAnnotations) {
-- 
cgit v1.2.3-70-g09d2


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


From 9ec4a529dc886acca8f147cfe913e60f938f3bda Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Wed, 12 Jun 2019 14:35:24 -0400
Subject: reverse annotating

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

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

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


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

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

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

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


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

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

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

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index deacdd623..e54dfea6f 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -278,7 +278,9 @@ class Viewer extends React.Component<IViewerProps> {
                     parent={this.props.parent}
                     renderAnnotations={this.renderAnnotations}
                     makePin={this.createPinAnnotation}
+                    createAnnotation={this.createAnnotation}
                     sendAnnotations={this.receiveAnnotations}
+                    makeAnnotationDocuments={this.makeAnnotationDocuments}
                     receiveAnnotations={this.sendAnnotations}
                     {...this.props} />
             ));
@@ -318,7 +320,9 @@ class Viewer extends React.Component<IViewerProps> {
                         parent={this.props.parent}
                         makePin={this.createPinAnnotation}
                         renderAnnotations={this.renderAnnotations}
+                        createAnnotation={this.createAnnotation}
                         sendAnnotations={this.receiveAnnotations}
+                        makeAnnotationDocuments={this.makeAnnotationDocuments}
                         receiveAnnotations={this.sendAnnotations}
                         {...this.props} />
                 );
@@ -334,7 +338,13 @@ class Viewer extends React.Component<IViewerProps> {
 
     @action
     receiveAnnotations = (annotations: HTMLDivElement[], page: number) => {
-        this._savedAnnotations.setValue(page, annotations);
+        if (page === -1) {
+            this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
+            this._savedAnnotations.keys().forEach(k => this._savedAnnotations.setValue(k, annotations));
+        }
+        else {
+            this._savedAnnotations.setValue(page, annotations);
+        }
     }
 
     sendAnnotations = (page: number): HTMLDivElement[] | undefined => {
@@ -346,7 +356,7 @@ class Viewer extends React.Component<IViewerProps> {
 
         let pinAnno = new Doc();
         pinAnno.x = x;
-        pinAnno.y = y;
+        pinAnno.y = y + this.getPageHeight(page);
         pinAnno.width = pinAnno.height = PinRadius;
         pinAnno.page = page;
         pinAnno.target = targetDoc;
@@ -362,12 +372,6 @@ class Viewer extends React.Component<IViewerProps> {
         else {
             this.props.parent.Document.annotations = new List<Doc>([annoDoc]);
         }
-        // let pinAnno = document.createElement("div");
-        // pinAnno.className = "pdfViewer-pinAnnotation";
-        // pinAnno.style.top = (y - (radius / 2)).toString();
-        // pinAnno.style.left = (x - (radius / 2)).toString();
-        // pinAnno.style.height = pinAnno.style.width = radius.toString();
-        // if (this._annotationLayer.current) this._annotationLayer.current.append(pinAnno);
     }
 
     // get the page index that the vertical offset passed in is on
@@ -404,10 +408,7 @@ class Viewer extends React.Component<IViewerProps> {
                 <div key={`pdfviewer-placeholder-${i}`} className="pdfviewer-placeholder" style={{ width: this._pageSizes[i] ? this._pageSizes[i].width : 0, height: this._pageSizes[i] ? this._pageSizes[i].height : 0 }} />
             ));
             this._visibleElements = new Array<JSX.Element>(...divs);
-            // On load, render pdf
-            // setTimeout(() => {
             this.renderPages(this.startIndex, this.endIndex, true);
-            // }, 1000);
         }
     }
 
@@ -423,15 +424,32 @@ class Viewer extends React.Component<IViewerProps> {
         return counter;
     }
 
+    createAnnotation = (div: HTMLDivElement, page: number) => {
+        if (this._annotationLayer.current) {
+            if (div.style.top) {
+                div.style.top = (parseInt(div.style.top) + this.getPageHeight(page)).toString();
+            }
+            this._annotationLayer.current.append(div);
+            let savedPage = this._savedAnnotations.getValue(page);
+            if (savedPage) {
+                savedPage.push(div);
+                this._savedAnnotations.setValue(page, savedPage);
+            }
+            else {
+                this._savedAnnotations.setValue(page, [div]);
+            }
+        }
+    }
+
     renderAnnotation = (anno: Doc): JSX.Element[] => {
         let annotationDocs = DocListCast(anno.annotations);
         let res = annotationDocs.map(a => {
             let type = NumCast(a.type);
             switch (type) {
                 case AnnotationTypes.Pin:
-                    return <PinAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y) + this.getPageHeight(NumCast(a.page))} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
+                    return <PinAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
                 case AnnotationTypes.Region:
-                    return <RegionAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y) + this.getPageHeight(NumCast(a.page))} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
+                    return <RegionAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
                 default:
                     return <div></div>;
             }
@@ -439,18 +457,6 @@ class Viewer extends React.Component<IViewerProps> {
         return res;
     }
 
-    // ScreenToLocalTransform = (): Transform => {
-    //     if (this._annotationLayer.current) {
-    //         let boundingRect = this._annotationLayer.current.getBoundingClientRect();
-    //         let x = boundingRect.left;
-    //         let y = boundingRect.top;
-    //         let scale = NumCast(this.props.parent.Document.nativeWidth) / boundingRect.width;
-    //         let t = new Transform(x, y, scale);
-    //         return t;
-    //     }
-    //     return Transform.Identity();
-    // }
-
     render() {
         trace();
         return (
@@ -458,8 +464,8 @@ class Viewer extends React.Component<IViewerProps> {
                 <div className="viewer">
                     {this._visibleElements}
                 </div>
-                <div className="pdfViewer-annotationLayer" ref={this._annotationLayer} style={{ height: this.props.parent.Document.nativeHeight, width: `100%`, pointerEvents: this._pointerEvents }}>
-                    <div className="pdfViewer-annotationLayer-subCont" style={{ transform: `translateY(${-this.scrollY}px)` }}>
+                <div className="pdfViewer-annotationLayer" style={{ height: this.props.parent.Document.nativeHeight, width: `100%`, pointerEvents: this._pointerEvents }}>
+                    <div className="pdfViewer-annotationLayer-subCont" style={{ transform: `translateY(${-this.scrollY}px)` }} ref={this._annotationLayer}>
                         {this._annotations.map(anno => this.renderAnnotation(anno))}
                     </div>
                 </div>
@@ -542,26 +548,10 @@ class PinAnnotation extends React.Component<IAnnotationProps> {
 class RegionAnnotation extends React.Component<IAnnotationProps> {
     @observable private _backgroundColor: string = "red";
 
-    // private _reactionDisposer?: IReactionDisposer;
-
-    constructor(props: IAnnotationProps) {
-        super(props);
-
-        // this._reactionDisposer = reaction(
-        //     () => { BoolCast(this.props.document.selected); },
-        //     () => { this._backgroundColor = BoolCast(this.props.document.selected) ? "yellow" : "red"; },
-        //     { fireImmediately: true }
-        // )
-    }
-
     onPointerDown = (e: React.PointerEvent) => {
         let targetDoc = Cast(this.props.document.target, Doc, null);
         if (targetDoc) {
             DocumentManager.Instance.jumpToDocument(targetDoc);
-            // let annotations = DocListCast(targetDoc.proto!.linkedFromDocs);
-            // if (annotations && annotations.length) {
-            //     annotations.forEach(anno => anno.selected = true);
-            // }
         }
     }
 
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 998ac25fd..9e3bf4954 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -26,6 +26,8 @@ interface IPageProps {
     makePin: (x: number, y: number, page: number) => void;
     sendAnnotations: (annotations: HTMLDivElement[], page: number) => void;
     receiveAnnotations: (page: number) => HTMLDivElement[] | undefined;
+    createAnnotation: (div: HTMLDivElement, page: number) => void;
+    makeAnnotationDocuments: (doc: Doc) => Doc;
 }
 
 @observer
@@ -46,7 +48,6 @@ export default class Page extends React.Component<IPageProps> {
     private _annotationLayer: React.RefObject<HTMLDivElement>;
     private _marquee: React.RefObject<HTMLDivElement>;
     private _curly: React.RefObject<HTMLImageElement>;
-    private _currentAnnotations: HTMLDivElement[] = [];
     private _marqueeing: boolean = false;
     private _dragging: boolean = false;
     private _reactionDisposer?: IReactionDisposer;
@@ -64,18 +65,9 @@ export default class Page extends React.Component<IPageProps> {
         if (this.props.pdf) {
             this.update(this.props.pdf);
         }
-
-        let received = this.props.receiveAnnotations(this.props.page);
-        this._currentAnnotations = received ? received : [];
-        if (this._annotationLayer.current) {
-            this._annotationLayer.current.append(...this._currentAnnotations);
-        }
     }
 
     componentWillUnmount = (): void => {
-        console.log(this._currentAnnotations);
-        this.props.sendAnnotations(this._currentAnnotations, this.props.page);
-
         if (this._reactionDisposer) {
             this._reactionDisposer();
         }
@@ -139,33 +131,6 @@ export default class Page extends React.Component<IPageProps> {
         }
     }
 
-    /**
-     * @param targetDoc: Document that annotations are linked to
-     * This method makes the list of current annotations into documents linked to
-     * the parameter passed in.
-     */
-    makeAnnotationDocuments = (targetDoc: Doc): Doc => {
-        let annoDocs: Doc[] = [];
-
-        for (let anno of this._currentAnnotations) {
-            let annoDoc = new Doc();
-            annoDoc.x = anno.offsetLeft;
-            annoDoc.y = anno.offsetTop;
-            annoDoc.height = anno.offsetHeight;
-            annoDoc.width = anno.offsetWidth;
-            annoDoc.page = this.props.page;
-            annoDoc.target = targetDoc;
-            annoDoc.type = AnnotationTypes.Region;
-            annoDocs.push(annoDoc);
-            anno.remove();
-        }
-        let annoDoc = new Doc();
-        annoDoc.annotations = new List<Doc>(annoDocs);
-        DocUtils.MakeLink(annoDoc, targetDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
-        this._currentAnnotations = [];
-        return annoDoc;
-    }
-
     /**
      * This is temporary for creating annotations from highlights. It will
      * start a drag event and create or put the necessary info into the drag event.
@@ -184,7 +149,7 @@ export default class Page extends React.Component<IPageProps> {
         let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" });
         targetDoc.targetPage = this.props.page;
         // creates annotation documents for current highlights
-        let annotationDoc = this.makeAnnotationDocuments(targetDoc);
+        let annotationDoc = this.props.makeAnnotationDocuments(targetDoc);
         let targetAnnotations = DocListCast(this.props.parent.Document.annotations);
         if (targetAnnotations) {
             targetAnnotations.push(annotationDoc);
@@ -246,10 +211,7 @@ export default class Page extends React.Component<IPageProps> {
             document.removeEventListener("pointerup", this.onSelectEnd);
             document.addEventListener("pointerup", this.onSelectEnd);
             if (!e.ctrlKey) {
-                for (let anno of this._currentAnnotations) {
-                    anno.remove();
-                }
-                this._currentAnnotations = [];
+                this.props.sendAnnotations([], -1);
             }
         }
     }
@@ -336,10 +298,7 @@ export default class Page extends React.Component<IPageProps> {
                     copy.style.opacity = this._marquee.current.style.opacity;
                 }
                 copy.className = this._marquee.current.className;
-                if (this._annotationLayer.current) {
-                    this._annotationLayer.current.append(copy);
-                    this._currentAnnotations.push(copy);
-                }
+                this.props.createAnnotation(copy, this.props.page);
                 this._marquee.current.style.opacity = "0";
             }
 
@@ -362,8 +321,7 @@ export default class Page extends React.Component<IPageProps> {
                             annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString();
                             annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString();
                             annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString();
-                            if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox);
-                            this._currentAnnotations.push(annoBox);
+                            this.props.createAnnotation(annoBox, this.props.page);
                         }
                     }
                 }
@@ -395,19 +353,6 @@ export default class Page extends React.Component<IPageProps> {
         }
     }
 
-    renderAnnotation = (anno: Doc | undefined): HTMLDivElement => {
-        let annoBox = document.createElement("div");
-        if (anno) {
-            annoBox.className = "pdfViewer-annotationBox";
-            // transforms the positions from screen onto the pdf div
-            annoBox.style.top = NumCast(anno.x).toString();
-            annoBox.style.left = NumCast(anno.y).toString();
-            annoBox.style.width = NumCast(anno.width).toString();
-            annoBox.style.height = NumCast(anno.height).toString()
-        }
-        return annoBox;
-    }
-
     render() {
         return (
             <div onPointerDown={this.onPointerDown} onDoubleClick={this.doubleClick} className={this.props.name} style={{ "width": this._width, "height": this._height }}>
-- 
cgit v1.2.3-70-g09d2


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

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

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

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


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

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

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

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


From 6a2cb71af332d4c782c1678750ba955757eaab45 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 14 Jun 2019 11:56:36 -0400
Subject: fixes

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

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

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


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

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

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

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 4b949aa3e..d6081142a 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -586,7 +586,7 @@ class PinAnnotation extends React.Component<IAnnotationProps> {
                             PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)}
                             PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)}
                             focus={emptyFunction}
-                            selectOnLoad={true}
+                            selectOnLoad={false}
                             parentActive={this.props.parent.props.parent.props.active}
                             whenActiveChanged={this.props.parent.props.parent.props.whenActiveChanged}
                             bringToFront={emptyFunction}
-- 
cgit v1.2.3-70-g09d2


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

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

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

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


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

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

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

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


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

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

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

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


From 747fdb6d26afabad067f3f8d98789bfc7ca44f8d Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Fri, 14 Jun 2019 18:11:41 -0400
Subject: small stuffs

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

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

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


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

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

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

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


From ee1e6f815b76f9c0e076c8fecc89dd4fa1f5cf4b Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 16 Jun 2019 21:39:48 -0400
Subject: fixed access out-of-bounds error..  fixed event handling for
 unselected pdf.

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

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

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


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

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

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

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


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

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

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

diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index b0735f63b..b44370e3d 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -18,7 +18,7 @@ export default class PDFMenu extends React.Component {
     @observable private _pinned: boolean = false;
 
     StartDrag: (e: PointerEvent) => void = emptyFunction;
-    Highlight: (d: Doc | undefined) => void = emptyFunction;
+    Highlight: (d: Doc | undefined, color: string | undefined) => void = emptyFunction;
     @observable Highlighting: boolean = false;
 
     private _timeout: NodeJS.Timeout | undefined;
@@ -129,11 +129,11 @@ export default class PDFMenu extends React.Component {
     @action
     highlightClicked = (e: React.MouseEvent) => {
         if (!this._pinned) {
-            this.Highlight(undefined);
+            this.Highlight(undefined, "#f4f442");
         }
         else {
             this.Highlighting = !this.Highlighting;
-            this.Highlight(undefined);
+            this.Highlight(undefined, "#f4f442");
         }
     }
 
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 9becfb419..d74a16f3f 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -23,6 +23,7 @@ import { Dictionary } from "typescript-collections";
 import * as rp from "request-promise";
 import { restProperty } from "babel-types";
 import { DocServer } from "../../DocServer";
+import { number } from "prop-types";
 
 export const scale = 2;
 interface IPDFViewerProps {
@@ -141,10 +142,33 @@ class Viewer extends React.Component<IViewerProps> {
 
         setTimeout(() => {
             // this.renderPages(this.startIndex, this.endIndex, true);
-            this.saveThumbnail();
+            this.initialLoad();
         }, 1000);
     }
 
+    @action
+    initialLoad = () => {
+        let pdf = this.props.pdf;
+        if (pdf) {
+            this._pageSizes = Array<{ width: number, height: number }>(pdf.numPages);
+            let rendered = 0;
+            for (let i = 0; i < pdf.numPages; i++) {
+                pdf.getPage(i + 1).then(
+                    (page: Pdfjs.PDFPageProxy) => {
+                        runInAction(() => {
+                            this._pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale };
+                        });
+                        console.log(`page ${i} size retreieved`);
+                        rendered++;
+                        if (rendered === pdf!.numPages - 1) {
+                            this.saveThumbnail();
+                        }
+                    }
+                );
+            }
+        }
+    }
+
     private mainCont = (div: HTMLDivElement | null) => {
         if (this._dropDisposer) {
             this._dropDisposer();
@@ -186,7 +210,7 @@ class Viewer extends React.Component<IViewerProps> {
     drop = async (e: Event, de: DragManager.DropEvent) => {
         if (de.data instanceof DragManager.LinkDragData) {
             let sourceDoc = de.data.linkSourceDocument;
-            let destDoc = this.makeAnnotationDocument(sourceDoc);
+            let destDoc = this.makeAnnotationDocument(sourceDoc, 1, "red");
             let targetAnnotations = DocListCast(this.props.parent.Document.annotations);
             if (targetAnnotations) {
                 targetAnnotations.push(destDoc);
@@ -392,7 +416,7 @@ class Viewer extends React.Component<IViewerProps> {
         let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
         let index = 0;
         let currOffset = vOffset;
-        while (index < numPages && currOffset - (this._pageSizes[index] ? this._pageSizes[index].height : 792 * scale) > 0) {
+        while (index < this._pageSizes.length && currOffset - (this._pageSizes[index] ? this._pageSizes[index].height : 792 * scale) > 0) {
             currOffset -= this._pageSizes[index] ? this._pageSizes[index].height : this._pageSizes[0].height;
             index++;
         }
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index e706a0d5c..bb87ec9d4 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -133,9 +133,9 @@ export default class Page extends React.Component<IPageProps> {
     }
 
     @action
-    highlight = (targetDoc?: Doc) => {
+    highlight = (targetDoc?: Doc, color: string = "red") => {
         // creates annotation documents for current highlights
-        let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, "#f4f442");
+        let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, color);
         let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc));
         if (targetAnnotations === undefined) {
             Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]);
@@ -162,7 +162,7 @@ export default class Page extends React.Component<IPageProps> {
         // document that this annotation is linked to
         let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" });
         targetDoc.targetPage = this.props.page;
-        let annotationDoc = this.highlight(targetDoc);
+        let annotationDoc = this.highlight(targetDoc, "red");
         // create dragData and star tdrag
         let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc);
         if (this._textLayer.current) {
@@ -323,7 +323,7 @@ export default class Page extends React.Component<IPageProps> {
 
 
         if (PDFMenu.Instance.Highlighting) {
-            this.highlight(undefined);
+            this.highlight(undefined, "#f4f442");
         }
         else {
             PDFMenu.Instance.StartDrag = this.startDrag;
-- 
cgit v1.2.3-70-g09d2


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

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

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

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


From 2f5c38c6a0a5220c2a31931c34d94e199854d703 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 18 Jun 2019 08:36:37 -0400
Subject: more streamlining

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

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

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


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

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

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

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 645afc636..e5fb32e53 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -234,8 +234,7 @@ class Viewer extends React.Component<IViewerProps> {
 
     // endIndex: where to end rendering pages
     @computed get endIndex(): number {
-        let width = this._pageSizes.map(i => i.width);
-        return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.scrollY + Math.max(...width)) + this._pageBuffer);
+        return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.scrollY) + this._pageBuffer);
     }
 
     @action
-- 
cgit v1.2.3-70-g09d2


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

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

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

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


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

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

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

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


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

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

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

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


From 13e301dea2f537b67b338cc6a98d3f3b5a8e1f36 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Tue, 18 Jun 2019 20:58:32 -0400
Subject: Fixed linter errors

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

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

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


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

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

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

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


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

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

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

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 67278b1eb..f0e55705d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -19,6 +19,7 @@ import Page from "./Page";
 import "./PDFViewer.scss";
 import React = require("react");
 import PDFMenu from "./PDFMenu";
+import { UndoManager } from "../../util/UndoManager";
 
 export const scale = 2;
 interface IPDFViewerProps {
@@ -190,7 +191,7 @@ class Viewer extends React.Component<IViewerProps> {
         if (this._isPage[page] !== "none") {
             this._isPage[page] = "none";
             this._visibleElements[page] = (
-                <div key={`placeholder-${page}`} className="pdfviewer-placeholder"
+                <div key={`${this.props.url}-placeholder-${page + 1}`} className="pdfviewer-placeholder"
                     style={{ width: this._pageSizes[page].width, height: this._pageSizes[page].height }} />
             );
         }
@@ -204,11 +205,11 @@ class Viewer extends React.Component<IViewerProps> {
                     pdf={this.props.pdf}
                     page={page}
                     numPages={this.props.pdf.numPages}
-                    key={`rendered-${page + 1}`}
+                    key={`${this.props.url}-rendered-${page + 1}`}
                     name={`${this.props.pdf.fingerprint + `-page${page + 1}`}`}
                     pageLoaded={this.pageLoaded}
                     parent={this.props.parent}
-                    makePin={this.createPinAnnotation}
+                    makePin={emptyFunction}
                     renderAnnotations={this.renderAnnotations}
                     createAnnotation={this.createAnnotation}
                     sendAnnotations={this.receiveAnnotations}
@@ -285,27 +286,27 @@ class Viewer extends React.Component<IViewerProps> {
         return this._savedAnnotations.getValue(page);
     }
 
-    createPinAnnotation = (x: number, y: number, page: number): void => {
-        let targetDoc = Docs.TextDocument({ width: 100, height: 50, title: "New Pin Annotation" });
-        let pinAnno = new Doc();
-        pinAnno.x = x;
-        pinAnno.y = y + this.getScrollFromPage(page);
-        pinAnno.width = pinAnno.height = PinRadius;
-        pinAnno.page = page;
-        pinAnno.target = targetDoc;
-        pinAnno.type = AnnotationTypes.Pin;
-        // this._annotations.push(pinAnno);
-        let annoDoc = new Doc();
-        annoDoc.annotations = new List<Doc>([pinAnno]);
-        let annotations = DocListCast(this.props.parent.Document.annotations);
-        if (annotations && annotations.length) {
-            annotations.push(annoDoc);
-            this.props.parent.Document.annotations = new List<Doc>(annotations);
-        }
-        else {
-            this.props.parent.Document.annotations = new List<Doc>([annoDoc]);
-        }
-    }
+    // createPinAnnotation = (x: number, y: number, page: number): void => {
+    //     let targetDoc = Docs.TextDocument({ width: 100, height: 50, title: "New Pin Annotation" });
+    //     let pinAnno = new Doc();
+    //     pinAnno.x = x;
+    //     pinAnno.y = y + this.getScrollFromPage(page);
+    //     pinAnno.width = pinAnno.height = PinRadius;
+    //     pinAnno.page = page;
+    //     pinAnno.target = targetDoc;
+    //     pinAnno.type = AnnotationTypes.Pin;
+    //     // this._annotations.push(pinAnno);
+    //     let annoDoc = new Doc();
+    //     annoDoc.annotations = new List<Doc>([pinAnno]);
+    //     let annotations = DocListCast(this.props.parent.Document.annotations);
+    //     if (annotations && annotations.length) {
+    //         annotations.push(annoDoc);
+    //         this.props.parent.Document.annotations = new List<Doc>(annotations);
+    //     }
+    //     else {
+    //         this.props.parent.Document.annotations = new List<Doc>([annoDoc]);
+    //     }
+    // }
 
     // get the page index that the vertical offset passed in is on
     getPageFromScroll = (vOffset: number) => {
@@ -339,26 +340,6 @@ class Viewer extends React.Component<IViewerProps> {
             else {
                 this._savedAnnotations.setValue(page, [div]);
             }
-            PDFMenu.Instance.StartDrag = this.startDrag;
-        }
-    }
-
-    startDrag = (e: PointerEvent) => {
-        e.preventDefault();
-        e.stopPropagation();
-        let thisDoc = this.props.parent.Document;
-        // document that this annotation is linked to
-        let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" });
-        targetDoc.targetPage = Math.min(...this._savedAnnotations.keys());
-        let annotationDoc = this.makeAnnotationDocument(targetDoc, 1, "red");
-        let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc);
-        if (this._annotationLayer.current) {
-            DragManager.StartAnnotationDrag([this._annotationLayer.current], dragData, e.pageX, e.pageY, {
-                handlers: {
-                    dragComplete: action(emptyFunction),
-                },
-                hideSource: false
-            });
         }
     }
 
@@ -367,8 +348,8 @@ class Viewer extends React.Component<IViewerProps> {
         let res = annotationDocs.map(a => {
             let type = NumCast(a.type);
             switch (type) {
-                case AnnotationTypes.Pin:
-                    return <PinAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
+                // case AnnotationTypes.Pin:
+                //     return <PinAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
                 case AnnotationTypes.Region:
                     return <RegionAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
                 default:
@@ -399,7 +380,7 @@ class Viewer extends React.Component<IViewerProps> {
 }
 
 export enum AnnotationTypes {
-    Region, Pin
+    Region
 }
 
 interface IAnnotationProps {
@@ -411,132 +392,139 @@ interface IAnnotationProps {
     document: Doc;
 }
 
-@observer
-class PinAnnotation extends React.Component<IAnnotationProps> {
-    @observable private _backgroundColor: string = "green";
-    @observable private _display: string = "initial";
-
-    private _mainCont: React.RefObject<HTMLDivElement>;
-
-    constructor(props: IAnnotationProps) {
-        super(props);
-        this._mainCont = React.createRef();
-    }
-
-    componentDidMount = () => {
-        let selected = this.props.document.selected;
-        if (!BoolCast(selected)) {
-            runInAction(() => {
-                this._backgroundColor = "red";
-                this._display = "none";
-            });
-        }
-        if (selected) {
-            if (BoolCast(selected)) {
-                runInAction(() => {
-                    this._backgroundColor = "green";
-                    this._display = "initial";
-                });
-            }
-            else {
-                runInAction(() => {
-                    this._backgroundColor = "red";
-                    this._display = "none";
-                });
-            }
-        }
-        else {
-            runInAction(() => {
-                this._backgroundColor = "red";
-                this._display = "none";
-            });
-        }
-    }
-
-    @action
-    pointerDown = (e: React.PointerEvent) => {
-        let selected = this.props.document.selected;
-        if (selected && BoolCast(selected)) {
-            this._backgroundColor = "red";
-            this._display = "none";
-            this.props.document.selected = false;
-        }
-        else {
-            this._backgroundColor = "green";
-            this._display = "initial";
-            this.props.document.selected = true;
-        }
-        e.preventDefault();
-        e.stopPropagation();
-    }
-
-    @action
-    doubleClick = (e: React.MouseEvent) => {
-        if (this._mainCont.current) {
-            let annotations = DocListCast(this.props.parent.props.parent.Document.annotations);
-            if (annotations && annotations.length) {
-                let index = annotations.indexOf(this.props.document);
-                annotations.splice(index, 1);
-                this.props.parent.props.parent.Document.annotations = new List<Doc>(annotations);
-            }
-            // this._mainCont.current.childNodes.forEach(e => e.remove());
-            this._mainCont.current.style.display = "none";
-            // if (this._mainCont.current.parentElement) {
-            //     this._mainCont.current.remove();
-            // }
-        }
-        e.stopPropagation();
-    }
-
-    render() {
-        let targetDoc = Cast(this.props.document.target, Doc);
-        if (targetDoc instanceof Doc) {
-            return (
-                <div className="pdfViewer-pinAnnotation" onPointerDown={this.pointerDown}
-                    onDoubleClick={this.doubleClick} ref={this._mainCont}
-                    style={{
-                        top: this.props.y * scale - PinRadius / 2, left: this.props.x * scale - PinRadius / 2, width: PinRadius,
-                        height: PinRadius, pointerEvents: "all", backgroundColor: this._backgroundColor
-                    }}>
-                    <div style={{
-                        position: "absolute", top: "25px", left: "25px", transform: "scale(3)", transformOrigin: "top left",
-                        display: this._display, width: targetDoc[WidthSym](), height: targetDoc[HeightSym]()
-                    }}>
-                        <DocumentView Document={targetDoc}
-                            ContainingCollectionView={undefined}
-                            ScreenToLocalTransform={this.props.parent.props.parent.props.ScreenToLocalTransform}
-                            isTopMost={false}
-                            ContentScaling={() => 1}
-                            PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)}
-                            PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)}
-                            focus={emptyFunction}
-                            selectOnLoad={false}
-                            parentActive={this.props.parent.props.parent.props.active}
-                            whenActiveChanged={this.props.parent.props.parent.props.whenActiveChanged}
-                            bringToFront={emptyFunction}
-                            addDocTab={this.props.parent.props.parent.props.addDocTab}
-                        />
-                    </div>
-                </div >
-            );
-        }
-        return null;
-    }
-}
+// @observer
+// class PinAnnotation extends React.Component<IAnnotationProps> {
+//     @observable private _backgroundColor: string = "green";
+//     @observable private _display: string = "initial";
+
+//     private _mainCont: React.RefObject<HTMLDivElement>;
+
+//     constructor(props: IAnnotationProps) {
+//         super(props);
+//         this._mainCont = React.createRef();
+//     }
+
+//     componentDidMount = () => {
+//         let selected = this.props.document.selected;
+//         if (!BoolCast(selected)) {
+//             runInAction(() => {
+//                 this._backgroundColor = "red";
+//                 this._display = "none";
+//             });
+//         }
+//         if (selected) {
+//             if (BoolCast(selected)) {
+//                 runInAction(() => {
+//                     this._backgroundColor = "green";
+//                     this._display = "initial";
+//                 });
+//             }
+//             else {
+//                 runInAction(() => {
+//                     this._backgroundColor = "red";
+//                     this._display = "none";
+//                 });
+//             }
+//         }
+//         else {
+//             runInAction(() => {
+//                 this._backgroundColor = "red";
+//                 this._display = "none";
+//             });
+//         }
+//     }
+
+//     @action
+//     pointerDown = (e: React.PointerEvent) => {
+//         let selected = this.props.document.selected;
+//         if (selected && BoolCast(selected)) {
+//             this._backgroundColor = "red";
+//             this._display = "none";
+//             this.props.document.selected = false;
+//         }
+//         else {
+//             this._backgroundColor = "green";
+//             this._display = "initial";
+//             this.props.document.selected = true;
+//         }
+//         e.preventDefault();
+//         e.stopPropagation();
+//     }
+
+//     @action
+//     doubleClick = (e: React.MouseEvent) => {
+//         if (this._mainCont.current) {
+//             let annotations = DocListCast(this.props.parent.props.parent.Document.annotations);
+//             if (annotations && annotations.length) {
+//                 let index = annotations.indexOf(this.props.document);
+//                 annotations.splice(index, 1);
+//                 this.props.parent.props.parent.Document.annotations = new List<Doc>(annotations);
+//             }
+//             // this._mainCont.current.childNodes.forEach(e => e.remove());
+//             this._mainCont.current.style.display = "none";
+//             // if (this._mainCont.current.parentElement) {
+//             //     this._mainCont.current.remove();
+//             // }
+//         }
+//         e.stopPropagation();
+//     }
+
+//     render() {
+//         let targetDoc = Cast(this.props.document.target, Doc);
+//         if (targetDoc instanceof Doc) {
+//             return (
+//                 <div className="pdfViewer-pinAnnotation" onPointerDown={this.pointerDown}
+//                     onDoubleClick={this.doubleClick} ref={this._mainCont}
+//                     style={{
+//                         top: this.props.y * scale - PinRadius / 2, left: this.props.x * scale - PinRadius / 2, width: PinRadius,
+//                         height: PinRadius, pointerEvents: "all", backgroundColor: this._backgroundColor
+//                     }}>
+//                     <div style={{
+//                         position: "absolute", top: "25px", left: "25px", transform: "scale(3)", transformOrigin: "top left",
+//                         display: this._display, width: targetDoc[WidthSym](), height: targetDoc[HeightSym]()
+//                     }}>
+//                         <DocumentView Document={targetDoc}
+//                             ContainingCollectionView={undefined}
+//                             ScreenToLocalTransform={this.props.parent.props.parent.props.ScreenToLocalTransform}
+//                             isTopMost={false}
+//                             ContentScaling={() => 1}
+//                             PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)}
+//                             PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)}
+//                             focus={emptyFunction}
+//                             selectOnLoad={false}
+//                             parentActive={this.props.parent.props.parent.props.active}
+//                             whenActiveChanged={this.props.parent.props.parent.props.whenActiveChanged}
+//                             bringToFront={emptyFunction}
+//                             addDocTab={this.props.parent.props.parent.props.addDocTab}
+//                         />
+//                     </div>
+//                 </div >
+//             );
+//         }
+//         return null;
+//     }
+// }
 
 class RegionAnnotation extends React.Component<IAnnotationProps> {
     @observable private _backgroundColor: string = "red";
 
-    onPointerDown = (e: React.MouseEvent) => {
-        let targetDoc = Cast(this.props.document.target, Doc, null);
-        if (targetDoc) {
-            DocumentManager.Instance.jumpToDocument(targetDoc);
+    onPointerDown = (e: React.PointerEvent) => {
+        if (e.button === 0) {
+            let targetDoc = Cast(this.props.document.target, Doc, null);
+            if (targetDoc) {
+                DocumentManager.Instance.jumpToDocument(targetDoc);
+            }
         }
+        // if (e.button === 2) {
+        //     console.log("right");
+        //     e.stopPropagation();
+        //     e.preventDefault();
+        // }
     }
 
     render() {
         return (
-            <div className="pdfViewer-annotationBox" onClick={this.onPointerDown}
+            <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown}
                 style={{ top: this.props.y * scale, left: this.props.x * scale, width: this.props.width * scale, height: this.props.height * scale, pointerEvents: "all", backgroundColor: StrCast(this.props.document.color) }}></div>
         );
     }
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 2697c9eee..39e737c32 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -15,6 +15,7 @@ import { listSpec } from "../../../new_fields/Schema";
 import { menuBar } from "prosemirror-menu";
 import { AnnotationTypes, PDFViewer, scale } from "./PDFViewer";
 import PDFMenu from "./PDFMenu";
+import { UndoManager } from "../../util/UndoManager";
 
 
 interface IPageProps {
@@ -152,7 +153,6 @@ export default class Page extends React.Component<IPageProps> {
     startDrag = (e: PointerEvent): void => {
         e.preventDefault();
         e.stopPropagation();
-        console.log("dragging");
         let thisDoc = this.props.parent.Document;
         // document that this annotation is linked to
         let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" });
@@ -163,7 +163,7 @@ export default class Page extends React.Component<IPageProps> {
         if (this._textLayer.current) {
             DragManager.StartAnnotationDrag([this._textLayer.current], dragData, e.pageX, e.pageY, {
                 handlers: {
-                    dragComplete: action(emptyFunction),
+                    dragComplete: emptyFunction,
                 },
                 hideSource: false
             });
@@ -325,68 +325,6 @@ export default class Page extends React.Component<IPageProps> {
             PDFMenu.Instance.StartDrag = this.startDrag;
             PDFMenu.Instance.Highlight = this.highlight;
         }
-        // let x = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width);
-        // let y = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height);
-        // if (this._marqueeing) {
-        //     this._marqueeing = false;
-        //     if (this._marquee.current) {
-        //         let copy = document.createElement("div");
-        //         // make a copy of the marquee
-        //         copy.style.left = this._marquee.current.style.left;
-        //         copy.style.top = this._marquee.current.style.top;
-        //         copy.style.width = this._marquee.current.style.width;
-        //         copy.style.height = this._marquee.current.style.height;
-
-        //         // apply the appropriate background, opacity, and transform
-        //         let { background, opacity, transform } = this.getCurlyTransform();
-        //         copy.style.background = background;
-        //         // if curly bracing, add a curly brace
-        //         if (opacity === "1" && this._curly.current) {
-        //             copy.style.opacity = opacity;
-        //             let img = this._curly.current.cloneNode();
-        //             (img as any).style.opacity = opacity;
-        //             (img as any).style.transform = transform;
-        //             copy.appendChild(img);
-        //         }
-        //         else {
-        //             copy.style.opacity = this._marquee.current.style.opacity;
-        //         }
-        //         copy.className = this._marquee.current.className;
-        //         this.props.createAnnotation(copy, this.props.page);
-        //         this._marquee.current.style.opacity = "0";
-        //     }
-
-        //     this._marqueeHeight = this._marqueeWidth = 0;
-        // }
-        // else {
-        //     let sel = window.getSelection();
-        //     // if selecting over a range of things
-        //     if (sel && sel.type === "Range") {
-        //         let clientRects = sel.getRangeAt(0).getClientRects();
-        //         if (this._textLayer.current) {
-        //             let boundingRect = this._textLayer.current.getBoundingClientRect();
-        //             for (let i = 0; i < clientRects.length; i++) {
-        //                 let rect = clientRects.item(i);
-        //                 if (rect) {
-        //                     let annoBox = document.createElement("div");
-        //                     annoBox.className = "pdfViewer-annotationBox";
-        //                     // transforms the positions from screen onto the pdf div
-        //                     annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString();
-        //                     annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString();
-        //                     annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString();
-        //                     annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString();
-        //                     this.props.createAnnotation(annoBox, this.props.page);
-        //                 }
-        //             }
-        //         }
-        //         // clear selection
-        //         if (sel.empty) {  // Chrome
-        //             sel.empty();
-        //         } else if (sel.removeAllRanges) {  // Firefox
-        //             sel.removeAllRanges();
-        //         }
-        //     }
-        // }
         document.removeEventListener("pointermove", this.onSelectStart);
         document.removeEventListener("pointerup", this.onSelectEnd);
     }
@@ -399,7 +337,6 @@ export default class Page extends React.Component<IPageProps> {
             for (let i = 0; i < clientRects.length; i++) {
                 let rect = clientRects.item(i);
                 if (rect && rect.width !== this._textLayer.current.getBoundingClientRect().width && rect.height !== this._textLayer.current.getBoundingClientRect().height) {
-                    console.log(rect);
                     let annoBox = document.createElement("div");
                     annoBox.className = "pdfViewer-annotationBox";
                     // transforms the positions from screen onto the pdf div
-- 
cgit v1.2.3-70-g09d2


From 46d57bc21cda4703855b85a4603bd471975d845b Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Wed, 19 Jun 2019 14:25:47 -0400
Subject: deleting annotations

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

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

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


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

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

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

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


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

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

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

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


From 135e252902a3ca93e95672602122afb3be6cd015 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Thu, 20 Jun 2019 10:43:58 -0400
Subject: basic pdf snippetting

---
 src/client/views/MainView.tsx      |  3 ++-
 src/client/views/nodes/PDFBox.tsx  | 20 ++++++++++++-----
 src/client/views/pdf/PDFMenu.tsx   | 45 +++++++++++++++++++++++++++++++++++---
 src/client/views/pdf/PDFViewer.tsx |  6 ++++-
 src/client/views/pdf/Page.tsx      | 31 +++++++++++++++++++++-----
 5 files changed, 89 insertions(+), 16 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index e3d4ff8b5..2645e2789 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, faThumbtack, 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, faCut } 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';
@@ -91,6 +91,7 @@ export class MainView extends React.Component {
         library.add(faFilm);
         library.add(faMusic);
         library.add(faTree);
+        library.add(faCut);
         library.add(faCommentAlt);
         library.add(faThumbtack);
         this.initEventListeners();
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index d2de1cb1c..0aeb9afc8 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -27,8 +27,22 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
 
     @observable private _alt = false;
     @observable private _scrollY: number = 0;
+    private _mainCont: React.RefObject<HTMLDivElement>;
     private _reactionDisposer?: IReactionDisposer;
 
+    constructor(props: FieldViewProps) {
+        super(props);
+
+        this._mainCont = React.createRef();
+        this._reactionDisposer = reaction(
+            () => this.props.Document.scrollY,
+            () => {
+                if (this._mainCont.current) {
+                    this._mainCont.current && this._mainCont.current.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "smooth" });
+                }
+            });
+    }
+
     componentDidMount() {
         if (this.props.setPdfBox) this.props.setPdfBox(this);
     }
@@ -60,10 +74,6 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     }
 
     createRef = (ele: HTMLDivElement | null) => {
-        if (this._reactionDisposer) this._reactionDisposer();
-        this._reactionDisposer = reaction(() => this.props.Document.scrollY, () => {
-            ele && ele.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "auto" });
-        });
     }
 
     loaded = (nw: number, nh: number, np: number) => {
@@ -105,7 +115,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}
+                ref={this._mainCont}
                 onWheel={(e: React.WheelEvent) => {
                     e.stopPropagation();
                 }} className={classname}>
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index 39b15fb11..a8e176858 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -5,6 +5,8 @@ import { observer } from "mobx-react";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { emptyFunction } from "../../../Utils";
 import { Doc } from "../../../new_fields/Doc";
+import { DragManager } from "../../util/DragManager";
+import { DocUtils } from "../../documents/Documents";
 
 @observer
 export default class PDFMenu extends React.Component {
@@ -16,19 +18,23 @@ export default class PDFMenu extends React.Component {
     @observable private _transition: string = "opacity 0.5s";
     @observable private _transitionDelay: string = "";
 
-    @observable public Pinned: boolean = false;
 
     StartDrag: (e: PointerEvent) => void = emptyFunction;
     Highlight: (d: Doc | undefined, color: string | undefined) => void = emptyFunction;
     Delete: () => void = emptyFunction;
+    Snippet: (marquee: { left: number, top: number, width: number, height: number }) => void = emptyFunction;
 
     @observable public Highlighting: boolean = false;
-    @observable public Status: "pdf" | "annotation" | "" = "";
+    @observable public Status: "pdf" | "annotation" | "snippet" | "" = "";
+    @observable public Pinned: boolean = false;
+
+    public Marquee: { left: number; top: number; width: number; height: number; } | undefined;
 
     private _offsetY: number = 0;
     private _offsetX: number = 0;
     private _mainCont: React.RefObject<HTMLDivElement>;
     private _dragging: boolean = false;
+    private _snippetButton: React.RefObject<HTMLButtonElement>;
 
     constructor(props: Readonly<{}>) {
         super(props);
@@ -36,6 +42,7 @@ export default class PDFMenu extends React.Component {
         PDFMenu.Instance = this;
 
         this._mainCont = React.createRef();
+        this._snippetButton = React.createRef();
     }
 
     pointerDown = (e: React.PointerEvent) => {
@@ -171,13 +178,45 @@ export default class PDFMenu extends React.Component {
         e.preventDefault();
     }
 
+    snippetStart = (e: React.PointerEvent) => {
+        document.removeEventListener("pointermove", this.snippetDrag);
+        document.addEventListener("pointermove", this.snippetDrag);
+        document.removeEventListener("pointerup", this.snippetEnd);
+        document.addEventListener("pointerup", this.snippetEnd);
+
+        e.stopPropagation();
+        e.preventDefault();
+    }
+
+    snippetDrag = (e: PointerEvent) => {
+        e.stopPropagation();
+        e.preventDefault();
+        if (this._dragging) {
+            return;
+        }
+        this._dragging = true;
+
+        if (this.Marquee) {
+            this.Snippet(this.Marquee);
+        }
+    }
+
+    snippetEnd = (e: PointerEvent) => {
+        this._dragging = false;
+        document.removeEventListener("pointermove", this.snippetDrag);
+        document.removeEventListener("pointerup", this.snippetEnd);
+        e.stopPropagation();
+        e.preventDefault();
+    }
+
     render() {
-        let buttons = this.Status === "pdf" ? [
+        let buttons = this.Status === "pdf" || this.Status === "snippet" ? [
             <button className="pdfMenu-button" title="Click to Highlight" onClick={this.highlightClicked}
                 style={this.Highlighting ? { backgroundColor: "#121212" } : {}}>
                 <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} />
             </button>,
             <button className="pdfMenu-button" title="Drag to Annotate" onPointerDown={this.pointerDown}><FontAwesomeIcon icon="comment-alt" size="lg" /></button>,
+            this.Status === "snippet" ? <button className="pdfMenu-button" title="Drag to Snippetize Selection" onPointerDown={this.snippetStart} ref={this._snippetButton}><FontAwesomeIcon icon="cut" size="lg" /></button> : undefined,
             <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)" : "" }} />
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 7000352e7..015d8f878 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -125,6 +125,10 @@ class Viewer extends React.Component<IViewerProps> {
             runInAction(() =>
                 Array.from(Array((this._pageSizes = pageSizes).length).keys()).map(this.getPlaceholderPage));
             this.props.loaded(Math.max(...pageSizes.map(i => i.width)), pageSizes[0].height, this.props.pdf.numPages);
+
+            let startY = NumCast(this.props.parent.Document.startY);
+            this.props.parent.Document.scrollY = startY;
+            console.log(startY);
             // this.props.loaded(Math.max(...pageSizes.map(i => i.width)), pageSizes[0].height, this.props.pdf.numPages);
         }
     }
@@ -215,7 +219,7 @@ class Viewer extends React.Component<IViewerProps> {
                     createAnnotation={this.createAnnotation}
                     sendAnnotations={this.receiveAnnotations}
                     makeAnnotationDocuments={this.makeAnnotationDocument}
-                    receiveAnnotations={this.sendAnnotations}
+                    getScrollFromPage={this.getScrollFromPage}
                     {...this.props} />
             );
         }
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 734dff7fc..984d38681 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -16,6 +16,7 @@ import { menuBar } from "prosemirror-menu";
 import { AnnotationTypes, PDFViewer, scale } from "./PDFViewer";
 import PDFMenu from "./PDFMenu";
 import { UndoManager } from "../../util/UndoManager";
+import { copy } from "typescript-collections/dist/lib/arrays";
 
 
 interface IPageProps {
@@ -28,9 +29,9 @@ interface IPageProps {
     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;
     createAnnotation: (div: HTMLDivElement, page: number) => void;
     makeAnnotationDocuments: (doc: Doc | undefined, scale: number, color: string) => Doc;
+    getScrollFromPage: (page: number) => number;
 }
 
 @observer
@@ -177,6 +178,18 @@ export default class Page extends React.Component<IPageProps> {
         e.stopPropagation();
     }
 
+    createSnippet = (marquee: { left: number, top: number, width: number, height: number }): void => {
+        let doc = this.props.parent.Document;
+        let view = Doc.MakeAlias(doc);
+        let data = Doc.MakeDelegate(doc.proto!);
+        view.proto = data;
+        view.nativeHeight = marquee.height;
+        view.startY = marquee.top + this.props.getScrollFromPage(this.props.page);
+        view.width = doc[WidthSym]();
+        let dragData = new DragManager.DocumentDragData([view]);
+        DragManager.StartDocumentDrag([], dragData, 0, 0);
+    }
+
     @action
     onPointerDown = (e: React.PointerEvent): void => {
         // if alt+left click, drag and annotate
@@ -191,6 +204,7 @@ export default class Page extends React.Component<IPageProps> {
         else if (e.button === 0) {
             PDFMenu.Instance.StartDrag = this.startDrag;
             PDFMenu.Instance.Highlight = this.highlight;
+            PDFMenu.Instance.Snippet = this.createSnippet;
             PDFMenu.Instance.Status = "pdf";
             PDFMenu.Instance.fadeOut(true);
             let target: any = e.target;
@@ -280,10 +294,11 @@ export default class Page extends React.Component<IPageProps> {
             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;
+                let style = this._marquee.current.style;
+                copy.style.left = style.left;
+                copy.style.top = style.top;
+                copy.style.width = style.width;
+                copy.style.height = style.height;
 
                 // apply the appropriate background, opacity, and transform
                 let { background, opacity, transform } = this.getCurlyTransform();
@@ -297,7 +312,7 @@ export default class Page extends React.Component<IPageProps> {
                     copy.appendChild(img);
                 }
                 else {
-                    copy.style.opacity = this._marquee.current.style.opacity;
+                    copy.style.opacity = style.opacity;
                 }
                 copy.className = this._marquee.current.className;
                 this.props.createAnnotation(copy, this.props.page);
@@ -305,6 +320,10 @@ export default class Page extends React.Component<IPageProps> {
             }
 
             if (this._marqueeWidth > 10 || this._marqueeHeight > 10) {
+                if (!e.ctrlKey) {
+                    PDFMenu.Instance.Status = "snippet";
+                    PDFMenu.Instance.Marquee = { left: this._marqueeX, top: this._marqueeY, width: this._marqueeWidth, height: this._marqueeHeight };
+                }
                 PDFMenu.Instance.jumpTo(e.clientX, e.clientY);
             }
 
-- 
cgit v1.2.3-70-g09d2


From aea1f5d804efd5bd38d72caf17d275304c3f7962 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Thu, 20 Jun 2019 12:37:28 -0400
Subject: asdjfkl

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

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

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 0aeb9afc8..8daea2541 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -38,13 +38,29 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
             () => this.props.Document.scrollY,
             () => {
                 if (this._mainCont.current) {
-                    this._mainCont.current && this._mainCont.current.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "smooth" });
+                    this._mainCont.current && this._mainCont.current.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "auto" });
                 }
-            });
+            }
+        );
     }
 
+    @action
     componentDidMount() {
         if (this.props.setPdfBox) this.props.setPdfBox(this);
+
+        this._scrollY = NumCast(this.Document.startY);
+        this.props.Document.scrollY = this.Document.startY;
+        // let ccv = this.props.ContainingCollectionView;
+        // if (ccv) {
+        //     ccv.props.Document.scrollY = this.Document.startY;
+        // }
+    }
+
+    componentWillUnmount() {
+        let ccv = this.props.ContainingCollectionView;
+        if (ccv) {
+            ccv.props.Document.scrollY = this.Document.startY;
+        }
     }
 
     public GetPage() {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 015d8f878..bb549a743 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -128,7 +128,6 @@ class Viewer extends React.Component<IViewerProps> {
 
             let startY = NumCast(this.props.parent.Document.startY);
             this.props.parent.Document.scrollY = startY;
-            console.log(startY);
             // this.props.loaded(Math.max(...pageSizes.map(i => i.width)), pageSizes[0].height, this.props.pdf.numPages);
         }
     }
-- 
cgit v1.2.3-70-g09d2


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

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

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

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


From 25561f70206871432ebb24e787e7222e98584a49 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Thu, 20 Jun 2019 19:43:25 -0400
Subject: artgh

---
 src/client/documents/Documents.ts  |   2 +-
 src/client/views/nodes/PDFBox.tsx  |  24 ++---
 src/client/views/pdf/PDFViewer.tsx | 173 +++++--------------------------------
 3 files changed, 33 insertions(+), 166 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index fcd1010c6..084e155e1 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -200,7 +200,7 @@ export namespace Docs {
         return audioProto;
     }
 
-    function CreateInstance(proto: Doc, data: Field, options: DocumentOptions, delegId?: string) {
+    export function CreateInstance(proto: Doc, data: Field, options: DocumentOptions, delegId?: string) {
         const { omit: protoProps, extract: delegateProps } = OmitKeys(options, delegateKeys);
         if (!("author" in protoProps)) {
             protoProps.author = CurrentUserUtils.email;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 8daea2541..10a346269 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,7 +1,7 @@
 import { action, IReactionDisposer, observable, reaction, trace, untracked } from 'mobx';
 import { observer } from "mobx-react";
 import 'react-image-lightbox/style.css';
-import { WidthSym } from "../../../new_fields/Doc";
+import { WidthSym, Doc } from "../../../new_fields/Doc";
 import { makeInterface } from "../../../new_fields/Schema";
 import { Cast, NumCast } from "../../../new_fields/Types";
 import { PdfField } from "../../../new_fields/URLField";
@@ -17,6 +17,8 @@ import { FieldView, FieldViewProps } from './FieldView';
 import { pageSchema } from "./ImageBox";
 import "./PDFBox.scss";
 import React = require("react");
+import { CompileScript } from '../../util/Scripting';
+import { ScriptField } from '../../../fields/ScriptField';
 
 type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
 const PdfDocument = makeInterface(positionSchema, pageSchema);
@@ -42,25 +44,15 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
                 }
             }
         );
+
+        let script = CompileScript("return this.page === 2", { params: { this: Doc.name } });
+        if (script.compiled) {
+            this.props.Document.filterScript = new ScriptField(script);
+        }
     }
 
-    @action
     componentDidMount() {
         if (this.props.setPdfBox) this.props.setPdfBox(this);
-
-        this._scrollY = NumCast(this.Document.startY);
-        this.props.Document.scrollY = this.Document.startY;
-        // let ccv = this.props.ContainingCollectionView;
-        // if (ccv) {
-        //     ccv.props.Document.scrollY = this.Document.startY;
-        // }
-    }
-
-    componentWillUnmount() {
-        let ccv = this.props.ContainingCollectionView;
-        if (ccv) {
-            ccv.props.Document.scrollY = this.Document.startY;
-        }
     }
 
     public GetPage() {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index bb549a743..75a8b042d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -10,7 +10,7 @@ import { List } from "../../../new_fields/List";
 import { BoolCast, Cast, NumCast, StrCast, FieldValue } from "../../../new_fields/Types";
 import { emptyFunction } from "../../../Utils";
 import { DocServer } from "../../DocServer";
-import { Docs, DocUtils } from "../../documents/Documents";
+import { Docs, DocUtils, DocumentOptions } from "../../documents/Documents";
 import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager } from "../../util/DragManager";
 import { DocumentView } from "../nodes/DocumentView";
@@ -20,6 +20,8 @@ import "./PDFViewer.scss";
 import React = require("react");
 import PDFMenu from "./PDFMenu";
 import { UndoManager } from "../../util/UndoManager";
+import { ScriptField } from "../../../fields/ScriptField";
+import { CompileScript, CompiledScript } from "../../util/Scripting";
 
 export const scale = 2;
 interface IPDFViewerProps {
@@ -75,12 +77,14 @@ class Viewer extends React.Component<IViewerProps> {
     @observable private _pageSizes: { width: number, height: number }[] = [];
     @observable private _annotations: Doc[] = [];
     @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
+    @observable private _script: ScriptField | undefined = this.props.parent.Document.filterScript;
 
     private _pageBuffer: number = 1;
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     private _reactionDisposer?: IReactionDisposer;
     private _annotationReactionDisposer?: IReactionDisposer;
     private _dropDisposer?: DragManager.DragDropDisposer;
+    private _filterReactionDisposer?: IReactionDisposer;
 
     componentDidUpdate = (prevProps: IViewerProps) => {
         if (this.scrollY !== prevProps.scrollY) {
@@ -103,6 +107,15 @@ class Viewer extends React.Component<IViewerProps> {
             (annotations: Doc[]) =>
                 annotations && annotations.length && this.renderAnnotations(annotations, true),
             { fireImmediately: true });
+
+        if (this.props.parent.props.ContainingCollectionView) {
+            this._filterReactionDisposer = reaction(
+                () => this.props.parent.Document.filterScript || this.props.parent.props.ContainingCollectionView!.props.Document.filterScript,
+                () => {
+                    this._script = Cast(this.props.parent.Document.filterScript, ScriptField);
+                }
+            );
+        }
     }
 
     componentWillUnmount = () => {
@@ -125,10 +138,15 @@ class Viewer extends React.Component<IViewerProps> {
             runInAction(() =>
                 Array.from(Array((this._pageSizes = pageSizes).length).keys()).map(this.getPlaceholderPage));
             this.props.loaded(Math.max(...pageSizes.map(i => i.width)), pageSizes[0].height, this.props.pdf.numPages);
+            // this.props.loaded(Math.max(...pageSizes.map(i => i.width)), pageSizes[0].height, this.props.pdf.numPages);
 
             let startY = NumCast(this.props.parent.Document.startY);
-            this.props.parent.Document.scrollY = startY;
-            // this.props.loaded(Math.max(...pageSizes.map(i => i.width)), pageSizes[0].height, this.props.pdf.numPages);
+            let ccv = this.props.parent.props.ContainingCollectionView;
+            if (ccv) {
+                ccv.props.Document.panY = startY;
+            }
+            this.props.parent.Document.scrollY = 0;
+            this.props.parent.Document.scrollY = startY + 1;
         }
     }
 
@@ -144,7 +162,7 @@ class Viewer extends React.Component<IViewerProps> {
         let mainAnnoDoc = new Doc();
         this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => {
             for (let anno of value) {
-                let annoDoc = new Doc();
+                let annoDoc = Docs.CreateInstance(new Doc(), this.props.parent.Document, {});
                 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;
@@ -290,28 +308,6 @@ class Viewer extends React.Component<IViewerProps> {
         return this._savedAnnotations.getValue(page);
     }
 
-    // createPinAnnotation = (x: number, y: number, page: number): void => {
-    //     let targetDoc = Docs.TextDocument({ width: 100, height: 50, title: "New Pin Annotation" });
-    //     let pinAnno = new Doc();
-    //     pinAnno.x = x;
-    //     pinAnno.y = y + this.getScrollFromPage(page);
-    //     pinAnno.width = pinAnno.height = PinRadius;
-    //     pinAnno.page = page;
-    //     pinAnno.target = targetDoc;
-    //     pinAnno.type = AnnotationTypes.Pin;
-    //     // this._annotations.push(pinAnno);
-    //     let annoDoc = new Doc();
-    //     annoDoc.annotations = new List<Doc>([pinAnno]);
-    //     let annotations = DocListCast(this.props.parent.Document.annotations);
-    //     if (annotations && annotations.length) {
-    //         annotations.push(annoDoc);
-    //         this.props.parent.Document.annotations = new List<Doc>(annotations);
-    //     }
-    //     else {
-    //         this.props.parent.Document.annotations = new List<Doc>([annoDoc]);
-    //     }
-    // }
-
     // get the page index that the vertical offset passed in is on
     getPageFromScroll = (vOffset: number) => {
         let index = 0;
@@ -364,6 +360,7 @@ class Viewer extends React.Component<IViewerProps> {
     }
 
     render() {
+        let compiled = this._script ? CompileScript(this._script.scriptString, { params: { this: Doc.name } }) : CompileScript("return true");
         return (
             <div ref={this.mainCont} style={{ pointerEvents: "all" }}>
                 <div className="viewer">
@@ -375,7 +372,7 @@ class Viewer extends React.Component<IViewerProps> {
                         pointerEvents: this.props.parent.props.active() ? "none" : "all"
                     }}>
                     <div className="pdfViewer-annotationLayer-subCont" ref={this._annotationLayer}>
-                        {this._annotations.map(anno => this.renderAnnotation(anno))}
+                        {this._annotations.filter(anno => compiled.compiled ? compiled.run(anno) : true).map(anno => this.renderAnnotation(anno))}
                     </div>
                 </div>
             </div >
@@ -396,119 +393,6 @@ interface IAnnotationProps {
     document: Doc;
 }
 
-// @observer
-// class PinAnnotation extends React.Component<IAnnotationProps> {
-//     @observable private _backgroundColor: string = "green";
-//     @observable private _display: string = "initial";
-
-//     private _mainCont: React.RefObject<HTMLDivElement>;
-
-//     constructor(props: IAnnotationProps) {
-//         super(props);
-//         this._mainCont = React.createRef();
-//     }
-
-//     componentDidMount = () => {
-//         let selected = this.props.document.selected;
-//         if (!BoolCast(selected)) {
-//             runInAction(() => {
-//                 this._backgroundColor = "red";
-//                 this._display = "none";
-//             });
-//         }
-//         if (selected) {
-//             if (BoolCast(selected)) {
-//                 runInAction(() => {
-//                     this._backgroundColor = "green";
-//                     this._display = "initial";
-//                 });
-//             }
-//             else {
-//                 runInAction(() => {
-//                     this._backgroundColor = "red";
-//                     this._display = "none";
-//                 });
-//             }
-//         }
-//         else {
-//             runInAction(() => {
-//                 this._backgroundColor = "red";
-//                 this._display = "none";
-//             });
-//         }
-//     }
-
-//     @action
-//     pointerDown = (e: React.PointerEvent) => {
-//         let selected = this.props.document.selected;
-//         if (selected && BoolCast(selected)) {
-//             this._backgroundColor = "red";
-//             this._display = "none";
-//             this.props.document.selected = false;
-//         }
-//         else {
-//             this._backgroundColor = "green";
-//             this._display = "initial";
-//             this.props.document.selected = true;
-//         }
-//         e.preventDefault();
-//         e.stopPropagation();
-//     }
-
-//     @action
-//     doubleClick = (e: React.MouseEvent) => {
-//         if (this._mainCont.current) {
-//             let annotations = DocListCast(this.props.parent.props.parent.Document.annotations);
-//             if (annotations && annotations.length) {
-//                 let index = annotations.indexOf(this.props.document);
-//                 annotations.splice(index, 1);
-//                 this.props.parent.props.parent.Document.annotations = new List<Doc>(annotations);
-//             }
-//             // this._mainCont.current.childNodes.forEach(e => e.remove());
-//             this._mainCont.current.style.display = "none";
-//             // if (this._mainCont.current.parentElement) {
-//             //     this._mainCont.current.remove();
-//             // }
-//         }
-//         e.stopPropagation();
-//     }
-
-//     render() {
-//         let targetDoc = Cast(this.props.document.target, Doc);
-//         if (targetDoc instanceof Doc) {
-//             return (
-//                 <div className="pdfViewer-pinAnnotation" onPointerDown={this.pointerDown}
-//                     onDoubleClick={this.doubleClick} ref={this._mainCont}
-//                     style={{
-//                         top: this.props.y * scale - PinRadius / 2, left: this.props.x * scale - PinRadius / 2, width: PinRadius,
-//                         height: PinRadius, pointerEvents: "all", backgroundColor: this._backgroundColor
-//                     }}>
-//                     <div style={{
-//                         position: "absolute", top: "25px", left: "25px", transform: "scale(3)", transformOrigin: "top left",
-//                         display: this._display, width: targetDoc[WidthSym](), height: targetDoc[HeightSym]()
-//                     }}>
-//                         <DocumentView Document={targetDoc}
-//                             ContainingCollectionView={undefined}
-//                             ScreenToLocalTransform={this.props.parent.props.parent.props.ScreenToLocalTransform}
-//                             isTopMost={false}
-//                             ContentScaling={() => 1}
-//                             PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)}
-//                             PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)}
-//                             focus={emptyFunction}
-//                             selectOnLoad={false}
-//                             parentActive={this.props.parent.props.parent.props.active}
-//                             whenActiveChanged={this.props.parent.props.parent.props.whenActiveChanged}
-//                             bringToFront={emptyFunction}
-//                             addDocTab={this.props.parent.props.parent.props.addDocTab}
-//                         />
-//                     </div>
-//                 </div >
-//             );
-//         }
-//         return null;
-//     }
-// }
-
 class RegionAnnotation extends React.Component<IAnnotationProps> {
     @observable private _backgroundColor: string = "red";
 
@@ -555,15 +439,6 @@ class RegionAnnotation extends React.Component<IAnnotationProps> {
         PDFMenu.Instance.fadeOut(true);
     }
 
-
-    // annotateThis = (e: PointerEvent) => {
-    //     e.preventDefault();
-    //     e.stopPropagation();
-    //     // document that this annotation is linked to
-    //     let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" });
-    //     let group = FieldValue(Cast(this.props.document.group, Doc));
-    // }
-
     @action
     onPointerDown = (e: React.PointerEvent) => {
         if (e.button === 0) {
-- 
cgit v1.2.3-70-g09d2


From 542501fc3247dafa5879482f62a28147ec4b3793 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 21 Jun 2019 13:13:18 -0400
Subject: lots more template stuff.

---
 src/client/util/DocumentManager.ts                 |   6 +-
 src/client/util/DragManager.ts                     |  18 ++--
 src/client/views/DocumentDecorations.tsx           |  35 ++++----
 src/client/views/MainOverlayTextBox.tsx            |   8 +-
 .../views/collections/CollectionDockingView.tsx    | 100 +++++++++++----------
 .../views/collections/CollectionSchemaView.tsx     |   2 +-
 .../views/collections/CollectionTreeView.tsx       |  33 +++++--
 src/client/views/collections/CollectionView.tsx    |   4 +-
 .../views/collections/ParentDocumentSelector.tsx   |   4 +-
 src/client/views/nodes/DocumentView.tsx            |  27 +++---
 src/client/views/nodes/FieldView.tsx               |   2 +-
 src/client/views/nodes/FormattedTextBox.tsx        |   2 +-
 src/client/views/nodes/ImageBox.tsx                |  43 +++++----
 src/client/views/pdf/PDFViewer.tsx                 |   1 -
 src/new_fields/Doc.ts                              |   8 ++
 15 files changed, 174 insertions(+), 119 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 862395d74..8421c902e 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -138,7 +138,7 @@ export class DocumentManager {
                         docContext.panTransformType = "Ease";
                         targetContextView.props.focus(docDelegate);
                     } else {
-                        (dockFunc || CollectionDockingView.Instance.AddRightSplit)(docContext);
+                        (dockFunc || CollectionDockingView.Instance.AddRightSplit)(docContext, docContext);
                         setTimeout(() => {
                             this.jumpToDocument(docDelegate, forceDockFunc, dockFunc, linkPage);
                         }, 10);
@@ -147,7 +147,7 @@ export class DocumentManager {
                     const actualDoc = Doc.MakeAlias(docDelegate);
                     actualDoc.libraryBrush = true;
                     if (linkPage !== undefined) actualDoc.curPage = linkPage;
-                    (dockFunc || CollectionDockingView.Instance.AddRightSplit)(actualDoc);
+                    (dockFunc || CollectionDockingView.Instance.AddRightSplit)(actualDoc, actualDoc);
                 }
             } else {
                 let contextView: DocumentView | null;
@@ -156,7 +156,7 @@ export class DocumentManager {
                     contextDoc.panTransformType = "Ease";
                     contextView.props.focus(docDelegate);
                 } else {
-                    (dockFunc || CollectionDockingView.Instance.AddRightSplit)(contextDoc);
+                    (dockFunc || CollectionDockingView.Instance.AddRightSplit)(contextDoc, contextDoc);
                     setTimeout(() => {
                         this.jumpToDocument(docDelegate, forceDockFunc, dockFunc, linkPage);
                     }, 10);
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index c3c92daa5..cc83b7346 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -15,7 +15,8 @@ export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: ()
 
         document.removeEventListener("pointermove", onRowMove);
         document.removeEventListener('pointerup', onRowUp);
-        var dragData = new DragManager.DocumentDragData([await docFunc()]);
+        let doc = await docFunc();
+        var dragData = new DragManager.DocumentDragData([doc], [doc]);
         dragData.dropAction = dropAction;
         dragData.moveDocument = moveFunc;
         dragData.options = options;
@@ -31,7 +32,7 @@ export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: ()
         if (e.button === 0) {
             e.stopPropagation();
             if (e.shiftKey && CollectionDockingView.Instance) {
-                CollectionDockingView.Instance.StartOtherDrag([await docFunc()], e);
+                CollectionDockingView.Instance.StartOtherDrag(e, [await docFunc()]);
             } else {
                 document.addEventListener("pointermove", onRowMove);
                 document.addEventListener("pointerup", onRowUp);
@@ -59,7 +60,8 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n
             let doc = await Cast(draggedDoc.annotationOn, Doc);
             if (doc) moddrag.push(doc);
         }
-        let dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs);
+        let linkDocs = moddrag.length ? moddrag : draggedDocs;
+        let dragData = new DragManager.DocumentDragData(linkDocs, linkDocs); // bcz: dataDocs?
         DragManager.StartDocumentDrag([dragEle], dragData, x, y, {
             handlers: {
                 dragComplete: action(emptyFunction),
@@ -141,13 +143,15 @@ export namespace DragManager {
 
     export type MoveFunction = (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
     export class DocumentDragData {
-        constructor(dragDoc: Doc[]) {
+        constructor(dragDoc: Doc[], dragDataDocs: Doc[]) {
             this.draggedDocuments = dragDoc;
+            this.draggedDataDocs = dragDataDocs;
             this.droppedDocuments = dragDoc;
             this.xOffset = 0;
             this.yOffset = 0;
         }
         draggedDocuments: Doc[];
+        draggedDataDocs: Doc[];
         droppedDocuments: Doc[];
         xOffset: number;
         yOffset: number;
@@ -238,6 +242,8 @@ export namespace DragManager {
 
         const docs: Doc[] =
             dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof AnnotationDragData ? [dragData.dragDocument] : [];
+        const datadocs: Doc[] =
+            dragData instanceof DocumentDragData ? dragData.draggedDataDocs : dragData instanceof AnnotationDragData ? [dragData.dragDocument] : [];
         let dragElements = eles.map(ele => {
             const w = ele.offsetWidth,
                 h = ele.offsetHeight;
@@ -314,12 +320,12 @@ export namespace DragManager {
             }
             if (e.shiftKey && CollectionDockingView.Instance) {
                 AbortDrag();
-                CollectionDockingView.Instance.StartOtherDrag(docs, {
+                CollectionDockingView.Instance.StartOtherDrag({
                     pageX: e.pageX,
                     pageY: e.pageY,
                     preventDefault: emptyFunction,
                     button: 0
-                });
+                }, docs, datadocs);
             }
             //TODO: Why can't we use e.movementX and e.movementY?
             let moveX = e.pageX - lastX;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 3a366b4d2..8f02f04d7 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -166,7 +166,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         let dragDocView = SelectionManager.SelectedDocuments()[0];
         const [left, top] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).inverse().transformPoint(0, 0);
         const [xoff, yoff] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).transformDirection(e.x - left, e.y - top);
-        let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
+        let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document), SelectionManager.SelectedDocuments().map(dv => dv.props.DataDoc));
         dragData.xOffset = xoff;
         dragData.yOffset = yoff;
         dragData.moveDocument = SelectionManager.SelectedDocuments()[0].props.moveDocument;
@@ -485,9 +485,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 let doc = PositionDocument(element.props.Document);
                 let nwidth = doc.nativeWidth || 0;
                 let nheight = doc.nativeHeight || 0;
-                let zoomBasis = NumCast(doc.zoomBasis, 1);
-                let width = (doc.width || 0) / zoomBasis;
-                let height = (doc.height || (nheight / nwidth * width)) / zoomBasis;
+                let width = (doc.width || 0);
+                let height = (doc.height || (nheight / nwidth * width));
                 let scale = element.props.ScreenToLocalTransform().Scale;
                 let actualdW = Math.max(width + (dW * scale), 20);
                 let actualdH = Math.max(height + (dH * scale), 20);
@@ -502,25 +501,27 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 }
                 if (nwidth > 0 && nheight > 0) {
                     if (Math.abs(dW) > Math.abs(dH)) {
-                        if (!fixedAspect) proto.nativeWidth = zoomBasis * actualdW / (doc.width || 1) * NumCast(proto.nativeWidth);
-                        doc.width = zoomBasis * actualdW;
-                        // doc.zoomBasis = zoomBasis * width / actualdW;
+                        if (!fixedAspect) {
+                            Doc.SetInPlace(element.props.Document, "nativeWidth", actualdW / (doc.width || 1) * (doc.nativeWidth || 0), true);
+                        }
+                        doc.width = actualdW;
                         if (fixedAspect) doc.height = nheight / nwidth * doc.width;
-                        else doc.height = zoomBasis * actualdH;
-                        proto.nativeHeight = (doc.height || 0) / doc.width * NumCast(proto.nativeWidth);
+                        else doc.height = actualdH;
+                        Doc.SetInPlace(element.props.Document, "nativeHeight", (doc.height || 0) / doc.width * (doc.nativeWidth || 0), true);
                     }
                     else {
-                        if (!fixedAspect) proto.nativeHeight = zoomBasis * actualdH / (doc.height || 1) * NumCast(proto.nativeHeight);
-                        doc.height = zoomBasis * actualdH;
-                        //doc.zoomBasis = zoomBasis * height / actualdH;
+                        if (!fixedAspect) {
+                            Doc.SetInPlace(element.props.Document, "nativeHeight", actualdH / (doc.height || 1) * (doc.nativeHeight || 0), true);
+                        }
+                        doc.height = actualdH;
                         if (fixedAspect) doc.width = nwidth / nheight * doc.height;
-                        else doc.width = zoomBasis * actualdW;
-                        proto.nativeWidth = (doc.width || 0) / doc.height * NumCast(proto.nativeHeight);
+                        else doc.width = actualdW;
+                        Doc.SetInPlace(element.props.Document, "nativeWidth", (doc.width || 0) / doc.height * (doc.nativeHeight || 0), true);
                     }
                 } else {
-                    dW && (doc.width = zoomBasis * actualdW);
-                    dH && (doc.height = zoomBasis * actualdH);
-                    proto.autoHeight = undefined;
+                    dW && (doc.width = actualdW);
+                    dH && (doc.height = actualdH);
+                    Doc.SetInPlace(element.props.Document, "autoHeight", undefined, true);
                 }
             }
         });
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 64271650b..73d59ce31 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -82,10 +82,10 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
     }
     @action
     textBoxMove = (e: PointerEvent) => {
-        if (e.movementX > 1 || e.movementY > 1) {
+        if ((e.movementX > 1 || e.movementY > 1) && FormattedTextBox.InputBoxOverlay) {
             document.removeEventListener("pointermove", this.textBoxMove);
             document.removeEventListener('pointerup', this.textBoxUp);
-            let dragData = new DragManager.DocumentDragData(FormattedTextBox.InputBoxOverlay ? [FormattedTextBox.InputBoxOverlay.props.Document] : []);
+            let dragData = new DragManager.DocumentDragData([FormattedTextBox.InputBoxOverlay.props.Document], [FormattedTextBox.InputBoxOverlay.props.DataDoc]);
             const [left, top] = this._textXf().inverse().transformPoint(0, 0);
             dragData.xOffset = e.clientX - left;
             dragData.yOffset = e.clientY - top;
@@ -102,9 +102,9 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
         document.removeEventListener('pointerup', this.textBoxUp);
     }
 
-    addDocTab = (doc: Doc, location: string) => {
+    addDocTab = (doc: Doc, dataDoc: Doc, location: string) => {
         if (true) { // location === "onRight") { need to figure out stack to add "inTab"
-            CollectionDockingView.Instance.AddRightSplit(doc);
+            CollectionDockingView.Instance.AddRightSplit(doc, dataDoc);
         }
     }
     render() {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 79a0d18df..89fadab57 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -28,7 +28,7 @@ import { MainView } from '../MainView';
 @observer
 export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
     public static Instance: CollectionDockingView;
-    public static makeDocumentConfig(document: Doc, width?: number) {
+    public static makeDocumentConfig(document: Doc, dataDoc: Doc, width?: number) {
         return {
             type: 'react-component',
             component: 'DocumentFrameRenderer',
@@ -36,6 +36,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             width: width,
             props: {
                 documentId: document[Id],
+                dataDocumentId: dataDoc[Id]
                 //collectionDockingView: CollectionDockingView.Instance
             }
         };
@@ -56,19 +57,19 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     }
     hack: boolean = false;
     undohack: any = null;
-    public StartOtherDrag(dragDocs: Doc[], e: any) {
+    public StartOtherDrag(e: any, dragDocs: Doc[], dragDataDocs?: Doc[]) {
         this.hack = true;
         this.undohack = UndoManager.StartBatch("goldenDrag");
-        dragDocs.map(dragDoc =>
-            this.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener.
+        dragDocs.map((dragDoc, i) =>
+            this.AddRightSplit(dragDoc, dragDataDocs ? dragDataDocs[i] : dragDoc, true).contentItems[0].tab._dragListener.
                 onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 }));
     }
 
     @action
-    public OpenFullScreen(document: Doc) {
+    public OpenFullScreen(document: Doc, dataDoc: Doc) {
         let newItemStackConfig = {
             type: 'stack',
-            content: [CollectionDockingView.makeDocumentConfig(document)]
+            content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)]
         };
         var docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
         this._goldenLayout.root.contentItems[0].addChild(docconfig);
@@ -123,14 +124,14 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     //  Creates a vertical split on the right side of the docking view, and then adds the Document to that split
     //
     @action
-    public AddRightSplit = (document: Doc, minimize: boolean = false) => {
+    public AddRightSplit = (document: Doc, dataDoc: Doc, minimize: boolean = false) => {
         let docs = Cast(this.props.Document.data, listSpec(Doc));
         if (docs) {
             docs.push(document);
         }
         let newItemStackConfig = {
             type: 'stack',
-            content: [CollectionDockingView.makeDocumentConfig(document)]
+            content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)]
         };
 
         var newContentItem = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
@@ -160,12 +161,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         return newContentItem;
     }
     @action
-    public AddTab = (stack: any, document: Doc) => {
+    public AddTab = (stack: any, document: Doc, dataDocument: Doc) => {
         let docs = Cast(this.props.Document.data, listSpec(Doc));
         if (docs) {
             docs.push(document);
         }
-        let docContentConfig = CollectionDockingView.makeDocumentConfig(document);
+        let docContentConfig = CollectionDockingView.makeDocumentConfig(document, dataDocument);
         var newContentItem = stack.layoutManager.createContentItem(docContentConfig, this._goldenLayout);
         stack.addChild(newContentItem.contentItems[0], undefined);
         this.layoutChanged();
@@ -281,14 +282,16 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                 let x = e.clientX;
                 let y = e.clientY;
                 let docid = (e.target as any).DashDocId;
+                let datadocid = (e.target as any).DashDataDocId;
                 let tab = (e.target as any).parentElement as HTMLElement;
                 let glTab = (e.target as any).Tab;
                 if (glTab && glTab.contentItem && glTab.contentItem.parent) {
                     glTab.contentItem.parent.setActiveContentItem(glTab.contentItem);
                 }
-                DocServer.GetRefField(docid).then(action((f: Opt<Field>) => {
+                DocServer.GetRefField(docid).then(action(async (f: Opt<Field>) => {
                     if (f instanceof Doc) {
-                        DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f]), x, y,
+                        let dataDoc = (datadocid !== docid) ? await DocServer.GetRefField(datadocid) : f;
+                        DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f], [dataDoc instanceof Doc ? dataDoc : f]), x, y,
                             {
                                 handlers: {
                                     dragComplete: emptyFunction,
@@ -338,33 +341,34 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             if (tab.contentItem.config.fixed) {
                 tab.contentItem.parent.config.fixed = true;
             }
-            DocServer.GetRefField(tab.contentItem.config.props.documentId).then(async doc => {
-                if (doc instanceof Doc) {
-                    let counter: any = this.htmlToElement(`<span class="messageCounter">0</div>`);
-                    tab.element.append(counter);
-                    let upDiv = document.createElement("span");
-                    const stack = tab.contentItem.parent;
-                    // shifts the focus to this tab when another tab is dragged over it
-                    tab.element[0].onmouseenter = (e: any) => {
-                        if (!this._isPointerDown) return;
-                        var activeContentItem = tab.header.parent.getActiveContentItem();
-                        if (tab.contentItem !== activeContentItem) {
-                            tab.header.parent.setActiveContentItem(tab.contentItem);
-                        }
-                        tab.setActive(true);
-                    };
-                    ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={(doc, location) => CollectionDockingView.Instance.AddTab(stack, doc)} />, upDiv);
-                    tab.reactComponents = [upDiv];
-                    tab.element.append(upDiv);
-                    counter.DashDocId = tab.contentItem.config.props.documentId;
-                    tab.reactionDisposer = reaction(() => [doc.linkedFromDocs, doc.LinkedToDocs, doc.title],
-                        () => {
-                            counter.innerHTML = DocListCast(doc.linkedFromDocs).length + DocListCast(doc.linkedToDocs).length;
-                            tab.titleElement[0].textContent = doc.title;
-                        }, { fireImmediately: true });
-                    tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
-                }
-            });
+            let doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId) as Doc;
+            let dataDoc = await DocServer.GetRefField(tab.contentItem.config.props.dataDocumentId) as Doc;
+            if (doc instanceof Doc && dataDoc instanceof Doc) {
+                let counter: any = this.htmlToElement(`<span class="messageCounter">0</div>`);
+                tab.element.append(counter);
+                let upDiv = document.createElement("span");
+                const stack = tab.contentItem.parent;
+                // shifts the focus to this tab when another tab is dragged over it
+                tab.element[0].onmouseenter = (e: any) => {
+                    if (!this._isPointerDown) return;
+                    var activeContentItem = tab.header.parent.getActiveContentItem();
+                    if (tab.contentItem !== activeContentItem) {
+                        tab.header.parent.setActiveContentItem(tab.contentItem);
+                    }
+                    tab.setActive(true);
+                };
+                ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={(doc, dataDoc, location) => CollectionDockingView.Instance.AddTab(stack, doc, dataDoc)} />, upDiv);
+                tab.reactComponents = [upDiv];
+                tab.element.append(upDiv);
+                counter.DashDocId = tab.contentItem.config.props.documentId;
+                counter.DashDataDocId = tab.contentItem.config.props.dataDocumentId;
+                tab.reactionDisposer = reaction(() => [doc.linkedFromDocs, doc.LinkedToDocs, doc.title],
+                    () => {
+                        counter.innerHTML = DocListCast(doc.linkedFromDocs).length + DocListCast(doc.linkedToDocs).length;
+                        tab.titleElement[0].textContent = doc.title;
+                    }, { fireImmediately: true });
+                tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
+            }
         }
         tab.titleElement[0].Tab = tab;
         tab.closeElement.off('click') //unbind the current click handler
@@ -427,6 +431,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
 
 interface DockedFrameProps {
     documentId: FieldId;
+    dataDocumentId: FieldId;
     //collectionDockingView: CollectionDockingView
 }
 @observer
@@ -435,6 +440,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     @observable private _panelWidth = 0;
     @observable private _panelHeight = 0;
     @observable private _document: Opt<Doc>;
+    @observable private _dataDoc: Opt<Doc>;
     get _stack(): any {
         let parent = (this.props as any).glContainer.parent.parent;
         if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1) {
@@ -444,7 +450,11 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     }
     constructor(props: any) {
         super(props);
-        DocServer.GetRefField(this.props.documentId).then(action((f: Opt<Field>) => this._document = f as Doc));
+        DocServer.GetRefField(this.props.documentId).then(action((f: Opt<Field>) => {
+            this._document = f as Doc;
+            if (!this.props.dataDocumentId || this.props.documentId === this.props.dataDocumentId) this._dataDoc = this._document;
+        }));
+        if (this.props.dataDocumentId && this.props.documentId !== this.props.dataDocumentId) DocServer.GetRefField(this.props.dataDocumentId).then(action((f: Opt<Field>) => this._dataDoc = f as Doc));
     }
 
     nativeWidth = () => NumCast(this._document!.nativeWidth, this._panelWidth);
@@ -481,17 +491,17 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     }
     get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; }
 
-    addDocTab = (doc: Doc, location: string) => {
+    addDocTab = (doc: Doc, dataDoc: Doc, location: string) => {
         if (doc.dockingConfig) {
             MainView.Instance.openWorkspace(doc);
         } else if (location === "onRight") {
-            CollectionDockingView.Instance.AddRightSplit(doc);
+            CollectionDockingView.Instance.AddRightSplit(doc, dataDoc);
         } else {
-            CollectionDockingView.Instance.AddTab(this._stack, doc);
+            CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc);
         }
     }
     get content() {
-        if (!this._document) {
+        if (!this._document || !this._dataDoc) {
             return (null);
         }
         return (
@@ -499,7 +509,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                 style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px) scale(${this.scaleToFitMultiplier}, ${this.scaleToFitMultiplier})` }}>
                 <DocumentView key={this._document[Id]}
                     Document={this._document}
-                    DataDoc={this._document}
+                    DataDoc={this._dataDoc}
                     bringToFront={emptyFunction}
                     addDocument={undefined}
                     removeDocument={undefined}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index d539835b9..9f5cef3cd 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -394,7 +394,7 @@ interface CollectionSchemaPreviewProps {
     removeDocument: (document: Doc) => boolean;
     active: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
-    addDocTab: (document: Doc, where: string) => void;
+    addDocTab: (document: Doc, dataDoc: Doc, where: string) => void;
     setPreviewScript: (script: string) => void;
     previewScript?: string;
 }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 18d6751c6..83a7c9e3a 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -26,6 +26,8 @@ import { CollectionSubView } from "./CollectionSubView";
 import "./CollectionTreeView.scss";
 import React = require("react");
 import { FormattedTextBox } from '../nodes/FormattedTextBox';
+import { ImageField } from '../../../new_fields/URLField';
+import { ImageBox } from '../nodes/ImageBox';
 
 
 export interface TreeViewProps {
@@ -35,7 +37,7 @@ export interface TreeViewProps {
     deleteDoc: (doc: Doc) => boolean;
     moveDocument: DragManager.MoveFunction;
     dropAction: "alias" | "copy" | undefined;
-    addDocTab: (doc: Doc, where: string) => void;
+    addDocTab: (doc: Doc, dataDoc: Doc, where: string) => void;
     panelWidth: () => number;
     panelHeight: () => number;
     addDocument: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean;
@@ -73,7 +75,7 @@ class TreeView extends React.Component<TreeViewProps> {
     }
 
     @undoBatch delete = () => this.props.deleteDoc(this.props.document);
-    @undoBatch openRight = async () => this.props.addDocTab(this.props.document, "onRight");
+    @undoBatch openRight = async () => this.props.addDocTab(this.props.document, this.props.dataDoc, "onRight");
 
     onPointerDown = (e: React.PointerEvent) => e.stopPropagation();
     onPointerEnter = (e: React.PointerEvent): void => {
@@ -148,6 +150,25 @@ class TreeView extends React.Component<TreeViewProps> {
         SetValue={(value: string) => {
             let res = (Doc.GetProto(this.props.document)[key] = value) ? true : true;
 
+            if (value.startsWith(">>")) {
+                let metaKey = value.slice(2, value.length);
+                let collection = this.props.containingCollection;
+                Doc.GetProto(collection)[metaKey] = new ImageField("http://www.cs.brown.edu/~bcz/face.gif");
+                let template = Doc.MakeAlias(collection);
+                template.title = metaKey;
+                template.embed = true;
+                template.layout = ImageBox.LayoutString(metaKey);
+                template.x = 0;
+                template.y = 0;
+                template.nativeWidth = 300;
+                template.nativeHeight = 300;
+                template.width = 300;
+                template.height = 300;
+                template.isTemplate = true;
+                template.templates = new List<string>([Templates.TitleBar(metaKey)]);//`{props.DataDoc.${metaKey}_text}`)]);
+                Doc.AddDocToList(collection, "data", template);
+                this.delete();
+            }
             if (value.startsWith(">")) {
                 let metaKey = value.slice(1, value.length);
                 let collection = this.props.containingCollection;
@@ -236,10 +257,10 @@ class TreeView extends React.Component<TreeViewProps> {
     onWorkspaceContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
             ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.props.document)) });
-            ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.KVPDocument(this.props.document, { width: 300, height: 300 }), "onRight"), icon: "layer-group" });
+            ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); }, icon: "layer-group" });
             if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) {
-                ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, "inTab"), icon: "folder" });
-                ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, "onRight"), icon: "caret-square-right" });
+                ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.props.dataDoc, "inTab"), icon: "folder" });
+                ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.props.dataDoc, "onRight"), icon: "caret-square-right" });
                 if (DocumentManager.Instance.getDocumentViews(this.props.document).length) {
                     ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.props.document).map(view => view.props.focus(this.props.document)) });
                 }
@@ -339,7 +360,7 @@ class TreeView extends React.Component<TreeViewProps> {
         remove: ((doc: Doc) => boolean),
         move: DragManager.MoveFunction,
         dropAction: dropActionType,
-        addDocTab: (doc: Doc, where: string) => void,
+        addDocTab: (doc: Doc, dataDoc: Doc, where: string) => void,
         screenToLocalXf: () => Transform,
         outerXf: () => { translateX: number, translateY: number },
         active: () => boolean,
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 89e5ad74c..10e6fb885 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -18,6 +18,7 @@ import { Doc } from '../../../new_fields/Doc';
 import { FormattedTextBox } from '../nodes/FormattedTextBox';
 import { Docs } from '../../documents/Documents';
 import { List } from '../../../new_fields/List';
+import { ImageField } from '../../../new_fields/URLField';
 export const COLLECTION_BORDER_WIDTH = 2;
 
 library.add(faTh);
@@ -63,8 +64,9 @@ export class CollectionView extends React.Component<FieldViewProps> {
                     let otherdoc = Docs.TextDocument({ width: 100, height: 50, title: "applied template" });
                     Doc.GetProto(otherdoc).description = "THIS DESCRIPTION IS REALLY IMPORTANT!";
                     Doc.GetProto(otherdoc).summary = "THIS SUMMARY IS MEANINGFUL!";
+                    Doc.GetProto(otherdoc).photo = new ImageField("http://www.cs.brown.edu/~bcz/snowbeast.JPG");
                     Doc.GetProto(otherdoc).layout = Doc.MakeDelegate(this.props.Document);
-                    this.props.addDocTab && this.props.addDocTab(otherdoc, "onRight");
+                    this.props.addDocTab && this.props.addDocTab(otherdoc, otherdoc, "onRight");
                 }), icon: "project-diagram"
             });
         }
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index f11af04a3..fa7058d5a 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -9,7 +9,7 @@ import { CollectionDockingView } from "./CollectionDockingView";
 import { NumCast } from "../../../new_fields/Types";
 import { CollectionViewType } from "./CollectionBaseView";
 
-type SelectorProps = { Document: Doc, addDocTab(doc: Doc, location: string): void };
+type SelectorProps = { Document: Doc, addDocTab(doc: Doc, dataDoc: Doc, location: string): void };
 @observer
 export class SelectorContextMenu extends React.Component<SelectorProps> {
     @observable private _docs: { col: Doc, target: Doc }[] = [];
@@ -43,7 +43,7 @@ export class SelectorContextMenu extends React.Component<SelectorProps> {
                 col.panX = newPanX;
                 col.panY = newPanY;
             }
-            this.props.addDocTab(col, "inTab");
+            this.props.addDocTab(col, col, "inTab"); // bcz: dataDoc?
         };
     }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index da50d0b53..08f58dcd6 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -81,7 +81,7 @@ export interface DocumentViewProps {
     parentActive: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     bringToFront: (doc: Doc) => void;
-    addDocTab: (doc: Doc, where: string) => void;
+    addDocTab: (doc: Doc, dataDoc: Doc, where: string) => void;
     animateBetweenIcon?: (iconPos: number[], startTime: number, maximizing: boolean) => void;
 }
 
@@ -212,8 +212,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean) {
         if (this._mainCont.current) {
             let allConnected = [this.props.Document, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])];
+            let alldataConnected = [this.props.DataDoc, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])];
             const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0);
-            let dragData = new DragManager.DocumentDragData(allConnected);
+            let dragData = new DragManager.DocumentDragData(allConnected, alldataConnected);
             const [xoff, yoff] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top);
             dragData.dropAction = dropAction;
             dragData.xOffset = xoff;
@@ -268,7 +269,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let altKey = e.altKey;
         let ctrlKey = e.ctrlKey;
         if (this._doubleTap && !this.props.isTopMost) {
-            this.props.addDocTab(this.props.Document, "inTab");
+            this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab");
             SelectionManager.DeselectAll();
             this.props.Document.libraryBrush = false;
         }
@@ -311,7 +312,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                         if (dataDocs) {
                             expandedDocs.forEach(maxDoc =>
                                 (!CollectionDockingView.Instance.CloseRightSplit(Doc.GetProto(maxDoc)) &&
-                                    this.props.addDocTab(getDispDoc(maxDoc), maxLocation)));
+                                    this.props.addDocTab(getDispDoc(maxDoc), getDispDoc(maxDoc), maxLocation)));
                         }
                     } else {
                         let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2);
@@ -334,7 +335,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     if (!linkedFwdDocs.some(l => l instanceof Promise)) {
                         let maxLocation = StrCast(linkedFwdDocs[altKey ? 1 : 0].maximizeLocation, "inTab");
                         let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined;
-                        DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, document => this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey ? 1 : 0], targetContext);
+                        DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, document => this.props.addDocTab(document, document, maxLocation), linkedFwdPage[altKey ? 1 : 0], targetContext);
                     }
                 }
             }
@@ -345,7 +346,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         this._downY = e.clientY;
         this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0;
         if (e.shiftKey && e.buttons === 1 && CollectionDockingView.Instance) {
-            CollectionDockingView.Instance.StartOtherDrag([Doc.MakeAlias(this.props.Document)], e);
+            CollectionDockingView.Instance.StartOtherDrag(e, [Doc.MakeAlias(this.props.Document)], [this.props.DataDoc]);
             e.stopPropagation();
         } else {
             if (this.active) e.stopPropagation(); // events stop at the lowest document that is active.  
@@ -376,7 +377,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
 
     deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); };
-    fieldsClicked = (): void => { this.props.addDocTab(Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }), "onRight"); };
+    fieldsClicked = (): void => { let kvp = Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); };
     makeBtnClicked = (): void => {
         let doc = Doc.GetProto(this.props.Document);
         doc.isButton = !BoolCast(doc.isButton, false);
@@ -390,7 +391,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
     }
     fullScreenClicked = (): void => {
-        CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(Doc.MakeCopy(this.props.Document, false));
+        CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(Doc.MakeAlias(this.props.Document), this.props.DataDoc);
         SelectionManager.DeselectAll();
     }
 
@@ -473,10 +474,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const cm = ContextMenu.Instance;
         let subitems: ContextMenuProps[] = [];
         subitems.push({ description: "Open Full Screen", event: this.fullScreenClicked, icon: "desktop" });
-        subitems.push({ description: "Open Tab", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, "inTab"), icon: "folder" });
-        subitems.push({ description: "Open Tab Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), "inTab"), icon: "folder" });
-        subitems.push({ description: "Open Right", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, "onRight"), icon: "caret-square-right" });
-        subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), "onRight"), icon: "caret-square-right" });
+        subitems.push({ description: "Open Tab", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab"), icon: "folder" });
+        subitems.push({ description: "Open Tab Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "inTab"), icon: "folder" });
+        subitems.push({ description: "Open Right", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.props.DataDoc, "onRight"), icon: "caret-square-right" });
+        subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "onRight"), icon: "caret-square-right" });
         subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" });
         cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" });
         cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "edit" });
@@ -486,7 +487,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         cm.addItem({
             description: "Find aliases", event: async () => {
                 const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document);
-                this.props.addDocTab && this.props.addDocTab(Docs.SchemaDocument(["title"], aliases, {}), "onRight");
+                this.props.addDocTab && this.props.addDocTab(Docs.SchemaDocument(["title"], aliases, {}), Docs.SchemaDocument(["title"], aliases, {}), "onRight"); // bcz: dataDoc?
             }, icon: "search"
         });
         cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document), icon: "crosshairs" });
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 35608af86..8879da55f 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -36,7 +36,7 @@ export interface FieldViewProps {
     isTopMost: boolean;
     selectOnLoad: boolean;
     addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean;
-    addDocTab: (document: Doc, where: string) => void;
+    addDocTab: (document: Doc, dataDoc: Doc, where: string) => void;
     removeDocument?: (document: Doc) => boolean;
     moveDocument?: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
     ScreenToLocalTransform: () => Transform;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index f59af226d..39444fd6a 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -270,7 +270,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                     this._linkClicked = href.replace(DocServer.prepend("/doc/"), "").split("?")[0];
                     if (this._linkClicked) {
                         DocServer.GetRefField(this._linkClicked).then(f => {
-                            (f instanceof Doc) && DocumentManager.Instance.jumpToDocument(f, ctrlKey, document => this.props.addDocTab(document, "inTab"));
+                            (f instanceof Doc) && DocumentManager.Instance.jumpToDocument(f, ctrlKey, document => this.props.addDocTab(document, document, "inTab"));
                         });
                         e.stopPropagation();
                         e.preventDefault();
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index f56a2d926..241a593b0 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,13 +1,13 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faImage } from '@fortawesome/free-solid-svg-icons';
-import { action, observable } from 'mobx';
+import { action, observable, computed } from 'mobx';
 import { observer } from "mobx-react";
 import Lightbox from 'react-image-lightbox';
 import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
 import { Doc, HeightSym, WidthSym } from '../../../new_fields/Doc';
 import { List } from '../../../new_fields/List';
 import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema';
-import { Cast, FieldValue, NumCast, StrCast } from '../../../new_fields/Types';
+import { Cast, FieldValue, NumCast, StrCast, BoolCast } from '../../../new_fields/Types';
 import { ImageField } from '../../../new_fields/URLField';
 import { Utils } from '../../../Utils';
 import { DragManager } from '../../util/DragManager';
@@ -36,7 +36,7 @@ const ImageDocument = makeInterface(pageSchema, positionSchema);
 @observer
 export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageDocument) {
 
-    public static LayoutString() { return FieldView.LayoutString(ImageBox); }
+    public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(ImageBox, fieldKey); }
     private _imgRef: React.RefObject<HTMLImageElement> = React.createRef();
     private _downX: number = 0;
     private _downY: number = 0;
@@ -46,6 +46,8 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
     private dropDisposer?: DragManager.DragDropDisposer;
 
 
+    @computed get dataDoc() { return this.props.DataDoc && BoolCast(this.props.Document.isTemplate) ? this.props.DataDoc : this.props.Document; }
+
 
     protected createDropTarget = (ele: HTMLDivElement) => {
         if (this.dropDisposer) {
@@ -66,19 +68,24 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (de.data instanceof DragManager.DocumentDragData) {
             de.data.droppedDocuments.forEach(action((drop: Doc) => {
-                let layout = StrCast(drop.backgroundLayout);
-                if (layout.indexOf(ImageBox.name) !== -1) {
-                    let imgData = this.props.Document[this.props.fieldKey];
-                    if (imgData instanceof ImageField) {
-                        Doc.SetOnPrototype(this.props.Document, "data", new List([imgData]));
-                    }
-                    let imgList = Cast(this.props.Document[this.props.fieldKey], listSpec(ImageField), [] as any[]);
-                    if (imgList) {
-                        let field = drop.data;
-                        if (field instanceof ImageField) imgList.push(field);
-                        else if (field instanceof List) imgList.concat(field);
-                    }
+                if (this.dataDoc !== this.props.Document && drop.data instanceof ImageField) {
+                    this.dataDoc[this.props.fieldKey] = new ImageField(drop.data.url);
                     e.stopPropagation();
+                } else {
+                    let layout = StrCast(drop.backgroundLayout);
+                    if (layout.indexOf(ImageBox.name) !== -1) {
+                        let imgData = this.dataDoc[this.props.fieldKey];
+                        if (imgData instanceof ImageField) {
+                            Doc.SetOnPrototype(this.dataDoc, this.props.fieldKey, new List([imgData]));
+                        }
+                        let imgList = Cast(this.dataDoc[this.props.fieldKey], listSpec(ImageField), [] as any[]);
+                        if (imgList) {
+                            let field = drop.data;
+                            if (field instanceof ImageField) imgList.push(field);
+                            else if (field instanceof List) imgList.concat(field);
+                        }
+                        e.stopPropagation();
+                    }
                 }
             }));
             // de.data.removeDocument()  bcz: need to implement
@@ -206,7 +213,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
         let paths: string[] = ["http://www.cs.brown.edu/~bcz/noImage.png"];
         // this._curSuffix = "";
         // if (w > 20) {
-        let field = this.Document[this.props.fieldKey];
+        let field = this.dataDoc[this.props.fieldKey];
         // if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s";
         // else if (w < 600 && this._mediumRetryCount < 10) this._curSuffix = "_m";
         // else if (this._largeRetryCount < 10) this._curSuffix = "_l";
@@ -214,8 +221,8 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
         else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => this.choosePath((p as ImageField).url));
         // }
         let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive";
-        let rotation = NumCast(this.props.Document.rotation, 0);
-        let aspect = (rotation % 180) ? this.props.Document[HeightSym]() / this.props.Document[WidthSym]() : 1;
+        let rotation = NumCast(this.dataDoc.rotation, 0);
+        let aspect = (rotation % 180) ? this.dataDoc[HeightSym]() / this.dataDoc[WidthSym]() : 1;
         let shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0;
         return (
             <div id={id} className={`imageBox-cont${interactive}`} style={{ background: "transparent" }}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 6adead626..7000352e7 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -203,7 +203,6 @@ class Viewer extends React.Component<IViewerProps> {
             this._isPage[page] = "page";
             this._visibleElements[page] = (
                 <Page
-                    size={this._pageSizes[page]}
                     pdf={this.props.pdf}
                     page={page}
                     numPages={this.props.pdf.numPages}
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 9bacf49ba..27b3473f8 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -166,6 +166,14 @@ export namespace Doc {
     export function IsPrototype(doc: Doc) {
         return GetT(doc, "isPrototype", "boolean", true);
     }
+    export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) {
+        let hasProto = doc.proto instanceof Doc;
+        let onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1;
+        let onProto = Object.getOwnPropertyNames(doc.proto).indexOf(key) !== -1;
+        if (onDeleg || !hasProto || (!onProto && !defaultProto))
+            doc[key] = value;
+        else doc.proto![key] = value;
+    }
     export async function SetOnPrototype(doc: Doc, key: string, value: Field) {
         const proto = Object.getOwnPropertyNames(doc).indexOf("isPrototype") === -1 ? doc.proto : doc;
 
-- 
cgit v1.2.3-70-g09d2


From 6df09d7d646c16e6469b198e7d270b6a1e45b0c7 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 24 Jun 2019 13:23:28 -0400
Subject: fixes for templates with annotations and more.

---
 src/client/util/TooltipTextMenu.tsx                |   2 +-
 src/client/views/DocumentDecorations.tsx           |  55 +++++------
 src/client/views/InkingCanvas.tsx                  |   7 +-
 src/client/views/InkingControl.tsx                 |   3 +-
 src/client/views/MainView.tsx                      |   4 +-
 .../views/collections/CollectionBaseView.tsx       |   1 -
 .../views/collections/CollectionTreeView.tsx       | 106 +++++++++------------
 src/client/views/collections/CollectionView.tsx    |  17 +---
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   5 +-
 src/client/views/pdf/PDFViewer.tsx                 |   1 +
 src/new_fields/Doc.ts                              |   2 +-
 11 files changed, 82 insertions(+), 121 deletions(-)

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

diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index c9216199b..048fb7133 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -222,7 +222,7 @@ export class TooltipTextMenu {
                                 if (DocumentManager.Instance.getDocumentView(f)) {
                                     DocumentManager.Instance.getDocumentView(f)!.props.focus(f);
                                 }
-                                else if (CollectionDockingView.Instance) CollectionDockingView.Instance.AddRightSplit(f);
+                                else if (CollectionDockingView.Instance) CollectionDockingView.Instance.AddRightSplit(f, f);
                             }
                         }));
                     }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 0d5cca9f1..9be5c9cd6 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -24,9 +24,10 @@ import { LinkMenu } from "./nodes/LinkMenu";
 import { TemplateMenu } from "./TemplateMenu";
 import { Template, Templates } from "./Templates";
 import React = require("react");
-import { URLField } from '../../new_fields/URLField';
+import { URLField, ImageField } from '../../new_fields/URLField';
 import { templateLiteral } from 'babel-types';
 import { CollectionViewType } from './collections/CollectionBaseView';
+import { ImageBox } from './nodes/ImageBox';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -75,41 +76,29 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             if (text[0] === '#') {
                 this._fieldKey = text.slice(1, text.length);
                 this._title = this.selectionTitle;
-            } else if (text.startsWith(">>>")) {
-                let metaKey = text.slice(3, text.length);
-                let collection = SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView!.props.Document;
-                Doc.GetProto(collection)[metaKey] = new List<Doc>([
-                    Docs.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { width: 300, height: 300 }),
-                    Docs.TextDocument({ documentText: "hello world!", width: 300, height: 300 }),
-                ]);
-                let template = Doc.MakeAlias(collection);
+            } else if (text.startsWith(">")) {
+                let metaKey = text.slice(text.startsWith(">>>") ? 3 : text.startsWith(">>") ? 2 : 1, text.length);
+                let field = SelectionManager.SelectedDocuments()[0];
+                let collectionKey = field.props.ContainingCollectionView!.props.fieldKey;
+                let collection = field.props.ContainingCollectionView!.props.Document;
+                let collectionKeyProp = `fieldKey={"${collectionKey}"}`;
+                let collectionAnnotationsKeyProp = `fieldKey={"annotations"}`;
+                let metaKeyProp = `fieldKey={"${metaKey}"}`;
+                let metaAnnotationsKeyProp = `fieldKey={"${metaKey}_annotations"}`;
+                let template = Doc.MakeAlias(field.props.Document);
+                template.proto = collection;
                 template.title = metaKey;
+                template.nativeWidth = Cast(field.nativeWidth, "number");
+                template.nativeHeight = Cast(field.nativeHeight, "number");
                 template.embed = true;
-                template.layout = CollectionView.LayoutString(metaKey);
-                template.viewType = CollectionViewType.Freeform;
-                template.x = 0;
-                template.y = 0;
-                template.width = 300;
-                template.height = 300;
                 template.isTemplate = true;
-                template.templates = new List<string>([Templates.TitleBar(metaKey)]);//`{props.DataDoc.${metaKey}_text}`)]);
-                Doc.AddDocToList(collection, "data", template);
-                SelectionManager.SelectedDocuments().map(dv => dv.props.removeDocument && dv.props.removeDocument(dv.props.Document));
-            } else if (text[0] === ">") {
-                let metaKey = text.slice(1, text.length);
-                let first = SelectionManager.SelectedDocuments()[0].props.Document!;
-                let collection = SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView!.props.Document;
-                Doc.GetProto(collection)[metaKey] = "-empty field-";
-                let template = Doc.MakeAlias(collection);
-                template.title = metaKey;
-                template.layout = FormattedTextBox.LayoutString(metaKey);
-                template.isTemplate = true;
-                template.x = NumCast(first.x);
-                template.y = NumCast(first.y);
-                template.width = first[WidthSym]();
-                template.height = first[HeightSym]();
-                template.templates = new List<string>([Templates.TitleBar(metaKey)]);//`{props.DataDoc.${metaKey}_text}`)]);
-                Doc.AddDocToList(collection, "data", template);
+                template.templates = new List<string>([Templates.TitleBar(metaKey)]);
+                template.layout = StrCast(field.props.Document.layout).replace(collectionKeyProp, metaKeyProp);
+                if (field.props.Document.backgroundLayout) {
+                    template.layout = StrCast(field.props.Document.layout).replace(collectionAnnotationsKeyProp, metaAnnotationsKeyProp);
+                    template.backgroundLayout = StrCast(field.props.Document.backgroundLayout).replace(collectionKeyProp, metaKeyProp);
+                }
+                Doc.AddDocToList(collection, collectionKey, template);
                 SelectionManager.SelectedDocuments().map(dv => dv.props.removeDocument && dv.props.removeDocument(dv.props.Document));
             }
             else {
diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx
index 5d4ea76cd..fd7e5b07d 100644
--- a/src/client/views/InkingCanvas.tsx
+++ b/src/client/views/InkingCanvas.tsx
@@ -14,6 +14,7 @@ import { Cast, PromiseValue, NumCast } from "../../new_fields/Types";
 interface InkCanvasProps {
     getScreenTransform: () => Transform;
     Document: Doc;
+    inkFieldKey: string;
     children: () => JSX.Element[];
 }
 
@@ -40,7 +41,7 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
     }
 
     componentDidMount() {
-        PromiseValue(Cast(this.props.Document.ink, InkField)).then(ink => runInAction(() => {
+        PromiseValue(Cast(this.props.Document[this.props.inkFieldKey], InkField)).then(ink => runInAction(() => {
             if (ink) {
                 let bounds = Array.from(ink.inkData).reduce(([mix, max, miy, may], [id, strokeData]) =>
                     strokeData.pathData.reduce(([mix, max, miy, may], p) =>
@@ -55,12 +56,12 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
 
     @computed
     get inkData(): Map<string, StrokeData> {
-        let map = Cast(this.props.Document.ink, InkField);
+        let map = Cast(this.props.Document[this.props.inkFieldKey], InkField);
         return !map ? new Map : new Map(map.inkData);
     }
 
     set inkData(value: Map<string, StrokeData>) {
-        Doc.GetProto(this.props.Document).ink = new InkField(value);
+        Doc.GetProto(this.props.Document)[this.props.inkFieldKey] = new InkField(value);
     }
 
     @action
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 0837e07a9..6cde73933 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -8,6 +8,7 @@ import { faPen, faHighlighter, faEraser, faBan } from '@fortawesome/free-solid-s
 import { SelectionManager } from "../util/SelectionManager";
 import { InkTool } from "../../new_fields/InkField";
 import { Doc } from "../../new_fields/Doc";
+import { InkingCanvas } from "./InkingCanvas";
 
 library.add(faPen, faHighlighter, faEraser, faBan);
 
@@ -39,7 +40,7 @@ export class InkingControl extends React.Component {
     @action
     switchColor = (color: ColorResult): void => {
         this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff");
-        SelectionManager.SelectedDocuments().forEach(doc => Doc.GetProto(doc.props.Document).backgroundColor = this._selectedColor);
+        if (InkingControl.Instance.selectedTool === InkTool.None) SelectionManager.SelectedDocuments().forEach(doc => Doc.GetProto(doc.props.Document).backgroundColor = this._selectedColor);
     }
 
     @action
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 8198b88d2..a72f25b99 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -144,7 +144,7 @@ export class MainView extends React.Component {
         const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc));
         if (list) {
             let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, width: this.pwidth * .7, height: this.pheight, title: `WS collection ${list.length + 1}` });
-            var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(CurrentUserUtils.UserDocument, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] };
+            var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(CurrentUserUtils.UserDocument, CurrentUserUtils.UserDocument, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] };
             let mainDoc = Docs.DockDocument([CurrentUserUtils.UserDocument, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` }, id);
             list.push(mainDoc);
             // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
@@ -177,7 +177,7 @@ export class MainView extends React.Component {
 
     openNotifsCol = () => {
         if (this._notifsCol && CollectionDockingView.Instance) {
-            CollectionDockingView.Instance.AddRightSplit(this._notifsCol);
+            CollectionDockingView.Instance.AddRightSplit(this._notifsCol, this._notifsCol);
         }
     }
 
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 75bdf755c..79a9f3be0 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -100,7 +100,6 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         }
         return false;
     }
-    @computed get isAnnotationOverlay() { return this.props.fieldKey === "annotations"; }
 
     @action.bound
     addDocument(doc: Doc, allowDuplicates: boolean = false): boolean {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index b3f1b1c88..f5f323269 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -28,7 +28,6 @@ import React = require("react");
 import { FormattedTextBox } from '../nodes/FormattedTextBox';
 import { ImageField } from '../../../new_fields/URLField';
 import { ImageBox } from '../nodes/ImageBox';
-import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 import { CollectionView } from './CollectionView';
 
 
@@ -71,11 +70,23 @@ class TreeView extends React.Component<TreeViewProps> {
     @observable _collapsed: boolean = true;
 
     @computed get fieldKey() {
+        let keys = Array.from(Object.keys(this.dataDoc));
+        if (this.dataDoc.proto instanceof Doc) {
+            keys.push(...Array.from(Object.keys(this.dataDoc.proto)));
+            while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1);
+        }
+        let keyList: string[] = [];
+        keys.map(key => {
+            let docList = Cast(this.dataDoc[key], listSpec(Doc));
+            if (docList && docList.length > 0) {
+                keyList.push(key);
+            }
+        });
         let layout = StrCast(this.props.document.layout);
         if (layout.indexOf("fieldKey={\"") !== -1) {
             return layout.split("fieldKey={\"")[1].split("\"")[0];
         }
-        return "data";
+        return keyList.length ? keyList[0] : "data";
     }
 
     @computed get dataDoc() { return (BoolCast(this.props.document.isTemplate) ? this.props.dataDoc : this.props.document); }
@@ -163,63 +174,39 @@ class TreeView extends React.Component<TreeViewProps> {
         SetValue={(value: string) => {
             let res = (Doc.GetProto(this.dataDoc)[key] = value) ? true : true;
 
-            if (value.startsWith(">>>")) {
-                let metaKey = value.slice(3, value.length);
+            if (value.startsWith(">")) {
+                let metaKey = value.slice(value.startsWith(">>>") ? 3 : value.startsWith(">>") ? 2 : 1, value.length);
                 let collection = this.props.containingCollection;
-                Doc.GetProto(collection)[metaKey] = new List<Doc>([
-                    Docs.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { width: 300, height: 300 }),
-                    Docs.TextDocument({ documentText: "hello world!", width: 300, height: 300 }),
-                ]);
                 let template = Doc.MakeAlias(collection);
                 template.title = metaKey;
                 template.embed = true;
-                template.layout = CollectionView.LayoutString(metaKey);
-                template.viewType = CollectionViewType.Freeform;
                 template.x = 0;
                 template.y = 0;
                 template.width = 300;
                 template.height = 300;
                 template.isTemplate = true;
                 template.templates = new List<string>([Templates.TitleBar(metaKey)]);//`{props.DataDoc.${metaKey}_text}`)]);
-                Doc.AddDocToList(collection, "data", template);
-                this.delete();
-            } else
-                if (value.startsWith(">>")) {
-                    let metaKey = value.slice(2, value.length);
-                    let collection = this.props.containingCollection;
+                if (value.startsWith(">>>")) { // Collection
+                    Doc.GetProto(collection)[metaKey] = new List<Doc>([
+                        Docs.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { width: 300, height: 300 }),
+                        Docs.TextDocument({ documentText: "hello world!", width: 300, height: 300 }),
+                    ]);
+                    template.layout = CollectionView.LayoutString(metaKey);
+                    template.viewType = CollectionViewType.Freeform;
+                } else if (value.startsWith(">>")) { // Image
                     Doc.GetProto(collection)[metaKey] = new ImageField("http://www.cs.brown.edu/~bcz/face.gif");
-                    let template = Doc.MakeAlias(collection);
-                    template.title = metaKey;
-                    template.embed = true;
                     template.layout = ImageBox.LayoutString(metaKey);
-                    template.x = 0;
-                    template.y = 0;
                     template.nativeWidth = 300;
                     template.nativeHeight = 300;
-                    template.width = 300;
-                    template.height = 300;
-                    template.isTemplate = true;
-                    template.templates = new List<string>([Templates.TitleBar(metaKey)]);//`{props.DataDoc.${metaKey}_text}`)]);
-                    Doc.AddDocToList(collection, "data", template);
-                    this.delete();
-                } else
-                    if (value.startsWith(">")) {
-                        let metaKey = value.slice(1, value.length);
-                        let collection = this.props.containingCollection;
-                        Doc.GetProto(collection)[metaKey] = "-empty field-";
-                        let template = Doc.MakeAlias(collection);
-                        template.title = metaKey;
-                        template.embed = true;
-                        template.layout = FormattedTextBox.LayoutString(metaKey);
-                        template.x = 0;
-                        template.y = 0;
-                        template.width = 100;
-                        template.height = 50;
-                        template.isTemplate = true;
-                        template.templates = new List<string>([Templates.TitleBar(metaKey)]);//`{props.DataDoc.${metaKey}_text}`)]);
-                        Doc.AddDocToList(collection, "data", template);
-                        this.delete();
-                    }
+                } else if (value.startsWith(">")) {  // Text
+                    Doc.GetProto(collection)[metaKey] = "-empty field-";
+                    template.layout = FormattedTextBox.LayoutString(metaKey);
+                    template.width = 100;
+                    template.height = 50;
+                }
+                Doc.AddDocToList(collection, "data", template);
+                this.delete();
+            }
 
             return res;
         }}
@@ -238,14 +225,8 @@ class TreeView extends React.Component<TreeViewProps> {
             keys.push(...Array.from(Object.keys(this.dataDoc.proto)));
             while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1);
         }
-        let keyList: string[] = [];
-        keys.map(key => {
-            let docList = Cast(this.dataDoc[key], listSpec(Doc));
-            let doc = Cast(this.dataDoc[key], Doc);
-            if (doc instanceof Doc || docList) {
-                keyList.push(key);
-            }
-        });
+        let keyList: string[] = keys.reduce((l, key) => Cast(this.dataDoc[key], listSpec(Doc)) ? [...l, key] : l, [] as string[]);
+        keys.map(key => Cast(this.dataDoc[key], Doc) instanceof Doc && keyList.push(key));
         if (keyList.indexOf(this.fieldKey) !== -1) {
             keyList.splice(keyList.indexOf(this.fieldKey), 1);
         }
@@ -291,16 +272,16 @@ class TreeView extends React.Component<TreeViewProps> {
     onWorkspaceContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
             ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.dataDoc)) });
-            ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.dataDoc, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); }, icon: "layer-group" });
+            ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); }, icon: "layer-group" });
             if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) {
-                ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.props.document, "inTab"), icon: "folder" });
-                ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.props.document, "onRight"), icon: "caret-square-right" });
+                ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.dataDoc, "inTab"), icon: "folder" });
+                ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.dataDoc, "onRight"), icon: "caret-square-right" });
                 if (DocumentManager.Instance.getDocumentViews(this.dataDoc).length) {
                     ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.dataDoc).map(view => view.props.focus(this.props.document)) });
                 }
-                ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.dataDoc)) });
+                ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.props.document)) });
             } else {
-                ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.dataDoc)) });
+                ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.props.document)) });
             }
             ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15);
             e.stopPropagation();
@@ -349,14 +330,14 @@ class TreeView extends React.Component<TreeViewProps> {
         if (!this._collapsed) {
             if (!this.props.document.embed) {
                 contentElement = <ul key={this._chosenKey + "more"}>
-                    {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this.props.document, this.props.dataDoc, this._chosenKey, addDoc, remDoc, this.move,
+                    {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this.props.document, this.dataDoc, this._chosenKey, addDoc, remDoc, this.move,
                         this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth)}
                 </ul >;
             } else {
                 contentElement = <div ref={this._dref} style={{ display: "inline-block", height: this.props.panelHeight() }} key={this.props.document[Id]}>
                     <CollectionSchemaPreview
                         Document={this.props.document}
-                        DataDocument={this.props.dataDoc}
+                        DataDocument={this.dataDoc}
                         width={docWidth}
                         height={this.props.panelHeight}
                         getTransform={this.docTransform}
@@ -400,7 +381,7 @@ class TreeView extends React.Component<TreeViewProps> {
         active: () => boolean,
         panelWidth: () => number,
     ) {
-        let docList = docs.filter(child => !child.excludeFromLibrary && (key !== this.fieldKey || !child.isMinimized));
+        let docList = docs.filter(child => !child.excludeFromLibrary);
         let rowWidth = () => panelWidth() - 20;
         return docList.map((child, i) => {
             let indent = i === 0 ? undefined : () => {
@@ -475,6 +456,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
 
     outerXf = () => Utils.GetScreenTransform(this._mainEle!);
     onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {});
+    @computed get dataDoc() { return (BoolCast(this.props.DataDoc.isTemplate) ? this.props.DataDoc : this.props.Document); }
 
     render() {
         let dropAction = StrCast(this.props.Document.dropAction) as dropActionType;
@@ -502,7 +484,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
                     }} />
                 <ul className="no-indent" style={{ width: "max-content" }} >
                     {
-                        TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, addDoc, this.remove,
+                        TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.dataDoc, this.props.fieldKey, addDoc, this.remove,
                             moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth)
                     }
                 </ul>
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 872cb3f1c..cc097f371 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -2,8 +2,10 @@ import { library } from '@fortawesome/fontawesome-svg-core';
 import { faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons';
 import { observer } from "mobx-react";
 import * as React from 'react';
+import { Doc } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
+import { Docs } from '../../documents/Documents';
 import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from '../ContextMenuItem';
@@ -14,11 +16,6 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
 import { CollectionSchemaView } from "./CollectionSchemaView";
 import { CollectionStackingView } from './CollectionStackingView';
 import { CollectionTreeView } from "./CollectionTreeView";
-import { Doc } from '../../../new_fields/Doc';
-import { FormattedTextBox } from '../nodes/FormattedTextBox';
-import { Docs } from '../../documents/Documents';
-import { List } from '../../../new_fields/List';
-import { ImageField } from '../../../new_fields/URLField';
 export const COLLECTION_BORDER_WIDTH = 2;
 
 library.add(faTh);
@@ -62,17 +59,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
             ContextMenu.Instance.addItem({
                 description: "Apply Template", event: undoBatch(() => {
                     let otherdoc = Docs.TextDocument({ width: 100, height: 50, title: "applied template" });
-                    Doc.GetProto(otherdoc).description = "THIS DESCRIPTION IS REALLY IMPORTANT!";
-                    Doc.GetProto(otherdoc).summary = "THIS SUMMARY IS MEANINGFUL!";
-                    Doc.GetProto(otherdoc).photo = new ImageField("http://www.cs.brown.edu/~bcz/snowbeast.JPG");
                     Doc.GetProto(otherdoc).layout = Doc.MakeDelegate(this.props.Document);
-                    Doc.GetProto(otherdoc).publication = new List<Doc>([
-                        Docs.TextDocument({ documentText: "hello world!", width: 300, height: 300 }),
-                        Docs.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { width: 300, height: 300 }),
-                        Docs.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { width: 300, height: 300 }),
-                        Docs.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { width: 300, height: 300 }),
-                        Docs.TextDocument({ documentText: "hello world!", width: 300, height: 300 }),
-                    ]);
                     this.props.addDocTab && this.props.addDocTab(otherdoc, otherdoc, "onRight");
                 }), icon: "project-diagram"
             });
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 71964ef82..c6f003a81 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -370,7 +370,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     private childViews = () => [
-        <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />,
+        <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} DataDoc={this.props.DataDoc} />,
         ...this.views
     ]
     render() {
@@ -387,7 +387,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                         easing={easing} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
 
                         <CollectionFreeFormLinksView {...this.props} key="freeformLinks">
-                            <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
+                            <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.DataDoc} inkFieldKey={this.props.fieldKey + "_ink"} >
                                 {this.childViews}
                             </InkingCanvas>
                         </CollectionFreeFormLinksView>
@@ -414,6 +414,7 @@ class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps &
 @observer
 class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps & { isSelected: () => boolean }> {
     @computed get backgroundView() {
+        let props = this.props;
         return (<DocumentContentsView {...this.props} layoutKey={"backgroundLayout"}
             isTopMost={this.props.isTopMost} isSelected={this.props.isSelected} select={emptyFunction} />);
     }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 7000352e7..6adead626 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -203,6 +203,7 @@ class Viewer extends React.Component<IViewerProps> {
             this._isPage[page] = "page";
             this._visibleElements[page] = (
                 <Page
+                    size={this._pageSizes[page]}
                     pdf={this.props.pdf}
                     page={page}
                     numPages={this.props.pdf.numPages}
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 27b3473f8..0a1964dea 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -215,7 +215,7 @@ export namespace Doc {
 
     // gets the document's prototype or returns the document if it is a prototype
     export function GetProto(doc: Doc) {
-        return Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc);
+        return Doc.GetT(doc, "isPrototype", "boolean", true) || doc.isTemplate ? doc : (doc.proto || doc);
     }
 
     export function allKeys(doc: Doc): string[] {
-- 
cgit v1.2.3-70-g09d2


From a5955ff8e8da67c9617f724f8df258b88d63b49e Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 24 Jun 2019 13:43:32 -0400
Subject: fixed pdfs for templates.

---
 src/client/views/DocumentDecorations.tsx |  2 +-
 src/client/views/nodes/PDFBox.scss       | 11 +++++++----
 src/client/views/nodes/PDFBox.tsx        | 28 +++++++++++++++-------------
 src/client/views/pdf/PDFViewer.tsx       | 14 +++++++++-----
 4 files changed, 32 insertions(+), 23 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9be5c9cd6..2667b7632 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -77,7 +77,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 this._fieldKey = text.slice(1, text.length);
                 this._title = this.selectionTitle;
             } else if (text.startsWith(">")) {
-                let metaKey = text.slice(text.startsWith(">>>") ? 3 : text.startsWith(">>") ? 2 : 1, text.length);
+                let metaKey = text.slice(1, text.length - 1);
                 let field = SelectionManager.SelectedDocuments()[0];
                 let collectionKey = field.props.ContainingCollectionView!.props.fieldKey;
                 let collection = field.props.ContainingCollectionView!.props.Document;
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 8bcae4f1e..67f0b817c 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -32,10 +32,15 @@
     height: 100px;
 }
 
-.pdfBox-cont {
-    pointer-events: none;
+.pdfBox-cont, .pdfBox-cont-interactive {
     display: flex;
     flex-direction: row;
+    height: 100%;
+    overflow-y: scroll;
+    overflow-x: hidden;
+}
+.pdfBox-cont {
+    pointer-events: none;
     .textlayer {
         pointer-events: none;
         span {
@@ -49,8 +54,6 @@
 
 .pdfBox-cont-interactive {
     pointer-events: all;
-    display: flex;
-    flex-direction: row;
     .textlayer {
         span {
             pointer-events: all !important;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index d2de1cb1c..61789bb30 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,9 +1,9 @@
-import { action, IReactionDisposer, observable, reaction, trace, untracked } from 'mobx';
+import { action, IReactionDisposer, observable, reaction, trace, untracked, computed } from 'mobx';
 import { observer } from "mobx-react";
 import 'react-image-lightbox/style.css';
 import { WidthSym } from "../../../new_fields/Doc";
 import { makeInterface } from "../../../new_fields/Schema";
-import { Cast, NumCast } from "../../../new_fields/Types";
+import { Cast, NumCast, BoolCast } from "../../../new_fields/Types";
 import { PdfField } from "../../../new_fields/URLField";
 //@ts-ignore
 // import { Document, Page } from "react-pdf";
@@ -27,6 +27,8 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
 
     @observable private _alt = false;
     @observable private _scrollY: number = 0;
+    @computed get dataDoc() { return this.props.DataDoc && BoolCast(this.props.Document.isTemplate) ? this.props.DataDoc : this.props.Document; }
+
     private _reactionDisposer?: IReactionDisposer;
 
     componentDidMount() {
@@ -34,20 +36,20 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     }
 
     public GetPage() {
-        return Math.floor(NumCast(this.props.Document.scrollY) / NumCast(this.Document.pdfHeight)) + 1;
+        return Math.floor(NumCast(this.props.Document.scrollY) / NumCast(this.dataDoc.pdfHeight)) + 1;
     }
     public BackPage() {
-        let cp = Math.ceil(NumCast(this.props.Document.scrollY) / NumCast(this.Document.pdfHeight)) + 1;
+        let cp = Math.ceil(NumCast(this.props.Document.scrollY) / NumCast(this.dataDoc.pdfHeight)) + 1;
         cp = cp - 1;
         if (cp > 0) {
             this.props.Document.curPage = cp;
-            this.props.Document.scrollY = (cp - 1) * NumCast(this.Document.pdfHeight);
+            this.props.Document.scrollY = (cp - 1) * NumCast(this.dataDoc.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);
+            this.props.Document.scrollY = (p - 1) * NumCast(this.dataDoc.pdfHeight);
         }
     }
 
@@ -55,7 +57,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
         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);
+            this.props.Document.scrollY = (cp - 1) * NumCast(this.dataDoc.pdfHeight);
         }
     }
 
@@ -68,7 +70,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
 
     loaded = (nw: number, nh: number, np: number) => {
         if (this.props.Document) {
-            let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
+            let doc = this.dataDoc;
             doc.numPages = np;
             if (doc.nativeWidth && doc.nativeHeight) return;
             let oldaspect = NumCast(doc.nativeHeight) / NumCast(doc.nativeWidth, 1);
@@ -96,19 +98,19 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
 
     render() {
         // uses mozilla pdf as default
-        const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"));
+        const pdfUrl = Cast(this.props.Document.data, PdfField);
+        if (!(pdfUrl instanceof PdfField)) return <div>{`pdf, ${this.props.Document.data}, not found`}</div>;
         let classname = "pdfBox-cont" + (this.props.active() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
         return (
-            <div onScroll={this.onScroll}
+            <div className={classname}
+                onScroll={this.onScroll}
                 style={{
-                    height: "100%",
-                    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.pathname} loaded={this.loaded} scrollY={this._scrollY} parent={this} />
                 {/* <div style={{ width: "100px", height: "300px" }}></div> */}
             </div>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 6adead626..b9e93b4da 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -229,11 +229,15 @@ class Viewer extends React.Component<IViewerProps> {
         let handleError = () => this.getRenderedPage(page);
         if (this._isPage[page] !== "image") {
             this._isPage[page] = "image";
-            const address = this.props.url;
-            let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`)));
-            runInAction(() => this._visibleElements[page] =
-                <img key={res.path} src={res.path} onError={handleError}
-                    style={{ width: `${parseInt(res.width) * scale}px`, height: `${parseInt(res.height) * scale}px` }} />);
+            const address = this.props.url
+            try {
+                let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`)));
+                runInAction(() => this._visibleElements[page] =
+                    <img key={res.path} src={res.path} onError={handleError}
+                        style={{ width: `${parseInt(res.width) * scale}px`, height: `${parseInt(res.height) * scale}px` }} />);
+            } catch (e) {
+
+            }
         }
     }
 
-- 
cgit v1.2.3-70-g09d2


From 65288e33b49404d21012323fcb53fefb3f646fbf Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Mon, 24 Jun 2019 13:57:59 -0400
Subject: basic viewspecs

---
 src/client/views/MainView.tsx      |  3 +-
 src/client/views/nodes/PDFBox.scss | 68 +++++++++++++++++++++++++++++++
 src/client/views/nodes/PDFBox.tsx  | 83 +++++++++++++++++++++++++++++++++++++-
 src/client/views/pdf/PDFViewer.tsx | 36 +++++++++++++----
 4 files changed, 180 insertions(+), 10 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 2645e2789..08755b427 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, faThumbtack, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt, faCut } from '@fortawesome/free-solid-svg-icons';
+import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faCheck, faPenNib, faThumbtack, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt, faCut } 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';
@@ -94,6 +94,7 @@ export class MainView extends React.Component {
         library.add(faCut);
         library.add(faCommentAlt);
         library.add(faThumbtack);
+        library.add(faCheck);
         this.initEventListeners();
         this.initAuthenticationRouters();
     }
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 8bcae4f1e..5edff69f3 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -36,12 +36,15 @@
     pointer-events: none;
     display: flex;
     flex-direction: row;
+
     .textlayer {
         pointer-events: none;
+
         span {
             pointer-events: none !important;
         }
     }
+
     .page-cont {
         pointer-events: none;
     }
@@ -51,6 +54,7 @@
     pointer-events: all;
     display: flex;
     flex-direction: row;
+
     .textlayer {
         span {
             pointer-events: all !important;
@@ -62,4 +66,68 @@
 .pdfBox-contentContainer {
     position: absolute;
     transform-origin: left top;
+}
+
+.pdfBox-settingsCont {
+    position: absolute;
+    right: 0;
+    top: 0;
+
+    .pdfBox-settingsButton {
+        border-bottom-left-radius: 50%;
+        display: flex;
+        justify-content: space-evenly;
+        align-items: center;
+        height: 70px;
+        background: none;
+        padding: 0;
+
+        .pdfBox-settingsButton-arrow {
+            width: 0;
+            height: 0;
+            border-top: 25px solid transparent;
+            border-bottom: 25px solid transparent;
+            border-right: 25px solid #121721;
+            transition: all 0.5s;
+        }
+
+        .pdfBox-settingsButton-iconCont {
+            background: #121721;
+            height: 50px;
+            width: 70px;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            margin-left: -2px;
+            border-radius: 3px;
+        }
+    }
+
+    .pdfBox-settingsButton:hover {
+        background: none;
+    }
+
+    .pdfBox-settingsFlyout {
+        width: 600px;
+        position: absolute;
+        background: #323232;
+        box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
+        left: -400px;
+        border-radius: 7px;
+        padding: 20px;
+        display: flex;
+        flex-direction: column;
+        font-size: 30px;
+        transition: all 0.5s;
+
+        .pdfBox-settingsFlyout-title {
+            color: white;
+        }
+
+        .pdfBox-settingsFlyout-kvpInput {
+            margin-top: 20px;
+            display: grid;
+            grid-template-columns: 47.5% 5% 47.5%;
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 10a346269..aa421ff9c 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -19,6 +19,8 @@ import "./PDFBox.scss";
 import React = require("react");
 import { CompileScript } from '../../util/Scripting';
 import { ScriptField } from '../../../fields/ScriptField';
+import { Flyout, anchorPoints } from '../DocumentDecorations';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 
 type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
 const PdfDocument = makeInterface(positionSchema, pageSchema);
@@ -29,8 +31,12 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
 
     @observable private _alt = false;
     @observable private _scrollY: number = 0;
+    @observable private _flyout: boolean = false;
     private _mainCont: React.RefObject<HTMLDivElement>;
     private _reactionDisposer?: IReactionDisposer;
+    private _keyValue: string = "";
+    private _valueValue: string = "";
+    private _scriptValue: string = "";
 
     constructor(props: FieldViewProps) {
         super(props);
@@ -45,7 +51,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
             }
         );
 
-        let script = CompileScript("return this.page === 2", { params: { this: Doc.name } });
+        let script = CompileScript("return this.page === 0", { params: { this: Doc.name } });
         if (script.compiled) {
             this.props.Document.filterScript = new ScriptField(script);
         }
@@ -55,6 +61,10 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
         if (this.props.setPdfBox) this.props.setPdfBox(this);
     }
 
+    componentWillUnmount() {
+        this._reactionDisposer && this._reactionDisposer();
+    }
+
     public GetPage() {
         return Math.floor(NumCast(this.props.Document.scrollY) / NumCast(this.Document.pdfHeight)) + 1;
     }
@@ -81,7 +91,75 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
         }
     }
 
-    createRef = (ele: HTMLDivElement | null) => {
+    private newKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this._keyValue = e.currentTarget.value;
+    }
+
+    private newValueChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this._valueValue = e.currentTarget.value;
+    }
+
+    private newScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this._scriptValue = e.currentTarget.value;
+    }
+
+    private applyFilter = (e: React.MouseEvent<HTMLButtonElement>) => {
+        let scriptText = "";
+        if (this._scriptValue.length > 0) {
+            scriptText = this._scriptValue;
+        } else if (this._keyValue.length > 0 && this._valueValue.length > 0) {
+            scriptText = `return this.${this._keyValue} === ${this._valueValue}`;
+        }
+        let script = CompileScript(scriptText, { params: { this: Doc.name } });
+        if (script.compiled) {
+            this.props.Document.filterScript = new ScriptField(script);
+        }
+    }
+
+    @action
+    private toggleFlyout = () => {
+        this._flyout = !this._flyout;
+    }
+
+    settingsPanel() {
+        return !this.props.active() ? (null) :
+            (
+                <div className="pdfBox-settingsCont" onPointerDown={(e) => e.stopPropagation()}>
+                    <button className="pdfBox-settingsButton" onClick={this.toggleFlyout}
+                        style={{ marginTop: `${NumCast(this.props.ContainingCollectionView!.props.Document.panY)}px` }}>
+                        <div className="pdfBox-settingsButton-arrow"
+                            style={{
+                                borderTop: `25px solid ${this._flyout ? "#121721" : "transparent"}`,
+                                borderBottom: `25px solid ${this._flyout ? "#121721" : "transparent"}`,
+                                borderRight: `25px solid ${this._flyout ? "transparent" : "#121721"}`,
+                                transform: `scaleX(${this._flyout ? -1 : 1})`
+                            }}></div>
+                        <div className="pdfBox-settingsButton-iconCont">
+                            <FontAwesomeIcon style={{ color: "white" }} icon="cog" size="3x" />
+                        </div>
+                    </button>
+                    <div className="pdfBox-settingsFlyout" style={{ left: `${this._flyout ? -600 : 100}px` }} >
+                        <div className="pdfBox-settingsFlyout-title">
+                            Annotation View Settings
+                        </div>
+                        <div className="pdfBox-settingsFlyout-kvpInput">
+                            <input placeholder="Key" className="pdfBox-settingsFlyout-input" onChange={this.newKeyChange}
+                                style={{ gridColumn: 1 }} />
+                            <input placeholder="Value" className="pdfBox-settingsFlyout-input" onChange={this.newValueChange}
+                                style={{ gridColumn: 3 }} />
+                        </div>
+                        <div className="pdfBox-settingsFlyout-kvpInput">
+                            <input placeholder="Custom Script" onChange={this.newScriptChange} style={{ gridColumn: "1 / 4" }} />
+                        </div>
+                        <div className="pdfBox-settingsFlyout-kvpInput">
+                            <button style={{ gridColumn: 3 }} onClick={this.applyFilter}>
+                                <FontAwesomeIcon style={{ color: "white" }} icon="check" size="lg" />
+                                &nbsp; Apply
+                            </button>
+                        </div>
+                    </div>
+                </div>
+            );
     }
 
     loaded = (nw: number, nh: number, np: number) => {
@@ -129,6 +207,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
                 }} className={classname}>
                 <PDFViewer url={pdfUrl.url.pathname} loaded={this.loaded} scrollY={this._scrollY} parent={this} />
                 {/* <div style={{ width: "100px", height: "300px" }}></div> */}
+                {this.settingsPanel()}
             </div>
         );
     }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 75a8b042d..1fb208525 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -21,7 +21,7 @@ import React = require("react");
 import PDFMenu from "./PDFMenu";
 import { UndoManager } from "../../util/UndoManager";
 import { ScriptField } from "../../../fields/ScriptField";
-import { CompileScript, CompiledScript } from "../../util/Scripting";
+import { CompileScript, CompiledScript, CompileResult } from "../../util/Scripting";
 
 export const scale = 2;
 interface IPDFViewerProps {
@@ -77,7 +77,7 @@ class Viewer extends React.Component<IViewerProps> {
     @observable private _pageSizes: { width: number, height: number }[] = [];
     @observable private _annotations: Doc[] = [];
     @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
-    @observable private _script: ScriptField | undefined = this.props.parent.Document.filterScript;
+    @observable private _script: CompileResult | undefined;
 
     private _pageBuffer: number = 1;
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
@@ -86,6 +86,14 @@ class Viewer extends React.Component<IViewerProps> {
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _filterReactionDisposer?: IReactionDisposer;
 
+    @action
+        constructor(props: IViewerProps) {
+            super(props);
+
+            let scriptfield = Cast(this.props.parent.Document.filterScript, ScriptField);
+            this._script = scriptfield ? CompileScript(scriptfield.scriptString, { params: { this: Doc.name } }) : CompileScript("return true");;
+        }
+
     componentDidUpdate = (prevProps: IViewerProps) => {
         if (this.scrollY !== prevProps.scrollY) {
             this.renderPages();
@@ -112,7 +120,10 @@ class Viewer extends React.Component<IViewerProps> {
             this._filterReactionDisposer = reaction(
                 () => this.props.parent.Document.filterScript || this.props.parent.props.ContainingCollectionView!.props.Document.filterScript,
                 () => {
-                    this._script = Cast(this.props.parent.Document.filterScript, ScriptField);
+                    runInAction(() => {
+                        let scriptfield = Cast(this.props.parent.Document.filterScript, ScriptField);
+                        this._script = scriptfield ? CompileScript(scriptfield.scriptString, { params: { this: Doc.name } }) : CompileScript("return true");;
+                    });
                 }
             );
         }
@@ -121,6 +132,7 @@ class Viewer extends React.Component<IViewerProps> {
     componentWillUnmount = () => {
         this._reactionDisposer && this._reactionDisposer();
         this._annotationReactionDisposer && this._annotationReactionDisposer();
+        this._filterReactionDisposer && this._filterReactionDisposer();
     }
 
     @action
@@ -159,10 +171,12 @@ class Viewer extends React.Component<IViewerProps> {
 
     makeAnnotationDocument = (sourceDoc: Doc | undefined, s: number, color: string): Doc => {
         let annoDocs: Doc[] = [];
-        let mainAnnoDoc = new Doc();
+        let mainAnnoDoc = Docs.CreateInstance(new Doc(), "", {});
+
+        mainAnnoDoc.page = Math.round(Math.random());
         this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => {
             for (let anno of value) {
-                let annoDoc = Docs.CreateInstance(new Doc(), this.props.parent.Document, {});
+                let annoDoc = new Doc();
                 if (anno.style.left) annoDoc.x = parseInt(anno.style.left) / scale;
                 if (anno.style.top) annoDoc.y = parseInt(anno.style.top) / scale;
                 if (anno.style.height) annoDoc.height = parseInt(anno.style.height) / scale;
@@ -360,7 +374,7 @@ class Viewer extends React.Component<IViewerProps> {
     }
 
     render() {
-        let compiled = this._script ? CompileScript(this._script.scriptString, { params: { this: Doc.name } }) : CompileScript("return true");
+        let compiled = this._script;
         return (
             <div ref={this.mainCont} style={{ pointerEvents: "all" }}>
                 <div className="viewer">
@@ -372,7 +386,15 @@ class Viewer extends React.Component<IViewerProps> {
                         pointerEvents: this.props.parent.props.active() ? "none" : "all"
                     }}>
                     <div className="pdfViewer-annotationLayer-subCont" ref={this._annotationLayer}>
-                        {this._annotations.filter(anno => compiled.compiled ? compiled.run(anno) : true).map(anno => this.renderAnnotation(anno))}
+                        {this._annotations.filter(anno => {
+                            if (compiled && compiled.compiled) {
+                                let run = compiled.run({ this: anno });
+                                if (run.success) {
+                                    return run.result;
+                                }
+                            }
+                            return true;
+                        }).map(anno => this.renderAnnotation(anno))}
                     </div>
                 </div>
             </div >
-- 
cgit v1.2.3-70-g09d2


From 45ded4da2592bc2a8201e1260dfb6a80485684c7 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 25 Jun 2019 09:35:09 -0400
Subject: converted isTopMost into renderDepth and capped renderDepth at 7. 
 removed previous cycle detection.

---
 src/client/documents/Documents.ts                  |  6 +--
 src/client/util/DragManager.ts                     |  2 -
 src/client/views/DocumentDecorations.tsx           |  2 +-
 src/client/views/MainOverlayTextBox.tsx            |  2 +-
 src/client/views/MainView.tsx                      |  2 +-
 .../views/collections/CollectionBaseView.tsx       | 48 ++++------------------
 .../views/collections/CollectionDockingView.tsx    |  2 +-
 .../views/collections/CollectionSchemaView.tsx     |  4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  8 ++--
 src/client/views/nodes/DocumentContentsView.tsx    |  1 +
 src/client/views/nodes/DocumentView.tsx            | 11 ++---
 src/client/views/nodes/FieldView.tsx               |  4 +-
 src/client/views/nodes/KeyValuePair.tsx            |  2 +-
 src/client/views/pdf/PDFViewer.tsx                 |  2 +-
 14 files changed, 31 insertions(+), 65 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index b04fc401a..ac8a892b8 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -344,7 +344,7 @@ export namespace Docs {
         {layout}
     </div>
     <div style="height:(100% + 25px); width:100%; position:absolute">
-        <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"caption"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+        <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"caption"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} renderDepth={renderDepth}/>
     </div>
 </div>       
         `);
@@ -356,7 +356,7 @@ export namespace Docs {
             {layout}
         </div>
         <div style="height:25px; width:100%; position:absolute">
-            <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"caption"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+            <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"caption"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} renderDepth={renderDepth}/>
         </div>
     </div>       
             `);
@@ -379,7 +379,7 @@ export namespace Docs {
             {layout}
         </div>
         <div style="height:15%; width:100%; position:absolute">
-            <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"caption"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+            <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"caption"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} renderDepth={renderDepth}/>
         </div>
     </div>       
             `);
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 3143924fc..442187912 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -28,7 +28,6 @@ export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: ()
         document.removeEventListener('pointerup', onRowUp);
     };
     let onItemDown = async (e: React.PointerEvent) => {
-        // if (this.props.isSelected() || this.props.isTopMost) {
         if (e.button === 0) {
             e.stopPropagation();
             if (e.shiftKey && CollectionDockingView.Instance) {
@@ -38,7 +37,6 @@ export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: ()
                 document.addEventListener("pointerup", onRowUp);
             }
         }
-        //}
     };
     return onItemDown;
 }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9d5844426..042306997 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -153,7 +153,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @computed
     get Bounds(): { x: number, y: number, b: number, r: number } {
         return SelectionManager.SelectedDocuments().reduce((bounds, documentView) => {
-            if (documentView.props.isTopMost) {
+            if (documentView.props.renderDepth === 0) {
                 return bounds;
             }
             let transform = (documentView.props.ScreenToLocalTransform().scale(documentView.props.ContentScaling())).inverse();
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index b2cfc82c4..7363b08ef 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -123,7 +123,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
                         <FormattedTextBox color={`${this._textColor}`} fieldKey={this.TextFieldKey} hideOnLeave={this._textHideOnLeave} isOverlay={true}
                             Document={FormattedTextBox.InputBoxOverlay.props.Document}
                             DataDoc={FormattedTextBox.InputBoxOverlay.props.DataDoc}
-                            isSelected={returnTrue} select={emptyFunction} isTopMost={true} selectOnLoad={true}
+                            isSelected={returnTrue} select={emptyFunction} renderDepth={0} selectOnLoad={true}
                             ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue}
                             ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} addDocTab={this.addDocTab} outer_div={(dominus: HTMLElement) => this._dominus = dominus} />
                     </div>
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index a72f25b99..d26e24748 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -211,7 +211,7 @@ export class MainView extends React.Component {
                 ContentScaling={returnOne}
                 PanelWidth={this.getPWidth}
                 PanelHeight={this.getPHeight}
-                isTopMost={true}
+                renderDepth={0}
                 selectOnLoad={false}
                 focus={emptyFunction}
                 parentActive={returnTrue}
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 79a9f3be0..50d1a5071 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -64,8 +64,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
 
     active = (): boolean => {
         var isSelected = this.props.isSelected();
-        var topMost = this.props.isTopMost;
-        return isSelected || this._isChildActive || topMost;
+        return isSelected || this._isChildActive || this.props.renderDepth === 0;
     }
 
     //TODO should this be observable?
@@ -75,32 +74,6 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         this.props.whenActiveChanged(isActive);
     }
 
-    createsCycle(documentToAdd: Doc, containerDocument: Doc): boolean {
-        if (!(documentToAdd instanceof Doc)) {
-            return false;
-        }
-        if (StrCast(documentToAdd.layout).indexOf("CollectionView") !== -1) {
-            let data = DocListCast(documentToAdd.data);
-            for (const doc of data) {
-                if (this.createsCycle(doc, containerDocument)) {
-                    return true;
-                }
-            }
-        }
-        let annots = DocListCast(documentToAdd.annotations);
-        for (const annot of annots) {
-            if (this.createsCycle(annot, containerDocument)) {
-                return true;
-            }
-        }
-        for (let containerProto: Opt<Doc> = containerDocument; containerProto !== undefined; containerProto = FieldValue(containerProto.proto)) {
-            if (containerProto[Id] === documentToAdd[Id]) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     @action.bound
     addDocument(doc: Doc, allowDuplicates: boolean = false): boolean {
         var curPage = NumCast(this.props.Document.curPage, -1);
@@ -109,19 +82,16 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
             Doc.GetProto(doc).annotationOn = this.props.Document;
         }
         allowDuplicates = true;
-        if (!this.createsCycle(doc, this.dataDoc)) {
-            //TODO This won't create the field if it doesn't already exist
-            const value = Cast(this.dataDoc[this.props.fieldKey], listSpec(Doc));
-            if (value !== undefined) {
-                if (allowDuplicates || !value.some(v => v instanceof Doc && v[Id] === doc[Id])) {
-                    value.push(doc);
-                }
-            } else {
-                Doc.SetOnPrototype(this.dataDoc, this.props.fieldKey, new List([doc]));
+        //TODO This won't create the field if it doesn't already exist
+        const value = Cast(this.dataDoc[this.props.fieldKey], listSpec(Doc));
+        if (value !== undefined) {
+            if (allowDuplicates || !value.some(v => v instanceof Doc && v[Id] === doc[Id])) {
+                value.push(doc);
             }
-            return true;
+        } else {
+            Doc.SetOnPrototype(this.dataDoc, this.props.fieldKey, new List([doc]));
         }
-        return false;
+        return true;
     }
 
     @action.bound
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 82cb5dbbb..6d2f61173 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -535,7 +535,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                     PanelWidth={this.nativeWidth}
                     PanelHeight={this.nativeHeight}
                     ScreenToLocalTransform={this.ScreenToLocalTransform}
-                    isTopMost={true}
+                    renderDepth={0}
                     selectOnLoad={false}
                     parentActive={returnTrue}
                     whenActiveChanged={emptyFunction}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index b4158a5b1..f03afe7e0 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -104,7 +104,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             ContainingCollectionView: this.props.CollectionView,
             isSelected: returnFalse,
             select: emptyFunction,
-            isTopMost: false,
+            renderDepth: this.props.renderDepth + 1,
             selectOnLoad: false,
             ScreenToLocalTransform: Transform.Identity,
             focus: emptyFunction,
@@ -454,7 +454,7 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
                 <div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.centeringOffset}px, 0px)`, height: "100%" }}>
                     <DocumentView
                         DataDoc={this.props.DataDocument}
-                        Document={this.props.Document} isTopMost={false} selectOnLoad={false}
+                        Document={this.props.Document} renderDepth={0} selectOnLoad={false}
                         addDocument={this.props.addDocument} removeDocument={this.props.removeDocument} moveDocument={this.props.moveDocument}
                         ScreenToLocalTransform={this.getTransform}
                         ContentScaling={this.contentScaling}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c6f003a81..05909c9f7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -199,7 +199,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             var dv = DocumentManager.Instance.getDocumentView(doc);
             return dv && SelectionManager.IsSelected(dv) ? true : false;
         });
-        if (!this.props.isSelected() && !childSelected && !this.props.isTopMost) {
+        if (!this.props.isSelected() && !childSelected && this.props.renderDepth > 0) {
             return;
         }
         e.stopPropagation();
@@ -303,7 +303,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             removeDocument: this.props.removeDocument,
             moveDocument: this.props.moveDocument,
             ScreenToLocalTransform: this.getTransform,
-            isTopMost: false,
+            renderDepth: this.props.renderDepth + 1,
             selectOnLoad: layoutDoc[Id] === this._selectOnLoaded,
             PanelWidth: layoutDoc[WidthSym],
             PanelHeight: layoutDoc[HeightSym],
@@ -404,7 +404,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps & { isSelected: () => boolean }> {
     @computed get overlayView() {
         return (<DocumentContentsView {...this.props} layoutKey={"overlayLayout"}
-            isTopMost={this.props.isTopMost} isSelected={this.props.isSelected} select={emptyFunction} />);
+            renderDepth={this.props.renderDepth} isSelected={this.props.isSelected} select={emptyFunction} />);
     }
     render() {
         return this.overlayView;
@@ -416,7 +416,7 @@ class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps
     @computed get backgroundView() {
         let props = this.props;
         return (<DocumentContentsView {...this.props} layoutKey={"backgroundLayout"}
-            isTopMost={this.props.isTopMost} isSelected={this.props.isSelected} select={emptyFunction} />);
+            renderDepth={this.props.renderDepth} isSelected={this.props.isSelected} select={emptyFunction} />);
     }
     render() {
         return this.props.Document.backgroundLayout ? this.backgroundView : (null);
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index a4316c143..0da4888a1 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -103,6 +103,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     }
 
     render() {
+        if (this.props.renderDepth > 7) return (null);
         if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
         return <ObserverJsxParser
             components={{ FormattedTextBox, ImageBox, IconBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5530d5c01..c586885a3 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -72,7 +72,7 @@ export interface DocumentViewProps {
     removeDocument?: (doc: Doc) => boolean;
     moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
     ScreenToLocalTransform: () => Transform;
-    isTopMost: boolean;
+    renderDepth: number;
     ContentScaling: () => number;
     PanelWidth: () => number;
     PanelHeight: () => number;
@@ -120,7 +120,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     public get ContentDiv() { return this._mainCont.current; }
     @computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
-    @computed get topMost(): boolean { return this.props.isTopMost; }
+    @computed get topMost(): boolean { return this.props.renderDepth === 0; }
     @computed get templates(): List<string> {
         let field = this.props.Document.templates;
         if (field && field instanceof List) {
@@ -268,7 +268,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         e.stopPropagation();
         let altKey = e.altKey;
         let ctrlKey = e.ctrlKey;
-        if (this._doubleTap && !this.props.isTopMost) {
+        if (this._doubleTap && !this.props.renderDepth) {
             this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab");
             SelectionManager.DeselectAll();
             this.props.Document.libraryBrush = false;
@@ -436,7 +436,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @action
     addTemplate = (template: Template) => {
         this.templates.push(template.Layout);
-        this.templates = this.templates;
     }
 
     @action
@@ -447,7 +446,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 break;
             }
         }
-        this.templates = this.templates;
     }
 
     freezeNativeDimensions = (): void => {
@@ -549,7 +547,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%";
         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" : ""}`}
+            <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
                 style={{
                     outlineColor: "maroon",
@@ -565,7 +563,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     transform: `scale(${scaling}, ${scaling})`
                 }}
                 onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
-
                 onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}
             >
                 {this.contents}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 8879da55f..374a523cb 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -33,7 +33,7 @@ export interface FieldViewProps {
     DataDoc: Doc;
     isSelected: () => boolean;
     select: (isCtrlPressed: boolean) => void;
-    isTopMost: boolean;
+    renderDepth: number;
     selectOnLoad: boolean;
     addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean;
     addDocTab: (document: Doc, dataDoc: Doc, where: string) => void;
@@ -97,7 +97,7 @@ export class FieldView extends React.Component<FieldViewProps> {
             //         ContentScaling={returnOne}
             //         PanelWidth={returnHundred}
             //         PanelHeight={returnHundred}
-            //         isTopMost={true} //TODO Why is this top most?
+            //         renderDepth={0} //TODO Why is renderDepth reset?
             //         selectOnLoad={false}
             //         focus={emptyFunction}
             //         isSelected={this.props.isSelected}
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 3e03e7e75..3dda81db7 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -32,7 +32,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             fieldKey: this.props.keyName,
             isSelected: returnFalse,
             select: emptyFunction,
-            isTopMost: false,
+            renderDepth: 1,
             selectOnLoad: false,
             active: returnFalse,
             whenActiveChanged: emptyFunction,
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index b9e93b4da..10c66d318 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -492,7 +492,7 @@ interface IAnnotationProps {
 //                         <DocumentView Document={targetDoc}
 //                             ContainingCollectionView={undefined}
 //                             ScreenToLocalTransform={this.props.parent.props.parent.props.ScreenToLocalTransform}
-//                             isTopMost={false}
+//                             renderDepth={1} // what should renderDepth be?
 //                             ContentScaling={() => 1}
 //                             PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)}
 //                             PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)}
-- 
cgit v1.2.3-70-g09d2


From e910a6cd56936234e451994c893d8592e430f828 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Tue, 25 Jun 2019 13:54:20 -0400
Subject: pdf view specs fixes/changes

---
 src/client/views/collections/CollectionPDFView.tsx |  4 ++
 src/client/views/nodes/DocumentView.tsx            | 13 +++-
 src/client/views/nodes/PDFBox.tsx                  | 44 ++++++++++---
 src/client/views/pdf/PDFMenu.scss                  |  7 ++
 src/client/views/pdf/PDFMenu.tsx                   | 55 +++++++++++-----
 src/client/views/pdf/PDFViewer.tsx                 | 75 ++++++++++++++++------
 6 files changed, 152 insertions(+), 46 deletions(-)

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

diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index b2d016934..31a73ab36 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -43,6 +43,10 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
         );
     }
 
+    componentWillUnmount() {
+        this._reactionDisposer && this._reactionDisposer();
+    }
+
     public static LayoutString(fieldKey: string = "data") {
         return FieldView.LayoutString(CollectionPDFView, fieldKey);
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 522c37989..f5a3f4d5d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -117,6 +117,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     private _mainCont = React.createRef<HTMLDivElement>();
     private _dropDisposer?: DragManager.DragDropDisposer;
 
+    @observable private _opacity: number = this.Document.opacity ? NumCast(this.Document.opacity) : 1;
+
     public get ContentDiv() { return this._mainCont.current; }
     @computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
     @computed get topMost(): boolean { return this.props.isTopMost; }
@@ -136,6 +138,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     _animateToIconDisposer?: IReactionDisposer;
     _reactionDisposer?: IReactionDisposer;
+    _opacityDisposer?: IReactionDisposer;
     @action
     componentDidMount() {
         if (this._mainCont.current) {
@@ -160,6 +163,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             (values instanceof List) && this.animateBetweenIcon(values, values[2], values[3] ? true : false)
             , { fireImmediately: true });
         DocumentManager.Instance.DocumentViews.push(this);
+        this._opacityDisposer = reaction(
+            () => NumCast(this.props.Document.opacity),
+            () => {
+                runInAction(() => this._opacity = NumCast(this.props.Document.opacity));
+            }
+        );
     }
 
     animateBetweenIcon = (iconPos: number[], startTime: number, maximizing: boolean) => {
@@ -201,6 +210,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (this._reactionDisposer) this._reactionDisposer();
         if (this._animateToIconDisposer) this._animateToIconDisposer();
         if (this._dropDisposer) this._dropDisposer();
+        if (this._opacityDisposer) this._opacityDisposer();
         DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
     }
 
@@ -559,7 +569,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     background: this.Document.backgroundColor || "",
                     width: nativeWidth,
                     height: nativeHeight,
-                    transform: `scale(${scaling}, ${scaling})`
+                    transform: `scale(${scaling}, ${scaling})`,
+                    opacity: this._opacity
                 }}
                 onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
 
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index a129e89b9..c0f2d313a 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -18,9 +18,9 @@ import { pageSchema } from "./ImageBox";
 import "./PDFBox.scss";
 import React = require("react");
 import { CompileScript } from '../../util/Scripting';
-import { ScriptField } from '../../../fields/ScriptField';
 import { Flyout, anchorPoints } from '../DocumentDecorations';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { ScriptField } from '../../../new_fields/ScriptField';
 
 type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
 const PdfDocument = makeInterface(positionSchema, pageSchema);
@@ -37,6 +37,9 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     private _keyValue: string = "";
     private _valueValue: string = "";
     private _scriptValue: string = "";
+    private _keyRef: React.RefObject<HTMLInputElement>;
+    private _valueRef: React.RefObject<HTMLInputElement>;
+    private _scriptRef: React.RefObject<HTMLInputElement>;
 
     constructor(props: FieldViewProps) {
         super(props);
@@ -51,10 +54,9 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
             }
         );
 
-        let script = CompileScript("return this.page === 0", { params: { this: Doc.name } });
-        if (script.compiled) {
-            this.props.Document.filterScript = new ScriptField(script);
-        }
+        this._keyRef = React.createRef();
+        this._valueRef = React.createRef();
+        this._scriptRef = React.createRef();
     }
 
     componentDidMount() {
@@ -99,17 +101,21 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
         this._valueValue = e.currentTarget.value;
     }
 
+    @action
     private newScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         this._scriptValue = e.currentTarget.value;
     }
 
-    private applyFilter = (e: React.MouseEvent<HTMLButtonElement>) => {
+    private applyFilter = () => {
         let scriptText = "";
         if (this._scriptValue.length > 0) {
             scriptText = this._scriptValue;
         } else if (this._keyValue.length > 0 && this._valueValue.length > 0) {
             scriptText = `return this.${this._keyValue} === ${this._valueValue}`;
         }
+        else {
+            scriptText = "return true";
+        }
         let script = CompileScript(scriptText, { params: { this: Doc.name } });
         if (script.compiled) {
             this.props.Document.filterScript = new ScriptField(script);
@@ -121,6 +127,22 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
         this._flyout = !this._flyout;
     }
 
+    @action
+    private resetFilters = () => {
+        this._keyValue = this._valueValue = "";
+        this._scriptValue = "return true";
+        if (this._keyRef.current) {
+            this._keyRef.current.value = "";
+        }
+        if (this._valueRef.current) {
+            this._valueRef.current.value = "";
+        }
+        if (this._scriptRef.current) {
+            this._scriptRef.current.value = "";
+        }
+        this.applyFilter();
+    }
+
     settingsPanel() {
         return !this.props.active() ? (null) :
             (
@@ -144,14 +166,18 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
                         </div>
                         <div className="pdfBox-settingsFlyout-kvpInput">
                             <input placeholder="Key" className="pdfBox-settingsFlyout-input" onChange={this.newKeyChange}
-                                style={{ gridColumn: 1 }} />
+                                style={{ gridColumn: 1 }} ref={this._keyRef} />
                             <input placeholder="Value" className="pdfBox-settingsFlyout-input" onChange={this.newValueChange}
-                                style={{ gridColumn: 3 }} />
+                                style={{ gridColumn: 3 }} ref={this._valueRef} />
                         </div>
                         <div className="pdfBox-settingsFlyout-kvpInput">
-                            <input placeholder="Custom Script" onChange={this.newScriptChange} style={{ gridColumn: "1 / 4" }} />
+                            <input placeholder="Custom Script" onChange={this.newScriptChange} style={{ gridColumn: "1 / 4" }} ref={this._scriptRef} />
                         </div>
                         <div className="pdfBox-settingsFlyout-kvpInput">
+                            <button style={{ gridColumn: 1 }} onClick={this.resetFilters}>
+                                <FontAwesomeIcon style={{ color: "white" }} icon="trash" size="lg" />
+                                &nbsp; Reset Filters
+                            </button>
                             <button style={{ gridColumn: 3 }} onClick={this.applyFilter}>
                                 <FontAwesomeIcon style={{ color: "white" }} icon="check" size="lg" />
                                 &nbsp; Apply
diff --git a/src/client/views/pdf/PDFMenu.scss b/src/client/views/pdf/PDFMenu.scss
index 22868082a..a4624b1f6 100644
--- a/src/client/views/pdf/PDFMenu.scss
+++ b/src/client/views/pdf/PDFMenu.scss
@@ -22,4 +22,11 @@
         height: 100%;
         transition: width .2s;
     }
+
+    .pdfMenu-addTag {
+        display: grid;
+        width: 200px;
+        padding: 5px;
+        grid-template-columns: 90px 20px 90px;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index a8e176858..e58ad9ccc 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -1,9 +1,9 @@
 import React = require("react");
 import "./PDFMenu.scss";
-import { observable, action } from "mobx";
+import { observable, action, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { emptyFunction } from "../../../Utils";
+import { emptyFunction, returnZero, returnTrue, returnFalse } from "../../../Utils";
 import { Doc } from "../../../new_fields/Doc";
 import { DragManager } from "../../util/DragManager";
 import { DocUtils } from "../../documents/Documents";
@@ -23,6 +23,7 @@ export default class PDFMenu extends React.Component {
     Highlight: (d: Doc | undefined, color: string | undefined) => void = emptyFunction;
     Delete: () => void = emptyFunction;
     Snippet: (marquee: { left: number, top: number, width: number, height: number }) => void = emptyFunction;
+    AddTag: (key: string, value: string) => boolean = returnFalse;
 
     @observable public Highlighting: boolean = false;
     @observable public Status: "pdf" | "annotation" | "snippet" | "" = "";
@@ -35,6 +36,9 @@ export default class PDFMenu extends React.Component {
     private _mainCont: React.RefObject<HTMLDivElement>;
     private _dragging: boolean = false;
     private _snippetButton: React.RefObject<HTMLButtonElement>;
+    @observable private _keyValue: string = "";
+    @observable private _valueValue: string = "";
+    @observable private _added: boolean = false;;
 
     constructor(props: Readonly<{}>) {
         super(props);
@@ -209,35 +213,56 @@ export default class PDFMenu extends React.Component {
         e.preventDefault();
     }
 
+    @action
+    keyChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this._keyValue = e.currentTarget.value;
+    }
+
+    @action
+    valueChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this._valueValue = e.currentTarget.value;
+    }
+
+    @action
+    addTag = (e: React.PointerEvent) => {
+        if (this._keyValue.length > 0 && this._valueValue.length > 0) {
+            this._added = this.AddTag(this._keyValue, this._valueValue);
+
+            setTimeout(
+                () => {
+                    runInAction(() => {
+                        this._added = false;
+                    });
+                }, 1000
+            );
+        }
+    }
+
     render() {
         let buttons = this.Status === "pdf" || this.Status === "snippet" ? [
-            <button className="pdfMenu-button" title="Click to Highlight" onClick={this.highlightClicked}
+            <button className="pdfMenu-button" title="Click to Highlight" onClick={this.highlightClicked} key="1"
                 style={this.Highlighting ? { backgroundColor: "#121212" } : {}}>
                 <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} />
             </button>,
-            <button className="pdfMenu-button" title="Drag to Annotate" onPointerDown={this.pointerDown}><FontAwesomeIcon icon="comment-alt" size="lg" /></button>,
+            <button className="pdfMenu-button" title="Drag to Annotate" onPointerDown={this.pointerDown}><FontAwesomeIcon icon="comment-alt" size="lg" key="2" /></button>,
             this.Status === "snippet" ? <button className="pdfMenu-button" title="Drag to Snippetize Selection" onPointerDown={this.snippetStart} ref={this._snippetButton}><FontAwesomeIcon icon="cut" size="lg" /></button> : undefined,
-            <button className="pdfMenu-button" title="Pin Menu" onClick={this.togglePin}
+            <button className="pdfMenu-button" title="Pin Menu" onClick={this.togglePin} key="3"
                 style={this.Pinned ? { backgroundColor: "#121212" } : {}}>
                 <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transition: "transform 0.1s", transform: this.Pinned ? "rotate(45deg)" : "" }} />
             </button>
         ] : [
-                <button className="pdfMenu-button" title="Delete Anchor" onPointerDown={this.deleteClicked}><FontAwesomeIcon icon="trash-alt" size="lg" /></button>
+                <button className="pdfMenu-button" title="Delete Anchor" onPointerDown={this.deleteClicked}><FontAwesomeIcon icon="trash-alt" size="lg" key="1" /></button>,
+                <div className="pdfMenu-addTag" key="2">
+                    <input onChange={this.keyChanged} placeholder="Key" style={{ gridColumn: 1 }} />
+                    <input onChange={this.valueChanged} placeholder="Value" style={{ gridColumn: 3 }} />
+                </div>,
+                <button className="pdfMenu-button" title={`Add tag: ${this._keyValue} with value: ${this._valueValue}`} onPointerDown={this.addTag}><FontAwesomeIcon style={{ transition: "all .2s" }} color={this._added ? "#42f560" : "white"} icon="check" size="lg" key="3" /></button>,
             ];
 
         return (
             <div className="pdfMenu-cont" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont} onContextMenu={this.handleContextMenu}
                 style={{ left: this._left, top: this._top, opacity: this._opacity, transition: this._transition, transitionDelay: this._transitionDelay }}>
                 {buttons}
-                {/* <button className="pdfMenu-button" title="Highlight" onClick={this.highlightClicked}
-                    style={this.Highlighting ? { backgroundColor: "#121212" } : {}}>
-                    <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} />
-                </button>
-                <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 >
         );
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 1eab13bc5..3df7dd77b 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -20,8 +20,8 @@ import "./PDFViewer.scss";
 import React = require("react");
 import PDFMenu from "./PDFMenu";
 import { UndoManager } from "../../util/UndoManager";
-import { ScriptField } from "../../../fields/ScriptField";
 import { CompileScript, CompiledScript, CompileResult } from "../../util/Scripting";
+import { ScriptField } from "../../../new_fields/ScriptField";
 
 export const scale = 2;
 interface IPDFViewerProps {
@@ -63,8 +63,6 @@ interface IViewerProps {
     url: string;
 }
 
-const PinRadius = 25;
-
 /**
  * Handles rendering and virtualization of the pdf
  */
@@ -85,14 +83,18 @@ class Viewer extends React.Component<IViewerProps> {
     private _annotationReactionDisposer?: IReactionDisposer;
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _filterReactionDisposer?: IReactionDisposer;
+    private _viewer: React.RefObject<HTMLDivElement>;
+    private _mainCont: React.RefObject<HTMLDivElement>;
+    private _textContent: Pdfjs.TextContent[] = [];
 
-    @action
-        constructor(props: IViewerProps) {
-            super(props);
+    constructor(props: IViewerProps) {
+        super(props);
 
-            let scriptfield = Cast(this.props.parent.Document.filterScript, ScriptField);
-            this._script = scriptfield ? CompileScript(scriptfield.scriptString, { params: { this: Doc.name } }) : CompileScript("return true");;
-        }
+        let scriptfield = Cast(this.props.parent.Document.filterScript, ScriptField);
+        this._script = scriptfield ? scriptfield.script : CompileScript("return true");
+        this._viewer = React.createRef();
+        this._mainCont = React.createRef();
+    }
 
     componentDidUpdate = (prevProps: IViewerProps) => {
         if (this.scrollY !== prevProps.scrollY) {
@@ -118,21 +120,37 @@ class Viewer extends React.Component<IViewerProps> {
 
         if (this.props.parent.props.ContainingCollectionView) {
             this._filterReactionDisposer = reaction(
-                () => this.props.parent.Document.filterScript || this.props.parent.props.ContainingCollectionView!.props.Document.filterScript,
+                () => this.props.parent.Document.filterScript,
                 () => {
                     runInAction(() => {
                         let scriptfield = Cast(this.props.parent.Document.filterScript, ScriptField);
-                        this._script = scriptfield ? CompileScript(scriptfield.scriptString, { params: { this: Doc.name } }) : CompileScript("return true");;
+                        this._script = scriptfield ? scriptfield.script : CompileScript("return true");
+                        if (this.props.parent.props.ContainingCollectionView) {
+                            let ccvAnnos = DocListCast(this.props.parent.props.ContainingCollectionView.props.Document.annotations);
+                            ccvAnnos.forEach(d => {
+                                if (this._script && this._script.compiled) {
+                                    let run = this._script.run(d);
+                                    if (run.success) {
+                                        d.opacity = run.result ? 1 : 0;
+                                    }
+                                }
+                            })
+                        }
                     });
                 }
             );
         }
+
+        if (this._mainCont.current) {
+            this._dropDisposer = this._mainCont.current && DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } });
+        }
     }
 
     componentWillUnmount = () => {
         this._reactionDisposer && this._reactionDisposer();
         this._annotationReactionDisposer && this._annotationReactionDisposer();
         this._filterReactionDisposer && this._filterReactionDisposer();
+        this._dropDisposer && this._dropDisposer();
     }
 
     @action
@@ -140,10 +158,14 @@ class Viewer extends React.Component<IViewerProps> {
         if (this._pageSizes.length === 0) {
             let pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages);
             this._isPage = Array<string>(this.props.pdf.numPages);
+            this._textContent = Array<Pdfjs.TextContent>(this.props.pdf.numPages);
             for (let i = 0; i < this.props.pdf.numPages; i++) {
                 await this.props.pdf.getPage(i + 1).then(page => runInAction(() => {
                     // pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale };
                     let x = page.getViewport(scale);
+                    page.getTextContent().then((text: Pdfjs.TextContent) => {
+                        this._textContent[i] = text;
+                    })
                     pageSizes[i] = { width: x.width, height: x.height };
                 }));
             }
@@ -162,13 +184,6 @@ class Viewer extends React.Component<IViewerProps> {
         }
     }
 
-    private mainCont = (div: HTMLDivElement | null) => {
-        this._dropDisposer && this._dropDisposer();
-        if (div) {
-            this._dropDisposer = div && DragManager.MakeDropTarget(div, { handlers: { drop: this.drop.bind(this) } });
-        }
-    }
-
     makeAnnotationDocument = (sourceDoc: Doc | undefined, s: number, color: string): Doc => {
         let annoDocs: Doc[] = [];
         let mainAnnoDoc = Docs.CreateInstance(new Doc(), "", {});
@@ -222,6 +237,7 @@ class Viewer extends React.Component<IViewerProps> {
     pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => {
         this.props.loaded(page.width, page.height, this.props.pdf.numPages);
     }
+
     @action
     getPlaceholderPage = (page: number) => {
         if (this._isPage[page] !== "none") {
@@ -232,6 +248,7 @@ class Viewer extends React.Component<IViewerProps> {
             );
         }
     }
+
     @action
     getRenderedPage = (page: number) => {
         if (this._isPage[page] !== "page") {
@@ -374,11 +391,16 @@ class Viewer extends React.Component<IViewerProps> {
         return res;
     }
 
+    pointerDown = () => {
+
+        let x = this._textContent;
+    }
+
     render() {
         let compiled = this._script;
         return (
-            <div ref={this.mainCont} style={{ pointerEvents: "all" }}>
-                <div className="viewer">
+            <div ref={this._mainCont} style={{ pointerEvents: "all" }} onPointerDown={this.pointerDown}>
+                <div className="viewer" ref={this._viewer}>
                     {this._visibleElements}
                 </div>
                 <div className="pdfViewer-annotationLayer"
@@ -472,12 +494,23 @@ class RegionAnnotation extends React.Component<IAnnotationProps> {
         }
         if (e.button === 2) {
             PDFMenu.Instance.Status = "annotation";
-            PDFMenu.Instance.Delete = this.deleteAnnotation;
+            PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this);
             PDFMenu.Instance.Pinned = false;
+            PDFMenu.Instance.AddTag = this.addTag.bind(this);
             PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true);
         }
     }
 
+    addTag = (key: string, value: string): boolean => {
+        let group = FieldValue(Cast(this.props.document.group, Doc));
+        if (group) {
+            let valNum = parseInt(value);
+            group[key] = isNaN(valNum) ? value : valNum;
+            return true;
+        }
+        return false;
+    }
+
     render() {
         return (
             <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown} ref={this._mainCont}
-- 
cgit v1.2.3-70-g09d2


From c785c2471e2f30d493a7e70334b0f588a593a33e Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Tue, 25 Jun 2019 18:10:18 -0400
Subject: basic text searching

---
 src/client/views/pdf/PDFViewer.scss | 53 +++++++++++++++++++----
 src/client/views/pdf/PDFViewer.tsx  | 83 +++++++++++++++++++++++++++++++++++--
 2 files changed, 125 insertions(+), 11 deletions(-)

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

diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 53c33ce0b..2f705781f 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -1,9 +1,3 @@
-.textLayer {
-    div {
-        user-select: text;
-    }
-}
-
 .viewer-button-cont {
     position: absolute;
     display: flex;
@@ -20,8 +14,51 @@
     border-radius: 5px;
 }
 
-.textLayer {
-    user-select: auto;
+.viewer {
+    // position: absolute;
+    // top: 0;
+}
+
+.pdfViewer-text {
+
+    .page {
+        .canvasWrapper {
+            display: none;
+        }
+
+        .textLayer {
+            position: relative;
+            user-select: none;
+        }
+    }
+}
+
+.page-cont {
+    .textLayer {
+        user-select: auto;
+
+        div {
+            user-select: text;
+        }
+    }
+}
+
+.pdfViewer-overlayCont {
+    position: absolute;
+    width: 100%;
+    height: 100px;
+    background: #121721;
+    bottom: 0;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    padding: 20px;
+
+    .pdfViewer-overlaySearchBar {
+        width: 20%;
+        height: 100%;
+        font-size: 30px;
+    }
 }
 
 .pdfViewer-annotationBox {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 3df7dd77b..d0e0c3749 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -22,6 +22,8 @@ import PDFMenu from "./PDFMenu";
 import { UndoManager } from "../../util/UndoManager";
 import { CompileScript, CompiledScript, CompileResult } from "../../util/Scripting";
 import { ScriptField } from "../../../new_fields/ScriptField";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
 
 export const scale = 2;
 interface IPDFViewerProps {
@@ -76,6 +78,7 @@ class Viewer extends React.Component<IViewerProps> {
     @observable private _annotations: Doc[] = [];
     @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
     @observable private _script: CompileResult | undefined;
+    @observable private _searching: boolean = false;
 
     private _pageBuffer: number = 1;
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
@@ -86,6 +89,8 @@ class Viewer extends React.Component<IViewerProps> {
     private _viewer: React.RefObject<HTMLDivElement>;
     private _mainCont: React.RefObject<HTMLDivElement>;
     private _textContent: Pdfjs.TextContent[] = [];
+    private _pdfFindController: any;
+    private _searchString: string = "";
 
     constructor(props: IViewerProps) {
         super(props);
@@ -164,8 +169,13 @@ class Viewer extends React.Component<IViewerProps> {
                     // pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale };
                     let x = page.getViewport(scale);
                     page.getTextContent().then((text: Pdfjs.TextContent) => {
+                        // let tc = new Pdfjs.TextContentItem()
+                        // let tc = {str: }
                         this._textContent[i] = text;
-                    })
+                        // text.items.forEach(t => {
+                        //     tcStr += t.str;
+                        // })
+                    });
                     pageSizes[i] = { width: x.width, height: x.height };
                 }));
             }
@@ -391,18 +401,81 @@ class Viewer extends React.Component<IViewerProps> {
         return res;
     }
 
+    @action
     pointerDown = () => {
+        this._searching = false;
+        this._pdfFindController = null;
+        if (this._viewer.current) {
+            let cns = this._viewer.current.childNodes;
+            for (let i = cns.length - 1; i >= 0; i--) {
+                cns.item(i).remove();
+            }
+        }
+    }
+
+    @action
+    search = (searchString: string) => {
+        if (searchString.length === 0) {
+            return;
+        }
+        this._searching = true;
+
+        let container = this._mainCont.current;
+        let viewer = this._viewer.current;
+
+        if (!this._pdfFindController) {
+            if (container && viewer) {
+                let simpleLinkService = new PDFJSViewer.SimpleLinkService();
+                let pdfViewer = new PDFJSViewer.PDFViewer({
+                    container: container,
+                    viewer: viewer,
+                    linkService: simpleLinkService
+                });
+                simpleLinkService.setPdf(this.props.pdf);
+                container.addEventListener("pagesinit", () => {
+                    pdfViewer.currentScaleValue = 1;
+                });
+                container.addEventListener("pagerendered", () => {
+                    console.log("rendered");
+                    this._pdfFindController.executeCommand('find',
+                        {
+                            caseSensitive: false,
+                            findPrevious: undefined,
+                            highlightAll: true,
+                            phraseSearch: true,
+                            query: searchString
+                        });
+                });
+                pdfViewer.setDocument(this.props.pdf);
+                this._pdfFindController = new PDFJSViewer.PDFFindController(pdfViewer);
+                // this._pdfFindController._linkService = pdfLinkService;
+                pdfViewer.findController = this._pdfFindController;
+            }
+        }
+        else {
+            this._pdfFindController.executeCommand('find',
+                {
+                    caseSensitive: false,
+                    findPrevious: undefined,
+                    highlightAll: true,
+                    phraseSearch: true,
+                    query: searchString
+                });
+        }
+    }
 
-        let x = this._textContent;
+    searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this._searchString = e.currentTarget.value;
     }
 
     render() {
         let compiled = this._script;
         return (
             <div ref={this._mainCont} style={{ pointerEvents: "all" }} onPointerDown={this.pointerDown}>
-                <div className="viewer" ref={this._viewer}>
+                <div className="viewer" style={this._searching ? { position: "absolute", top: 0 } : {}}>
                     {this._visibleElements}
                 </div>
+                <div className="pdfViewer-text" ref={this._viewer} style={{ transform: "scale(1.5)", transformOrigin: "top left" }} />
                 <div className="pdfViewer-annotationLayer"
                     style={{
                         height: this.props.parent.Document.nativeHeight, width: `100%`,
@@ -420,6 +493,10 @@ class Viewer extends React.Component<IViewerProps> {
                         }).map(anno => this.renderAnnotation(anno))}
                     </div>
                 </div>
+                <div className="pdfViewer-overlayCont" onPointerDown={(e) => e.stopPropagation()}>
+                    <input placeholder="Search" className="pdfViewer-overlaySearchBar" onChange={this.searchStringChanged} />
+                    <button title="Search" onClick={() => this.search(this._searchString)}><FontAwesomeIcon icon="search" size="3x" color="white" /></button>
+                </div>
             </div >
         );
     }
-- 
cgit v1.2.3-70-g09d2


From bb4d4b4193466778562a383654dd4c195fe69da6 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Tue, 25 Jun 2019 18:17:26 -0400
Subject: simple link service class

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

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

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index d0e0c3749..41961602d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -425,7 +425,7 @@ class Viewer extends React.Component<IViewerProps> {
 
         if (!this._pdfFindController) {
             if (container && viewer) {
-                let simpleLinkService = new PDFJSViewer.SimpleLinkService();
+                let simpleLinkService = new SimpleLinkService();
                 let pdfViewer = new PDFJSViewer.PDFViewer({
                     container: container,
                     viewer: viewer,
@@ -594,4 +594,41 @@ class RegionAnnotation extends React.Component<IAnnotationProps> {
                 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>
         );
     }
+}
+
+class SimpleLinkService {
+    externalLinkTarget: any = null;
+    externalLinkRel: any = null;
+    pdf: any = null;
+
+    constructor() { }
+
+    navigateTo(dest: any) { }
+
+    getDestinationHash(dest: any) { return "#"; }
+
+    getAnchorUrl(hash: any) { return "#"; }
+
+    setHash(hash: any) { }
+
+    executeNamedAction(action: any) { }
+
+    cachePageRef(pageNum: any, pageRef: any) { }
+
+    get pagesCount() {
+        return this.pdf ? this.pdf.numPages : 0;
+    }
+
+    get page() {
+        return 0;
+    }
+
+    setPdf(pdf: any) {
+        this.pdf = pdf;
+    }
+
+    get rotation() {
+        return 0;
+    }
+    set rotation(value: any) { }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 914eb8cb8e8ba25f3adb31da01bd005fb3bce234 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Wed, 26 Jun 2019 16:41:34 -0400
Subject: better search, prev/next annotations

---
 src/client/views/MainView.tsx       |   4 +-
 src/client/views/nodes/PDFBox.tsx   |   6 +
 src/client/views/pdf/PDFViewer.scss |  46 +++++-
 src/client/views/pdf/PDFViewer.tsx  | 294 +++++++++++++++++++++++++++++-------
 4 files changed, 290 insertions(+), 60 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 226eb458b..a9932feed 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, faCheck, faPenNib, faThumbtack, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt, faCut } from '@fortawesome/free-solid-svg-icons';
+import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faArrowDown, faArrowUp, faCheck, faPenNib, faThumbtack, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt, faCut } 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';
@@ -95,6 +95,8 @@ export class MainView extends React.Component {
         library.add(faCommentAlt);
         library.add(faThumbtack);
         library.add(faCheck);
+        library.add(faArrowDown);
+        library.add(faArrowUp);
         this.initEventListeners();
         this.initAuthenticationRouters();
     }
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index c0f2d313a..44028ddf7 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -143,6 +143,12 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
         this.applyFilter();
     }
 
+    scrollTo(y: number) {
+        if (this._mainCont.current) {
+            this._mainCont.current.scrollTo({ top: y });
+        }
+    }
+
     settingsPanel() {
         return !this.props.active() ? (null) :
             (
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 2f705781f..5a89a85f4 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -53,14 +53,52 @@
     justify-content: center;
     align-items: center;
     padding: 20px;
+    overflow: hidden;
+    transition: left .5s;
+}
+
+.pdfViewer-overlaySearchBar {
+    width: 20%;
+    height: 100%;
+    font-size: 30px;
+    padding: 5px;
+}
+
+.pdfViewer-overlayButton {
+    border-bottom-left-radius: 50%;
+    display: flex;
+    justify-content: space-evenly;
+    align-items: center;
+    height: 70px;
+    background: none;
+    padding: 0;
+    position: absolute;
+
+    .pdfViewer-overlayButton-arrow {
+        width: 0;
+        height: 0;
+        border-top: 25px solid transparent;
+        border-bottom: 25px solid transparent;
+        border-right: 25px solid #121721;
+        transition: all 0.5s;
+    }
 
-    .pdfViewer-overlaySearchBar {
-        width: 20%;
-        height: 100%;
-        font-size: 30px;
+    .pdfViewer-overlayButton-iconCont {
+        background: #121721;
+        height: 50px;
+        width: 70px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        margin-left: -2px;
+        border-radius: 3px;
     }
 }
 
+.pdfViewer-overlayButton:hover {
+    background: none;
+}
+
 .pdfViewer-annotationBox {
     position: absolute;
     background-color: red;
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 41961602d..d1d239f41 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -80,17 +80,23 @@ class Viewer extends React.Component<IViewerProps> {
     @observable private _script: CompileResult | undefined;
     @observable private _searching: boolean = false;
 
+    @observable public Index: number = -1;
+
     private _pageBuffer: number = 1;
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     private _reactionDisposer?: IReactionDisposer;
     private _annotationReactionDisposer?: IReactionDisposer;
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _filterReactionDisposer?: IReactionDisposer;
+    private _activeReactionDisposer?: IReactionDisposer;
     private _viewer: React.RefObject<HTMLDivElement>;
     private _mainCont: React.RefObject<HTMLDivElement>;
-    private _textContent: Pdfjs.TextContent[] = [];
+    // private _textContent: Pdfjs.TextContent[] = [];
     private _pdfFindController: any;
     private _searchString: string = "";
+    private _rendered: boolean = false;
+    private _pageIndex: number = -1;
+    private _matchIndex: number = 0;
 
     constructor(props: IViewerProps) {
         super(props);
@@ -123,6 +129,24 @@ class Viewer extends React.Component<IViewerProps> {
                 annotations && annotations.length && this.renderAnnotations(annotations, true),
             { fireImmediately: true });
 
+        this._activeReactionDisposer = reaction(
+            () => this.props.parent.props.active(),
+            () => {
+                runInAction(() => {
+                    if (!this.props.parent.props.active()) {
+                        this._searching = false;
+                        this._pdfFindController = null;
+                        if (this._viewer.current) {
+                            let cns = this._viewer.current.childNodes;
+                            for (let i = cns.length - 1; i >= 0; i--) {
+                                cns.item(i).remove();
+                            }
+                        }
+                    }
+                });
+            }
+        )
+
         if (this.props.parent.props.ContainingCollectionView) {
             this._filterReactionDisposer = reaction(
                 () => this.props.parent.Document.filterScript,
@@ -158,24 +182,28 @@ class Viewer extends React.Component<IViewerProps> {
         this._dropDisposer && this._dropDisposer();
     }
 
+    scrollTo(y: number) {
+        this.props.parent.scrollTo(y);
+    }
+
     @action
     initialLoad = async () => {
         if (this._pageSizes.length === 0) {
             let pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages);
             this._isPage = Array<string>(this.props.pdf.numPages);
-            this._textContent = Array<Pdfjs.TextContent>(this.props.pdf.numPages);
+            // this._textContent = Array<Pdfjs.TextContent>(this.props.pdf.numPages);
             for (let i = 0; i < this.props.pdf.numPages; i++) {
                 await this.props.pdf.getPage(i + 1).then(page => runInAction(() => {
                     // pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale };
                     let x = page.getViewport(scale);
-                    page.getTextContent().then((text: Pdfjs.TextContent) => {
-                        // let tc = new Pdfjs.TextContentItem()
-                        // let tc = {str: }
-                        this._textContent[i] = text;
-                        // text.items.forEach(t => {
-                        //     tcStr += t.str;
-                        // })
-                    });
+                    // page.getTextContent().then((text: Pdfjs.TextContent) => {
+                    //     // let tc = new Pdfjs.TextContentItem()
+                    //     // let tc = {str: }
+                    //     this._textContent[i] = text;
+                    //     // text.items.forEach(t => {
+                    //     //     tcStr += t.str;
+                    //     // })
+                    // });
                     pageSizes[i] = { width: x.width, height: x.height };
                 }));
             }
@@ -385,7 +413,7 @@ class Viewer extends React.Component<IViewerProps> {
         }
     }
 
-    renderAnnotation = (anno: Doc): JSX.Element[] => {
+    renderAnnotation = (anno: Doc, index: number): JSX.Element[] => {
         let annotationDocs = DocListCast(anno.annotations);
         let res = annotationDocs.map(a => {
             let type = NumCast(a.type);
@@ -393,7 +421,7 @@ class Viewer extends React.Component<IViewerProps> {
                 // case AnnotationTypes.Pin:
                 //     return <PinAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
                 case AnnotationTypes.Region:
-                    return <RegionAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
+                    return <RegionAnnotation parent={this} document={a} index={index} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
                 default:
                     return <div></div>;
             }
@@ -403,14 +431,7 @@ class Viewer extends React.Component<IViewerProps> {
 
     @action
     pointerDown = () => {
-        this._searching = false;
-        this._pdfFindController = null;
-        if (this._viewer.current) {
-            let cns = this._viewer.current.childNodes;
-            for (let i = cns.length - 1; i >= 0; i--) {
-                cns.item(i).remove();
-            }
-        }
+        // this._searching = false;
     }
 
     @action
@@ -418,23 +439,20 @@ class Viewer extends React.Component<IViewerProps> {
         if (searchString.length === 0) {
             return;
         }
-        this._searching = true;
-
-        let container = this._mainCont.current;
-        let viewer = this._viewer.current;
-
-        if (!this._pdfFindController) {
-            if (container && viewer) {
-                let simpleLinkService = new SimpleLinkService();
-                let pdfViewer = new PDFJSViewer.PDFViewer({
-                    container: container,
-                    viewer: viewer,
-                    linkService: simpleLinkService
-                });
-                simpleLinkService.setPdf(this.props.pdf);
-                container.addEventListener("pagesinit", () => {
-                    pdfViewer.currentScaleValue = 1;
+
+        if (this._rendered) {
+            this._pdfFindController.executeCommand('find',
+                {
+                    caseSensitive: false,
+                    findPrevious: undefined,
+                    highlightAll: true,
+                    phraseSearch: true,
+                    query: searchString
                 });
+        }
+        else {
+            let container = this._mainCont.current;
+            if (container) {
                 container.addEventListener("pagerendered", () => {
                     console.log("rendered");
                     this._pdfFindController.executeCommand('find',
@@ -445,29 +463,151 @@ class Viewer extends React.Component<IViewerProps> {
                             phraseSearch: true,
                             query: searchString
                         });
+                    this._rendered = true;
                 });
-                pdfViewer.setDocument(this.props.pdf);
-                this._pdfFindController = new PDFJSViewer.PDFFindController(pdfViewer);
-                // this._pdfFindController._linkService = pdfLinkService;
-                pdfViewer.findController = this._pdfFindController;
             }
         }
-        else {
-            this._pdfFindController.executeCommand('find',
-                {
-                    caseSensitive: false,
-                    findPrevious: undefined,
-                    highlightAll: true,
-                    phraseSearch: true,
-                    query: searchString
-                });
-        }
+
+        // let viewer = this._viewer.current;
+
+        // if (!this._pdfFindController) {
+        //     if (container && viewer) {
+        //         let simpleLinkService = new SimpleLinkService();
+        //         let pdfViewer = new PDFJSViewer.PDFViewer({
+        //             container: container,
+        //             viewer: viewer,
+        //             linkService: simpleLinkService
+        //         });
+        //         simpleLinkService.setPdf(this.props.pdf);
+        //         container.addEventListener("pagesinit", () => {
+        //             pdfViewer.currentScaleValue = 1;
+        //         });
+        //         container.addEventListener("pagerendered", () => {
+        //             console.log("rendered");
+        //             this._pdfFindController.executeCommand('find',
+        //                 {
+        //                     caseSensitive: false,
+        //                     findPrevious: undefined,
+        //                     highlightAll: true,
+        //                     phraseSearch: true,
+        //                     query: searchString
+        //                 });
+        //         });
+        //         pdfViewer.setDocument(this.props.pdf);
+        //         this._pdfFindController = new PDFJSViewer.PDFFindController(pdfViewer);
+        //         // this._pdfFindController._linkService = pdfLinkService;
+        //         pdfViewer.findController = this._pdfFindController;
+        //     }
+        // }
+        // else {
+        //     this._pdfFindController.executeCommand('find',
+        //         {
+        //             caseSensitive: false,
+        //             findPrevious: undefined,
+        //             highlightAll: true,
+        //             phraseSearch: true,
+        //             query: searchString
+        //         });
+        // }
     }
 
     searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
         this._searchString = e.currentTarget.value;
     }
 
+    @action
+    toggleSearch = (e: React.MouseEvent) => {
+        e.stopPropagation();
+        this._searching = !this._searching;
+
+        if (this._searching) {
+            let container = this._mainCont.current;
+            let viewer = this._viewer.current;
+
+            if (!this._pdfFindController) {
+                if (container && viewer) {
+                    let simpleLinkService = new SimpleLinkService();
+                    let pdfViewer = new PDFJSViewer.PDFViewer({
+                        container: container,
+                        viewer: viewer,
+                        linkService: simpleLinkService
+                    });
+                    simpleLinkService.setPdf(this.props.pdf);
+                    container.addEventListener("pagesinit", () => {
+                        pdfViewer.currentScaleValue = 1;
+                    });
+                    container.addEventListener("pagerendered", () => {
+                        console.log("rendered");
+                        this._rendered = true;
+                    });
+                    pdfViewer.setDocument(this.props.pdf);
+                    this._pdfFindController = new PDFJSViewer.PDFFindController(pdfViewer);
+                    // this._pdfFindController._linkService = pdfLinkService;
+                    pdfViewer.findController = this._pdfFindController;
+                }
+            }
+        }
+        else {
+            this._pdfFindController = null;
+            if (this._viewer.current) {
+                let cns = this._viewer.current.childNodes;
+                for (let i = cns.length - 1; i >= 0; i--) {
+                    cns.item(i).remove();
+                }
+            }
+        }
+    }
+
+    @action
+    prevAnnotation = (e: React.MouseEvent) => {
+        e.stopPropagation();
+
+        if (this.Index > 0) {
+            this.Index--;
+        }
+    }
+
+    @action
+    nextAnnotation = (e: React.MouseEvent) => {
+        e.stopPropagation();
+
+        let compiled = this._script;
+        if (this.Index < this._annotations.filter(anno => {
+            if (compiled && compiled.compiled) {
+                let run = compiled.run({ this: anno });
+                if (run.success) {
+                    return run.result;
+                }
+            }
+            return true;
+        }).length) {
+            this.Index++;
+        }
+    }
+
+    nextResult = () => {
+        // if (this._viewer.current) {
+        //     let results = this._pdfFindController.pageMatches;
+        //     if (results && results.length) {
+        //         if (this._pageIndex === this.props.pdf.numPages && this._matchIndex === results[this._pageIndex].length - 1) {
+        //             return;
+        //         }
+        //         if (this._pageIndex === -1 || this._matchIndex === results[this._pageIndex].length - 1) {
+        //             this._matchIndex = 0;
+        //             this._pageIndex++;
+        //         }
+        //         else {
+        //             this._matchIndex++;
+        //         }
+        //         this._pdfFindController._nextMatch()
+        // let nextMatch = this._viewer.current.children[this._pageIndex].children[1].children[results[this._pageIndex][this._matchIndex]];
+        // rconsole.log(nextMatch);
+        // this.props.parent.scrollTo(nextMatch.getBoundingClientRect().top);
+        // nextMatch.setAttribute("style", nextMatch.getAttribute("style") ? nextMatch.getAttribute("style") + ", background-color: green" : "background-color: green");
+        // }
+        // }
+    }
+
     render() {
         let compiled = this._script;
         return (
@@ -490,13 +630,39 @@ class Viewer extends React.Component<IViewerProps> {
                                 }
                             }
                             return true;
-                        }).map(anno => this.renderAnnotation(anno))}
+                        }).map((anno: Doc, index: number) => this.renderAnnotation(anno, index))}
                     </div>
                 </div>
-                <div className="pdfViewer-overlayCont" onPointerDown={(e) => e.stopPropagation()}>
+                <div className="pdfViewer-overlayCont" onPointerDown={(e) => e.stopPropagation()}
+                    style={{
+                        bottom: -this.props.scrollY,
+                        left: `${this._searching ? 0 : 100}%`
+                    }}>
+                    <button className="pdfViewer-overlayButton" title="Open Search Bar"></button>
+                    {/* <button title="Previous Result" onClick={() => this.search(this._searchString)}><FontAwesomeIcon icon="arrow-up" size="3x" color="white" /></button>
+                    <button title="Next Result" onClick={this.nextResult}><FontAwesomeIcon icon="arrow-down" size="3x" color="white" /></button> */}
                     <input placeholder="Search" className="pdfViewer-overlaySearchBar" onChange={this.searchStringChanged} />
                     <button title="Search" onClick={() => this.search(this._searchString)}><FontAwesomeIcon icon="search" size="3x" color="white" /></button>
                 </div>
+                <button className="pdfViewer-overlayButton" onClick={this.prevAnnotation} title="Previous Annotation"
+                    style={{ bottom: -this.props.scrollY + 280, right: 10, display: this.props.parent.props.active() ? "flex" : "none" }}>
+                    <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}>
+                        <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-up"} size="3x" />
+                    </div>
+                </button>
+                <button className="pdfViewer-overlayButton" onClick={this.nextAnnotation} title="Next Annotation"
+                    style={{ bottom: -this.props.scrollY + 200, right: 10, display: this.props.parent.props.active() ? "flex" : "none" }}>
+                    <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}>
+                        <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-down"} size="3x" />
+                    </div>
+                </button>
+                <button className="pdfViewer-overlayButton" onClick={this.toggleSearch} title="Open Search Bar"
+                    style={{ bottom: -this.props.scrollY + 10, right: 0, display: this.props.parent.props.active() ? "flex" : "none" }}>
+                    <div className="pdfViewer-overlayButton-arrow" onPointerDown={(e) => e.stopPropagation()}></div>
+                    <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}>
+                        <FontAwesomeIcon style={{ color: "white" }} icon={this._searching ? "times" : "search"} size="3x" />
+                    </div>
+                </button>
             </div >
         );
     }
@@ -511,14 +677,17 @@ interface IAnnotationProps {
     y: number;
     width: number;
     height: number;
+    index: number;
     parent: Viewer;
     document: Doc;
 }
 
+@observer
 class RegionAnnotation extends React.Component<IAnnotationProps> {
     @observable private _backgroundColor: string = "red";
 
     private _reactionDisposer?: IReactionDisposer;
+    private _scrollDisposer?: IReactionDisposer;
     private _mainCont: React.RefObject<HTMLDivElement>;
 
     constructor(props: IAnnotationProps) {
@@ -539,10 +708,20 @@ class RegionAnnotation extends React.Component<IAnnotationProps> {
             },
             { fireImmediately: true }
         );
+
+        this._scrollDisposer = reaction(
+            () => this.props.parent.Index,
+            () => {
+                if (this.props.parent.Index === this.props.index) {
+                    this.props.parent.scrollTo(this.props.y - 50);
+                }
+            }
+        )
     }
 
     componentWillUnmount() {
         this._reactionDisposer && this._reactionDisposer();
+        this._scrollDisposer && this._scrollDisposer();
     }
 
     deleteAnnotation = () => {
@@ -591,7 +770,14 @@ class RegionAnnotation extends React.Component<IAnnotationProps> {
     render() {
         return (
             <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown} ref={this._mainCont}
-                style={{ top: this.props.y * scale, left: this.props.x * scale, width: this.props.width * scale, height: this.props.height * scale, pointerEvents: "all", backgroundColor: StrCast(this.props.document.color) }}></div>
+                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.props.parent.Index === this.props.index ? "goldenrod" : StrCast(this.props.document.color)
+                }}></div>
         );
     }
 }
@@ -601,8 +787,6 @@ class SimpleLinkService {
     externalLinkRel: any = null;
     pdf: any = null;
 
-    constructor() { }
-
     navigateTo(dest: any) { }
 
     getDestinationHash(dest: any) { return "#"; }
-- 
cgit v1.2.3-70-g09d2


From 088eec96750fcbdfc30b42f24e8b57926441f4e5 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Thu, 27 Jun 2019 12:29:55 -0400
Subject: pdf fixes

---
 .../collectionFreeForm/CollectionFreeFormView.scss      | 17 +++++++++++------
 .../collectionFreeForm/CollectionFreeFormView.tsx       | 13 +++++++++++--
 src/client/views/nodes/LinkMenuItem.tsx                 | 13 +++++++++----
 src/client/views/nodes/PDFBox.tsx                       |  2 ++
 src/client/views/pdf/PDFViewer.tsx                      |  4 +++-
 src/client/views/pdf/Page.tsx                           |  4 ++--
 6 files changed, 38 insertions(+), 15 deletions(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 5ac2e1f9c..ccf261c95 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -1,7 +1,7 @@
 @import "../../globalCssVariables";
 
 .collectionfreeformview-ease {
-    position: absolute;
+    position: inherit;
     top: 0;
     left: 0;
     width: 100%;
@@ -25,8 +25,9 @@
         height: 100%;
         width: 100%;
     }
+
     >.jsx-parser {
-        z-index:0;
+        z-index: 0;
     }
 
     //nested freeform views
@@ -40,25 +41,27 @@
     border-radius: $border-radius;
     box-sizing: border-box;
     position: absolute;
+
     .marqueeView {
         overflow: hidden;
     }
+
     top: 0;
     left: 0;
     width: 100%;
     height: 100%;
 }
-  
+
 
 .collectionfreeformview-overlay {
     .collectionfreeformview>.jsx-parser {
         position: inherit;
         height: 100%;
     }
-    
+
     >.jsx-parser {
-        position:absolute;
-        z-index:0;
+        position: absolute;
+        z-index: 0;
     }
 
     .formattedTextBox-cont {
@@ -72,9 +75,11 @@
     box-sizing: border-box;
     position:absolute;
     z-index: -1;
+
     .marqueeView {
         overflow: hidden;
     }
+
     top: 0;
     left: 0;
     width: 100%;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 996032b1d..15185ecb0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -239,8 +239,17 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         var scale = this.getLocalTransform().inverse().Scale;
         const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
         const newPanY = Math.min((1 - 1 / scale) * this.nativeHeight, Math.max(0, panY));
-        this.props.Document.panX = this.isAnnotationOverlay ? newPanX : panX;
-        this.props.Document.panY = this.isAnnotationOverlay ? newPanY : panY;
+        // this.props.Document.panX = this.isAnnotationOverlay ? newPanX : panX;
+        // this.props.Document.panY = this.isAnnotationOverlay ? newPanY : panY;
+        this.props.Document.panX = panX;
+        if (this.props.Document.scrollY) {
+            this.props.Document.scrollY = panY;
+            this.props.Document.panY = panY;
+        }
+        else {
+
+            this.props.Document.panY = panY;
+        }
     }
 
     @action
diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx
index 732e6de84..4dee6741f 100644
--- a/src/client/views/nodes/LinkMenuItem.tsx
+++ b/src/client/views/nodes/LinkMenuItem.tsx
@@ -7,7 +7,7 @@ import { undoBatch } from "../../util/UndoManager";
 import './LinkMenu.scss';
 import React = require("react");
 import { Doc } from '../../../new_fields/Doc';
-import { StrCast, Cast } from '../../../new_fields/Types';
+import { StrCast, Cast, BoolCast, FieldValue } from '../../../new_fields/Types';
 import { observable, action } from 'mobx';
 import { LinkManager } from '../../util/LinkManager';
 import { DragLinkAsDocument } from '../../util/DragManager';
@@ -32,10 +32,15 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
     @undoBatch
     onFollowLink = async (e: React.PointerEvent): Promise<void> => {
         e.stopPropagation();
-        if (DocumentManager.Instance.getDocumentView(this.props.destinationDoc)) {
-            DocumentManager.Instance.jumpToDocument(this.props.destinationDoc, e.altKey);
+        let jumpToDoc = this.props.destinationDoc;
+        let pdfDoc = FieldValue(Cast(this.props.destinationDoc, Doc));
+        if (pdfDoc) {
+            jumpToDoc = pdfDoc;
+        }
+        if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
+            DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey);
         } else {
-            CollectionDockingView.Instance.AddRightSplit(this.props.destinationDoc, undefined);
+            CollectionDockingView.Instance.AddRightSplit(jumpToDoc, undefined);
         }
     }
 
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 8fb18e8fc..83dedb71d 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -217,10 +217,12 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
 
     @action
     onScroll = (e: React.UIEvent<HTMLDivElement>) => {
+
         if (e.currentTarget) {
             this._scrollY = e.currentTarget.scrollTop;
             let ccv = this.props.ContainingCollectionView;
             if (ccv) {
+                ccv.props.Document.panTransformType = "None";
                 ccv.props.Document.scrollY = this._scrollY;
             }
         }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index bafc3cbae..a440a1f27 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -226,7 +226,8 @@ class Viewer extends React.Component<IViewerProps> {
         let annoDocs: Doc[] = [];
         let mainAnnoDoc = Docs.CreateInstance(new Doc(), "", {});
 
-        mainAnnoDoc.page = Math.round(Math.random());
+        mainAnnoDoc.title = "Annotation on " + StrCast(this.props.parent.Document.title);
+        mainAnnoDoc.pdfDoc = this.props.parent.Document;
         this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => {
             for (let anno of value) {
                 let annoDoc = new Doc();
@@ -244,6 +245,7 @@ class Viewer extends React.Component<IViewerProps> {
             }
         });
 
+        mainAnnoDoc.y = Math.max((NumCast(annoDocs[0].y) * scale) - 100, 0);
         mainAnnoDoc.annotations = new List<Doc>(annoDocs);
         if (sourceDoc) {
             DocUtils.MakeLink(sourceDoc, mainAnnoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 01aa96705..57e36be43 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -31,7 +31,7 @@ interface IPageProps {
     makePin: (x: number, y: number, page: number) => void;
     sendAnnotations: (annotations: HTMLDivElement[], page: number) => void;
     createAnnotation: (div: HTMLDivElement, page: number) => void;
-    makeAnnotationDocuments: (doc: Doc | undefined, scale: number, color: string) => Doc;
+    makeAnnotationDocuments: (doc: Doc | undefined, scale: number, color: string, linkTo: boolean) => Doc;
     getScrollFromPage: (page: number) => number;
 }
 
@@ -137,7 +137,7 @@ export default class Page extends React.Component<IPageProps> {
     @action
     highlight = (targetDoc?: Doc, color: string = "red") => {
         // creates annotation documents for current highlights
-        let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, color);
+        let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, color, false);
         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 22c5ae30ab7835bfeae148642b182a2075760bc1 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Fri, 28 Jun 2019 15:14:12 -0400
Subject: ahh

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

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

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index a440a1f27..0cc81d469 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -194,8 +194,8 @@ class Viewer extends React.Component<IViewerProps> {
             // this._textContent = Array<Pdfjs.TextContent>(this.props.pdf.numPages);
             for (let i = 0; i < this.props.pdf.numPages; i++) {
                 await this.props.pdf.getPage(i + 1).then(page => runInAction(() => {
-                    // pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale };
-                    let x = page.getViewport(scale);
+                    pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale };
+                    // let x = page.getViewport(scale);
                     // page.getTextContent().then((text: Pdfjs.TextContent) => {
                     //     // let tc = new Pdfjs.TextContentItem()
                     //     // let tc = {str: }
@@ -204,7 +204,7 @@ class Viewer extends React.Component<IViewerProps> {
                     //     //     tcStr += t.str;
                     //     // })
                     // });
-                    pageSizes[i] = { width: x.width, height: x.height };
+                    // pageSizes[i] = { width: x.width, height: x.height };
                 }));
             }
             runInAction(() =>
-- 
cgit v1.2.3-70-g09d2


From 8891c0763fd86882d9e1d40c4fa4713aa14cac86 Mon Sep 17 00:00:00 2001
From: ab <abdullah_ahmed@brown.edu>
Date: Fri, 28 Jun 2019 15:25:39 -0400
Subject: small fix

---
 src/client/views/pdf/PDFViewer.tsx | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 0cc81d469..63a103aa2 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -192,9 +192,10 @@ class Viewer extends React.Component<IViewerProps> {
             let pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages);
             this._isPage = Array<string>(this.props.pdf.numPages);
             // this._textContent = Array<Pdfjs.TextContent>(this.props.pdf.numPages);
+            const proms: Pdfjs.PDFPromise<any>[] = [];
             for (let i = 0; i < this.props.pdf.numPages; i++) {
-                await this.props.pdf.getPage(i + 1).then(page => runInAction(() => {
-                    pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale };
+                proms.push(this.props.pdf.getPage(i + 1).then(page => runInAction(() => {
+                    pageSizes[i] = { width: page.view[page.rotate === 0 ? 2 : 3] * scale, height: page.view[page.rotate === 0 ? 3 : 2] * scale };
                     // let x = page.getViewport(scale);
                     // page.getTextContent().then((text: Pdfjs.TextContent) => {
                     //     // let tc = new Pdfjs.TextContentItem()
@@ -205,8 +206,9 @@ class Viewer extends React.Component<IViewerProps> {
                     //     // })
                     // });
                     // pageSizes[i] = { width: x.width, height: x.height };
-                }));
+                })));
             }
+            await Promise.all(proms);
             runInAction(() =>
                 Array.from(Array((this._pageSizes = pageSizes).length).keys()).map(this.getPlaceholderPage));
             this.props.loaded(Math.max(...pageSizes.map(i => i.width)), pageSizes[0].height, this.props.pdf.numPages);
-- 
cgit v1.2.3-70-g09d2


From ef3bf3c1080712c615c133217c1a6f6884d7d785 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Fri, 28 Jun 2019 15:37:32 -0400
Subject: fix

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

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

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 63a103aa2..a645b0041 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -195,7 +195,10 @@ class Viewer extends React.Component<IViewerProps> {
             const proms: Pdfjs.PDFPromise<any>[] = [];
             for (let i = 0; i < this.props.pdf.numPages; i++) {
                 proms.push(this.props.pdf.getPage(i + 1).then(page => runInAction(() => {
-                    pageSizes[i] = { width: page.view[page.rotate === 0 ? 2 : 3] * scale, height: page.view[page.rotate === 0 ? 3 : 2] * scale };
+                    pageSizes[i] = {
+                        width: (page.view[page.rotate === 0 || page.rotate === 180 ? 2 : 3] - page.view[page.rotate === 0 || page.rotate === 180 ? 0 : 1]) * scale,
+                        height: (page.view[page.rotate === 0 || page.rotate === 180 ? 3 : 2] - page.view[page.rotate === 0 || page.rotate === 180 ? 1 : 0]) * scale
+                    };
                     // let x = page.getViewport(scale);
                     // page.getTextContent().then((text: Pdfjs.TextContent) => {
                     //     // let tc = new Pdfjs.TextContentItem()
-- 
cgit v1.2.3-70-g09d2


From c20afe5bf81491db78781184e03257272a1179a0 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Fri, 28 Jun 2019 16:06:54 -0400
Subject: trace

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

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

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index a645b0041..380ba3c45 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,4 +1,4 @@
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from "mobx";
 import { observer } from "mobx-react";
 import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
@@ -620,6 +620,7 @@ class Viewer extends React.Component<IViewerProps> {
     }
 
     render() {
+        trace();
         let compiled = this._script;
         return (
             <div ref={this._mainCont} style={{ pointerEvents: "all" }} onPointerDown={this.pointerDown}>
-- 
cgit v1.2.3-70-g09d2


From ca95473dca3b577bf05aed3c76ccf800fc670220 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Fri, 28 Jun 2019 16:48:56 -0400
Subject: bleh

---
 src/client/views/pdf/Annotation.tsx | 144 ++++++++++++++++++++++++++++++++++++
 src/client/views/pdf/PDFViewer.tsx  | 132 ++-------------------------------
 2 files changed, 150 insertions(+), 126 deletions(-)
 create mode 100644 src/client/views/pdf/Annotation.tsx

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

diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
new file mode 100644
index 000000000..74f4be51a
--- /dev/null
+++ b/src/client/views/pdf/Annotation.tsx
@@ -0,0 +1,144 @@
+import React = require("react");
+import { Doc, DocListCast, WidthSym, HeightSym } from "../../../new_fields/Doc";
+import { AnnotationTypes, Viewer, scale } from "./PDFViewer";
+import { observer } from "mobx-react";
+import { observable, IReactionDisposer, reaction, action } from "mobx";
+import { BoolCast, NumCast, FieldValue, Cast, StrCast } from "../../../new_fields/Types";
+import { Id } from "../../../new_fields/FieldSymbols";
+import { List } from "../../../new_fields/List";
+import PDFMenu from "./PDFMenu";
+import { DocumentManager } from "../../util/DocumentManager";
+
+interface IAnnotationProps {
+    anno: Doc,
+    index: number,
+    parent: Viewer
+}
+
+export default class Annotation extends React.Component<IAnnotationProps> {
+    render() {
+        let annotationDocs = DocListCast(this.props.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)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
+                case AnnotationTypes.Region:
+                    return <RegionAnnotation parent={this.props.parent} document={a} index={this.props.index} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
+                default:
+                    return <div></div>;
+            }
+        });
+        return res;
+    }
+}
+
+interface IRegionAnnotationProps {
+    x: number;
+    y: number;
+    width: number;
+    height: number;
+    index: number;
+    parent: Viewer;
+    document: Doc;
+}
+
+@observer
+class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
+    @observable private _backgroundColor: string = "red";
+
+    private _reactionDisposer?: IReactionDisposer;
+    private _scrollDisposer?: IReactionDisposer;
+    private _mainCont: React.RefObject<HTMLDivElement>;
+
+    constructor(props: IRegionAnnotationProps) {
+        super(props);
+
+        this._mainCont = React.createRef();
+    }
+
+    componentDidMount() {
+        this._reactionDisposer = reaction(
+            () => BoolCast(this.props.document.delete),
+            () => {
+                if (BoolCast(this.props.document.delete)) {
+                    if (this._mainCont.current) {
+                        this._mainCont.current.style.display = "none";
+                    }
+                }
+            },
+            { fireImmediately: true }
+        );
+
+        this._scrollDisposer = reaction(
+            () => this.props.parent.Index,
+            () => {
+                if (this.props.parent.Index === this.props.index) {
+                    this.props.parent.scrollTo(this.props.y - 50);
+                }
+            }
+        );
+    }
+
+    componentWillUnmount() {
+        this._reactionDisposer && this._reactionDisposer();
+        this._scrollDisposer && this._scrollDisposer();
+    }
+
+    deleteAnnotation = () => {
+        let annotation = DocListCast(this.props.parent.props.parent.Document.annotations);
+        let group = FieldValue(Cast(this.props.document.group, Doc));
+        if (group && annotation.indexOf(group) !== -1) {
+            let newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc)));
+            this.props.parent.props.parent.Document.annotations = new List<Doc>(newAnnotations);
+        }
+
+        if (group) {
+            let groupAnnotations = DocListCast(group.annotations);
+            groupAnnotations.forEach(anno => anno.delete = true);
+        }
+
+        PDFMenu.Instance.fadeOut(true);
+    }
+
+    @action
+    onPointerDown = (e: React.PointerEvent) => {
+        if (e.button === 0) {
+            let targetDoc = Cast(this.props.document.target, Doc, null);
+            if (targetDoc) {
+                DocumentManager.Instance.jumpToDocument(targetDoc, true);
+            }
+        }
+        if (e.button === 2) {
+            PDFMenu.Instance.Status = "annotation";
+            PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this);
+            PDFMenu.Instance.Pinned = false;
+            PDFMenu.Instance.AddTag = this.addTag.bind(this);
+            PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true);
+        }
+    }
+
+    addTag = (key: string, value: string): boolean => {
+        let group = FieldValue(Cast(this.props.document.group, Doc));
+        if (group) {
+            let valNum = parseInt(value);
+            group[key] = isNaN(valNum) ? value : valNum;
+            return true;
+        }
+        return false;
+    }
+
+    render() {
+        return (
+            <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown} ref={this._mainCont}
+                style={{
+                    top: this.props.y * scale,
+                    left: this.props.x * scale,
+                    width: this.props.width * scale,
+                    height: this.props.height * scale,
+                    pointerEvents: "all",
+                    backgroundColor: this.props.parent.Index === this.props.index ? "goldenrod" : StrCast(this.props.document.color)
+                }}></div>
+        );
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index a645b0041..6875e5000 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,4 +1,4 @@
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from "mobx";
 import { observer } from "mobx-react";
 import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
@@ -23,6 +23,7 @@ import { UndoManager } from "../../util/UndoManager";
 import { CompileScript, CompiledScript, CompileResult } from "../../util/Scripting";
 import { ScriptField } from "../../../new_fields/ScriptField";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import Annotation from "./Annotation";
 const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
 
 export const scale = 2;
@@ -69,7 +70,7 @@ interface IViewerProps {
  * Handles rendering and virtualization of the pdf
  */
 @observer
-class Viewer extends React.Component<IViewerProps> {
+export class Viewer extends React.Component<IViewerProps> {
     // _visibleElements is the array of JSX elements that gets rendered
     @observable.shallow private _visibleElements: JSX.Element[] = [];
     // _isPage is an array that tells us whether or not an index is rendered as a page or as a placeholder
@@ -424,20 +425,8 @@ class Viewer extends React.Component<IViewerProps> {
         }
     }
 
-    renderAnnotation = (anno: Doc, index: number): 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)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
-                case AnnotationTypes.Region:
-                    return <RegionAnnotation parent={this} document={a} index={index} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
-                default:
-                    return <div></div>;
-            }
-        });
-        return res;
+    renderAnnotation = (anno: Doc, index: number): JSX.Element => {
+        return <Annotation anno={anno} index={index} parent={this} />
     }
 
     @action
@@ -620,6 +609,7 @@ class Viewer extends React.Component<IViewerProps> {
     }
 
     render() {
+        trace();
         let compiled = this._script;
         return (
             <div ref={this._mainCont} style={{ pointerEvents: "all" }} onPointerDown={this.pointerDown}>
@@ -683,116 +673,6 @@ export enum AnnotationTypes {
     Region
 }
 
-interface IAnnotationProps {
-    x: number;
-    y: number;
-    width: number;
-    height: number;
-    index: number;
-    parent: Viewer;
-    document: Doc;
-}
-
-@observer
-class RegionAnnotation extends React.Component<IAnnotationProps> {
-    @observable private _backgroundColor: string = "red";
-
-    private _reactionDisposer?: IReactionDisposer;
-    private _scrollDisposer?: IReactionDisposer;
-    private _mainCont: React.RefObject<HTMLDivElement>;
-
-    constructor(props: IAnnotationProps) {
-        super(props);
-
-        this._mainCont = React.createRef();
-    }
-
-    componentDidMount() {
-        this._reactionDisposer = reaction(
-            () => BoolCast(this.props.document.delete),
-            () => {
-                if (BoolCast(this.props.document.delete)) {
-                    if (this._mainCont.current) {
-                        this._mainCont.current.style.display = "none";
-                    }
-                }
-            },
-            { fireImmediately: true }
-        );
-
-        this._scrollDisposer = reaction(
-            () => this.props.parent.Index,
-            () => {
-                if (this.props.parent.Index === this.props.index) {
-                    this.props.parent.scrollTo(this.props.y - 50);
-                }
-            }
-        );
-    }
-
-    componentWillUnmount() {
-        this._reactionDisposer && this._reactionDisposer();
-        this._scrollDisposer && this._scrollDisposer();
-    }
-
-    deleteAnnotation = () => {
-        let annotation = DocListCast(this.props.parent.props.parent.Document.annotations);
-        let group = FieldValue(Cast(this.props.document.group, Doc));
-        if (group && annotation.indexOf(group) !== -1) {
-            let newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc)));
-            this.props.parent.props.parent.Document.annotations = new List<Doc>(newAnnotations);
-        }
-
-        if (group) {
-            let groupAnnotations = DocListCast(group.annotations);
-            groupAnnotations.forEach(anno => anno.delete = true);
-        }
-
-        PDFMenu.Instance.fadeOut(true);
-    }
-
-    @action
-    onPointerDown = (e: React.PointerEvent) => {
-        if (e.button === 0) {
-            let targetDoc = Cast(this.props.document.target, Doc, null);
-            if (targetDoc) {
-                DocumentManager.Instance.jumpToDocument(targetDoc, true);
-            }
-        }
-        if (e.button === 2) {
-            PDFMenu.Instance.Status = "annotation";
-            PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this);
-            PDFMenu.Instance.Pinned = false;
-            PDFMenu.Instance.AddTag = this.addTag.bind(this);
-            PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true);
-        }
-    }
-
-    addTag = (key: string, value: string): boolean => {
-        let group = FieldValue(Cast(this.props.document.group, Doc));
-        if (group) {
-            let valNum = parseInt(value);
-            group[key] = isNaN(valNum) ? value : valNum;
-            return true;
-        }
-        return false;
-    }
-
-    render() {
-        return (
-            <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown} ref={this._mainCont}
-                style={{
-                    top: this.props.y * scale,
-                    left: this.props.x * scale,
-                    width: this.props.width * scale,
-                    height: this.props.height * scale,
-                    pointerEvents: "all",
-                    backgroundColor: this.props.parent.Index === this.props.index ? "goldenrod" : StrCast(this.props.document.color)
-                }}></div>
-        );
-    }
-}
-
 class SimpleLinkService {
     externalLinkTarget: any = null;
     externalLinkRel: any = null;
-- 
cgit v1.2.3-70-g09d2


From 8ef971485492e3dc461cd93b2ae89e01b9995741 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Fri, 28 Jun 2019 16:57:46 -0400
Subject: undo tests

---
 src/client/views/nodes/DocumentContentsView.tsx | 3 +--
 src/client/views/nodes/KeyValueBox.tsx          | 1 -
 src/client/views/pdf/PDFViewer.tsx              | 3 +--
 src/new_fields/Doc.ts                           | 1 -
 4 files changed, 2 insertions(+), 6 deletions(-)

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

diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 240b21714..0da4888a1 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -52,8 +52,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         const layout = Cast(layoutDoc[this.props.layoutKey], "string");
         if (layout === undefined) {
             return this.props.Document.data ?
-                "null" :
-                // "<FieldView {...props} fieldKey='data' />" :
+                "<FieldView {...props} fieldKey='data' />" :
                 KeyValueBox.LayoutString(layoutDoc.proto ? "proto" : "");
         } else if (typeof layout === "string") {
             return layout;
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index eee4ec670..0e798d291 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -207,7 +207,6 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     }
 
     render() {
-        return null;
         let dividerDragger = this.splitPercentage === 0 ? (null) :
             <div className="keyValueBox-dividerDragger" style={{ transform: `translate(calc(${100 - this.splitPercentage}% - 5px), 0px)` }}>
                 <div className="keyValueBox-dividerDraggerThumb" onPointerDown={this.onDividerDown} />
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 380ba3c45..a645b0041 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,4 +1,4 @@
-import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
@@ -620,7 +620,6 @@ class Viewer extends React.Component<IViewerProps> {
     }
 
     render() {
-        trace();
         let compiled = this._script;
         return (
             <div ref={this._mainCont} style={{ pointerEvents: "all" }} onPointerDown={this.pointerDown}>
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 0340806ef..27dcfba08 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -281,7 +281,6 @@ export namespace Doc {
     }
 
     export function expandTemplateLayout(templateLayoutDoc: Doc, dataDoc?: Doc) {
-        return templateLayoutDoc;
         let resolvedDataDoc = (templateLayoutDoc !== dataDoc) ? dataDoc : undefined;
         if (!dataDoc || !(templateLayoutDoc && !(Cast(templateLayoutDoc.layout, Doc) instanceof Doc) && resolvedDataDoc && resolvedDataDoc !== templateLayoutDoc)) {
             return templateLayoutDoc;
-- 
cgit v1.2.3-70-g09d2


From 9f37f932586dc213e0640e1186e5d43d9d73d734 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 29 Jun 2019 15:37:13 -0400
Subject: added document previews to search results.  enbled by click now, but
 probably better activated as an option.

---
 src/client/views/pdf/PDFViewer.scss     |  3 ++
 src/client/views/pdf/PDFViewer.tsx      |  2 +-
 src/client/views/search/SearchItem.scss | 50 ++++++++++++++++++++++++++-----
 src/client/views/search/SearchItem.tsx  | 53 +++++++++++++++++++++++++++------
 4 files changed, 90 insertions(+), 18 deletions(-)

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

diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 11d3d7e27..0fde764d0 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -41,6 +41,9 @@
         }
     }
 }
+.pdfViewer-viewerCont {
+    width:100%;
+}
 
 .page-cont {
     .textLayer {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 9e9ddbd2d..6fab390d4 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -49,7 +49,7 @@ export class PDFViewer extends React.Component<IPDFViewerProps> {
 
     render() {
         return (
-            <div ref={this._mainDiv}>
+            <div className="pdfViewer-viewerCont" ref={this._mainDiv}>
                 {!this._pdf ? (null) :
                     <Viewer pdf={this._pdf} loaded={this.props.loaded} scrollY={this.props.scrollY} parent={this.props.parent} mainCont={this._mainDiv} url={this.props.url} />}
             </div>
diff --git a/src/client/views/search/SearchItem.scss b/src/client/views/search/SearchItem.scss
index 946680f0e..b3472ddec 100644
--- a/src/client/views/search/SearchItem.scss
+++ b/src/client/views/search/SearchItem.scss
@@ -5,6 +5,7 @@
     flex-direction: row-reverse;
     justify-content: flex-end;
     height: 70px;
+    z-index: 0;
 
     .search-item {
         width: 500px;
@@ -13,6 +14,7 @@
         border-bottom-style: solid;
         padding: 10px;
         height: 70px;
+        z-index: 0;
         display: inline-block;
 
         .main-search-info {
@@ -23,16 +25,17 @@
             .search-title {
                 text-transform: uppercase;
                 text-align: left;
-                width: 80%;
+                width: 100%;
                 font-weight: bold;
             }
 
             .search-info {
                 display: flex;
                 justify-content: flex-end;
-                width: 40%;
 
                 .link-container.item {
+                    margin-left: auto;
+                    margin-right: auto;
                     height: 26px;
                     width: 26px;
                     border-radius: 13px;
@@ -41,7 +44,6 @@
                     display: flex;
                     justify-content: center;
                     align-items: center;
-                    right: 15px;
                     -webkit-transition: all 0.2s ease-in-out;
                     -moz-transition: all 0.2s ease-in-out;
                     -o-transition: all 0.2s ease-in-out;
@@ -84,19 +86,36 @@
                 .link-container.item:hover .link-extended {
                     opacity: 1;
                 }
+                
+                .icon-icons {
+                    width:50px
+                }
+                .icon-live {
+                    width:175px;
+                }
 
-                .icon {
-
+                .icon-icons, .icon-live {
+                    height:50px;
+                    margin:auto;
+                    overflow: hidden;
                     .search-type {
-                        width: 25PX;
-                        height: 25PX;
-                        display: flex;
+                        display: inline-block;
+                        width:100%;
+                        position: absolute;
                         justify-content: center;
                         align-items: center;
                         position: relative;
                         margin-right: 5px;
                     }
 
+                    .pdfBox-cont {
+                        overflow: hidden;
+
+                        img {
+                            width:100% !important;
+                            height:auto !important;
+                        }
+                    }
                     .search-type:hover+.search-label {
                         opacity: 1;
                     }
@@ -114,6 +133,18 @@
                         transition: opacity 0.2s ease-in-out;
                     }
                 }
+
+                .icon-live:hover {
+                    height:175px;
+                    .pdfBox-cont {
+                        img {
+                            width:100% !important;
+                        }
+                    }
+                }
+            }
+            .search-info:hover {
+                width:60%; 
             }
         }
     }
@@ -146,4 +177,7 @@
         // height: 100%
     }
 
+}
+.search-overview:hover {
+    z-index: 1;
 }
\ No newline at end of file
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index b495975cb..d992d0fd1 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -2,10 +2,10 @@ import React = require("react");
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faMusic, faLink, faChartBar, faGlobeAsia } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Cast, NumCast } from "../../../new_fields/Types";
+import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { observable, runInAction, computed, action } from "mobx";
 import { listSpec } from "../../../new_fields/Schema";
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, WidthSym } from "../../../new_fields/Doc";
 import { DocumentManager } from "../../util/DocumentManager";
 import { SetupDrag } from "../../util/DragManager";
 import { SearchUtil } from "../../util/SearchUtil";
@@ -20,6 +20,10 @@ import { DocumentView } from "../nodes/DocumentView";
 import "./SelectorContextMenu.scss";
 import { SearchBox } from "./SearchBox";
 import { LinkManager } from "../../util/LinkManager";
+import { DocumentContentsView } from "../nodes/DocumentContentsView";
+import { ImageBox } from "../nodes/ImageBox";
+import { emptyFunction, returnFalse, returnOne } from "../../../Utils";
+import { Transform } from "../../util/Transform";
 
 export interface SearchItemProps {
     doc: Doc;
@@ -90,10 +94,40 @@ export class SearchItem extends React.Component<SearchItemProps> {
     onClick = () => {
         DocumentManager.Instance.jumpToDocument(this.props.doc, false);
     }
+    @observable _useIcons = true;
+    @observable _displayDim = 50;
 
     @computed
     public get DocumentIcon() {
-        let layoutresult = Cast(this.props.doc.type, "string", "");
+        let layoutresult = StrCast(this.props.doc.type);
+        if (!this._useIcons) {
+            let returnXDimension = () => this._useIcons ? 50 : 250;
+            let returnYDimension = () => this._displayDim;
+            let scale = () => returnXDimension() / NumCast(this.props.doc.nativeWidth, returnXDimension());
+            return <div
+                onPointerEnter={action(() => this._displayDim = this._useIcons ? 50 : 250)}
+                onPointerLeave={action(() => this._displayDim = this._useIcons ? 50 : 250)} >
+                <DocumentView
+                    Document={this.props.doc}
+                    addDocument={returnFalse}
+                    removeDocument={returnFalse}
+                    ScreenToLocalTransform={Transform.Identity}
+                    addDocTab={returnFalse}
+                    renderDepth={1}
+                    PanelWidth={returnXDimension}
+                    PanelHeight={returnYDimension}
+                    focus={emptyFunction}
+                    selectOnLoad={false}
+                    parentActive={returnFalse}
+                    whenActiveChanged={returnFalse}
+                    bringToFront={emptyFunction}
+                    zoomToScale={emptyFunction}
+                    getScale={returnOne}
+                    ContainingCollectionView={undefined}
+                    ContentScaling={scale}
+                />
+            </div>
+        }
 
         let button = layoutresult.indexOf(DocTypes.PDF) !== -1 ? faFilePdf :
             layoutresult.indexOf(DocTypes.IMG) !== -1 ? faImage :
@@ -131,7 +165,8 @@ export class SearchItem extends React.Component<SearchItemProps> {
         return num.toString() + " links";
     }
 
-    pointerDown = (e: React.PointerEvent) => { SearchBox.Instance.openSearch(e); };
+    @action
+    pointerDown = (e: React.PointerEvent) => { this._useIcons = !this._useIcons; SearchBox.Instance.openSearch(e); };
 
     highlightDoc = (e: React.PointerEvent) => {
         if (this.props.doc.type === DocTypes.LINK) {
@@ -176,15 +211,15 @@ export class SearchItem extends React.Component<SearchItemProps> {
                 }} >
                     <div className="main-search-info">
                         <div className="search-title" id="result" >{this.props.doc.title}</div>
-                        <div className="search-info">
+                        <div className="search-info" style={{ width: this._useIcons ? "15%" : "400px" }}>
+                            <div className={`icon-${this._useIcons ? "icons" : "live"}`}>
+                                <div className="search-type" >{this.DocumentIcon}</div>
+                                <div className="search-label">{this.props.doc.type}</div>
+                            </div>
                             <div className="link-container item">
                                 <div className="link-count">{this.linkCount}</div>
                                 <div className="link-extended">{this.linkString}</div>
                             </div>
-                            <div className="icon">
-                                <div className="search-type" >{this.DocumentIcon}</div>
-                                <div className="search-label">{this.props.doc.type}</div>
-                            </div>
                         </div>
                     </div>
                 </div>
-- 
cgit v1.2.3-70-g09d2


From 13fcbc4c500fec36e69cac4fc3c0ca6822322809 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sun, 30 Jun 2019 16:04:52 -0400
Subject: Fixed errors and linter warnings

---
 src/client/util/TooltipTextMenu.tsx                | 70 ++++++----------------
 .../views/collections/CollectionStackingView.tsx   |  2 +-
 .../views/collections/CollectionTreeView.tsx       |  3 +-
 src/client/views/pdf/Annotation.tsx                |  6 +-
 src/client/views/pdf/PDFViewer.tsx                 |  2 +-
 .../views/presentationview/PresentationElement.tsx |  3 +-
 src/client/views/search/FilterBox.tsx              |  7 ++-
 src/client/views/search/SearchItem.tsx             | 15 +++--
 src/new_fields/Doc.ts                              |  3 +-
 9 files changed, 37 insertions(+), 74 deletions(-)

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

diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 390bb2f44..9f8d0b2f6 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -1,57 +1,37 @@
-import { action, IReactionDisposer, reaction } from "mobx";
-import { Dropdown, DropdownSubmenu, MenuItem, MenuItemSpec, renderGrouped, icons, } from "prosemirror-menu"; //no import css
-import { baseKeymap, lift, deleteSelection } from "prosemirror-commands";
-import { history, redo, undo } from "prosemirror-history";
-import { keymap } from "prosemirror-keymap";
-import { EditorState, Transaction, NodeSelection, TextSelection } from "prosemirror-state";
+import { action } from "mobx";
+import { Dropdown, MenuItem, icons, } from "prosemirror-menu"; //no import css
+import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
 import { schema } from "./RichTextSchema";
 import { Schema, NodeType, MarkType, Mark, ResolvedPos } from "prosemirror-model";
-import { Node as ProsNode } from "prosemirror-model"
-import React = require("react");
+import { Node as ProsNode } from "prosemirror-model";
 import "./TooltipTextMenu.scss";
-const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands");
+const { toggleMark, setBlockType } = require("prosemirror-commands");
 import { library } from '@fortawesome/fontawesome-svg-core';
-import { wrapInList, bulletList, liftListItem, listItem, } from 'prosemirror-schema-list';
-import { liftTarget, RemoveMarkStep, AddMarkStep } from 'prosemirror-transform';
-import {
-    faListUl, faGrinTongueSquint,
-} from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { wrapInList, liftListItem, } from 'prosemirror-schema-list';
+import { faListUl } from '@fortawesome/free-solid-svg-icons';
 import { FieldViewProps } from "../views/nodes/FieldView";
-import { throwStatement } from "babel-types";
 const { openPrompt, TextField } = require("./ProsemirrorCopy/prompt.js");
-import { View } from "@react-pdf/renderer";
 import { DragManager } from "./DragManager";
 import { Doc, Opt, Field } from "../../new_fields/Doc";
 import { DocServer } from "../DocServer";
-import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import { DocumentManager } from "./DocumentManager";
 import { Id } from "../../new_fields/FieldSymbols";
-import { Utils } from "../../Utils";
 import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox";
-import { text } from "body-parser";
-import { type } from "os";
-// import { wrap } from "module";
-
-const SVG = "http://www.w3.org/2000/svg";
 
 //appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc.
 export class TooltipTextMenu {
 
     public tooltip: HTMLElement;
-    private num_icons = 0;
     private view: EditorView;
     private fontStyles: MarkType[];
     private fontSizes: MarkType[];
     private listTypes: NodeType[];
     private editorProps: FieldViewProps & FormattedTextBoxProps;
-    private state: EditorState;
     private fontSizeToNum: Map<MarkType, number>;
     private fontStylesToName: Map<MarkType, string>;
     private listTypeToIcon: Map<NodeType, string>;
-    private fontSizeIndicator: HTMLSpanElement = document.createElement("span");
     private link: HTMLAnchorElement;
 
     private linkEditor?: HTMLDivElement;
@@ -68,7 +48,6 @@ export class TooltipTextMenu {
 
     constructor(view: EditorView, editorProps: FieldViewProps & FormattedTextBoxProps) {
         this.view = view;
-        this.state = view.state;
         this.editorProps = editorProps;
         this.tooltip = document.createElement("div");
         this.tooltip.className = "tooltipMenu";
@@ -333,7 +312,7 @@ export class TooltipTextMenu {
 
     //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected text
     changeToMarkInGroup = (markType: MarkType, view: EditorView, fontMarks: MarkType[]) => {
-        let { empty, $cursor, ranges } = view.state.selection as TextSelection;
+        let { $cursor, ranges } = view.state.selection as TextSelection;
         let state = view.state;
         let dispatch = view.dispatch;
 
@@ -345,13 +324,12 @@ export class TooltipTextMenu {
                         dispatch(state.tr.removeStoredMark(type));
                     }
                 } else {
-                    let has = false, tr = state.tr;
+                    let has = false;
                     for (let i = 0; !has && i < ranges.length; i++) {
                         let { $from, $to } = ranges[i];
                         has = state.doc.rangeHasMark($from.pos, $to.pos, type);
                     }
                     for (let i of ranges) {
-                        let { $from, $to } = i;
                         if (has) {
                             toggleMark(type)(view.state, view.dispatch, view);
                         }
@@ -373,7 +351,7 @@ export class TooltipTextMenu {
     }
 
     //remove all node typeand apply the passed-in one to the selected text
-    changeToNodeType(nodeType: NodeType | undefined, view: EditorView, allNodes: NodeType[]) {
+    changeToNodeType(nodeType: NodeType | undefined, view: EditorView) {
         //remove old
         liftListItem(schema.nodes.list_item)(view.state, view.dispatch);
         if (nodeType) { //add new
@@ -390,7 +368,7 @@ export class TooltipTextMenu {
             execEvent: "",
             class: "menuicon",
             css: css,
-            enable(state) { return true; },
+            enable() { return true; },
             run() {
                 changeToMarkInGroup(markType, view, groupMarks);
             }
@@ -405,7 +383,7 @@ export class TooltipTextMenu {
             css: "color:white;",
             class: "summarize",
             execEvent: "",
-            run: (state, dispatch, view) => {
+            run: (state, dispatch) => {
                 TooltipTextMenu.insertStar(state, dispatch);
             }
 
@@ -420,7 +398,7 @@ export class TooltipTextMenu {
             execEvent: "",
             css: "color:white;",
             class: "summarize",
-            run: (state, dispatch, view) => {
+            run: () => {
                 this.collapseToolTip();
             }
         });
@@ -499,7 +477,7 @@ export class TooltipTextMenu {
             execEvent: "",
             class: "menuicon",
             css: css,
-            enable(state) { return true; },
+            enable() { return true; },
             run() {
                 changeToNodeInGroup(nodeType, view, groupNodes);
             }
@@ -586,17 +564,6 @@ export class TooltipTextMenu {
             //return;
         }
 
-        //let linksInSelection = this.activeMarksOnSelection([schema.marks.link]);
-        // if (linksInSelection.length > 0) {
-        //     let attributes = this.getMarksInSelection(this.view.state, [schema.marks.link])[0].attrs;
-        //     this.link.href = attributes.href;
-        //     this.link.textContent = attributes.title;
-        //     this.link.style.visibility = "visible";
-        // } else this.link.style.visibility = "hidden";
-
-        // Otherwise, reposition it and update its content
-        //this.tooltip.style.display = "";
-        let { from, to } = state.selection;
 
         //UPDATE LIST ITEM DROPDOWN
 
@@ -641,17 +608,16 @@ export class TooltipTextMenu {
     //finds all active marks on selection in given group
     activeMarksOnSelection(markGroup: MarkType[]) {
         //current selection
-        let { empty, $cursor, ranges } = this.view.state.selection as TextSelection;
+        let { empty, ranges } = this.view.state.selection as TextSelection;
         let state = this.view.state;
         let dispatch = this.view.dispatch;
         let activeMarks: MarkType[];
         if (!empty) {
             activeMarks = markGroup.filter(mark => {
                 if (dispatch) {
-                    let has = false, tr = state.tr;
+                    let has = false;
                     for (let i = 0; !has && i < ranges.length; i++) {
                         let { $from, $to } = ranges[i];
-                        let hasmark: boolean = state.doc.rangeHasMark($from.pos, $to.pos, mark);
                         return state.doc.rangeHasMark($from.pos, $to.pos, mark);
                     }
                 }
@@ -662,9 +628,7 @@ export class TooltipTextMenu {
             const pos = this.view.state.selection.$from;
             const ref_node: ProsNode = this.reference_node(pos);
             if (ref_node !== null && ref_node !== this.view.state.doc) {
-                let text_node_type: NodeType;
                 if (ref_node.isText) {
-                    text_node_type = ref_node.type;
                 }
                 else {
                     return [];
@@ -699,7 +663,7 @@ export class TooltipTextMenu {
         else if (pos.pos > 0) {
             let skip = false;
             for (let i: number = pos.pos - 1; i > 0; i--) {
-                this.view.state.doc.nodesBetween(i, pos.pos, (node: ProsNode, pos: number, parent: ProsNode, index: number) => {
+                this.view.state.doc.nodesBetween(i, pos.pos, (node: ProsNode) => {
                     if (node.isLeaf && !skip) {
                         ref_node = node;
                         skip = true;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 088a9bae8..b10907937 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -78,7 +78,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                 style={{ width: width(), height: height() }} >
                 <CollectionSchemaPreview
                     Document={layoutDoc}
-                    DataDocument={d != this.props.DataDoc ? this.props.DataDoc : undefined}
+                    DataDocument={d !== this.props.DataDoc ? this.props.DataDoc : undefined}
                     renderDepth={this.props.renderDepth}
                     width={width}
                     height={height}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index dd165e90c..927aa363f 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -186,8 +186,7 @@ class TreeView extends React.Component<TreeViewProps> {
         }
         let keyList: string[] = keys.reduce((l, key) => {
             let listspec = DocListCast(this.resolvedDataDoc[key]);
-            if (listspec && listspec.length)
-                return [...l, key];
+            if (listspec && listspec.length) return [...l, key];
             return l;
         }, [] as string[]);
         keys.map(key => Cast(this.resolvedDataDoc[key], Doc) instanceof Doc && keyList.push(key));
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 74f4be51a..73fca229b 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -10,9 +10,9 @@ import PDFMenu from "./PDFMenu";
 import { DocumentManager } from "../../util/DocumentManager";
 
 interface IAnnotationProps {
-    anno: Doc,
-    index: number,
-    parent: Viewer
+    anno: Doc;
+    index: number;
+    parent: Viewer;
 }
 
 export default class Annotation extends React.Component<IAnnotationProps> {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 6fab390d4..bb148e738 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -426,7 +426,7 @@ export class Viewer extends React.Component<IViewerProps> {
     }
 
     renderAnnotation = (anno: Doc, index: number): JSX.Element => {
-        return <Annotation anno={anno} index={index} parent={this} />
+        return <Annotation anno={anno} index={index} parent={this} />;
     }
 
     @action
diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx
index 4afc0210f..d63c0b066 100644
--- a/src/client/views/presentationview/PresentationElement.tsx
+++ b/src/client/views/presentationview/PresentationElement.tsx
@@ -13,12 +13,11 @@ import { faFile as fileRegular } from '@fortawesome/free-regular-svg-icons';
 import { List } from "../../../new_fields/List";
 import { listSpec } from "../../../new_fields/Schema";
 
-
 library.add(faArrowUp);
 library.add(fileSolid);
 library.add(faLocationArrow);
+library.add(fileRegular as any);
 library.add(faSearch);
-library.add(fileRegular);
 
 interface PresentationElementProps {
     mainDocument: Doc;
diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx
index c39ec1145..8bbbf3012 100644
--- a/src/client/views/search/FilterBox.tsx
+++ b/src/client/views/search/FilterBox.tsx
@@ -65,9 +65,9 @@ export class FilterBox extends React.Component {
 
     setupAccordion() {
         $('document').ready(function () {
-            var acc = document.getElementsByClassName('filter-header');
-
-            for (var i = 0; i < acc.length; i++) {
+            const acc = document.getElementsByClassName('filter-header');
+            // tslint:disable-next-line: prefer-for-of
+            for (let i = 0; i < acc.length; i++) {
                 acc[i].addEventListener("click", function (this: HTMLElement) {
                     this.classList.toggle("active");
 
@@ -96,6 +96,7 @@ export class FilterBox extends React.Component {
         $('document').ready(function () {
             var acc = document.getElementsByClassName('filter-header');
 
+            // tslint:disable-next-line: prefer-for-of
             for (var i = 0; i < acc.length; i++) {
                 let classList = acc[i].classList;
                 if (classList.contains("active")) {
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index b72f8c814..f8a0c7b16 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -83,9 +83,8 @@ export class SelectorContextMenu extends React.Component<SearchItemProps> {
                             <FontAwesomeIcon icon={faStickyNote} />
                         </div>
                         <a className="title" onClick={this.getOnClick(doc)}>{doc.col.title}</a>
-                    </div>
-                }
-                )}
+                    </div>;
+                })}
             </div>
         );
     }
@@ -111,7 +110,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
             if (layoutresult.indexOf(DocTypes.COL) !== -1) {
                 renderDoc = Doc.MakeDelegate(renderDoc);
                 let bounds = DocListCast(renderDoc.data).reduce((bounds, doc) => {
-                    var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)]
+                    var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
                     let [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
                     return {
                         x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y),
@@ -124,7 +123,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
             let returnYDimension = () => this._displayDim;
             let scale = () => returnXDimension() / NumCast(renderDoc.nativeWidth, returnXDimension());
             return <div
-                onPointerDown={action(() => { this._useIcons = !this._useIcons; this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE) })}
+                onPointerDown={action(() => { this._useIcons = !this._useIcons; this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE); })}
                 onPointerEnter={action(() => this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE))}
                 onPointerLeave={action(() => this._displayDim = 50)} >
                 <DocumentView
@@ -147,7 +146,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
                     ContainingCollectionView={undefined}
                     ContentScaling={scale}
                 />
-            </div>
+            </div>;
         }
 
         let button = layoutresult.indexOf(DocTypes.PDF) !== -1 ? faFilePdf :
@@ -160,7 +159,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
                                     layoutresult.indexOf(DocTypes.HIST) !== -1 ? faChartBar :
                                         layoutresult.indexOf(DocTypes.WEB) !== -1 ? faGlobeAsia :
                                             faCaretUp;
-        return <div onPointerDown={action(() => { this._useIcons = false; this._displayDim = Number(SEARCH_THUMBNAIL_SIZE) })} >
+        return <div onPointerDown={action(() => { this._useIcons = false; this._displayDim = Number(SEARCH_THUMBNAIL_SIZE); })} >
             <FontAwesomeIcon icon={button} size="2x" />
         </div>;
     }
@@ -189,7 +188,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
     }
 
     @action
-    pointerDown = (e: React.PointerEvent) => SearchBox.Instance.openSearch(e);
+    pointerDown = (e: React.PointerEvent) => SearchBox.Instance.openSearch(e)
 
     highlightDoc = (e: React.PointerEvent) => {
         if (this.props.doc.type === DocTypes.LINK) {
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index e76e94d63..734a90731 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -293,11 +293,12 @@ export namespace Doc {
         if (expandedTemplateLayout instanceof Doc) {
             return expandedTemplateLayout;
         }
-        if (expandedTemplateLayout === undefined)
+        if (expandedTemplateLayout === undefined) {
             setTimeout(() => {
                 templateLayoutDoc["_expanded_" + dataDoc[Id]] = Doc.MakeDelegate(templateLayoutDoc);
                 (templateLayoutDoc["_expanded_" + dataDoc[Id]] as Doc).title = templateLayoutDoc.title + " applied to " + dataDoc.title;
             }, 0);
+        }
         return templateLayoutDoc;
     }
 
-- 
cgit v1.2.3-70-g09d2


From fc50df38df449f45d1a75ce3baee6b3755ea8d5a Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Mon, 1 Jul 2019 10:30:50 -0400
Subject: annotation border

---
 src/client/views/pdf/Annotation.tsx |  2 +-
 src/client/views/pdf/PDFViewer.tsx  |  2 +-
 src/client/views/pdf/Page.tsx       | 87 +++++++++++++++++++------------------
 3 files changed, 46 insertions(+), 45 deletions(-)

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

diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 73fca229b..ff77612d6 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -137,7 +137,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
                     width: this.props.width * scale,
                     height: this.props.height * scale,
                     pointerEvents: "all",
-                    backgroundColor: this.props.parent.Index === this.props.index ? "goldenrod" : StrCast(this.props.document.color)
+                    backgroundColor: this.props.parent.Index === this.props.index ? "green" : StrCast(this.props.document.color)
                 }}></div>
         );
     }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index bb148e738..35bf1c4d7 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -580,7 +580,7 @@ export class Viewer extends React.Component<IViewerProps> {
                 }
             }
             return true;
-        }).length) {
+        }).length - 1) {
             this.Index++;
         }
     }
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 57e36be43..782dbd261 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -52,7 +52,7 @@ export default class Page extends React.Component<IPageProps> {
     private _textLayer: React.RefObject<HTMLDivElement>;
     private _annotationLayer: React.RefObject<HTMLDivElement>;
     private _marquee: React.RefObject<HTMLDivElement>;
-    private _curly: React.RefObject<HTMLImageElement>;
+    // private _curly: React.RefObject<HTMLImageElement>;
     private _marqueeing: boolean = false;
     private _reactionDisposer?: IReactionDisposer;
 
@@ -62,7 +62,7 @@ export default class Page extends React.Component<IPageProps> {
         this._textLayer = React.createRef();
         this._annotationLayer = React.createRef();
         this._marquee = React.createRef();
-        this._curly = React.createRef();
+        // this._curly = React.createRef();
     }
 
     componentDidMount = (): void => {
@@ -244,9 +244,9 @@ export default class Page extends React.Component<IPageProps> {
                 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._marquee.current /*&& this._curly.current*/) {
                     this._marquee.current.style.background = background;
-                    this._curly.current.style.opacity = opacity;
+                    // this._curly.current.style.opacity = opacity;
                     this._rotate = transform;
                 }
             }
@@ -259,33 +259,33 @@ export default class Page extends React.Component<IPageProps> {
     }
 
     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";
-            }
-
-            // 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, 5)";
-            }
-            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 };
+        // 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";
+        //     }
+
+        //     // 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, 5)";
+        //     }
+        //     else if (ratio < 0.5) {
+        //         // horizontal
+        //         transform = "scale(2, 1)";
+        //     }
+        //     else {
+        //         // diagonal
+        //         transform = "rotate(45deg) scale(1.5, 1.5)";
+        //     }
+        // }
+        return { background: "red", opacity: "0.5", transform: "" };
     }
 
     @action
@@ -305,16 +305,17 @@ export default class Page extends React.Component<IPageProps> {
                 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 = style.opacity;
-                }
+                // 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.border = style.border;
+                copy.style.opacity = style.opacity;
+                // }
                 copy.className = this._marquee.current.className;
                 this.props.createAnnotation(copy, this.props.page);
                 this._marquee.current.style.opacity = "0";
@@ -401,8 +402,8 @@ export default class Page extends React.Component<IPageProps> {
                 </div>
                 <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}` }} />
+                        style={{ left: `${this._marqueeX}px`, top: `${this._marqueeY}px`, width: `${this._marqueeWidth}px`, height: `${this._marqueeHeight}px`, background: "red", border: "10px dashed white" }}>
+                        {/* <img ref={this._curly} src="https://static.thenounproject.com/png/331760-200.png" style={{ width: "100%", height: "100%", transform: `${this._rotate}` }} /> */}
                     </div>
                 </div>
                 <div className="textlayer" ref={this._textLayer} style={{ "position": "relative", "top": `-${2 * this._height}px`, "height": `${this._height}px` }} />
-- 
cgit v1.2.3-70-g09d2


From 6c5468eee0ec59d4ddaf116e67d067b567ccb87a Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Wed, 3 Jul 2019 12:12:08 -0400
Subject: pdf next/prev annotation fixes/improvements

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

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

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 83dedb71d..cc02bb282 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -149,7 +149,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
 
     scrollTo(y: number) {
         if (this._mainCont.current) {
-            this._mainCont.current.scrollTo({ top: y });
+            this._mainCont.current.scrollTo({ top: y, behavior: "auto" });
         }
     }
 
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 9718c1406..0a1661a1a 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -75,7 +75,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
             () => this.props.parent.Index,
             () => {
                 if (this.props.parent.Index === this.props.index) {
-                    this.props.parent.scrollTo(this.props.y - 50);
+                    this.props.parent.scrollTo(this.props.y * scale - (NumCast(this.props.parent.props.parent.Document.pdfHeight) / 2));
                 }
             }
         );
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 35bf1c4d7..ad062a54d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -166,6 +166,7 @@ export class Viewer extends React.Component<IViewerProps> {
                                 }
                             });
                         }
+                        this.Index = -1;
                     });
                 }
             );
@@ -234,11 +235,15 @@ export class Viewer extends React.Component<IViewerProps> {
 
         mainAnnoDoc.title = "Annotation on " + StrCast(this.props.parent.Document.title);
         mainAnnoDoc.pdfDoc = this.props.parent.Document;
+        let minY = Number.MAX_VALUE;
         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) / scale;
-                if (anno.style.top) annoDoc.y = parseInt(anno.style.top) / scale;
+                if (anno.style.top) {
+                    annoDoc.y = parseInt(anno.style.top) / scale;
+                    minY = Math.min(parseInt(anno.style.top), minY);
+                }
                 if (anno.style.height) annoDoc.height = parseInt(anno.style.height) / scale;
                 if (anno.style.width) annoDoc.width = parseInt(anno.style.width) / scale;
                 annoDoc.page = key;
@@ -251,12 +256,13 @@ export class Viewer extends React.Component<IViewerProps> {
             }
         });
 
-        mainAnnoDoc.y = Math.max((NumCast(annoDocs[0].y) * scale) - 100, 0);
+        mainAnnoDoc.y = Math.max(minY, 0);
         mainAnnoDoc.annotations = new List<Doc>(annoDocs);
         if (sourceDoc) {
             DocUtils.MakeLink(sourceDoc, mainAnnoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
         }
         this._savedAnnotations.clear();
+        this.Index = -1;
         return mainAnnoDoc;
     }
 
@@ -562,9 +568,10 @@ export class Viewer extends React.Component<IViewerProps> {
     prevAnnotation = (e: React.MouseEvent) => {
         e.stopPropagation();
 
-        if (this.Index > 0) {
-            this.Index--;
-        }
+        // if (this.Index > 0) {
+        //     this.Index--;
+        // }
+        this.Index = Math.max(this.Index - 1, 0);
     }
 
     @action
@@ -572,7 +579,7 @@ export class Viewer extends React.Component<IViewerProps> {
         e.stopPropagation();
 
         let compiled = this._script;
-        if (this.Index < this._annotations.filter(anno => {
+        let filtered = this._annotations.filter(anno => {
             if (compiled && compiled.compiled) {
                 let run = compiled.run({ this: anno });
                 if (run.success) {
@@ -580,9 +587,8 @@ export class Viewer extends React.Component<IViewerProps> {
                 }
             }
             return true;
-        }).length - 1) {
-            this.Index++;
-        }
+        });
+        this.Index = Math.min(this.Index + 1, filtered.length - 1)
     }
 
     nextResult = () => {
@@ -630,7 +636,7 @@ export class Viewer extends React.Component<IViewerProps> {
                                 }
                             }
                             return true;
-                        }).map((anno: Doc, index: number) => this.renderAnnotation(anno, index))}
+                        }).sort((a: Doc, b: Doc) => NumCast(a.y) - NumCast(b.y)).map((anno: Doc, index: number) => this.renderAnnotation(anno, index))}
                     </div>
                 </div>
                 <div className="pdfViewer-overlayCont" onPointerDown={(e) => e.stopPropagation()}
-- 
cgit v1.2.3-70-g09d2


From 88cec4b18b8e49f8598cab817955ca4dccb6228c Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Wed, 3 Jul 2019 13:40:32 -0400
Subject: pdf keys error

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

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

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index ad062a54d..8af29110f 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -432,7 +432,7 @@ export class Viewer extends React.Component<IViewerProps> {
     }
 
     renderAnnotation = (anno: Doc, index: number): JSX.Element => {
-        return <Annotation anno={anno} index={index} parent={this} />;
+        return <Annotation anno={anno} index={index} parent={this} key={`${anno[Id]}-annotation`} />;
     }
 
     @action
@@ -636,7 +636,8 @@ export class Viewer extends React.Component<IViewerProps> {
                                 }
                             }
                             return true;
-                        }).sort((a: Doc, b: Doc) => NumCast(a.y) - NumCast(b.y)).map((anno: Doc, index: number) => this.renderAnnotation(anno, index))}
+                        }).sort((a: Doc, b: Doc) => NumCast(a.y) - NumCast(b.y))
+                            .map((anno: Doc, index: number) => this.renderAnnotation(anno, index))}
                     </div>
                 </div>
                 <div className="pdfViewer-overlayCont" onPointerDown={(e) => e.stopPropagation()}
-- 
cgit v1.2.3-70-g09d2

