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/pdf/PDFViewer.tsx | 6 +++++-
 1 file changed, 5 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 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} />
             );
         }
-- 
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 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

