From 280675c94b482147bbc81fdd317de6184b33f4e3 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Sat, 7 Sep 2019 15:58:34 -0400
Subject: pdf stuffs

---
 src/client/views/pdf/Annotation.tsx | 27 +++++++++++++--------------
 1 file changed, 13 insertions(+), 14 deletions(-)

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

diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 6f77a0a5b..eeb2531a2 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -1,5 +1,5 @@
 import React = require("react");
-import { action, IReactionDisposer, observable, reaction } from "mobx";
+import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, DocListCast, HeightSym, WidthSym } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
@@ -13,10 +13,7 @@ import { PresBox } from "../nodes/PresBox";
 
 interface IAnnotationProps {
     anno: Doc;
-    index: number;
-    ParentIndex: () => number;
     fieldExtensionDoc: Doc;
-    scrollTo?: (n: number) => void;
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
     pinToPres: (document: Doc) => void;
 }
@@ -33,10 +30,7 @@ interface IRegionAnnotationProps {
     y: number;
     width: number;
     height: number;
-    index: number;
-    ParentIndex: () => number;
     fieldExtensionDoc: Doc;
-    scrollTo?: (n: number) => void;
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
     pinToPres: (document: Doc) => void;
     document: Doc;
@@ -45,9 +39,11 @@ interface IRegionAnnotationProps {
 @observer
 class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
     private _reactionDisposer?: IReactionDisposer;
-    private _scrollDisposer?: IReactionDisposer;
+    private _brushDisposer?: IReactionDisposer;
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
 
+    @observable private _brushed: boolean = false;
+
     componentDidMount() {
         this._reactionDisposer = reaction(
             () => this.props.document.delete,
@@ -55,15 +51,18 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
             { fireImmediately: true }
         );
 
-        this._scrollDisposer = reaction(
-            () => this.props.ParentIndex(),
-            (ind) => ind === this.props.index && this.props.scrollTo && this.props.scrollTo(this.props.y * scale)
-        );
+        this._brushDisposer = reaction(
+            () => FieldValue(Cast(this.props.document.group, Doc)) && Doc.IsBrushed(FieldValue(Cast(this.props.document.group, Doc))!),
+            (brushed) => {
+                if (brushed !== undefined) {
+                    runInAction(() => this._brushed = brushed);
+                }
+            }
+        )
     }
 
     componentWillUnmount() {
         this._reactionDisposer && this._reactionDisposer();
-        this._scrollDisposer && this._scrollDisposer();
     }
 
     deleteAnnotation = () => {
@@ -126,7 +125,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
                 left: this.props.x,
                 width: this.props.width,
                 height: this.props.height,
-                backgroundColor: this.props.ParentIndex() === this.props.index ? "green" : StrCast(this.props.document.color)
+                backgroundColor: this._brushed ? "green" : StrCast(this.props.document.color)
             }} />);
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 3e6f24e4ac4a4b64620d8c9f614f214f8f1c7b94 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 17 Sep 2019 19:47:05 -0400
Subject: cleanup for animating document icon collapse/expand - still need to
 fix for docs without native dimensions.   lots of lint fixes.

---
 src/Utils.ts                                       | 17 ++---
 src/client/documents/Documents.ts                  |  4 +-
 src/client/util/ProsemirrorExampleTransfer.ts      |  4 +-
 src/client/util/RichTextRules.ts                   |  6 +-
 src/client/util/RichTextSchema.tsx                 | 14 ++--
 src/client/util/TooltipTextMenu.tsx                |  4 +-
 src/client/views/DocumentDecorations.tsx           |  2 +-
 src/client/views/GlobalKeyHandler.ts               |  2 +-
 src/client/views/OverlayView.tsx                   |  1 +
 src/client/views/ScriptBox.tsx                     |  2 +-
 src/client/views/collections/CollectionView.tsx    |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 14 ++--
 src/client/views/nodes/ButtonBox.tsx               |  2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 35 ++++------
 src/client/views/nodes/DocumentView.tsx            | 75 +++++++++-------------
 src/client/views/nodes/FormattedTextBox.tsx        |  6 +-
 src/client/views/nodes/ImageBox.tsx                |  8 +--
 src/client/views/nodes/PDFBox.tsx                  |  6 +-
 src/client/views/nodes/VideoBox.tsx                |  6 +-
 src/client/views/pdf/Annotation.tsx                |  2 +-
 src/client/views/pdf/PDFViewer.tsx                 |  2 +-
 src/new_fields/Doc.ts                              | 12 ++--
 .../authentication/models/current_user_utils.ts    |  2 +-
 23 files changed, 99 insertions(+), 129 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 415023ac4..65eb3cffd 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -115,28 +115,23 @@ export class Utils {
         // Calculate hue
 
         // No difference
-        if (delta == 0)
-            h = 0;
+        if (delta === 0) h = 0;
         // Red is max
-        else if (cmax == r)
-            h = ((g - b) / delta) % 6;
+        else if (cmax === r) h = ((g - b) / delta) % 6;
         // Green is max
-        else if (cmax == g)
-            h = (b - r) / delta + 2;
+        else if (cmax === g) h = (b - r) / delta + 2;
         // Blue is max
-        else
-            h = (r - g) / delta + 4;
+        else h = (r - g) / delta + 4;
 
         h = Math.round(h * 60);
 
         // Make negative hues positive behind 360°
-        if (h < 0)
-            h += 360; // Calculate lightness
+        if (h < 0) h += 360; // Calculate lightness
 
         l = (cmax + cmin) / 2;
 
         // Calculate saturation
-        s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
+        s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
 
         // Multiply l and s by 100
         // s = +(s * 100).toFixed(1);
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 673acfbaf..206e2c4f1 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -631,8 +631,8 @@ export namespace DocUtils {
                                 LinkManager.Instance.deleteLink(link);
                                 LinkManager.Instance.addLink(link);
                             }
-                        })
-                    })
+                        });
+                    });
                 }
             }
         });
diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts
index 1d2d33800..3e3d3155c 100644
--- a/src/client/util/ProsemirrorExampleTransfer.ts
+++ b/src/client/util/ProsemirrorExampleTransfer.ts
@@ -97,7 +97,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
         tx2.doc.descendants((node: any, offset: any, index: any) => {
             if (node.type === schema.nodes.ordered_list || node.type === schema.nodes.list_item) {
                 let path = (tx2.doc.resolve(offset) as any).path;
-                let depth = Array.from(path).reduce((p: number, c: any) => p + (c.hasOwnProperty("type") && (c as any).type === schema.nodes.ordered_list ? 1 : 0), 0);
+                let depth = Array.from(path).reduce((p: number, c: any) => p + (c.hasOwnProperty("type") && c.type === schema.nodes.ordered_list ? 1 : 0), 0);
                 if (node.type === schema.nodes.ordered_list) depth++;
                 tx2.setNodeMarkup(offset, node.type, { ...node.attrs, mapStyle: node.attrs.mapStyle, bulletStyle: depth }, node.marks);
             }
@@ -145,7 +145,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
         marks && tx.ensureMarks(marks.filter((val: any) => val.type !== schema.marks.metadata && val.type !== schema.marks.metadataKey && val.type !== schema.marks.metadataVal));
         marks && tx.setStoredMarks(marks.filter((val: any) => val.type !== schema.marks.metadata && val.type !== schema.marks.metadataKey && val.type !== schema.marks.metadataVal));
         return tx;
-    }
+    };
     bind("Enter", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
         var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
         if (!splitListItem(schema.nodes.list_item)(state, (tx3: Transaction) => dispatch(tx3))) {
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index c727eec73..cd37ea0bb 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -20,7 +20,7 @@ export const inpRules = {
             /^1\.\s$/,
             schema.nodes.ordered_list,
             () => {
-                return ({ mapStyle: "decimal", bulletStyle: 1 })
+                return ({ mapStyle: "decimal", bulletStyle: 1 });
             },
             (match: any, node: any) => {
                 return node.childCount + node.attrs.order === +match[1];
@@ -33,7 +33,7 @@ export const inpRules = {
             schema.nodes.ordered_list,
             // match => {
             () => {
-                return ({ mapStyle: "alpha", bulletStyle: 1 })
+                return ({ mapStyle: "alpha", bulletStyle: 1 });
                 // return ({ order: +match[1] })
             },
             (match: any, node: any) => {
@@ -67,7 +67,7 @@ export const inpRules = {
                     (Cast(FormattedTextBox.InputBoxOverlay!.props.Document, Doc) as Doc).heading = Number(match[1]);
                     return state.tr.deleteRange(start, end);
                 }
-                return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontSize.create({ fontSize: Number(match[1]) }))
+                return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontSize.create({ fontSize: Number(match[1]) }));
             }),
         new InputRule(
             new RegExp(/^\^\^\s$/),
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index f027a4bf7..ba4b92a25 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -349,8 +349,9 @@ export const marks: { [index: string]: MarkSpec } = {
                         let style = getComputedStyle(p);
                         if (style.textDecoration === "underline") return null;
                         if (p.parentElement.outerHTML.indexOf("text-decoration: underline") !== -1 &&
-                            p.parentElement.outerHTML.indexOf("text-decoration-style: dotted") !== -1)
+                            p.parentElement.outerHTML.indexOf("text-decoration-style: dotted") !== -1) {
                             return null;
+                        }
                     }
                     return false;
                 }
@@ -371,10 +372,9 @@ export const marks: { [index: string]: MarkSpec } = {
                 getAttrs: (p: any) => {
                     if (typeof (p) !== "string") {
                         let style = getComputedStyle(p);
-                        if (style.textDecoration === "underline")
-                            return null;
-                        if (p.parentElement.outerHTML.indexOf("text-decoration-style:line") !== -1)
+                        if (style.textDecoration === "underline" || p.parentElement.outerHTML.indexOf("text-decoration-style:line") !== -1) {
                             return null;
+                        }
                     }
                     return false;
                 }
@@ -633,11 +633,11 @@ export class ImageResizeView {
                             DocumentManager.Instance.jumpToDocument(jumpToDoc, e.ctrlKey, false, document => addDocTab(document, undefined, location ? location : "inTab"));
                         } else {
                             DocumentManager.Instance.jumpToDocument(linkDoc, e.ctrlKey, false, document => addDocTab(document, undefined, location ? location : "inTab"));
-                        } e.ctrlKey
+                        }
                     }
                 });
             }
-        }
+        };
         this._handle.onpointerdown = function (e: any) {
             e.preventDefault();
             e.stopPropagation();
@@ -776,7 +776,7 @@ export class FootnoteView {
         this.innerView.updateState(state);
 
         if (!tr.getMeta("fromOutside")) {
-            let outerTr = this.outerView.state.tr, offsetMap = StepMap.offset(this.getPos() + 1)
+            let outerTr = this.outerView.state.tr, offsetMap = StepMap.offset(this.getPos() + 1);
             for (let i = 0; i < transactions.length; i++) {
                 let steps = transactions[i].steps;
                 for (let j = 0; j < steps.length; j++) {
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 84d045e6f..5764af282 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -523,12 +523,12 @@ export class TooltipTextMenu {
         tx2.doc.descendants((node: any, offset: any, index: any) => {
             if (node.type === schema.nodes.ordered_list || node.type === schema.nodes.list_item) {
                 let path = (tx2.doc.resolve(offset) as any).path;
-                let depth = Array.from(path).reduce((p: number, c: any) => p + (c.hasOwnProperty("type") && (c as any).type === schema.nodes.ordered_list ? 1 : 0), 0);
+                let depth = Array.from(path).reduce((p: number, c: any) => p + (c.hasOwnProperty("type") && c.type === schema.nodes.ordered_list ? 1 : 0), 0);
                 if (node.type === schema.nodes.ordered_list) depth++;
                 tx2.setNodeMarkup(offset, node.type, { mapStyle: style, bulletStyle: depth }, node.marks);
             }
         });
-    };
+    }
     //remove all node typeand apply the passed-in one to the selected text
     changeToNodeType = (nodeType: NodeType | undefined, view: EditorView) => {
         //remove oldif (nodeType) { //add new
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 7829bd7f1..9a2105467 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -439,7 +439,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             let heading = NumCast(dv.props.Document.heading);
             ruleProvider && heading && (Doc.GetProto(ruleProvider)["ruleRounding_" + heading] = `${Math.min(100, dist)}%`);
             usingRule = usingRule || (ruleProvider && heading ? true : false);
-        })
+        });
         !usingRule && SelectionManager.SelectedDocuments().map(dv => dv.props.Document.layout instanceof Doc ? dv.props.Document.layout : dv.props.Document.isTemplate ? dv.props.Document : Doc.GetProto(dv.props.Document)).
             map(d => d.borderRounding = `${Math.min(100, dist)}%`);
         e.stopPropagation();
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index ba125d6e5..59229418d 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -166,7 +166,7 @@ export default class KeyManager {
                 break;
             case "o":
                 let target = SelectionManager.SelectedDocuments()[0];
-                target && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(target)
+                target && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(target);
                 break;
             case "r":
                 preventDefault = false;
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index da4b71e5c..15faea3cd 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -172,6 +172,7 @@ export class OverlayView extends React.Component {
                     ChromeHeight={returnZero}
                     isSelected={returnFalse}
                     select={emptyFunction}
+                    ruleProvider={undefined}
                     layoutKey={"layout"}
                     bringToFront={emptyFunction}
                     addDocument={undefined}
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index 8f06cf770..8ef9f3be6 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -98,7 +98,7 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
         // tslint:disable-next-line: no-unnecessary-callback-wrapper
         let params: string[] = [];
         let setParams = (p: string[]) => params.splice(0, params.length, ...p);
-        let scriptingBox = <ScriptBox initialText={originalText} setParams={setParams} onCancel={() => overlayDisposer()} onSave={(text, onError) => {
+        let scriptingBox = <ScriptBox initialText={originalText} setParams={setParams} onCancel={overlayDisposer} onSave={(text, onError) => {
             if (prewrapper) {
                 text = prewrapper + text + (postwrapper ? postwrapper : "");
             }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 548f663ec..5f4742834 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -99,7 +99,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
             subItems.push({
                 description: "Stacking (AutoHeight)", event: () => {
                     this.props.Document.viewType = CollectionViewType.Stacking;
-                    this.props.Document.autoHeight = true
+                    this.props.Document.autoHeight = true;
                 }, icon: "ellipsis-v"
             });
             subItems.push({ description: "Masonry", event: () => this.props.Document.viewType = CollectionViewType.Masonry, icon: "columns" });
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 03ac012b4..8d392d764 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -24,9 +24,9 @@ import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss"
 import { ContextMenu } from "../../ContextMenu";
 import { ContextMenuProps } from "../../ContextMenuItem";
 import { InkingCanvas } from "../../InkingCanvas";
-import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
+import { CollectionFreeFormDocumentView, positionSchema } from "../../nodes/CollectionFreeFormDocumentView";
 import { DocumentContentsView } from "../../nodes/DocumentContentsView";
-import { DocumentViewProps, positionSchema } from "../../nodes/DocumentView";
+import { DocumentViewProps, documentSchema } from "../../nodes/DocumentView";
 import { pageSchema } from "../../nodes/ImageBox";
 import { OverlayElementOptions, OverlayView } from "../../OverlayView";
 import PDFMenu from "../../pdf/PDFMenu";
@@ -176,8 +176,8 @@ export namespace PivotView {
 
 }
 
-type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof positionSchema, typeof pageSchema]>;
-const PanZoomDocument = makeInterface(panZoomSchema, positionSchema, pageSchema);
+type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof documentSchema, typeof positionSchema, typeof pageSchema]>;
+const PanZoomDocument = makeInterface(panZoomSchema, documentSchema, positionSchema, pageSchema);
 
 @observer
 export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@@ -341,7 +341,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                         this.bringToFront(d);
                     });
 
-                    de.data.droppedDocuments.length == 1 && this.updateCluster(de.data.droppedDocuments[0]);
+                    de.data.droppedDocuments.length === 1 && this.updateCluster(de.data.droppedDocuments[0]);
                 }
             }
             else if (de.data instanceof DragManager.AnnotationDragData) {
@@ -473,7 +473,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                 // choose a cluster color from a palette
                 let colors = ["#da42429e", "#31ea318c", "#8c4000", "#4a7ae2c4", "#d809ff", "#ff7601", "#1dffff", "yellow", "#1b8231f2", "#000000ad"];
                 clusterColor = colors[cluster % colors.length];
-                let set = this.sets.length > cluster ? this.sets[cluster].filter(s => s.backgroundColor && (s.backgroundColor != s.defaultBackgroundColor)) : undefined;
+                let set = this.sets.length > cluster ? this.sets[cluster].filter(s => s.backgroundColor && (s.backgroundColor !== s.defaultBackgroundColor)) : undefined;
                 // override the cluster color with an explicitly set color on a non-background document.  then override that with an explicitly set color on a background document
                 set && set.filter(s => !s.isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
                 set && set.filter(s => s.isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
@@ -848,7 +848,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                     Doc.GetProto(this.props.Document)["ruleColor_" + NumCast(headingLayout.heading)] = headingLayout.backgroundColor;
                 }
             })
-        )
+        );
     }
 
     analyzeStrokes = async () => {
diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx
index 68d3b8ae1..eebf6c167 100644
--- a/src/client/views/nodes/ButtonBox.tsx
+++ b/src/client/views/nodes/ButtonBox.tsx
@@ -49,7 +49,7 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt
         funcs.push({
             description: "Clear Script Params", event: () => {
                 let params = Cast(this.props.Document.buttonParams, listSpec("string"));
-                params && params.map(p => this.props.Document[p] = undefined)
+                params && params.map(p => this.props.Document[p] = undefined);
             }, icon: "trash"
         });
 
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 19d4a6784..bade3f8c1 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,10 +1,10 @@
-import { computed, action, observable, reaction, IReactionDisposer } from "mobx";
+import { computed, action, observable, reaction, IReactionDisposer, trace } from "mobx";
 import { observer } from "mobx-react";
 import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
 import { FieldValue, NumCast, StrCast, Cast } from "../../../new_fields/Types";
 import { Transform } from "../../util/Transform";
 import { DocComponent } from "../DocComponent";
-import { percent2frac } from "../../../Utils"
+import { percent2frac } from "../../../Utils";
 import { DocumentView, DocumentViewProps, documentSchema } from "./DocumentView";
 import "./CollectionFreeFormDocumentView.scss";
 import React = require("react");
@@ -18,7 +18,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     height?: number;
     jitterRotation: number;
 }
-const positionSchema = createSchema({
+export const positionSchema = createSchema({
     zIndex: "number",
     x: "number",
     y: "number",
@@ -32,8 +32,8 @@ export const PositionDocument = makeInterface(documentSchema, positionSchema);
 export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, PositionDocument>(PositionDocument) {
     _disposer: IReactionDisposer | undefined = undefined;
     @computed get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${random(-1, 1) * this.props.jitterRotation}deg)`; }
-    @computed get X() { return this._animx !== undefined ? this._animx : this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.Document.x || 0; }
-    @computed get Y() { return this._animy !== undefined ? this._animy : this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.Document.y || 0; }
+    @computed get X() { return this._animPos !== undefined ? this._animPos[0] : this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.Document.x || 0; }
+    @computed get Y() { return this._animPos !== undefined ? this._animPos[1] : this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.Document.y || 0; }
     @computed get width() { return this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.props.Document[WidthSym](); }
     @computed get height() { return this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.Document[HeightSym](); }
     @computed get nativeWidth() { return FieldValue(this.Document.nativeWidth, 0); }
@@ -59,22 +59,10 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         this._disposer && this._disposer();
     }
     componentDidMount() {
-        this._disposer = reaction(() => this.props.Document.iconTarget,
+        this._disposer = reaction(() => [this.props.Document.animateToPos, this.props.Document.isAnimating],
             () => {
-                const icon = this.props.Document.iconTarget ? Array.from(Cast(this.props.Document.iconTarget, listSpec("number"))!) : undefined;
-                if (icon) {
-                    let target = this.props.ScreenToLocalTransform().transformPoint(icon[0], icon[1]);
-                    if (icon[2] === 1) {
-                        this._animx = target[0];
-                        this._animy = target[1];
-                    }
-                    setTimeout(action(() => {
-                        this._animx = icon[2] === 1 ? this.Document.x : target[0];
-                        this._animy = icon[2] === 1 ? this.Document.y : target[1];
-                    }), 25);
-                } else {
-                    this._animx = this._animy = undefined;
-                }
+                const target = this.props.Document.animateToPos ? Array.from(Cast(this.props.Document.animateToPos, listSpec("number"))!) : undefined;
+                this._animPos = !target ? undefined : target[2] ? [this.Document.x || 0, this.Document.y || 0] : this.props.ScreenToLocalTransform().transformPoint(target[0], target[1]);
             }, { fireImmediately: true });
     }
 
@@ -83,7 +71,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     panelHeight = () => this.props.PanelHeight();
     getTransform = (): Transform => this.props.ScreenToLocalTransform()
         .translate(-this.X, -this.Y)
-        .scale(1 / this.contentScaling()).scale(1 / this.scaleToOverridingWidth);
+        .scale(1 / this.contentScaling()).scale(1 / this.scaleToOverridingWidth)
 
     borderRounding = () => {
         let ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined;
@@ -107,8 +95,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
     }
 
-    @observable _animx: number | undefined = undefined;
-    @observable _animy: number | undefined = undefined;
+    @observable _animPos: number[] | undefined = undefined;
 
     render() {
         const hasPosition = this.props.x !== undefined || this.props.y !== undefined;
@@ -123,7 +110,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                                         StrCast(this.layoutDoc.boxShadow, ""),
                     borderRadius: this.borderRounding(),
                     transform: this.transform,
-                    transition: this.props.Document.isIconAnimating ? "transform .5s" : hasPosition ? "transform 1s" : StrCast(this.layoutDoc.transition),
+                    transition: this.Document.isAnimating !== undefined ? "transform .5s ease-in" : hasPosition ? "transform 1s" : StrCast(this.layoutDoc.transition),
                     width: this.width,
                     height: this.height,
                     zIndex: this.Document.zIndex || 0,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 37c38cc04..669c58ac7 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,9 +1,9 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
 import * as fa from '@fortawesome/free-solid-svg-icons';
-import { action, computed, IReactionDisposer, reaction, runInAction } from "mobx";
+import { action, computed, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as rp from "request-promise";
-import { Doc, DocListCast, DocListCastAsync, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
+import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc";
 import { Copy, Id } from '../../../new_fields/FieldSymbols';
 import { List } from "../../../new_fields/List";
 import { ObjectField } from "../../../new_fields/ObjectField";
@@ -209,43 +209,31 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @action
     public collapseTargetsToPoint = (scrpt: number[], expandedDocs: Doc[] | undefined): void => {
         SelectionManager.DeselectAll();
-        if (expandedDocs) {
-            let isMinimized: boolean | undefined;
-            expandedDocs.map(maximizedDoc => {
-                if (isMinimized === undefined) {
-                    isMinimized = BoolCast(maximizedDoc.isMinimized);
+        expandedDocs && expandedDocs.map(expDoc => {
+            if (expDoc.isMinimized || expDoc.isAnimating === "min") { // MAXIMIZE DOC
+                if (expDoc.isMinimized) {  // docs are never actaully at the minimized location.  so when we unminimize one, we have to set our overrides to make it look like it was at the minimize location
+                    expDoc.isMinimized = false;
+                    expDoc.animateToPos = new List<number>([...scrpt, 0]);
+                    expDoc.animateToDimensions = new List<number>([0, 0]);
                 }
-                let w = NumCast(maximizedDoc.width);
-                let h = NumCast(maximizedDoc.height);
-                let iconAnimating = maximizedDoc.isIconAnimating ? Array.from(Cast(maximizedDoc.isIconAnimating, listSpec("number"))!) : undefined;
-                if (isMinimized || (iconAnimating && iconAnimating.length && iconAnimating[0] === 0)) {
-                    // MAXIMIZE DOC
-                    if (maximizedDoc.isMinimized) {
-                        maximizedDoc.isIconAnimating = new List<number>([0, 0]);
-                        maximizedDoc.isMinimized = false;
+                setTimeout(() => {
+                    expDoc.isAnimating = "max";
+                    expDoc.animateToPos = new List<number>([0, 0, 1]);
+                    expDoc.animateToDimensions = new List<number>([NumCast(expDoc.width), NumCast(expDoc.height)]);
+                    setTimeout(() => expDoc.isAnimating === "max" && (expDoc.isAnimating = expDoc.animateToPos = expDoc.animateToDimensions = undefined), 600);
+                }, 0);
+            } else {  // MINIMIZE DOC
+                expDoc.isAnimating = "min";
+                expDoc.animateToPos = new List<number>([...scrpt, 0]);
+                expDoc.animateToDimensions = new List<number>([0, 0]);
+                setTimeout(() => {
+                    if (expDoc.isAnimating === "min") {
+                        expDoc.isMinimized = true;
+                        expDoc.isAnimating = expDoc.animateToPos = expDoc.animateToDimensions = undefined;
                     }
-                    maximizedDoc.iconTarget = new List<number>([...scrpt, 1]);
-                    setTimeout(() => {
-                        maximizedDoc.isIconAnimating = new List<number>([w, h]);
-                        setTimeout(() => {
-                            if (maximizedDoc.isIconAnimating && Array.from(Cast(maximizedDoc.isIconAnimating, listSpec("number"))!)[0] !== 0) {
-                                maximizedDoc.isIconAnimating = undefined;
-                            }
-                        }, 750);
-                    }, 0);
-                } else {
-                    maximizedDoc.iconTarget = new List<number>([...scrpt, 0]);
-                    // MINIMIZE DOC
-                    maximizedDoc.isIconAnimating = new List<number>([0, 0]);
-                    setTimeout(() => {
-                        if (maximizedDoc.isIconAnimating && Array.from(Cast(maximizedDoc.isIconAnimating, listSpec("number"))!)[0] === 0) {
-                            maximizedDoc.isMinimized = true;
-                            maximizedDoc.isIconAnimating = undefined;
-                        }
-                    }, 750);
-                }
-            });
-        }
+                }, 600);
+            }
+        });
     }
 
     onClick = async (e: React.MouseEvent) => {
@@ -530,14 +518,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 let portal = existingPortal instanceof Doc ? existingPortal : Docs.Create.FreeformDocument([], { width: (this.Document.width || 0) + 10, height: this.Document.height || 0, title: portalID });
                 DocUtils.MakeLink(this.props.Document, portal, undefined, portalID);
                 Doc.GetProto(this.props.Document).isButton = true;
-            })
+            });
         }
     }
     @undoBatch
     @action
     toggleCustomView = (): void => {
         if (this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.DataDoc) {
-            Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.ContainingCollectionView.props.DataDoc)
+            Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.ContainingCollectionView.props.DataDoc);
         } else {
             if (this.Document.type !== DocumentType.COL && this.Document.type !== DocumentType.TEMPLATE) {
                 this.makeCustomViewClicked();
@@ -623,7 +611,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
         layoutItems.push({ description: this.Document.isBackground ? "As Foreground" : "As Background", event: this.makeBackground, icon: this.Document.lockedPosition ? "unlock" : "lock" });
         if (this.props.DataDoc) {
-            layoutItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc!), icon: "concierge-bell" })
+            layoutItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc!), icon: "concierge-bell" });
         }
         layoutItems.push({ description: `${this.layoutDoc.chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.layoutDoc.chromeStatus = (this.layoutDoc.chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
         layoutItems.push({ description: `${this.layoutDoc.autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc.autoHeight = !this.layoutDoc.autoHeight, icon: "plus" });
@@ -786,7 +774,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
         const borderRounding = this.Document.borderRounding || ruleRounding;
         const localScale = this.props.ScreenToLocalTransform().Scale * fullDegree;
-        const iconAnimating = this.Document.isIconAnimating ? Array.from(Cast(this.Document.isIconAnimating, listSpec("number"))!) : undefined;
         const searchHighlight = (!this.Document.searchFields ? (null) :
             <div className="documentView-searchHighlight" style={{ width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
                 {this.Document.searchFields}
@@ -817,7 +804,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
                 style={{
-                    transition: iconAnimating ? "transform .5s" : StrCast(this.layoutDoc.transition),
+                    transition: this.props.Document.isAnimating !== undefined ? "transform .5s linear" : StrCast(this.layoutDoc.transition),
                     pointerEvents: this.layoutDoc.isBackground && !this.isSelected() ? "none" : "all",
                     color: StrCast(this.layoutDoc.color),
                     outlineColor: ["transparent", "maroon", "maroon", "yellow"][fullDegree],
@@ -881,7 +868,7 @@ let makeNativeView = (doc: any): void => {
     doc.nonCustomNativeWidth = undefined;
     doc.nonCustomNativeHeight = undefined;
     doc.nonCustomIgnoreAspect = undefined;
-}
+};
 let makeCustomView = (doc: any): void => {
     doc.nativeLayout = doc.layout;
     doc.nativeType = doc.type;
@@ -911,7 +898,7 @@ let makeCustomView = (doc: any): void => {
         doc.customNativeHeight = undefined;
         doc.customIgnoreAspect = undefined;
     }
-}
+};
 Scripting.addGlobal(function toggleDetail(doc: any) {
     if (doc.type !== DocumentType.COL && doc.type !== DocumentType.TEMPLATE) {
         makeCustomView(doc);
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 77e29632e..1c946aa78 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -182,9 +182,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                     DocUtils.Publish(this.dataDoc[key] as Doc, value, this.props.addDocument, this.props.removeDocument);
                     if (linkDoc) { (linkDoc as Doc).anchor2 = this.dataDoc[key] as Doc; }
                     else DocUtils.MakeLink(this.dataDoc, this.dataDoc[key] as Doc, undefined, "Ref:" + value, undefined, undefined, id, true);
-                })
+                });
             });
-        })
+        });
         this.linkOnDeselect.clear();
     }
 
@@ -195,7 +195,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 let range = tx.selection.$from.blockRange(tx.selection.$to);
                 let text = range ? tx.doc.textBetween(range.start, range.end) : "";
                 let textEndSelection = tx.selection.to;
-                for (; textEndSelection < range!.end && text[textEndSelection - range!.start] != " "; textEndSelection++) { }
+                for (; textEndSelection < range!.end && text[textEndSelection - range!.start] !== " "; textEndSelection++) { }
                 text = text.substr(0, textEndSelection - range!.start);
                 text = text.split(" ")[text.split(" ").length - 1];
                 let split = text.split("::");
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index beccce9dd..42307ee02 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -23,7 +23,7 @@ import { ContextMenu } from "../../views/ContextMenu";
 import { ContextMenuProps } from '../ContextMenuItem';
 import { DocComponent } from '../DocComponent';
 import { InkingControl } from '../InkingControl';
-import { positionSchema } from './DocumentView';
+import { documentSchema } from './DocumentView';
 import FaceRectangles from './FaceRectangles';
 import { FieldView, FieldViewProps } from './FieldView';
 import "./ImageBox.scss";
@@ -50,8 +50,8 @@ declare class MediaRecorder {
     constructor(e: any);
 }
 
-type ImageDocument = makeInterface<[typeof pageSchema, typeof positionSchema]>;
-const ImageDocument = makeInterface(pageSchema, positionSchema);
+type ImageDocument = makeInterface<[typeof pageSchema, typeof documentSchema]>;
+const ImageDocument = makeInterface(pageSchema, documentSchema);
 
 @observer
 export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageDocument) {
@@ -220,7 +220,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
             let modes: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
             modes.push({ description: "Generate Tags", event: this.generateMetadata, icon: "tag" });
             modes.push({ description: "Find Faces", event: this.extractFaces, icon: "camera" });
-            !existingAnalyze && ContextMenu.Instance.addItem({ description: "Analyzers...", subitems: modes, icon: "hand-point-right" })
+            !existingAnalyze && ContextMenu.Instance.addItem({ description: "Analyzers...", subitems: modes, icon: "hand-point-right" });
 
             ContextMenu.Instance.addItem({ description: "Image Funcs...", subitems: funcs, icon: "asterisk" });
         }
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index df35b603c..31e8f122b 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -14,14 +14,14 @@ import { CompileScript } from '../../util/Scripting';
 import { DocComponent } from "../DocComponent";
 import { InkingControl } from "../InkingControl";
 import { PDFViewer } from "../pdf/PDFViewer";
-import { positionSchema } from "./DocumentView";
+import { documentSchema } from "./DocumentView";
 import { FieldView, FieldViewProps } from './FieldView';
 import { pageSchema } from "./ImageBox";
 import "./PDFBox.scss";
 import React = require("react");
 
-type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
-const PdfDocument = makeInterface(positionSchema, pageSchema);
+type PdfDocument = makeInterface<[typeof documentSchema, typeof pageSchema]>;
+const PdfDocument = makeInterface(documentSchema, pageSchema);
 export const handleBackspace = (e: React.KeyboardEvent) => { if (e.keyCode === KeyCodes.BACKSPACE) e.stopPropagation(); };
 
 @observer
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 96f011eff..a696f255d 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -14,7 +14,7 @@ import { ContextMenuProps } from "../ContextMenuItem";
 import { DocComponent } from "../DocComponent";
 import { DocumentDecorations } from "../DocumentDecorations";
 import { InkingControl } from "../InkingControl";
-import { positionSchema } from "./DocumentView";
+import { documentSchema } from "./DocumentView";
 import { FieldView, FieldViewProps } from './FieldView';
 import { pageSchema } from "./ImageBox";
 import "./VideoBox.scss";
@@ -25,8 +25,8 @@ import { Doc } from "../../../new_fields/Doc";
 import { ScriptField } from "../../../new_fields/ScriptField";
 var path = require('path');
 
-type VideoDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
-const VideoDocument = makeInterface(positionSchema, pageSchema);
+type VideoDocument = makeInterface<[typeof documentSchema, typeof pageSchema]>;
+const VideoDocument = makeInterface(documentSchema, pageSchema);
 
 library.add(faVideo);
 
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index eeb2531a2..34e3b0931 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -58,7 +58,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
                     runInAction(() => this._brushed = brushed);
                 }
             }
-        )
+        );
     }
 
     componentWillUnmount() {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 7bc1d3507..c508935f2 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -81,7 +81,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         return annotations.filter(anno => {
             let run = this._script.run({ this: anno });
             return run.success ? run.result : true;
-        })
+        });
     }
 
     @computed get nonDocAnnotations() {
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 614babd3c..0cf1208d7 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -144,12 +144,12 @@ export class Doc extends RefField {
     private [Self] = this;
     private [SelfProxy]: any;
     public [WidthSym] = () => {
-        let iconAnimating = this[SelfProxy].isIconAnimating ? Array.from(Cast(this[SelfProxy].isIconAnimating, listSpec("number"))!) : undefined;
-        return iconAnimating ? iconAnimating[0] : NumCast(this[SelfProxy].width);
+        let animDims = this[SelfProxy].animateToDimensions ? Array.from(Cast(this[SelfProxy].animateToDimensions, listSpec("number"))!) : undefined;
+        return animDims ? animDims[0] : NumCast(this[SelfProxy].width);
     }
     public [HeightSym] = () => {
-        let iconAnimating = this[SelfProxy].isIconAnimating ? Array.from(Cast(this[SelfProxy].isIconAnimating, listSpec("number"))!) : undefined;
-        return iconAnimating ? iconAnimating[1] : NumCast(this[SelfProxy].height);
+        let animDims = this[SelfProxy].animateToDimensions ? Array.from(Cast(this[SelfProxy].animateToDimensions, listSpec("number"))!) : undefined;
+        return animDims ? animDims[1] : NumCast(this[SelfProxy].height);
     }
 
     [ToScriptString]() {
@@ -334,7 +334,7 @@ export namespace Doc {
     }
 
     export function IndexOf(toFind: Doc, list: Doc[]) {
-        return list.findIndex(doc => doc === toFind || Doc.AreProtosEqual(doc, toFind))
+        return list.findIndex(doc => doc === toFind || Doc.AreProtosEqual(doc, toFind));
     }
     export function AddDocToList(target: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean) {
         if (target[key] === undefined) {
@@ -419,7 +419,7 @@ export namespace Doc {
     export function MakeAlias(doc: Doc) {
         let alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc) : Doc.MakeDelegate(doc);
         if (alias.layout instanceof Doc) {
-            alias.layout = Doc.MakeAlias(alias.layout as Doc);
+            alias.layout = Doc.MakeAlias(alias.layout);
         }
         let aliasNumber = Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1;
         let script = `return renameAlias(self, ${aliasNumber})`;
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index af5774ebe..8cac8550c 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -59,7 +59,7 @@ export class CurrentUserUtils {
             noteTypes.excludeFromLibrary = true;
             doc.noteTypes = noteTypes;
         }
-        PromiseValue(Cast(doc.noteTypes, Doc)).then(noteTypes => noteTypes && PromiseValue(noteTypes.data).then(vals => DocListCast(vals)));
+        PromiseValue(Cast(doc.noteTypes, Doc)).then(noteTypes => noteTypes && PromiseValue(noteTypes.data).then(DocListCast));
         if (doc.recentlyClosed === undefined) {
             const recentlyClosed = Docs.Create.TreeDocument([], { title: "Recently Closed", height: 75 });
             recentlyClosed.excludeFromLibrary = true;
-- 
cgit v1.2.3-70-g09d2


From 2cdca63ac039a7c66a9c93acb35fe51467269e64 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 19 Sep 2019 11:29:11 -0400
Subject: switche calls to COllectionDockingView... to addDocTabs.  fixed
 following links to navigate/restore properly.

---
 src/client/util/DocumentManager.ts                 | 15 ++--
 src/client/util/TooltipTextMenu.tsx                |  8 +-
 src/client/views/GlobalKeyHandler.ts               |  4 +-
 src/client/views/MainOverlayTextBox.tsx            |  4 +-
 src/client/views/MainView.tsx                      | 10 ++-
 src/client/views/OverlayView.tsx                   |  2 +-
 .../views/collections/CollectionBaseView.tsx       |  4 +-
 .../views/collections/CollectionDockingView.tsx    | 85 ++++++++++++----------
 .../views/collections/CollectionSchemaCells.tsx    |  2 +-
 .../views/collections/CollectionSchemaView.tsx     |  4 +-
 .../views/collections/CollectionTreeView.tsx       |  8 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 17 +++--
 src/client/views/linking/LinkFollowBox.tsx         | 16 ++--
 src/client/views/linking/LinkMenu.tsx              |  2 +-
 src/client/views/linking/LinkMenuGroup.tsx         | 24 +++---
 src/client/views/linking/LinkMenuItem.tsx          |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 24 ++----
 src/client/views/nodes/FieldView.tsx               |  2 +-
 src/client/views/nodes/KeyValueBox.tsx             |  2 +-
 src/client/views/nodes/KeyValuePair.tsx            |  7 +-
 src/client/views/pdf/Annotation.tsx                |  8 +-
 src/client/views/pdf/PDFViewer.tsx                 |  2 +-
 src/client/views/search/SearchItem.tsx             |  4 +-
 23 files changed, 127 insertions(+), 129 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index ec731da84..65ab32539 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -10,6 +10,7 @@ import { DocumentView } from '../views/nodes/DocumentView';
 import { LinkManager } from './LinkManager';
 import { undoBatch, UndoManager } from './UndoManager';
 import { Scripting } from './Scripting';
+import { emptyFunction } from '../../Utils';
 
 
 export class DocumentManager {
@@ -146,6 +147,7 @@ export class DocumentManager {
             if (!contextDoc) {
                 let docs = docContext ? await DocListCastAsync(docContext.data) : undefined;
                 let found = false;
+                // bcz: this just searches within the context for the target -- perhaps it should recursively search through all children?
                 docs && docs.map(d => found = found || Doc.AreProtosEqual(d, docDelegate));
                 if (docContext && found) {
                     let targetContextView: DocumentView | null;
@@ -154,16 +156,19 @@ export class DocumentManager {
                         docContext.panTransformType = "Ease";
                         targetContextView.props.focus(docDelegate, willZoom);
                     } else {
-                        (dockFunc || CollectionDockingView.Instance.AddRightSplit)(docContext, undefined);
+                        (dockFunc || CollectionDockingView.AddRightSplit)(docContext, undefined);
                         setTimeout(() => {
-                            this.jumpToDocument(docDelegate, willZoom, forceDockFunc, dockFunc, linkPage);
-                        }, 10);
+                            let dv = DocumentManager.Instance.getDocumentView(docContext);
+                            dv && this.jumpToDocument(docDelegate, willZoom, forceDockFunc,
+                                doc => dv!.props.focus(dv!.props.Document, true, 1, () => dv!.props.addDocTab(doc, undefined, "inPlace")),
+                                linkPage);
+                        }, 1050);
                     }
                 } else {
                     const actualDoc = Doc.MakeAlias(docDelegate);
                     Doc.BrushDoc(actualDoc);
                     if (linkPage !== undefined) actualDoc.curPage = linkPage;
-                    (dockFunc || CollectionDockingView.Instance.AddRightSplit)(actualDoc, undefined);
+                    (dockFunc || CollectionDockingView.AddRightSplit)(actualDoc, undefined);
                 }
             } else {
                 let contextView: DocumentView | null;
@@ -172,7 +177,7 @@ export class DocumentManager {
                     contextDoc.panTransformType = "Ease";
                     contextView.props.focus(docDelegate, willZoom);
                 } else {
-                    (dockFunc || CollectionDockingView.Instance.AddRightSplit)(contextDoc, undefined);
+                    (dockFunc || CollectionDockingView.AddRightSplit)(contextDoc, undefined);
                     setTimeout(() => {
                         this.jumpToDocument(docDelegate, willZoom, forceDockFunc, dockFunc, linkPage);
                     }, 10);
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index aa096dd64..b6de048e4 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -58,10 +58,6 @@ export class TooltipTextMenu {
 
     private _collapsed: boolean = false;
 
-    @observable
-    private _storedMarks: Mark<any>[] | null | undefined;
-
-
     constructor(view: EditorView, editorProps: FieldViewProps & FormattedTextBoxProps) {
         this.view = view;
         this.editorProps = editorProps;
@@ -84,8 +80,6 @@ export class TooltipTextMenu {
 
         this.dragElement(dragger);
 
-        this._storedMarks = this.view.state.storedMarks;
-
         // this.createCollapse();
         // if (this._collapseBtn) {
         //     this.tooltip.appendChild(this._collapseBtn.render(this.view).dom);
@@ -280,7 +274,7 @@ export class TooltipTextMenu {
                                 if (DocumentManager.Instance.getDocumentView(f)) {
                                     DocumentManager.Instance.getDocumentView(f)!.props.focus(f, false);
                                 }
-                                else if (CollectionDockingView.Instance) CollectionDockingView.Instance.AddRightSplit(f, undefined);
+                                else this.editorProps.addDocTab(f, undefined, "onRight");
                             }
                         }));
                     }
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index ef5d76ce8..2fa03e969 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -141,7 +141,7 @@ export default class KeyManager {
                         return { stopPropagation: false, preventDefault: false };
                     }
                 }
-                MainView.Instance.mainFreeform && CollectionDockingView.Instance.AddRightSplit(MainView.Instance.mainFreeform, undefined);
+                MainView.Instance.mainFreeform && CollectionDockingView.AddRightSplit(MainView.Instance.mainFreeform, undefined);
                 break;
             case "arrowleft":
                 if (document.activeElement) {
@@ -149,7 +149,7 @@ export default class KeyManager {
                         return { stopPropagation: false, preventDefault: false };
                     }
                 }
-                MainView.Instance.mainFreeform && CollectionDockingView.Instance.CloseRightSplit(MainView.Instance.mainFreeform);
+                MainView.Instance.mainFreeform && CollectionDockingView.CloseRightSplit(MainView.Instance.mainFreeform);
                 break;
             case "backspace":
                 if (document.activeElement) {
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index ac1b437af..f32fb584a 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -119,9 +119,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
     }
 
     addDocTab = (doc: Doc, dataDoc: Doc | undefined, location: string) => {
-        if (true) { // location === "onRight") { need to figure out stack to add "inTab"
-            CollectionDockingView.Instance.AddRightSplit(doc, dataDoc);
-        }
+        this._textBox && this._textBox.props.addDocTab(doc, dataDoc, location);
     }
     render() {
         this.TextDoc; this.TextDataDoc;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 8b0caf9a6..097040b6c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -288,6 +288,7 @@ export class MainView extends React.Component {
                 }
             }
         }, 100);
+        return true;
     }
 
     onDrop = (e: React.DragEvent<HTMLDivElement>) => {
@@ -319,7 +320,7 @@ export class MainView extends React.Component {
                         <DocumentView Document={mainCont}
                             DataDoc={undefined}
                             addDocument={undefined}
-                            addDocTab={emptyFunction}
+                            addDocTab={this.addDocTabFunc}
                             pinToPres={emptyFunction}
                             onClick={undefined}
                             ruleProvider={undefined}
@@ -368,11 +369,14 @@ export class MainView extends React.Component {
         document.removeEventListener("pointerup", this.onPointerUp);
     }
     flyoutWidthFunc = () => this.flyoutWidth;
-    addDocTabFunc = (doc: Doc) => {
+    addDocTabFunc = (doc: Doc, data: Opt<Doc>, where: string) => {
+        if (where === "close")
+            return CollectionDockingView.CloseRightSplit(doc);
         if (doc.dockingConfig) {
             this.openWorkspace(doc);
+            return true;
         } else {
-            CollectionDockingView.Instance.AddRightSplit(doc, undefined);
+            return CollectionDockingView.AddRightSplit(doc, undefined);
         }
     }
     @computed
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 8124ce653..45e0a3562 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -186,7 +186,7 @@ export class OverlayView extends React.Component {
                     whenActiveChanged={emptyFunction}
                     focus={emptyFunction}
                     backgroundColor={returnEmptyString}
-                    addDocTab={emptyFunction}
+                    addDocTab={returnFalse}
                     pinToPres={emptyFunction}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index fdb2f0dc9..0399371ff 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -103,7 +103,6 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         if (this.props.fieldExt) { // bcz: fieldExt !== undefined means this is an overlay layer
             Doc.GetProto(doc).annotationOn = this.props.Document;
         }
-        allowDuplicates = true;
         let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document;
         let targetField = (this.props.fieldExt || this.props.Document.isTemplate) && this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey;
         const value = Cast(targetDataDoc[targetField], listSpec(Doc));
@@ -126,7 +125,8 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document;
         let targetField = (this.props.fieldExt || this.props.Document.isTemplate) && this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey;
         let value = Cast(targetDataDoc[targetField], listSpec(Doc), []);
-        let index = value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
+        let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1);
+        index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
         PromiseValue(Cast(doc.annotationOn, Doc)).then(annotationOn =>
             annotationOn === this.dataDoc.Document && (doc.annotationOn = undefined));
 
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index cab085d9b..277fa0066 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,36 +1,35 @@
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faFile } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import 'golden-layout/src/css/goldenlayout-base.css';
 import 'golden-layout/src/css/goldenlayout-dark-theme.css';
-import { action, Lambda, observable, reaction, trace, computed } from "mobx";
+import { action, computed, Lambda, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
 import * as ReactDOM from 'react-dom';
 import Measure from "react-measure";
 import * as GoldenLayout from "../../../client/goldenLayout";
+import { DateField } from '../../../new_fields/DateField';
 import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc";
 import { Id } from '../../../new_fields/FieldSymbols';
+import { List } from '../../../new_fields/List';
 import { FieldId } from "../../../new_fields/RefField";
 import { listSpec } from "../../../new_fields/Schema";
-import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types";
-import { emptyFunction, returnTrue, Utils, returnOne, returnEmptyString } from "../../../Utils";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
+import { emptyFunction, returnEmptyString, returnFalse, returnOne, returnTrue, Utils } from "../../../Utils";
 import { DocServer } from "../../DocServer";
+import { Docs } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragLinksAsDocuments, DragManager } from "../../util/DragManager";
 import { SelectionManager } from '../../util/SelectionManager';
 import { Transform } from '../../util/Transform';
-import { undoBatch, UndoManager } from "../../util/UndoManager";
+import { undoBatch } from "../../util/UndoManager";
+import { MainView } from '../MainView';
 import { DocumentView } from "../nodes/DocumentView";
 import "./CollectionDockingView.scss";
 import { SubCollectionViewProps } from "./CollectionSubView";
 import { ParentDocSelector } from './ParentDocumentSelector';
 import React = require("react");
-import { MainView } from '../MainView';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faFile, faUnlockAlt } from '@fortawesome/free-solid-svg-icons';
-import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
-import { Docs } from '../../documents/Documents';
-import { DateField } from '../../../new_fields/DateField';
-import { List } from '../../../new_fields/List';
-import { DocumentType } from '../../documents/DocumentTypes';
 library.add(faFile);
 
 @observer
@@ -59,7 +58,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
 
     constructor(props: SubCollectionViewProps) {
         super(props);
-        if (props.addDocTab === emptyFunction) CollectionDockingView.Instance = this;
+        !CollectionDockingView.Instance && (CollectionDockingView.Instance = this);
         //Why is this here?
         (window as any).React = React;
         (window as any).ReactDOM = ReactDOM;
@@ -121,21 +120,25 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
 
     @undoBatch
     @action
-    public CloseRightSplit = (document: Doc): boolean => {
+    public static CloseRightSplit(document: Doc): boolean {
+        if (!CollectionDockingView.Instance) return false;
+        let instance = CollectionDockingView.Instance;
         let retVal = false;
-        if (this._goldenLayout.root.contentItems[0].isRow) {
-            retVal = Array.from(this._goldenLayout.root.contentItems[0].contentItems).some((child: any) => {
+        if (instance._goldenLayout.root.contentItems[0].isRow) {
+            retVal = Array.from(instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => {
                 if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" &&
+                    DocumentManager.Instance.getDocumentViewById(child.contentItems[0].config.props.documentId) &&
                     Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(child.contentItems[0].config.props.documentId)!.Document, document)) {
                     child.contentItems[0].remove();
-                    this.layoutChanged(document);
+                    instance.layoutChanged(document);
                     return true;
                 } else {
                     Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => {
-                        if (Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)!.Document, document)) {
+                        if (DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId) &&
+                            Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)!.Document, document)) {
                             child.contentItems[j].remove();
                             child.config.activeItemIndex = Math.max(child.contentItems.length - 1, 0);
-                            let docs = Cast(this.props.Document.data, listSpec(Doc));
+                            let docs = Cast(instance.props.Document.data, listSpec(Doc));
                             docs && docs.indexOf(document) !== -1 && docs.splice(docs.indexOf(document), 1);
                             return true;
                         }
@@ -146,7 +149,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             });
         }
         if (retVal) {
-            this.stateChanged();
+            instance.stateChanged();
         }
         return retVal;
     }
@@ -173,8 +176,10 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     //
     @undoBatch
     @action
-    public AddRightSplit = (document: Doc, dataDoc: Doc | undefined, minimize: boolean = false) => {
-        let docs = Cast(this.props.Document.data, listSpec(Doc));
+    public static AddRightSplit(document: Doc, dataDoc: Doc | undefined, minimize: boolean = false) {
+        if (!CollectionDockingView.Instance) return false;
+        let instance = CollectionDockingView.Instance;
+        let docs = Cast(instance.props.Document.data, listSpec(Doc));
         if (docs) {
             docs.push(document);
         }
@@ -183,15 +188,15 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)]
         };
 
-        var newContentItem = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
+        var newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
 
-        if (this._goldenLayout.root.contentItems.length === 0) {
-            this._goldenLayout.root.addChild(newContentItem);
-        } else if (this._goldenLayout.root.contentItems[0].isRow) {
-            this._goldenLayout.root.contentItems[0].addChild(newContentItem);
+        if (instance._goldenLayout.root.contentItems.length === 0) {
+            instance._goldenLayout.root.addChild(newContentItem);
+        } else if (instance._goldenLayout.root.contentItems[0].isRow) {
+            instance._goldenLayout.root.contentItems[0].addChild(newContentItem);
         } else {
-            var collayout = this._goldenLayout.root.contentItems[0];
-            var newRow = collayout.layoutManager.createContentItem({ type: "row" }, this._goldenLayout);
+            var collayout = instance._goldenLayout.root.contentItems[0];
+            var newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
             collayout.parent.replaceChild(collayout, newRow);
 
             newRow.addChild(newContentItem, undefined, true);
@@ -206,9 +211,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             // newContentItem.config.height = 10;
         }
         newContentItem.callDownwards('_$init');
-        this.layoutChanged();
-
-        return newContentItem;
+        instance.layoutChanged();
+        return true;
     }
 
     @undoBatch
@@ -238,6 +242,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             stack.addChild(docContentConfig, undefined);
         }
         this.layoutChanged();
+        return true;
     }
 
     setupGoldenLayout() {
@@ -416,7 +421,10 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                             hideSource: false
                         });
                     }}><FontAwesomeIcon icon="file" size="lg" /></span>, dragSpan);
-                ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={doc => CollectionDockingView.Instance.AddTab(stack, doc, dataDoc)} />, upDiv);
+                ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={(doc, data, where) => {
+                    where === "onRight" ? CollectionDockingView.AddRightSplit(doc, dataDoc) : CollectionDockingView.Instance.AddTab(stack, doc, dataDoc);
+                    return true;
+                }} />, upDiv);
                 tab.reactComponents = [dragSpan, upDiv];
                 tab.element.append(dragSpan);
                 tab.element.append(upDiv);
@@ -604,15 +612,16 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     }
     get previewPanelCenteringOffset() { return this.nativeWidth() && !BoolCast(this._document!.ignoreAspect) ? (this._panelWidth - this.nativeWidth()) / 2 : 0; }
 
-    addDocTab = (doc: Doc, dataDoc: Doc | undefined, location: string) => {
+    addDocTab = (doc: Doc, dataDoc: Opt<Doc>, location: string) => {
         if (doc.dockingConfig) {
             MainView.Instance.openWorkspace(doc);
+            return true;
         } else if (location === "onRight") {
-            CollectionDockingView.Instance.AddRightSplit(doc, dataDoc);
+            return CollectionDockingView.AddRightSplit(doc, dataDoc);
         } else if (location === "close") {
-            CollectionDockingView.Instance.CloseRightSplit(doc);
+            return CollectionDockingView.CloseRightSplit(doc);
         } else {
-            CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc);
+            return CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc);
         }
     }
     @computed get docView() {
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 059967a7d..0306d415c 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -39,7 +39,7 @@ export interface CellProps {
     Document: Doc;
     fieldKey: string;
     renderDepth: number;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
     isFocused: boolean;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index a742054d0..7bd2a1971 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -256,7 +256,7 @@ export interface SchemaTableProps {
     ScreenToLocalTransform: () => Transform;
     active: () => boolean;
     onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     isSelected: () => boolean;
     isFocused: (document: Doc) => boolean;
@@ -915,7 +915,7 @@ interface CollectionSchemaPreviewProps {
     removeDocument: (document: Doc) => boolean;
     active: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     setPreviewScript: (script: string) => void;
     previewScript?: string;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index b4026a810..08d87c7b2 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -40,7 +40,7 @@ export interface TreeViewProps {
     ruleProvider: Doc | undefined;
     moveDocument: DragManager.MoveFunction;
     dropAction: "alias" | "copy" | undefined;
-    addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void;
+    addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     panelWidth: () => number;
     panelHeight: () => number;
@@ -414,7 +414,7 @@ class TreeView extends React.Component<TreeViewProps> {
         remove: ((doc: Doc) => boolean),
         move: DragManager.MoveFunction,
         dropAction: dropActionType,
-        addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void,
+        addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => boolean,
         pinToPres: (document: Doc) => void,
         screenToLocalXf: () => Transform,
         outerXf: () => { translateX: number, translateY: number },
@@ -562,8 +562,8 @@ export class CollectionTreeView extends CollectionSubView(Document) {
     outerXf = () => Utils.GetScreenTransform(this._mainEle!);
     onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {});
     openNotifsCol = () => {
-        if (CollectionTreeView.NotifsCol && CollectionDockingView.Instance) {
-            CollectionDockingView.Instance.AddRightSplit(CollectionTreeView.NotifsCol, undefined);
+        if (CollectionTreeView.NotifsCol) {
+            this.props.addDocTab(CollectionTreeView.NotifsCol, undefined, "onRight");
         }
     }
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ad77e0428..7383c5551 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -508,14 +508,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                     });
                 }
 
-                let panelDim = this.props.ScreenToLocalTransform().transformDirection(this._pwidth / this.zoomScaling(),
-                    this._pheight / this.zoomScaling());
-                let panelwidth = panelDim[0];
-                let panelheight = panelDim[1];
-                if (ranges[0][0] - dx > (this.panX() + panelwidth / 2)) x = ranges[0][1] + panelwidth / 2;
-                if (ranges[0][1] - dx < (this.panX() - panelwidth / 2)) x = ranges[0][0] - panelwidth / 2;
-                if (ranges[1][0] - dy > (this.panY() + panelheight / 2)) y = ranges[1][1] + panelheight / 2;
-                if (ranges[1][1] - dy < (this.panY() - panelheight / 2)) y = ranges[1][0] - panelheight / 2;
+                let cscale = this.props.ContainingCollectionDoc ? NumCast(this.props.ContainingCollectionDoc.scale) : 1;
+                let panelDim = this.props.ScreenToLocalTransform().transformDirection(this._pwidth / this.zoomScaling() * cscale,
+                    this._pheight / this.zoomScaling() * cscale);
+                if (ranges[0][0] - dx > (this.panX() + panelDim[0] / 2)) x = ranges[0][1] + panelDim[0] / 2;
+                if (ranges[0][1] - dx < (this.panX() - panelDim[0] / 2)) x = ranges[0][0] - panelDim[0] / 2;
+                if (ranges[1][0] - dy > (this.panY() + panelDim[1] / 2)) y = ranges[1][1] + panelDim[1] / 2;
+                if (ranges[1][1] - dy < (this.panY() - panelDim[1] / 2)) y = ranges[1][0] - panelDim[1] / 2;
             }
             this.setPan(x - dx, y - dy);
             this._lastX = e.pageX;
@@ -613,8 +612,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         if (willZoom) {
             this.setScaleToZoom(doc, scale);
         }
+        console.log("Focused " + this.Document.title + " " + s);
         afterFocus && setTimeout(() => {
             if (afterFocus && afterFocus()) {
+                console.log("UnFocused " + this.Document.title + " " + s);
                 this.Document.panX = px;
                 this.Document.panY = py;
                 this.Document.scale = s;
diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx
index d7cb2585e..81b0249dd 100644
--- a/src/client/views/linking/LinkFollowBox.tsx
+++ b/src/client/views/linking/LinkFollowBox.tsx
@@ -2,7 +2,7 @@ import { observable, computed, action, runInAction, reaction, IReactionDisposer
 import React = require("react");
 import { observer } from "mobx-react";
 import { FieldViewProps, FieldView } from "../nodes/FieldView";
-import { Doc, DocListCastAsync } from "../../../new_fields/Doc";
+import { Doc, DocListCastAsync, Opt } from "../../../new_fields/Doc";
 import { undoBatch } from "../../util/UndoManager";
 import { NumCast, FieldValue, Cast, StrCast } from "../../../new_fields/Types";
 import { CollectionViewType } from "../collections/CollectionBaseView";
@@ -195,9 +195,9 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
 
     }
 
-    _addDocTab: (undefined | ((doc: Doc, dataDoc: Doc | undefined, where: string) => void));
+    _addDocTab: (undefined | ((doc: Doc, dataDoc: Opt<Doc>, where: string) => boolean));
 
-    setAddDocTab = (addFunc: (doc: Doc, dataDoc: Doc | undefined, where: string) => void) => {
+    setAddDocTab = (addFunc: (doc: Doc, dataDoc: Opt<Doc>, where: string) => boolean) => {
         this._addDocTab = addFunc;
     }
 
@@ -211,7 +211,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
                 options.context.panX = newPanX;
                 options.context.panY = newPanY;
             }
-            CollectionDockingView.Instance.AddRightSplit(options.context, undefined);
+            (this._addDocTab || this.props.addDocTab)(options.context, undefined, "onRight");
 
             if (options.shouldZoom) this.jumpToLink({ shouldZoom: options.shouldZoom });
 
@@ -224,7 +224,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
     openLinkRight = () => {
         if (LinkFollowBox.destinationDoc) {
             let alias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
-            CollectionDockingView.Instance.AddRightSplit(alias, undefined);
+            (this._addDocTab || this.props.addDocTab)(alias, undefined, "onRight");
             this.highlightDoc();
             SelectionManager.DeselectAll();
         }
@@ -244,7 +244,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
             let sourceContext = await Cast(proto.sourceContext, Doc);
             const shouldZoom = options ? options.shouldZoom : false;
 
-            let dockingFunc = (document: Doc) => { this._addDocTab && this._addDocTab(document, undefined, "inTab"); SelectionManager.DeselectAll(); };
+            let dockingFunc = (document: Doc) => { (this._addDocTab || this.props.addDocTab)(document, undefined, "inTab"); SelectionManager.DeselectAll(); };
 
             if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor2 && targetContext) {
                 DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, async document => dockingFunc(document), undefined, targetContext);
@@ -271,7 +271,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
         if (LinkFollowBox.destinationDoc) {
             let fullScreenAlias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
             // this.prosp.addDocTab is empty -- use the link source's addDocTab 
-            this._addDocTab && this._addDocTab(fullScreenAlias, undefined, "inTab");
+            (this._addDocTab || this.props.addDocTab)(fullScreenAlias, undefined, "inTab");
 
             this.highlightDoc();
             SelectionManager.DeselectAll();
@@ -288,7 +288,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
                 options.context.panX = newPanX;
                 options.context.panY = newPanY;
             }
-            this._addDocTab && this._addDocTab(options.context, undefined, "inTab");
+            (this._addDocTab || this.props.addDocTab)(options.context, undefined, "inTab");
             if (options.shouldZoom) this.jumpToLink({ shouldZoom: options.shouldZoom });
 
             this.highlightDoc();
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 842ce45b1..27af873b5 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -16,7 +16,7 @@ library.add(faTrash);
 interface Props {
     docView: DocumentView;
     changeFlyout: () => void;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
 }
 
 @observer
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index d711ac284..1891919ce 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -1,27 +1,25 @@
-import { action, observable } from "mobx";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action } from "mobx";
 import { observer } from "mobx-react";
-import { DocumentView } from "../nodes/DocumentView";
-import { LinkMenuItem } from "./LinkMenuItem";
-import { LinkEditor } from "./LinkEditor";
-import './LinkMenu.scss';
-import React = require("react");
-import { Doc, DocListCast } from "../../../new_fields/Doc";
+import { Doc } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
-import { LinkManager } from "../../util/LinkManager";
-import { DragLinksAsDocuments, DragManager, SetupDrag } from "../../util/DragManager";
+import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { emptyFunction } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { DragManager, SetupDrag } from "../../util/DragManager";
+import { LinkManager } from "../../util/LinkManager";
 import { UndoManager } from "../../util/UndoManager";
-import { StrCast } from "../../../new_fields/Types";
-import { SchemaHeaderField, RandomPastel } from "../../../new_fields/SchemaHeaderField";
+import { DocumentView } from "../nodes/DocumentView";
+import './LinkMenu.scss';
+import { LinkMenuItem } from "./LinkMenuItem";
+import React = require("react");
 
 interface LinkMenuGroupProps {
     sourceDoc: Doc;
     group: Doc[];
     groupType: string;
     showEditor: (linkDoc: Doc) => void;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     docView: DocumentView;
 
 }
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 19a0023e9..82fe3df23 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -21,7 +21,7 @@ interface LinkMenuItemProps {
     sourceDoc: Doc;
     destinationDoc: Doc;
     showEditor: (linkDoc: Doc) => void;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
 }
 
 @observer
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4a37457c0..50691fd38 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -86,7 +86,7 @@ export interface DocumentViewProps {
     parentActive: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     bringToFront: (doc: Doc, sendToBack?: boolean) => void;
-    addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void;
+    addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void;
     zoomToScale: (scale: number) => void;
@@ -242,23 +242,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (expandedDocs.length) {
             SelectionManager.DeselectAll();
             let maxLocation = StrCast(this.Document.maximizeLocation, "inPlace");
-            if (altKey || ctrlKey) {
-                maxLocation = this.Document.maximizeLocation = (ctrlKey ? maxLocation : (maxLocation === "inPlace" ? "inTab" : "inPlace"));
-                if (maxLocation === "inPlace") {
-                    let hadView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedDocs[0], this.props.ContainingCollectionView);
-                    let wasMinimized = !hadView && expandedDocs.reduce((min, d) => !min && !d.isMinimized, false);
-                    expandedDocs.forEach(maxDoc => maxDoc.isMinimized = false);
-                    let hasView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedDocs[0], this.props.ContainingCollectionView);
-                    !hasView && expandedDocs.forEach(async maxDoc => this.props.addDocument && this.props.addDocument(maxDoc, false));
-                    expandedDocs.forEach(maxDoc => maxDoc.isMinimized = wasMinimized);
-                }
-            }
-            if (maxLocation !== "inPlace" && CollectionDockingView.Instance) {
-                expandedDocs.forEach(maxDoc =>
-                    (!CollectionDockingView.Instance.CloseRightSplit(maxDoc) && this.props.addDocTab(maxDoc, undefined, maxLocation)));
-            } else {
+            maxLocation = this.Document.maximizeLocation = (!ctrlKey ? !altKey ? maxLocation : (maxLocation !== "inPlace" ? "inPlace" : "onRight") : (maxLocation !== "inPlace" ? "inPlace" : "inTab"));
+            if (maxLocation === "inPlace") {
+                expandedDocs.forEach(maxDoc => this.props.addDocument && this.props.addDocument(maxDoc, false));
                 let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2);
                 this.collapseTargetsToPoint(scrpt, expandedDocs);
+            } else {
+                expandedDocs.forEach(maxDoc => (!this.props.addDocTab(maxDoc, undefined, "close") && this.props.addDocTab(maxDoc, undefined, maxLocation)));
             }
         }
         else if (linkedDocs.length) {
@@ -277,7 +267,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionDoc) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined;
                 DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false,
                     // open up target if it's not already in view ... by zooming into the button document first and setting flag to reset zoom afterwards
-                    doc => this.props.focus(this.props.Document, true, 1, () => { this.props.addDocTab(doc, undefined, maxLocation); return true; }),
+                    doc => this.props.focus(this.props.Document, true, 1, () => this.props.addDocTab(doc, undefined, maxLocation)),
                     linkedFwdPage[altKey ? 1 : 0], targetContext);
             }
         }
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index faf11e9be..49fc2263d 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -39,7 +39,7 @@ export interface FieldViewProps {
     select: (isCtrlPressed: boolean) => void;
     renderDepth: number;
     addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     removeDocument?: (document: Doc) => boolean;
     moveDocument?: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index ee70942de..8fcd44f46 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -124,7 +124,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         let i = 0;
         const self = this;
         for (let key of Object.keys(ids).slice().sort()) {
-            rows.push(<KeyValuePair doc={realDoc} ref={(function () {
+            rows.push(<KeyValuePair doc={realDoc} addDocTab={this.props.addDocTab} ref={(function () {
                 let oldEl: KeyValuePair | undefined;
                 return (el: KeyValuePair) => {
                     if (oldEl) self.rows.splice(self.rows.indexOf(oldEl), 1);
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index c7e0f51d7..1fed4c8bb 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -1,7 +1,7 @@
 import { action, observable } from 'mobx';
 import { observer } from "mobx-react";
 import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
-import { Doc, Field } from '../../../new_fields/Doc';
+import { Doc, Field, Opt } from '../../../new_fields/Doc';
 import { emptyFunction, returnFalse, returnOne, returnZero } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
 import { Transform } from '../../util/Transform';
@@ -22,6 +22,7 @@ export interface KeyValuePairProps {
     keyName: string;
     doc: Doc;
     keyWidth: number;
+    addDocTab: (doc: Doc, data: Opt<Doc>, where: string) => boolean;
 }
 @observer
 export class KeyValuePair extends React.Component<KeyValuePairProps> {
@@ -45,7 +46,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
         if (value instanceof Doc) {
             e.stopPropagation();
             e.preventDefault();
-            ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.Create.KVPDocument(value, { width: 300, height: 300 }); CollectionDockingView.Instance.AddRightSplit(kvp, undefined); }, icon: "layer-group" });
+            ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(value, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" });
             ContextMenu.Instance.displayMenu(e.clientX, e.clientY);
         }
     }
@@ -68,7 +69,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             focus: emptyFunction,
             PanelWidth: returnZero,
             PanelHeight: returnZero,
-            addDocTab: returnZero,
+            addDocTab: returnFalse,
             pinToPres: returnZero,
             ContentScaling: returnOne
         };
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 34e3b0931..a9fa883c8 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -1,20 +1,18 @@
 import React = require("react");
 import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, WidthSym } from "../../../new_fields/Doc";
+import { Doc, DocListCast, HeightSym, WidthSym, Opt } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
 import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
 import { DocumentManager } from "../../util/DocumentManager";
 import PDFMenu from "./PDFMenu";
 import "./Annotation.scss";
-import { scale } from "./PDFViewer";
-import { PresBox } from "../nodes/PresBox";
 
 interface IAnnotationProps {
     anno: Doc;
     fieldExtensionDoc: Doc;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+    addDocTab: (document: Doc, dataDoc: Opt<Doc>, where: string) => boolean;
     pinToPres: (document: Doc) => void;
 }
 
@@ -31,7 +29,7 @@ interface IRegionAnnotationProps {
     width: number;
     height: number;
     fieldExtensionDoc: Doc;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     document: Doc;
 }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index c508935f2..19ef713c2 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -35,7 +35,7 @@ interface IViewerProps {
     scrollTo: (y: number) => void;
     active: () => boolean;
     setPanY?: (n: number) => void;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean;
 }
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index b2e886d95..96eefacc2 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -76,7 +76,7 @@ export class SelectorContextMenu extends React.Component<SearchItemProps> {
                 col.panX = newPanX;
                 col.panY = newPanY;
             }
-            CollectionDockingView.Instance.AddRightSplit(col, undefined);
+            CollectionDockingView.AddRightSplit(col, undefined);
         };
     }
     render() {
@@ -110,7 +110,7 @@ export class LinkContextMenu extends React.Component<LinkMenuProps> {
 
     unHighlightDoc = (doc: Doc) => () => Doc.UnBrushDoc(doc);
 
-    getOnClick = (col: Doc) => () => CollectionDockingView.Instance.AddRightSplit(col, undefined);
+    getOnClick = (col: Doc) => () => CollectionDockingView.AddRightSplit(col, undefined);
 
     render() {
         return (
-- 
cgit v1.2.3-70-g09d2


From c40480ec1a2da84b4223b0605bea2fe19df1104c Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 29 Sep 2019 11:49:53 -0400
Subject: fixed deleting pdf annotations. changed stacking view + fitToWidth to
 not be bigger than stacking view height.

---
 .../views/collections/CollectionStackingView.tsx       |  4 ++--
 src/client/views/nodes/FieldView.tsx                   |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx            | 13 ++++++-------
 src/client/views/pdf/Annotation.tsx                    | 18 +++++++++---------
 4 files changed, 18 insertions(+), 19 deletions(-)

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

diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 597f3f745..45de0fefa 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -160,13 +160,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         if (!d) return 0;
         let nw = NumCast(d.nativeWidth);
         let nh = NumCast(d.nativeHeight);
-        if (!d.ignoreAspect && nw && nh) {
+        if (!d.ignoreAspect && !d.fitWidth && nw && nh) {
             let aspect = nw && nh ? nh / nw : 1;
             let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
             if (!(d.nativeWidth && !d.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(d[WidthSym](), wid);
             return wid * aspect;
         }
-        return d[HeightSym]();
+        return d.fitWidth ? Math.min(this.props.PanelHeight() - 2 * this.yMargin, d[HeightSym]()) : d[HeightSym]();
     }
 
     columnDividerDown = (e: React.PointerEvent) => {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index ec1b03a40..b93c78cfd 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -95,7 +95,7 @@ export class FieldView extends React.Component<FieldViewProps> {
             return <p>{field.date.toLocaleString()}</p>;
         }
         else if (field instanceof Doc) {
-            return <p><b>{field.title}</b></p>;
+            return <p><b>{field.title && field.title.toString()}</b></p>;
             //return <p><b>{field.title + " : id= " + field[Id]}</b></p>;
             // let returnHundred = () => 100;
             // return (
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 565fb0505..63a16f90c 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -80,7 +80,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
     private _nodeClicked: any;
     private _undoTyping?: UndoManager.Batch;
     private _searchReactionDisposer?: Lambda;
-    private _scroolToRegionReactionDisposer: Opt<IReactionDisposer>;
+    private _scrollToRegionReactionDisposer: Opt<IReactionDisposer>;
     private _reactionDisposer: Opt<IReactionDisposer>;
     private _textReactionDisposer: Opt<IReactionDisposer>;
     private _heightReactionDisposer: Opt<IReactionDisposer>;
@@ -140,7 +140,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             DragManager.StartDragFunctions.push(() => FormattedTextBox.InputBoxOverlay = undefined);
         }
 
-        this._scroolToRegionReactionDisposer = reaction(
+        this._scrollToRegionReactionDisposer = reaction(
             () => StrCast(this.props.Document.scrollToLinkID),
             async (scrollToLinkID) => {
                 let findLinkFrag = (frag: Fragment, editor: EditorView) => {
@@ -165,7 +165,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 };
 
                 let start = -1;
-
                 if (this._editorView && scrollToLinkID) {
                     let editor = this._editorView;
                     let ret = findLinkFrag(editor.state.doc.content, editor);
@@ -179,12 +178,12 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                         const mark = editor.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
                         setTimeout(() => editor.dispatch(editor.state.tr.addMark(selection.from, selection.to, mark)), 0);
                         setTimeout(() => this.unhighlightSearchTerms(), 2000);
-
-                        this.props.Document.scrollToLinkID = undefined;
                     }
+                    this.props.Document.scrollToLinkID = undefined;
                 }
 
-            }
+            },
+            { fireImmediately: true }
         );
     }
 
@@ -793,7 +792,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
     }
 
     componentWillUnmount() {
-        this._scroolToRegionReactionDisposer && this._scroolToRegionReactionDisposer();
+        this._scrollToRegionReactionDisposer && this._scrollToRegionReactionDisposer();
         this._rulesReactionDisposer && this._rulesReactionDisposer();
         this._reactionDisposer && this._reactionDisposer();
         this._proxyReactionDisposer && this._proxyReactionDisposer();
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index a9fa883c8..3ed85f6a5 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -85,7 +85,15 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
 
     @action
     onPointerDown = async (e: React.PointerEvent) => {
-        if (e.button === 0) {
+        if (e.button === 2 || e.ctrlKey) {
+            PDFMenu.Instance.Status = "annotation";
+            PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this);
+            PDFMenu.Instance.Pinned = false;
+            PDFMenu.Instance.AddTag = this.addTag.bind(this);
+            PDFMenu.Instance.PinToPres = this.pinToPres;
+            PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true);
+        }
+        else if (e.button === 0) {
             let targetDoc = await Cast(this.props.document.target, Doc);
             if (targetDoc) {
                 let context = await Cast(targetDoc.targetContext, Doc);
@@ -96,14 +104,6 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
                 }
             }
         }
-        if (e.button === 2) {
-            PDFMenu.Instance.Status = "annotation";
-            PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this);
-            PDFMenu.Instance.Pinned = false;
-            PDFMenu.Instance.AddTag = this.addTag.bind(this);
-            PDFMenu.Instance.PinToPres = this.pinToPres;
-            PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true);
-        }
     }
 
     addTag = (key: string, value: string): boolean => {
-- 
cgit v1.2.3-70-g09d2


From 9427474b473d70974784a1517a1be902fb8d18ee Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 2 Oct 2019 18:26:55 -0400
Subject: many fixes to pdfs, linking, annotations, presentations.

---
 src/client/documents/Documents.ts                  |  24 ++--
 src/client/util/DocumentManager.ts                 | 125 +++++++++++----------
 src/client/util/RichTextSchema.tsx                 |   9 +-
 src/client/util/SharingManager.tsx                 |   2 +-
 .../views/collections/CollectionBaseView.tsx       |   9 +-
 .../views/collections/CollectionDockingView.tsx    |  10 +-
 src/client/views/collections/CollectionSubView.tsx |   1 +
 .../views/collections/CollectionTreeView.tsx       |   2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  10 +-
 src/client/views/linking/LinkFollowBox.tsx         |  11 +-
 src/client/views/nodes/DocumentView.tsx            |  44 ++------
 src/client/views/nodes/FormattedTextBox.tsx        |  79 ++++++-------
 src/client/views/nodes/ImageBox.tsx                |   2 +-
 src/client/views/nodes/PDFBox.tsx                  |  17 ++-
 src/client/views/nodes/PresBox.tsx                 |  24 ++--
 src/client/views/nodes/VideoBox.tsx                |   2 +-
 src/client/views/pdf/Annotation.tsx                |  17 ++-
 src/client/views/pdf/PDFMenu.tsx                   |   6 +-
 src/client/views/pdf/PDFViewer.tsx                 |  72 +++++-------
 .../views/presentationview/PresElementBox.tsx      |  32 +++---
 src/new_fields/Doc.ts                              |   2 +
 21 files changed, 224 insertions(+), 276 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 4d9698532..2d323ea4b 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -653,32 +653,30 @@ export namespace DocUtils {
             }
         });
     }
-    export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", sourceContext?: Doc, id?: string, anchored1?: boolean) {
-        let sv = DocumentManager.Instance.getDocumentView(source);
-        if (sv && sv.props.ContainingCollectionDoc === target) return;
-        if (target === CurrentUserUtils.UserDocument) return undefined;
+    export function MakeLink(source: {doc:Doc,ctx?:Doc}, target: {doc:Doc,ctx?:Doc}, title: string = "", description: string = "", id?: string, anchored1?: boolean) {
+        let sv = DocumentManager.Instance.getDocumentView(source.doc);
+        if (sv && sv.props.ContainingCollectionDoc === target.doc) return;
+        if (target.doc === CurrentUserUtils.UserDocument) return undefined;
 
         let linkDocProto = new Doc(id, true);
         UndoManager.RunInBatch(() => {
             linkDocProto.type = DocumentType.LINK;
 
-            linkDocProto.targetContext = targetContext;
-            linkDocProto.sourceContext = sourceContext;
-            linkDocProto.title = title === "" ? source.title + " to " + target.title : title;
+            linkDocProto.targetContext = target.ctx;
+            linkDocProto.sourceContext = source.ctx;
+            linkDocProto.title = title === "" ? source.doc.title + " to " + target.doc.title : title;
             linkDocProto.linkDescription = description;
 
-            linkDocProto.anchor1 = source;
-            linkDocProto.anchor1Page = source.curPage;
+            linkDocProto.anchor1 = source.doc;
             linkDocProto.anchor1Groups = new List<Doc>([]);
             linkDocProto.anchor1anchored = anchored1;
-            linkDocProto.anchor2 = target;
-            linkDocProto.anchor2Page = target.curPage;
+            linkDocProto.anchor2 = target.doc;
             linkDocProto.anchor2Groups = new List<Doc>([]);
 
             LinkManager.Instance.addLink(linkDocProto);
 
-            Doc.GetProto(source).links = ComputedField.MakeFunction("links(this)");
-            Doc.GetProto(target).links = ComputedField.MakeFunction("links(this)");
+            Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(this)");
+            Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(this)");
         }, "make link");
         return linkDocProto;
     }
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 4e6968f08..4ebcdf83c 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,7 +1,7 @@
 import { action, computed, observable } from 'mobx';
 import { Doc, DocListCastAsync } from '../../new_fields/Doc';
 import { Id } from '../../new_fields/FieldSymbols';
-import { Cast, NumCast } from '../../new_fields/Types';
+import { Cast, NumCast, StrCast } from '../../new_fields/Types';
 import { CollectionDockingView } from '../views/collections/CollectionDockingView';
 import { CollectionPDFView } from '../views/collections/CollectionPDFView';
 import { CollectionVideoView } from '../views/collections/CollectionVideoView';
@@ -56,9 +56,9 @@ export class DocumentManager {
         return this.getDocumentViewsById(doc[Id]);
     }
 
-    public getDocumentViewById(id: string, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | null {
+    public getDocumentViewById(id: string, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | undefined {
 
-        let toReturn: DocumentView | null = null;
+        let toReturn: DocumentView | undefined;
         let passes = preferredCollection ? [preferredCollection, undefined] : [undefined];
 
         for (let pass of passes) {
@@ -81,10 +81,14 @@ export class DocumentManager {
         return toReturn;
     }
 
-    public getDocumentView(toFind: Doc, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | null {
+    public getDocumentView(toFind: Doc, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | undefined {
         return this.getDocumentViewById(toFind[Id], preferredCollection);
     }
 
+    public getFirstDocumentView(toFind: Doc): DocumentView | undefined {
+        const views = this.getDocumentViews(toFind);
+        return views.length ? views[0] : undefined;
+    }
     public getDocumentViews(toFind: Doc): DocumentView[] {
 
         let toReturn: DocumentView[] = [];
@@ -127,72 +131,70 @@ export class DocumentManager {
         return pairs;
     }
 
-
-    @undoBatch
-    public jumpToDocument = async (docDelegate: Doc, willZoom: boolean, forceDockFunc: boolean = false, dockFunc?: (doc: Doc) => void, linkPage?: number, docContext?: Doc): Promise<void> => {
-        let doc = Doc.GetProto(docDelegate);
-        const contextDoc = await Cast(doc.annotationOn, Doc);
-        if (contextDoc) {
-            contextDoc.scrollY = NumCast(doc.y) - NumCast(contextDoc.height) / 2 * 72 / 96;
-        }
-
-        let docView: DocumentView | null;
-        // using forceDockFunc as a flag for splitting linked to doc to the right...can change later if needed
-        if (!forceDockFunc && (docView = DocumentManager.Instance.getDocumentView(doc))) {
-            Doc.BrushDoc(docView.props.Document);
-            if (linkPage !== undefined) docView.props.Document.curPage = linkPage;
-            UndoManager.RunInBatch(() => docView!.props.focus(docView!.props.Document, willZoom), "focus");
+    public jumpToDocument = async (targetDoc: Doc, willZoom: boolean, dockFunc?: (doc: Doc) => void, docContext?: Doc, closeContextIfNotFound: boolean = false): Promise<void> => {
+        const docView = DocumentManager.Instance.getFirstDocumentView(targetDoc);
+        const annotatedDoc = await Cast(targetDoc.annotationOn, Doc);
+        if (docView) {  // we have a docView already and aren't forced to create a new one ... just focus on the document.  TODO move into view if necessary otherwise just highlight?
+            annotatedDoc && docView.props.focus(annotatedDoc, false);
+            docView.props.focus(targetDoc, willZoom);
         } else {
-            if (!contextDoc) {
-                let docs = docContext ? await DocListCastAsync(docContext.data) : undefined;
-                let found = false;
-                // bcz: this just searches within the context for the target -- perhaps it should recursively search through all children?
-                docs && docs.map(d => found = found || Doc.AreProtosEqual(d, docDelegate));
-                if (docContext && found) {
-                    let targetContextView: DocumentView | null;
-
-                    if (!forceDockFunc && docContext && (targetContextView = DocumentManager.Instance.getDocumentView(docContext))) {
-                        docContext.panTransformType = "Ease";
-                        targetContextView.props.focus(docDelegate, willZoom);
-                    } else {
-                        (dockFunc || CollectionDockingView.AddRightSplit)(docContext, undefined);
-                        setTimeout(() => {
-                            let dv = DocumentManager.Instance.getDocumentView(docContext);
-                            dv && this.jumpToDocument(docDelegate, willZoom, forceDockFunc,
-                                doc => dv!.props.focus(dv!.props.Document, true, 1, () => dv!.props.addDocTab(doc, undefined, "inPlace")),
-                                linkPage);
-                        }, 1050);
-                    }
-                } else {
-                    const actualDoc = Doc.MakeAlias(docDelegate);
-                    Doc.BrushDoc(actualDoc);
-                    if (linkPage !== undefined) actualDoc.curPage = linkPage;
-                    (dockFunc || CollectionDockingView.AddRightSplit)(actualDoc, undefined);
-                }
+            const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined;
+            const contextDoc = contextDocs && contextDocs.find(doc => Doc.AreProtosEqual(doc, targetDoc)) ? docContext : undefined;
+            const targetDocContext = (annotatedDoc ? annotatedDoc : contextDoc);
+
+            if (!targetDocContext) { // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default
+                (dockFunc || CollectionDockingView.AddRightSplit)(Doc.BrushDoc(Doc.MakeAlias(targetDoc)), undefined);
             } else {
-                let contextView: DocumentView | null;
-                Doc.BrushDoc(docDelegate);
-                let contextViews = DocumentManager.Instance.getDocumentViews(contextDoc);
-                if (!forceDockFunc && contextViews.length) {
-                    contextView = contextViews[0];
-                    SelectionManager.SelectDoc(contextView, false); // force unrendered annotations to be created
-                    contextDoc.panTransformType = "Ease";
+                const targetDocContextView = DocumentManager.Instance.getFirstDocumentView(targetDocContext);
+                if (targetDocContextView) { // we have a context view and aren't forced to create a new one ... focus on the context
+                    targetDocContext.panTransformType = "Ease";
+                    targetDocContext.scrollY = 0;
+                    targetDocContextView.props.focus(targetDocContextView.props.Document, willZoom);
+
+                    // now find the target document within the context
                     setTimeout(() => {
-                        SelectionManager.DeselectDoc(contextView!);
-                        docView = DocumentManager.Instance.getDocumentView(docDelegate);
-                        docView && contextView!.props.focus(contextView!.props.Document, willZoom);
-                        docView && UndoManager.RunInBatch(() => docView!.props.focus(docView!.props.Document, willZoom), "focus");
+                        const retryDocView = DocumentManager.Instance.getDocumentView(targetDoc);
+                        if (retryDocView) {
+                            retryDocView.props.focus(targetDoc, willZoom); // focus on the target if it now exists in the context
+                        } else {
+                            if (closeContextIfNotFound && targetDocContextView.props.removeDocument) targetDocContextView.props.removeDocument(targetDocContextView.props.Document);
+                            (dockFunc || CollectionDockingView.AddRightSplit)(Doc.BrushDoc(Doc.MakeAlias(targetDoc)), undefined); // otherwise create a new view of the target
+                        }
                     }, 0);
-                } else {
-                    (dockFunc || CollectionDockingView.AddRightSplit)(contextDoc, undefined);
+                } else {  // there's no context view so we need to create one first and try again
+                    targetDocContext.scrollY = 0;
+                    (dockFunc || CollectionDockingView.AddRightSplit)(targetDocContext, undefined);
                     setTimeout(() => {
-                        this.jumpToDocument(docDelegate, willZoom, forceDockFunc, dockFunc, linkPage);
-                    }, 1000);
+                        const foundTargetDocContextView = DocumentManager.Instance.getDocumentView(targetDocContext);
+                        if (foundTargetDocContextView) { // we should always find a target context here....
+                            this.jumpToDocument(targetDoc, willZoom, dockFunc, undefined, true); // so call jump to doc again and if the doc isn't found, it will be created.
+                        }
+                    }, 2000); // the long timeout gives the context view a chance to create its children.  think pdf's which need to be activated to render their annotations.
                 }
             }
         }
     }
 
+    public async FollowLink(doc: Doc, focus: (doc: Doc, maxLocation: string) => void, zoom: boolean = false, reverse: boolean = false, currentContext?: Doc) {
+        let linkDocs = LinkManager.Instance.getAllRelatedLinks(doc);
+        SelectionManager.DeselectAll();
+        let firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc) && !linkDoc.anchor1anchored);
+        let secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc) && !linkDoc.anchor2anchored);
+        const firstDocWithoutView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
+        const secondDocWithoutView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0);
+        let first = firstDocWithoutView ? [firstDocWithoutView] : firstDocs;
+        let second = secondDocWithoutView ? [secondDocWithoutView] : secondDocs;
+        let linkFollowDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : second.length ? [second[0].anchor1 as Doc, second[0].anchor2 as Doc] : undefined;
+        let linkFollowDocContexts = first.length ? [await (first[0].targetContext) as Doc, await (first[0].sourceContext) as Doc] : second.length ? [await (second[0].sourceContext) as Doc, await (second[0].targetContext) as Doc] : [undefined, undefined];
+        if (linkFollowDocs && !linkFollowDocs.some(l => l instanceof Promise)) {
+            let maxLocation = StrCast(linkFollowDocs[0].maximizeLocation, "inTab");
+            let targetContext = !Doc.AreProtosEqual(linkFollowDocContexts[reverse ? 1 : 0], currentContext) ? linkFollowDocContexts[reverse ? 1 : 0] : undefined;
+            DocumentManager.Instance.jumpToDocument(linkFollowDocs[reverse ? 1 : 0], zoom,
+                // open up target if it's not already in view ... by zooming into the button document first and setting flag to reset zoom afterwards
+                (doc: Doc) => focus(doc, maxLocation), targetContext);
+        }
+    }
+
     @action
     zoomIntoScale = (docDelegate: Doc, scale: number) => {
         let docView = DocumentManager.Instance.getDocumentView(Doc.GetProto(docDelegate));
@@ -202,8 +204,7 @@ export class DocumentManager {
     getScaleOfDocView = (docDelegate: Doc) => {
         let doc = Doc.GetProto(docDelegate);
 
-        let docView: DocumentView | null;
-        docView = DocumentManager.Instance.getDocumentView(doc);
+        const docView = DocumentManager.Instance.getDocumentView(doc);
         if (docView) {
             return docView.props.getScale();
         } else {
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 64821d8db..49bd93942 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -627,17 +627,16 @@ export class ImageResizeView {
                         let jumpToDoc = await Cast(linkDoc.anchor2, Doc);
                         if (jumpToDoc) {
                             if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
-
-                                DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((jumpToDoc === linkDoc.anchor2 ? linkDoc.anchor2Page : linkDoc.anchor1Page)));
+                                DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey);
                                 return;
                             }
                         }
                         if (targetContext) {
-                            DocumentManager.Instance.jumpToDocument(targetContext, e.ctrlKey, false, document => addDocTab(document, undefined, location ? location : "inTab"));
+                            DocumentManager.Instance.jumpToDocument(targetContext, e.ctrlKey, document => addDocTab(document, undefined, location ? location : "inTab"));
                         } else if (jumpToDoc) {
-                            DocumentManager.Instance.jumpToDocument(jumpToDoc, e.ctrlKey, false, document => addDocTab(document, undefined, location ? location : "inTab"));
+                            DocumentManager.Instance.jumpToDocument(jumpToDoc, e.ctrlKey, document => addDocTab(document, undefined, location ? location : "inTab"));
                         } else {
-                            DocumentManager.Instance.jumpToDocument(linkDoc, e.ctrlKey, false, document => addDocTab(document, undefined, location ? location : "inTab"));
+                            DocumentManager.Instance.jumpToDocument(linkDoc, e.ctrlKey, document => addDocTab(document, undefined, location ? location : "inTab"));
                         }
                     }
                 });
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index f427e40b1..1cde2aa8e 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -192,7 +192,7 @@ export default class SharingManager extends React.Component<{}> {
                 onClick={() => {
                     let context: Opt<CollectionVideoView | CollectionPDFView | CollectionView>;
                     if (this.targetDoc && this.targetDocView && (context = this.targetDocView.props.ContainingCollectionView)) {
-                        DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, undefined, undefined, context.props.Document);
+                        DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, context.props.Document);
                     }
                 }}
                 onPointerEnter={action(() => {
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index e928887e2..38d050e5c 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -5,7 +5,7 @@ import { Doc, DocListCast } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { List } from '../../../new_fields/List';
 import { listSpec } from '../../../new_fields/Schema';
-import { BoolCast, Cast, NumCast, PromiseValue, StrCast } from '../../../new_fields/Types';
+import { BoolCast, Cast, NumCast, PromiseValue, StrCast, FieldValue } from '../../../new_fields/Types';
 import { DocumentManager } from '../../util/DocumentManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { ContextMenu } from '../ContextMenu';
@@ -130,8 +130,11 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         let value = Cast(targetDataDoc[targetField], listSpec(Doc), []);
         let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1);
         index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
-        PromiseValue(Cast(doc.annotationOn, Doc)).then(annotationOn =>
-            annotationOn === this.dataDoc.Document && (doc.annotationOn = undefined));
+        PromiseValue(Cast(doc.annotationOn, Doc)).then(annotationOn => {
+            if (Doc.AreProtosEqual(annotationOn, FieldValue(Cast(this.dataDoc.extendsDoc, Doc)))) {
+                Doc.GetProto(doc).annotationOn = undefined;
+            }
+        });
 
         if (index !== -1) {
             value.splice(index, 1);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 98aff41d3..14e513157 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -574,8 +574,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         let curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc;
         if (curPres) {
             let pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" });
-            Doc.GetProto(pinDoc).target = doc;
-            Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.target instanceof Doc) && this.target.title.toString()');
+            Doc.GetProto(pinDoc).presentationTargetDoc = doc;
+            Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.presentationTargetDoc instanceof Doc) && this.presentationTargetDoc.title.toString()');
             const data = Cast(curPres.data, listSpec(Doc));
             if (data) {
                 data.push(pinDoc);
@@ -614,8 +614,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         }
     }
 
-    panelWidth = () => this._document!.ignoreAspect || this._document!.fitWidth ? this._panelWidth : Math.min(this._panelWidth, Math.max(NumCast(this._document!.width), this.nativeWidth()));
-    panelHeight = () => this._document!.ignoreAspect || this._document!.fitWidth ? this._panelHeight : Math.min(this._panelHeight, Math.max(NumCast(this._document!.height), this.nativeHeight()));
+    panelWidth = () => this._document && this._document.maxWidth ? Math.min(Math.max(NumCast(this._document.width), NumCast(this._document.nativeWidth)), this._panelWidth) : this._panelWidth;
+    panelHeight = () => this._panelHeight;
 
     nativeWidth = () => !this._document!.ignoreAspect && !this._document!.fitWidth ? NumCast(this._document!.nativeWidth) || this._panelWidth : 0;
     nativeHeight = () => !this._document!.ignoreAspect && !this._document!.fitWidth ? NumCast(this._document!.nativeHeight) || this._panelHeight : 0;
@@ -644,7 +644,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         }
         return Transform.Identity();
     }
-    get previewPanelCenteringOffset() { return this.nativeWidth() && !BoolCast(this._document!.ignoreAspect) ? (this._panelWidth - this.nativeWidth() / this.ScreenToLocalTransform().Scale) / 2 : 0; }
+    get previewPanelCenteringOffset() { return this.nativeWidth() && !this._document!.ignoreAspect ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
 
     addDocTab = (doc: Doc, dataDoc: Opt<Doc>, location: string) => {
         SelectionManager.DeselectAll();
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 155f2718b..28d1eb384 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -31,6 +31,7 @@ export interface CollectionViewProps extends FieldViewProps {
     moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
     PanelWidth: () => number;
     PanelHeight: () => number;
+    VisibleHeight?: () => number;
     chromeCollapsed: boolean;
     setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
 }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ffd1f9170..882a0f144 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -217,7 +217,7 @@ class TreeView extends React.Component<TreeViewProps> {
         if (de.data instanceof DragManager.LinkDragData) {
             let sourceDoc = de.data.linkSourceDocument;
             let destDoc = this.props.document;
-            DocUtils.MakeLink(sourceDoc, destDoc);
+            DocUtils.MakeLink({doc:sourceDoc}, {doc:destDoc});
             e.stopPropagation();
         }
         if (de.data instanceof DragManager.DocumentDragData) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index eb40e0bcb..4bdede48a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -403,8 +403,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         SelectionManager.DeselectAll();
         if (this.props.Document.scrollHeight) {
             let annotOn = Cast(doc.annotationOn, Doc) as Doc;
-            let offset = annotOn && (NumCast(annotOn.height) / 2 * 72 / 96);
-            this.props.Document.scrollY = NumCast(doc.y) - offset;
+            if (!annotOn) {
+                this.props.focus(doc);
+            } else {
+                let contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn.height);
+                let offset = annotOn && (contextHgt / 2 * 96 / 72);
+                this.props.Document.scrollY = NumCast(doc.y) - offset;
+            }
         } else {
             const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2;
             const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2;
@@ -416,6 +421,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
             this.setPan(newPanX, newPanY);
             this.Document.panTransformType = "Ease";
+            Doc.BrushDoc(this.props.Document);
             this.props.focus(this.props.Document);
             willZoom && this.setScaleToZoom(doc, scale);
 
diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx
index 72fff8e53..53b720a9e 100644
--- a/src/client/views/linking/LinkFollowBox.tsx
+++ b/src/client/views/linking/LinkFollowBox.tsx
@@ -171,7 +171,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
     @undoBatch
     openFullScreen = () => {
         if (LinkFollowBox.destinationDoc) {
-            let view: DocumentView | null = DocumentManager.Instance.getDocumentView(LinkFollowBox.destinationDoc);
+            let view = DocumentManager.Instance.getDocumentView(LinkFollowBox.destinationDoc);
             view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view);
         }
     }
@@ -250,10 +250,10 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
             let dockingFunc = (document: Doc) => { (LinkFollowBox._addDocTab || this.props.addDocTab)(document, undefined, "inTab"); SelectionManager.DeselectAll(); };
 
             if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor2 && targetContext) {
-                DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, async document => dockingFunc(document), undefined, targetContext);
+                DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, async document => dockingFunc(document), targetContext);
             }
             else if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor1 && sourceContext) {
-                DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, document => dockingFunc(sourceContext!));
+                DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, document => dockingFunc(sourceContext!));
                 if (LinkFollowBox.sourceDoc && LinkFollowBox.destinationDoc) {
                     if (guid) {
                         let views = DocumentManager.Instance.getDocumentViews(jumpToDoc);
@@ -264,12 +264,11 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
                 }
             }
             else if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
-                DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, undefined, undefined,
-                    NumCast((LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor2 ? LinkFollowBox.linkDoc.anchor2Page : LinkFollowBox.linkDoc.anchor1Page)));
+                DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom);
 
             }
             else {
-                DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, dockingFunc);
+                DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, dockingFunc);
             }
 
             this.highlightDoc();
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a903f8fe2..3273abc1d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -206,7 +206,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     buttonClick = async (altKey: boolean, ctrlKey: boolean) => {
         let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs);
         let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs);
-        let linkedDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document);
+        let linkDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document);
         let expandedDocs: Doc[] = [];
         expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs;
         expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs;
@@ -223,28 +223,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 expandedDocs.forEach(maxDoc => (!this.props.addDocTab(maxDoc, undefined, "close") && this.props.addDocTab(maxDoc, undefined, maxLocation)));
             }
         }
-        else if (linkedDocs.length) {
-            SelectionManager.DeselectAll();
-            let first = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor1 as Doc, this.props.Document) && !d.anchor1anchored);
-            let second = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor2 as Doc, this.props.Document) && !d.anchor2anchored);
-            let firstUnshown = first.filter(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
-            let secondUnshown = second.filter(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0);
-            if (firstUnshown.length) first = [firstUnshown[0]];
-            if (secondUnshown.length) second = [secondUnshown[0]];
-            let linkedFwdDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : second.length ? [second[0].anchor1 as Doc, second[0].anchor1 as Doc] : undefined;
-
-            // @TODO: shouldn't always follow target context
-            let linkedFwdContextDocs = [first.length ? await (first[0].targetContext) as Doc : undefined, undefined];
-            let linkedFwdPage = [first.length ? NumCast(first[0].anchor2Page, undefined) : undefined, undefined];
-
-            if (linkedFwdDocs && !linkedFwdDocs.some(l => l instanceof Promise)) {
-                let maxLocation = StrCast(linkedFwdDocs[0].maximizeLocation, "inTab");
-                let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionDoc) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined;
-                DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false,
-                    // open up target if it's not already in view ... by zooming into the button document first and setting flag to reset zoom afterwards
-                    doc => this.props.focus(this.props.Document, true, 1, () => this.props.addDocTab(doc, undefined, maxLocation)),
-                    linkedFwdPage[altKey ? 1 : 0], targetContext);
-            }
+        else if (linkDocs.length) {
+            DocumentManager.Instance.FollowLink(this.props.Document,
+                (doc: Doc, maxLocation: string) => this.props.focus(this.props.Document, true, 1, () => this.props.addDocTab(doc, undefined, maxLocation)),
+                ctrlKey, altKey, this.props.ContainingCollectionDoc);
         }
     }
 
@@ -352,15 +334,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (de.data instanceof DragManager.AnnotationDragData) {
             /// this whole section for handling PDF annotations looks weird.  Need to rethink this to make it cleaner
             e.stopPropagation();
-            let sourceDoc = de.data.annotationDocument;
-            let targetDoc = this.props.Document;
-            let annotations = await DocListCastAsync(sourceDoc.annotations);
-            sourceDoc.linkedToDoc = true;
-            de.data.targetContext = this.props.ContainingCollectionDoc;
-            targetDoc.targetContext = de.data.targetContext;
-            annotations && annotations.forEach(anno => anno.target = targetDoc);
-
-            DocUtils.MakeLink(sourceDoc, targetDoc, this.props.ContainingCollectionDoc, `Link from ${StrCast(sourceDoc.title)}`);
+            (de.data as any).linkedToDoc = true;
+
+            DocUtils.MakeLink({ doc: de.data.annotationDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, `Link from ${StrCast(de.data.annotationDocument.title)}`);
         }
         if (de.data instanceof DragManager.DocumentDragData && de.data.applyAsTemplate) {
             Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document);
@@ -371,7 +347,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             // const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true);
             // const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView);
             de.data.linkSourceDocument !== this.props.Document &&
-                (de.data.linkDocument = DocUtils.MakeLink(de.data.linkSourceDocument, this.props.Document, this.props.ContainingCollectionDoc, undefined, "in-text link being created")); // TODODO this is where in text links get passed
+                (de.data.linkDocument = DocUtils.MakeLink({ doc: de.data.linkSourceDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, "in-text link being created")); // TODODO this is where in text links get passed
         }
     }
 
@@ -407,7 +383,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             let portalID = (this.Document.title + ".portal").replace(/^-/, "").replace(/\([0-9]*\)$/, "");
             DocServer.GetRefField(portalID).then(existingPortal => {
                 let portal = existingPortal instanceof Doc ? existingPortal : Docs.Create.FreeformDocument([], { width: (this.Document.width || 0) + 10, height: this.Document.height || 0, title: portalID });
-                DocUtils.MakeLink(this.props.Document, portal, undefined, portalID);
+                DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: portal }, portalID, "portal link");
                 this.Document.isButton = true;
             });
         }
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 449f56b16..749886d9a 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -9,7 +9,7 @@ import { Fragment, Node, Node as ProsNode, NodeType, Slice, Mark, ResolvedPos }
 import { EditorState, Plugin, Transaction, TextSelection, NodeSelection } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
 import { DateField } from '../../../new_fields/DateField';
-import { Doc, DocListCast, Opt, WidthSym } from "../../../new_fields/Doc";
+import { Doc, DocListCast, Opt, WidthSym, DocListCastAsync } from "../../../new_fields/Doc";
 import { Copy, Id } from '../../../new_fields/FieldSymbols';
 import { List } from '../../../new_fields/List';
 import { RichTextField } from "../../../new_fields/RichTextField";
@@ -230,7 +230,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                     this.dataDoc[key] = doc || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value);
                     DocUtils.Publish(this.dataDoc[key] as Doc, value, this.props.addDocument, this.props.removeDocument);
                     if (linkDoc) { (linkDoc as Doc).anchor2 = this.dataDoc[key] as Doc; }
-                    else DocUtils.MakeLink(this.dataDoc, this.dataDoc[key] as Doc, undefined, "Ref:" + value, undefined, undefined, id, true);
+                    else DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: this.dataDoc[key] as Doc }, "Ref:" + value, "link to named target", id, true);
                 });
             });
         });
@@ -342,7 +342,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             let model: NodeType = [".mov", ".mp4"].includes(url) ? schema.nodes.video : schema.nodes.image;
             let pos = this._editorView!.posAtCoords({ left: de.x, top: de.y });
             this._editorView!.dispatch(this._editorView!.state.tr.insert(pos!.pos, model.create({ src: url, docid: target[Id] })));
-            DocUtils.MakeLink(this.dataDoc, target, undefined, "ImgRef:" + target.title, undefined, undefined, target[Id]);
+            DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: target }, "ImgRef:" + target.title);
             this.tryUpdateHeight();
             e.stopPropagation();
         } else if (de.data instanceof DragManager.DocumentDragData) {
@@ -667,55 +667,42 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
 
     handlePaste = (view: EditorView, event: Event, slice: Slice): boolean => {
         let cbe = event as ClipboardEvent;
-        let docId: string;
-        let regionId: string;
-        if (!cbe.clipboardData) {
-            return false;
-        }
-        let linkId: string;
-        docId = cbe.clipboardData.getData("dash/pdfOrigin");
-        regionId = cbe.clipboardData.getData("dash/pdfRegion");
-        if (!docId || !regionId) {
-            return false;
-        }
-
-        DocServer.GetRefField(docId).then(doc => {
-            DocServer.GetRefField(regionId).then(region => {
-                if (!(doc instanceof Doc) || !(region instanceof Doc)) {
-                    return;
-                }
-
-                let annotations = DocListCast(region.annotations);
-                annotations.forEach(anno => anno.target = this.props.Document);
-                let fieldExtDoc = Doc.fieldExtensionDoc(doc, "data");
-                let targetAnnotations = DocListCast(fieldExtDoc.annotations);
-                if (targetAnnotations) {
-                    targetAnnotations.push(region);
-                    fieldExtDoc.annotations = new List<Doc>(targetAnnotations);
-                }
+        const pdfDocId = cbe.clipboardData && cbe.clipboardData.getData("dash/pdfOrigin");
+        const pdfRegionId = cbe.clipboardData && cbe.clipboardData.getData("dash/pdfRegion");
+        if (pdfDocId && pdfRegionId) {
+            DocServer.GetRefField(pdfDocId).then(pdfDoc => {
+                DocServer.GetRefField(pdfRegionId).then(pdfRegion => {
+                    if ((pdfDoc instanceof Doc) && (pdfRegion instanceof Doc)) {
+                        setTimeout(async () => {
+                            let targetAnnotations = await DocListCastAsync(Doc.fieldExtensionDoc(pdfDoc, "data").annotations);// bcz: NO... this assumes the pdf is using its 'data' field.  need to have the PDF's view handle updating its own annotations
+                            targetAnnotations && targetAnnotations.push(pdfRegion);
+                        });
 
-                let link = DocUtils.MakeLink(this.props.Document, region, doc);
-                if (link) {
-                    cbe.clipboardData!.setData("dash/linkDoc", link[Id]);
-                    linkId = link[Id];
-                    let frag = addMarkToFrag(slice.content, (node: Node) => addLinkMark(node, StrCast(doc.title)));
-                    slice = new Slice(frag, slice.openStart, slice.openEnd);
-                    var tr = view.state.tr.replaceSelection(slice);
-                    view.dispatch(tr.scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste"));
-                }
+                        let link = DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: pdfRegion, ctx: pdfDoc }, "note on " + pdfDoc.title, "pasted PDF link");
+                        if (link) {
+                            cbe.clipboardData!.setData("dash/linkDoc", link[Id]);
+                            let linkId = link[Id];
+                            let frag = addMarkToFrag(slice.content, (node: Node) => addLinkMark(node, StrCast(pdfDoc.title), linkId));
+                            slice = new Slice(frag, slice.openStart, slice.openEnd);
+                            var tr = view.state.tr.replaceSelection(slice);
+                            view.dispatch(tr.scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste"));
+                        }
+                    }
+                });
             });
-        });
+            return true;
+        }
+        return false;
 
-        return true;
 
         function addMarkToFrag(frag: Fragment, marker: (node: Node) => Node) {
             const nodes: Node[] = [];
             frag.forEach(node => nodes.push(marker(node)));
             return Fragment.fromArray(nodes);
         }
-        function addLinkMark(node: Node, title: string) {
+        function addLinkMark(node: Node, title: string, linkId: string) {
             if (!node.isText) {
-                const content = addMarkToFrag(node.content, (node: Node) => addLinkMark(node, title));
+                const content = addMarkToFrag(node.content, (node: Node) => addLinkMark(node, title, linkId));
                 return node.copy(content);
             }
             const marks = [...node.marks];
@@ -879,16 +866,16 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
 
                                 if (jumpToDoc) {
                                     if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
-                                        DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((jumpToDoc === linkDoc.anchor2 ? linkDoc.anchor2Page : linkDoc.anchor1Page)));
+                                        DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey);
                                         return;
                                     }
                                 }
                                 if (targetContext && (!jumpToDoc || targetContext !== await jumpToDoc.annotationOn)) {
-                                    DocumentManager.Instance.jumpToDocument(jumpToDoc || targetContext, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab"), undefined, targetContext);
+                                    DocumentManager.Instance.jumpToDocument(jumpToDoc || targetContext, ctrlKey, document => this.props.addDocTab(document, undefined, location ? location : "inTab"), targetContext);
                                 } else if (jumpToDoc) {
-                                    DocumentManager.Instance.jumpToDocument(jumpToDoc, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab"));
+                                    DocumentManager.Instance.jumpToDocument(jumpToDoc, ctrlKey, document => this.props.addDocTab(document, undefined, location ? location : "inTab"));
                                 } else {
-                                    DocumentManager.Instance.jumpToDocument(linkDoc, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab"));
+                                    DocumentManager.Instance.jumpToDocument(linkDoc, ctrlKey, document => this.props.addDocTab(document, undefined, location ? location : "inTab"));
                                 }
                             }
                         });
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 9e9fe1324..fe4f75cad 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -299,7 +299,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
                 let rotation = NumCast(this.dataDoc.rotation) % 180;
                 let realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size;
                 let aspect = realsize.height / realsize.width;
-                if (layoutdoc.nativeHeight !== 0 && layoutdoc.nativeWidth !== 0 && (Math.abs(NumCast(layoutdoc.height) - realsize.height) > 1 || Math.abs(NumCast(layoutdoc.width) - realsize.width) > 1)) {
+                if (layoutdoc.nativeHeight !== 0 && layoutdoc.nativeWidth !== 0 && (Math.abs(1 - NumCast(layoutdoc.nativeHeight) / NumCast(layoutdoc.nativeWidth) / (realsize.height / realsize.width)) > 0.1)) {
                     setTimeout(action(() => {
                         layoutdoc.height = layoutdoc[WidthSym]() * aspect;
                         layoutdoc.nativeHeight = realsize.height;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 617b580dd..88cd2cdc4 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -57,11 +57,8 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     }
     loaded = (nw: number, nh: number, np: number) => {
         this.dataDoc.numPages = np;
-        if (!this.Document.nativeWidth || !this.Document.nativeHeight || !this.Document.scrollHeight) {
-            let oldaspect = (this.Document.nativeHeight || 0) / (this.Document.nativeWidth || 1);
-            this.Document.nativeWidth = nw * 96 / 72;
-            this.Document.nativeHeight = this.Document.nativeHeight ? nw * 96 / 72 * oldaspect : nh * 96 / 72;
-        }
+        this.Document.nativeWidth = nw * 96 / 72;
+        this.Document.nativeHeight = nh * 96 / 72;
         !this.Document.fitWidth && !this.Document.ignoreAspect && (this.Document.height = this.Document[WidthSym]() * (nh / nw));
     }
 
@@ -177,12 +174,14 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
 
         ContextMenu.Instance.addItem({ description: "Pdf Funcs...", subitems: funcs, icon: "asterisk" });
     }
+    _initialScale: number | undefined;
     render() {
         const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
         let classname = "pdfBox-cont" + (InkingControl.Instance.selectedTool || !this.active ? "" : "-interactive");
         let noPdf = !(pdfUrl instanceof PdfField) || !this._pdf;
-        if (!noPdf && (this.props.isSelected() || this.props.ScreenToLocalTransform().Scale < 2.5)) this._everActive = true;
-        return (!this._everActive ?
+        if (this._initialScale === undefined) this._initialScale = this.props.ScreenToLocalTransform().Scale;
+        if (this.props.isSelected() || this.props.Document.scrollY !== undefined) this._everActive = true;
+        return (noPdf || (!this._everActive && this.props.ScreenToLocalTransform().Scale > 2.5) ?
             <div className="pdfBox-title-outer" >
                 <div className={classname} >
                     <strong className="pdfBox-title" >{` ${this.props.Document.title}`}</strong>
@@ -203,11 +202,11 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
                     setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView}
                     renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth}
                     Document={this.props.Document} DataDoc={this.dataDoc} ContentScaling={this.props.ContentScaling}
-                    addDocTab={this.props.addDocTab} GoToPage={this.gotoPage}
+                    addDocTab={this.props.addDocTab} GoToPage={this.gotoPage} focus={this.props.focus}
                     pinToPres={this.props.pinToPres} addDocument={this.props.addDocument}
                     ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select}
                     isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged}
-                    fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} startupLive={this.props.ScreenToLocalTransform().Scale < 2.5 ? true : false} />
+                    fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} startupLive={this._initialScale < 2.5 ? true : false} />
                 {this.settingsPanel()}
             </div>);
     }
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index f44ca99b9..180ed9032 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -43,8 +43,8 @@ export class PresBox extends React.Component<FieldViewProps> {
                 value.forEach((item, i) => {
                     if (item instanceof Doc && item.type !== DocumentType.PRESELEMENT) {
                         let pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" });
-                        Doc.GetProto(pinDoc).target = item;
-                        Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.target instanceof Doc) && this.target.title.toString()');
+                        Doc.GetProto(pinDoc).presentationTargetDoc = item;
+                        Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.presentationTargetDoc instanceof Doc) && this.presentationTargetDoc.title.toString()');
                         value.splice(i, 1, pinDoc);
                     }
                 });
@@ -124,13 +124,13 @@ export class PresBox extends React.Component<FieldViewProps> {
         this.childDocs.forEach((doc, ind) => {
             //the order of cases is aligned based on priority
             if (doc.hideTillShownButton && ind <= index) {
-                (doc.target as Doc).opacity = 1;
+                (doc.presentationTargetDoc as Doc).opacity = 1;
             }
             if (doc.hideAfterButton && ind < index) {
-                (doc.target as Doc).opacity = 0;
+                (doc.presentationTargetDoc as Doc).opacity = 0;
             }
             if (doc.fadeButton && ind < index) {
-                (doc.target as Doc).opacity = 0.5;
+                (doc.presentationTargetDoc as Doc).opacity = 0.5;
             }
         });
     }
@@ -145,13 +145,13 @@ export class PresBox extends React.Component<FieldViewProps> {
             //the order of cases is aligned based on priority
 
             if (key.hideAfterButton && ind >= index) {
-                (key.target as Doc).opacity = 1;
+                (key.presentationTargetDoc as Doc).opacity = 1;
             }
             if (key.fadeButton && ind >= index) {
-                (key.target as Doc).opacity = 1;
+                (key.presentationTargetDoc as Doc).opacity = 1;
             }
             if (key.hideTillShownButton && ind > index) {
-                (key.target as Doc).opacity = 0;
+                (key.presentationTargetDoc as Doc).opacity = 0;
             }
         });
     }
@@ -162,7 +162,7 @@ export class PresBox extends React.Component<FieldViewProps> {
      * te option open, navigates to that element.
      */
     navigateToElement = async (curDoc: Doc, fromDocIndex: number) => {
-        let fromDoc = this.childDocs[fromDocIndex].target as Doc;
+        let fromDoc = this.childDocs[fromDocIndex].presentationTargetDoc as Doc;
         let docToJump = curDoc;
         let willZoom = false;
 
@@ -190,7 +190,7 @@ export class PresBox extends React.Component<FieldViewProps> {
         //docToJump stayed same meaning, it was not in the group or was the last element in the group
         if (docToJump === curDoc) {
             //checking if curDoc has navigation open
-            let target = await curDoc.target as Doc;
+            let target = await curDoc.presentationTargetDoc as Doc;
             if (curDoc.navButton) {
                 DocumentManager.Instance.jumpToDocument(target, false);
             } else if (curDoc.showButton) {
@@ -210,8 +210,8 @@ export class PresBox extends React.Component<FieldViewProps> {
         let curScale = DocumentManager.Instance.getScaleOfDocView(fromDoc);
 
         //awaiting jump so that new scale can be found, since jumping is async
-        await DocumentManager.Instance.jumpToDocument(await docToJump.target as Doc, willZoom);
-        let newScale = DocumentManager.Instance.getScaleOfDocView(await curDoc.target as Doc);
+        await DocumentManager.Instance.jumpToDocument(await docToJump.presentationTargetDoc as Doc, willZoom);
+        let newScale = DocumentManager.Instance.getScaleOfDocView(await curDoc.presentationTargetDoc as Doc);
         curDoc.viewScale = newScale;
         //saving the scale that user was on
         if (curScale !== 1) {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index b7d9a1eab..573197117 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -129,7 +129,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
                         width: 150, height: height / width * 150, title: "--snapshot" + NumCast(this.props.Document.curPage) + " image-"
                     });
                     this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.addDocument && this.props.ContainingCollectionView.props.addDocument(imageSummary, false);
-                    DocUtils.MakeLink(imageSummary, this.props.Document);
+                    DocUtils.MakeLink({doc:imageSummary}, {doc: this.props.Document}, "snapshot from " + this.props.Document.title, "video frame snapshot");
                 }
             });
         }
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 3ed85f6a5..f7f52b3ef 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -1,7 +1,7 @@
 import React = require("react");
 import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, WidthSym, Opt } from "../../../new_fields/Doc";
+import { Doc, DocListCast, HeightSym, WidthSym, Opt, DocListCastAsync } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
 import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
@@ -14,6 +14,7 @@ interface IAnnotationProps {
     fieldExtensionDoc: Doc;
     addDocTab: (document: Doc, dataDoc: Opt<Doc>, where: string) => boolean;
     pinToPres: (document: Doc) => void;
+    focus: (doc: Doc) => void;
 }
 
 export default class Annotation extends React.Component<IAnnotationProps> {
@@ -60,6 +61,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
     }
 
     componentWillUnmount() {
+        this._brushDisposer && this._brushDisposer();
         this._reactionDisposer && this._reactionDisposer();
     }
 
@@ -94,14 +96,11 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
             PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true);
         }
         else if (e.button === 0) {
-            let targetDoc = await Cast(this.props.document.target, Doc);
-            if (targetDoc) {
-                let context = await Cast(targetDoc.targetContext, Doc);
-                if (context) {
-                    DocumentManager.Instance.jumpToDocument(targetDoc, false, false,
-                        ((doc) => this.props.addDocTab(targetDoc!, undefined, e.ctrlKey ? "onRight" : "inTab")),
-                        undefined, undefined);
-                }
+            let annoGroup = await Cast(this.props.document.group, Doc);
+            if (annoGroup) {
+                DocumentManager.Instance.FollowLink(annoGroup,
+                    (doc: Doc, maxLocation: string) => this.props.addDocTab(doc, undefined, e.ctrlKey ? "onRight" : "inTab"),
+                    false, false, undefined);
             }
         }
     }
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index 2202351ee..e62542014 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -31,7 +31,7 @@ export default class PDFMenu extends React.Component {
     @observable public Pinned: boolean = false;
 
     public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = emptyFunction;
-    public Highlight: (d: Doc | undefined, color: string) => void = emptyFunction;
+    public Highlight: (color: string) => void = emptyFunction;
     public Delete: () => void = emptyFunction;
     public Snippet: (marquee: { left: number, top: number, width: number, height: number }) => void = emptyFunction;
     public AddTag: (key: string, value: string) => boolean = returnFalse;
@@ -156,11 +156,11 @@ export default class PDFMenu extends React.Component {
     @action
     highlightClicked = (e: React.MouseEvent) => {
         if (!this.Pinned) {
-            this.Highlight(undefined, "#f4f442");
+            this.Highlight("#f4f442");
         }
         else {
             this.Highlighting = !this.Highlighting;
-            this.Highlight(undefined, "#f4f442");
+            this.Highlight("#f4f442");
         }
     }
 
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 33226eac4..20dfc4d8c 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -10,7 +10,6 @@ import { listSpec } from "../../../new_fields/Schema";
 import { ScriptField } from "../../../new_fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import smoothScroll, { Utils, emptyFunction, returnOne } from "../../../Utils";
-import { DocServer } from "../../DocServer";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
 import { CompiledScript, CompileScript } from "../../util/Scripting";
@@ -44,6 +43,7 @@ interface IViewerProps {
     select: (isCtrlPressed: boolean) => void;
     startupLive: boolean;
     renderDepth: number;
+    focus: (doc: Doc) => void;
     isSelected: () => boolean;
     loaded: (nw: number, nh: number, np: number) => void;
     active: () => boolean;
@@ -108,7 +108,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
         // file address of the pdf
         this._coverPath = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${this.props.url.substring("files/".length, this.props.url.length - ".pdf".length)}-${NumCast(this.props.Document.curPage, 1)}.PNG`)));
         runInAction(() => this._showWaiting = this._showCover = true);
-        this._selectionReactionDisposer = reaction(() => this.props.isSelected(), () => this.props.isSelected() && SelectionManager.SelectedDocuments().length === 1 && this.setupPdfJsViewer(), { fireImmediately: this.props.startupLive });
+        this.props.startupLive && this.setupPdfJsViewer();
+        this._selectionReactionDisposer = reaction(() => this.props.isSelected(), () => (this.props.isSelected() && SelectionManager.SelectedDocuments().length === 1) && this.setupPdfJsViewer(), { fireImmediately: true });
         this._reactionDisposer = reaction(
             () => this.props.Document.scrollY,
             (scrollY) => {
@@ -136,19 +137,11 @@ export class PDFViewer extends React.Component<IViewerProps> {
         if (this.props.active() && e.clipboardData) {
             e.clipboardData.setData("text/plain", this._selectionText);
             e.clipboardData.setData("dash/pdfOrigin", this.props.Document[Id]);
-            e.clipboardData.setData("dash/pdfRegion", this.makeAnnotationDocument(undefined, "#0390fc")[Id]);
+            e.clipboardData.setData("dash/pdfRegion", this.makeAnnotationDocument("#0390fc")[Id]);
             e.preventDefault();
         }
     }
 
-    paste = (e: ClipboardEvent) => {
-        if (e.clipboardData && e.clipboardData.getData("dash/pdfOrigin") === this.props.Document[Id]) {
-            let linkDocId = e.clipboardData.getData("dash/linkDoc");
-            linkDocId && DocServer.GetRefField(linkDocId).then(async (link) =>
-                (link instanceof Doc) && (Doc.GetProto(link).anchor2 = this.makeAnnotationDocument(await Cast(Doc.GetProto(link), Doc), "#0390fc", false)));
-        }
-    }
-
     setSelectionText = (text: string) => this._selectionText = text;
 
     @action
@@ -194,13 +187,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
             { fireImmediately: true }
         );
 
-        document.removeEventListener("copy", this.copy);
-        document.addEventListener("copy", this.copy);
-        document.addEventListener("pagesinit", action(() => {
-            this.pdfViewer.currentScaleValue = this._zoomed = 1;
-            this.gotoPage(NumCast(this.props.Document.curPage, 1));
-        }));
-        document.addEventListener("pagerendered", action(() => this._showCover = this._showWaiting = false));
         this.createPdfViewer();
     }
 
@@ -212,6 +198,13 @@ export class PDFViewer extends React.Component<IViewerProps> {
             }
             return;
         }
+        document.removeEventListener("copy", this.copy);
+        document.addEventListener("copy", this.copy);
+        document.addEventListener("pagesinit", action(() => {
+            this.pdfViewer.currentScaleValue = this._zoomed = 1;
+            this.gotoPage(NumCast(this.props.Document.curPage, 1));
+        }));
+        document.addEventListener("pagerendered", action(() => this._showCover = this._showWaiting = false));
         var pdfLinkService = new PDFJSViewer.PDFLinkService();
         let pdfFindController = new PDFJSViewer.PDFFindController({
             linkService: pdfLinkService,
@@ -229,19 +222,18 @@ export class PDFViewer extends React.Component<IViewerProps> {
     }
 
     @action
-    makeAnnotationDocument = (sourceDoc: Doc | undefined, color: string, createLink: boolean = true): Doc => {
+    makeAnnotationDocument = (color: string): Doc => {
         let mainAnnoDoc = Docs.Create.InstanceFromProto(new Doc(), "", {});
         let mainAnnoDocProto = Doc.GetProto(mainAnnoDoc);
         let annoDocs: Doc[] = [];
         let minY = Number.MAX_VALUE;
-        if (this._savedAnnotations.size() === 1 && this._savedAnnotations.values()[0].length === 1 && !createLink) {
+        if (this._savedAnnotations.size() === 1 && this._savedAnnotations.values()[0].length === 1) {
             let anno = this._savedAnnotations.values()[0][0];
             let annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: "rgba(255, 0, 0, 0.1)", title: "Annotation on " + StrCast(this.props.Document.title) });
             if (anno.style.left) annoDoc.x = parseInt(anno.style.left);
             if (anno.style.top) annoDoc.y = parseInt(anno.style.top);
             if (anno.style.height) annoDoc.height = parseInt(anno.style.height);
             if (anno.style.width) annoDoc.width = parseInt(anno.style.width);
-            annoDoc.target = sourceDoc;
             annoDoc.group = mainAnnoDoc;
             annoDoc.color = color;
             annoDoc.type = AnnotationTypes.Region;
@@ -258,7 +250,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 if (anno.style.top) annoDoc.y = parseInt(anno.style.top);
                 if (anno.style.height) annoDoc.height = parseInt(anno.style.height);
                 if (anno.style.width) annoDoc.width = parseInt(anno.style.width);
-                annoDoc.target = sourceDoc;
                 annoDoc.group = mainAnnoDoc;
                 annoDoc.color = color;
                 annoDoc.type = AnnotationTypes.Region;
@@ -272,9 +263,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
         }
         mainAnnoDocProto.title = "Annotation on " + StrCast(this.props.Document.title);
         mainAnnoDocProto.annotationOn = this.props.Document;
-        if (sourceDoc && createLink) {
-            DocUtils.MakeLink(sourceDoc, mainAnnoDocProto, undefined, `Annotation from ${StrCast(this.props.Document.title)}`);
-        }
         this._savedAnnotations.clear();
         this.Index = -1;
         return mainAnnoDoc;
@@ -529,7 +517,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         }
 
         if (PDFMenu.Instance.Highlighting) {
-            this.highlight(undefined, "goldenrod");
+            this.highlight("goldenrod");
         }
         else {
             PDFMenu.Instance.StartDrag = this.startDrag;
@@ -540,9 +528,9 @@ export class PDFViewer extends React.Component<IViewerProps> {
     }
 
     @action
-    highlight = (targetDoc: Doc | undefined, color: string) => {
+    highlight = (color: string) => {
         // creates annotation documents for current highlights
-        let annotationDoc = this.makeAnnotationDocument(targetDoc, color, false);
+        let annotationDoc = this.makeAnnotationDocument(color);
         Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, annotationDoc);
         return annotationDoc;
     }
@@ -555,20 +543,14 @@ export class PDFViewer extends React.Component<IViewerProps> {
     startDrag = (e: PointerEvent, ele: HTMLElement): void => {
         e.preventDefault();
         e.stopPropagation();
-        let targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "New Annotation" });
-        targetDoc.targetPage = this.getPageFromScroll(this._marqueeY);
-        let annotationDoc = this.highlight(undefined, "red");
-        annotationDoc.linkedToDoc = false;
+        let targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "Note linked to " + this.props.Document.title });
+        let annotationDoc = this.highlight("red");
         let dragData = new DragManager.AnnotationDragData(this.props.Document, annotationDoc, targetDoc);
         DragManager.StartAnnotationDrag([ele], dragData, e.pageX, e.pageY, {
             handlers: {
-                dragComplete: () => {
-                    if (!annotationDoc.linkedToDoc) {
-                        let annotations = DocListCast(annotationDoc.annotations);
-                        annotations && annotations.forEach(anno => anno.target = targetDoc);
-                        DocUtils.MakeLink(annotationDoc, targetDoc, dragData.targetContext, `Annotation from ${StrCast(this.props.Document.title)}`);
-                    }
-                }
+                dragComplete: () => !(dragData as any).linkedToDoc &&
+                    DocUtils.MakeLink({ doc: annotationDoc }, { doc: dragData.dropDocument, ctx: dragData.targetContext }, `Annotation from ${StrCast(this.props.Document.title)}`, "link from PDF")
+
             },
             hideSource: false
         });
@@ -602,6 +584,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
     @action.bound
     removeDocument(doc: Doc): boolean {
+        Doc.GetProto(doc).annotationOn = undefined;
         //TODO This won't create the field if it doesn't already exist
         let targetDataDoc = this.props.fieldExtensionDoc;
         let targetField = this.props.fieldExt;
@@ -634,10 +617,10 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
     getCoverImage = () => {
         if (!this.props.Document[HeightSym]() || !this.props.Document.nativeHeight) {
-            setTimeout(() => {
+            setTimeout((() => {
                 this.props.Document.height = this.props.Document[WidthSym]() * this._coverPath.height / this._coverPath.width;
                 this.props.Document.nativeHeight = nativeWidth * this._coverPath.height / this._coverPath.width;
-            }, 0);
+            }).bind(this), 0);
         }
         let nativeWidth = NumCast(this.props.Document.nativeWidth);
         let nativeHeight = NumCast(this.props.Document.nativeHeight);
@@ -659,7 +642,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     @computed get annotationLayer() {
         return <div className="pdfViewer-annotationLayer" style={{ height: NumCast(this.props.Document.nativeHeight) }} ref={this._annotationLayer}>
             {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) =>
-                <Annotation {...this.props} anno={anno} key={`${anno[Id]}-annotation`} />)}
+                <Annotation {...this.props} focus={this.props.focus} anno={anno} key={`${anno[Id]}-annotation`} />)}
         </div>;
     }
     @computed get pdfViewerDiv() {
@@ -686,7 +669,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
                     setPreviewCursor={this.setPreviewCursor}
                     PanelHeight={() => NumCast(this.props.Document.scrollHeight, NumCast(this.props.Document.nativeHeight))}
                     PanelWidth={() => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : NumCast(this.props.Document.nativeWidth)}
-                    focus={emptyFunction}
+                    VisibleHeight={() => this.props.PanelHeight() / this.props.ContentScaling() * 72 / 96}
+                    focus={this.props.focus}
                     isSelected={this.props.isSelected}
                     select={emptyFunction}
                     active={this.active}
@@ -694,7 +678,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                     whenActiveChanged={this.whenActiveChanged}
                     removeDocument={this.removeDocument}
                     moveDocument={this.moveDocument}
-                    addDocument={(doc: Doc, allow: boolean | undefined) => { Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc); return true; }}
+                    addDocument={(doc: Doc, allow: boolean | undefined) => { Doc.GetProto(doc).annotationOn = this.props.Document; Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc); return true; }}
                     CollectionView={this.props.ContainingCollectionView}
                     ScreenToLocalTransform={this.scrollXf}
                     ruleProvider={undefined}
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index de3242d32..daf000dc7 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -63,13 +63,11 @@ export class PresElementBox extends React.Component<FieldViewProps> {
         this.hideTillShownButton = !this.hideTillShownButton;
         if (!this.hideTillShownButton) {
             if (this.myIndex >= this.currentIndex) {
-                (this.props.Document.target as Doc).opacity = 1;
+                (this.props.Document.presentationTargetDoc as Doc).opacity = 1;
             }
         } else {
-            if (this.presentationDoc.presStatus) {
-                if (this.myIndex > this.currentIndex) {
-                    (this.props.Document.target as Doc).opacity = 0;
-                }
+            if (this.presentationDoc.presStatus && this.myIndex > this.currentIndex) {
+                (this.props.Document.presentationTargetDoc as Doc).opacity = 0;
             }
         }
     }
@@ -85,14 +83,12 @@ export class PresElementBox extends React.Component<FieldViewProps> {
         this.hideAfterButton = !this.hideAfterButton;
         if (!this.hideAfterButton) {
             if (this.myIndex <= this.currentIndex) {
-                (this.props.Document.target as Doc).opacity = 1;
+                (this.props.Document.presentationTargetDoc as Doc).opacity = 1;
             }
         } else {
             if (this.fadeButton) this.fadeButton = false;
-            if (this.presentationDoc.presStatus) {
-                if (this.myIndex < this.currentIndex) {
-                    (this.props.Document.target as Doc).opacity = 0;
-                }
+            if (this.presentationDoc.presStatus && this.myIndex < this.currentIndex) {
+                (this.props.Document.presentationTargetDoc as Doc).opacity = 0;
             }
         }
     }
@@ -108,14 +104,12 @@ export class PresElementBox extends React.Component<FieldViewProps> {
         this.fadeButton = !this.fadeButton;
         if (!this.fadeButton) {
             if (this.myIndex <= this.currentIndex) {
-                (this.props.Document.target as Doc).opacity = 1;
+                (this.props.Document.presentationTargetDoc as Doc).opacity = 1;
             }
         } else {
             this.hideAfterButton = false;
-            if (this.presentationDoc.presStatus) {
-                if (this.myIndex < this.currentIndex) {
-                    (this.props.Document.target as Doc).opacity = 0.5;
-                }
+            if (this.presentationDoc.presStatus && (this.myIndex < this.currentIndex)) {
+                (this.props.Document.presentationTargetDoc as Doc).opacity = 0.5;
             }
         }
     }
@@ -162,7 +156,7 @@ export class PresElementBox extends React.Component<FieldViewProps> {
      * presentation element.
      */
     renderEmbeddedInline = () => {
-        if (!this.embedOpen || !(this.props.Document.target instanceof Doc)) {
+        if (!this.embedOpen || !(this.props.Document.presentationTargetDoc instanceof Doc)) {
             return (null);
         }
 
@@ -175,8 +169,8 @@ export class PresElementBox extends React.Component<FieldViewProps> {
                 width: propDocWidth === 0 ? "auto" : propDocWidth * scale(),
             }}>
                 <CollectionSchemaPreview
-                    fitToBox={StrCast(this.props.Document.target.type).indexOf(DocumentType.COL) !== -1}
-                    Document={this.props.Document.target}
+                    fitToBox={StrCast(this.props.Document.presentationTargetDoc.type).indexOf(DocumentType.COL) !== -1}
+                    Document={this.props.Document.presentationTargetDoc}
                     addDocument={returnFalse}
                     removeDocument={returnFalse}
                     ruleProvider={undefined}
@@ -205,7 +199,7 @@ export class PresElementBox extends React.Component<FieldViewProps> {
         let pbi = "presElementBox-interaction";
         return (
             <div className={className} key={p.Document[Id] + this.myIndex}
-                style={{ outlineWidth: Doc.IsBrushed(p.Document.target as Doc) ? `1px` : "0px", }}
+                style={{ outlineWidth: Doc.IsBrushed(p.Document.presentationTargetDoc as Doc) ? `1px` : "0px", }}
                 onClick={e => { p.focus(p.Document); e.stopPropagation(); }}>
                 {treecontainer ? (null) : <>
                     <strong className="presElementBox-name">
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index aeffc81c4..58304cebb 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -665,10 +665,12 @@ export namespace Doc {
     export function BrushDoc(doc: Doc) {
         brushManager.BrushedDoc.set(doc, true);
         brushManager.BrushedDoc.set(Doc.GetDataDoc(doc), true);
+        return doc;
     }
     export function UnBrushDoc(doc: Doc) {
         brushManager.BrushedDoc.delete(doc);
         brushManager.BrushedDoc.delete(Doc.GetDataDoc(doc));
+        return doc;
     }
 
     export class HighlightBrush {
-- 
cgit v1.2.3-70-g09d2


From 456e9120857f20fb609ab13bb07cbd8a2d2f850b Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 3 Oct 2019 00:25:44 -0400
Subject: cleaned up link following code.  changed opening in place behavior to
 not open if view already exists.  fixed formattedText box scrolling.  fixed
 clicking on image in text box. more...

---
 src/client/util/DocumentManager.ts          |  49 +++++----
 src/client/util/RichTextSchema.tsx          |  26 +----
 src/client/util/SearchUtil.ts               |   1 -
 src/client/views/MetadataEntryMenu.tsx      |   2 +-
 src/client/views/linking/LinkFollowBox.tsx  |  82 ++++-----------
 src/client/views/nodes/DocumentView.scss    |   1 +
 src/client/views/nodes/DocumentView.tsx     |  10 +-
 src/client/views/nodes/FormattedTextBox.tsx | 152 ++++++++++++----------------
 src/client/views/nodes/ImageBox.tsx         |   2 +-
 src/client/views/pdf/Annotation.tsx         |   2 +-
 src/new_fields/Doc.ts                       |  17 +++-
 11 files changed, 148 insertions(+), 196 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 4ebcdf83c..305a77b14 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -12,6 +12,7 @@ import { undoBatch, UndoManager } from './UndoManager';
 import { Scripting } from './Scripting';
 import { List } from '../../new_fields/List';
 import { SelectionManager } from './SelectionManager';
+import { notDeepEqual } from 'assert';
 
 
 export class DocumentManager {
@@ -131,12 +132,12 @@ export class DocumentManager {
         return pairs;
     }
 
-    public jumpToDocument = async (targetDoc: Doc, willZoom: boolean, dockFunc?: (doc: Doc) => void, docContext?: Doc, closeContextIfNotFound: boolean = false): Promise<void> => {
+    public jumpToDocument = async (targetDoc: Doc, willZoom: boolean, dockFunc?: (doc: Doc) => void, docContext?: Doc, linkId?: string, closeContextIfNotFound: boolean = false): Promise<void> => {
         const docView = DocumentManager.Instance.getFirstDocumentView(targetDoc);
         const annotatedDoc = await Cast(targetDoc.annotationOn, Doc);
         if (docView) {  // we have a docView already and aren't forced to create a new one ... just focus on the document.  TODO move into view if necessary otherwise just highlight?
             annotatedDoc && docView.props.focus(annotatedDoc, false);
-            docView.props.focus(targetDoc, willZoom);
+            docView.props.focus(docView.props.Document, willZoom);
         } else {
             const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined;
             const contextDoc = contextDocs && contextDocs.find(doc => Doc.AreProtosEqual(doc, targetDoc)) ? docContext : undefined;
@@ -160,38 +161,52 @@ export class DocumentManager {
                             if (closeContextIfNotFound && targetDocContextView.props.removeDocument) targetDocContextView.props.removeDocument(targetDocContextView.props.Document);
                             (dockFunc || CollectionDockingView.AddRightSplit)(Doc.BrushDoc(Doc.MakeAlias(targetDoc)), undefined); // otherwise create a new view of the target
                         }
+                        const finalDocView = DocumentManager.Instance.getFirstDocumentView(targetDoc);
+                        finalDocView && (finalDocView.Document.scrollToLinkID = linkId);
+                        finalDocView && Doc.linkFollowHighlight(finalDocView.props.Document);
                     }, 0);
                 } else {  // there's no context view so we need to create one first and try again
                     targetDocContext.scrollY = 0;
                     (dockFunc || CollectionDockingView.AddRightSplit)(targetDocContext, undefined);
                     setTimeout(() => {
                         const foundTargetDocContextView = DocumentManager.Instance.getDocumentView(targetDocContext);
-                        if (foundTargetDocContextView) { // we should always find a target context here....
-                            this.jumpToDocument(targetDoc, willZoom, dockFunc, undefined, true); // so call jump to doc again and if the doc isn't found, it will be created.
+                        if (foundTargetDocContextView) { // we might be lucky and the context loads right away
+                            this.jumpToDocument(targetDoc, willZoom, dockFunc, undefined, linkId, true); // so call jump to doc again and if the doc isn't found, it will be created.
+                        } else {
+                            setTimeout(() => { // if not, wait a bit to see if the context can be loaded (e.g., a PDF).
+                                const foundTargetDocContextView = DocumentManager.Instance.getDocumentView(targetDocContext);
+                                if (foundTargetDocContextView) { // now we should always find a target context here....
+                                    this.jumpToDocument(targetDoc, willZoom, dockFunc, undefined, linkId, true); // so call jump to doc again and if the doc isn't found, it will be created.
+                                }
+                            }, 2000)
                         }
-                    }, 2000); // the long timeout gives the context view a chance to create its children.  think pdf's which need to be activated to render their annotations.
+                    }, 0);
                 }
             }
         }
+        const finalDocView = DocumentManager.Instance.getFirstDocumentView(targetDoc);
+        finalDocView && (finalDocView.Document.scrollToLinkID = linkId);
+        finalDocView && Doc.linkFollowHighlight(finalDocView.props.Document);
     }
 
-    public async FollowLink(doc: Doc, focus: (doc: Doc, maxLocation: string) => void, zoom: boolean = false, reverse: boolean = false, currentContext?: Doc) {
-        let linkDocs = LinkManager.Instance.getAllRelatedLinks(doc);
+    public async FollowLink(link: Doc | undefined, doc: Doc, focus: (doc: Doc, maxLocation: string) => void, zoom: boolean = false, reverse: boolean = false, currentContext?: Doc) {
+        const linkDocs = link ? [link] : LinkManager.Instance.getAllRelatedLinks(doc);
         SelectionManager.DeselectAll();
-        let firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc) && !linkDoc.anchor1anchored);
-        let secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc) && !linkDoc.anchor2anchored);
+        const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc) && !linkDoc.anchor1anchored);
+        const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc) && !linkDoc.anchor2anchored);
         const firstDocWithoutView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
         const secondDocWithoutView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0);
-        let first = firstDocWithoutView ? [firstDocWithoutView] : firstDocs;
-        let second = secondDocWithoutView ? [secondDocWithoutView] : secondDocs;
-        let linkFollowDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : second.length ? [second[0].anchor1 as Doc, second[0].anchor2 as Doc] : undefined;
-        let linkFollowDocContexts = first.length ? [await (first[0].targetContext) as Doc, await (first[0].sourceContext) as Doc] : second.length ? [await (second[0].sourceContext) as Doc, await (second[0].targetContext) as Doc] : [undefined, undefined];
-        if (linkFollowDocs && !linkFollowDocs.some(l => l instanceof Promise)) {
-            let maxLocation = StrCast(linkFollowDocs[0].maximizeLocation, "inTab");
-            let targetContext = !Doc.AreProtosEqual(linkFollowDocContexts[reverse ? 1 : 0], currentContext) ? linkFollowDocContexts[reverse ? 1 : 0] : undefined;
+        const first = firstDocWithoutView ? [firstDocWithoutView] : firstDocs;
+        const second = secondDocWithoutView ? [secondDocWithoutView] : secondDocs;
+        const linkDoc = first.length ? first[0] : second.length ? second[0] : undefined;
+        const linkFollowDocs = first.length ? [await first[0].anchor2 as Doc, await first[0].anchor1 as Doc] : second.length ? [await second[0].anchor1 as Doc, await second[0].anchor2 as Doc] : undefined;
+        const linkFollowDocContexts = first.length ? [await first[0].targetContext as Doc, await first[0].sourceContext as Doc] : second.length ? [await second[0].sourceContext as Doc, await second[0].targetContext as Doc] : [undefined, undefined];
+        if (linkFollowDocs && linkDoc) {
+            const maxLocation = StrCast(linkFollowDocs[0].maximizeLocation, "inTab");
+            const targetContext = !Doc.AreProtosEqual(linkFollowDocContexts[reverse ? 1 : 0], currentContext) ? linkFollowDocContexts[reverse ? 1 : 0] : undefined;
             DocumentManager.Instance.jumpToDocument(linkFollowDocs[reverse ? 1 : 0], zoom,
                 // open up target if it's not already in view ... by zooming into the button document first and setting flag to reset zoom afterwards
-                (doc: Doc) => focus(doc, maxLocation), targetContext);
+                (doc: Doc) => focus(doc, maxLocation), targetContext, linkDoc[Id]);
         }
     }
 
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 49bd93942..066266873 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -13,6 +13,7 @@ import { Cast, NumCast } from "../../new_fields/Types";
 import { DocumentManager } from "./DocumentManager";
 import ParagraphNodeSpec from "./ParagraphNodeSpec";
 import { times } from "async";
+import { LinkManager } from "./LinkManager";
 
 const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
     preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
@@ -619,27 +620,10 @@ export class ImageResizeView {
             if (!view.isOverlay || e.ctrlKey) {
                 e.preventDefault();
                 e.stopPropagation();
-                DocServer.GetRefField(node.attrs.docid).then(async linkDoc => {
-                    const location = node.attrs.location;
-                    if (linkDoc instanceof Doc) {
-                        let proto = Doc.GetProto(linkDoc);
-                        let targetContext = await Cast(proto.targetContext, Doc);
-                        let jumpToDoc = await Cast(linkDoc.anchor2, Doc);
-                        if (jumpToDoc) {
-                            if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
-                                DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey);
-                                return;
-                            }
-                        }
-                        if (targetContext) {
-                            DocumentManager.Instance.jumpToDocument(targetContext, e.ctrlKey, document => addDocTab(document, undefined, location ? location : "inTab"));
-                        } else if (jumpToDoc) {
-                            DocumentManager.Instance.jumpToDocument(jumpToDoc, e.ctrlKey, document => addDocTab(document, undefined, location ? location : "inTab"));
-                        } else {
-                            DocumentManager.Instance.jumpToDocument(linkDoc, e.ctrlKey, document => addDocTab(document, undefined, location ? location : "inTab"));
-                        }
-                    }
-                });
+                DocServer.GetRefField(node.attrs.docid).then(async linkDoc =>
+                    (linkDoc instanceof Doc) &&
+                    DocumentManager.Instance.FollowLink(linkDoc, (view.state.schema as any).Document,
+                        document => addDocTab(document, undefined, node.attrs.location ? node.attrs.location : "inTab"), false));
             }
         };
         this._handle.onpointerdown = function (e: any) {
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index d8b9dbec6..e37f1f90d 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -3,7 +3,6 @@ import { DocServer } from '../DocServer';
 import { Doc } from '../../new_fields/Doc';
 import { Id } from '../../new_fields/FieldSymbols';
 import { Utils } from '../../Utils';
-import { ResultParameters } from '../northstar/model/idea/idea';
 import { DocumentType } from '../documents/DocumentTypes';
 
 export namespace SearchUtil {
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx
index f1b101b8e..41453f8b2 100644
--- a/src/client/views/MetadataEntryMenu.tsx
+++ b/src/client/views/MetadataEntryMenu.tsx
@@ -3,7 +3,7 @@ import "./MetadataEntryMenu.scss";
 import { observer } from 'mobx-react';
 import { observable, action, runInAction, trace, computed, IReactionDisposer, reaction } from 'mobx';
 import { KeyValueBox } from './nodes/KeyValueBox';
-import { Doc, Field, DocListCast, DocListCastAsync } from '../../new_fields/Doc';
+import { Doc, Field, DocListCastAsync } from '../../new_fields/Doc';
 import * as Autosuggest from 'react-autosuggest';
 import { undoBatch } from '../util/UndoManager';
 import { emptyFunction } from '../../Utils';
diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx
index 53b720a9e..2bff3ded4 100644
--- a/src/client/views/linking/LinkFollowBox.tsx
+++ b/src/client/views/linking/LinkFollowBox.tsx
@@ -152,21 +152,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
         this.resetPan();
     }
 
-    unhighlight = () => {
-        Doc.UnhighlightAll();
-        document.removeEventListener("pointerdown", this.unhighlight);
-    }
-
-    @action
-    highlightDoc = () => {
-        if (LinkFollowBox.destinationDoc) {
-            document.removeEventListener("pointerdown", this.unhighlight);
-            Doc.HighlightDoc(LinkFollowBox.destinationDoc);
-            window.setTimeout(() => {
-                document.addEventListener("pointerdown", this.unhighlight);
-            }, 10000);
-        }
-    }
+    highlightDoc = () => LinkFollowBox.destinationDoc && Doc.linkFollowHighlight(LinkFollowBox.destinationDoc);
 
     @undoBatch
     openFullScreen = () => {
@@ -235,44 +221,11 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
 
     @undoBatch
     jumpToLink = async (options: { shouldZoom: boolean }) => {
-        if (LinkFollowBox.destinationDoc && LinkFollowBox.linkDoc) {
-            let jumpToDoc: Doc = LinkFollowBox.destinationDoc;
-            let pdfDoc = FieldValue(Cast(LinkFollowBox.destinationDoc, Doc));
-            if (pdfDoc) {
-                jumpToDoc = pdfDoc;
-            }
-            let proto = Doc.GetProto(LinkFollowBox.linkDoc);
-            let targetContext = await Cast(proto.targetContext, Doc);
-            let sourceContext = await Cast(proto.sourceContext, Doc);
-            let guid = StrCast(LinkFollowBox.linkDoc[Id]);
-            const shouldZoom = options ? options.shouldZoom : false;
-
-            let dockingFunc = (document: Doc) => { (LinkFollowBox._addDocTab || this.props.addDocTab)(document, undefined, "inTab"); SelectionManager.DeselectAll(); };
-
-            if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor2 && targetContext) {
-                DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, async document => dockingFunc(document), targetContext);
-            }
-            else if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor1 && sourceContext) {
-                DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, document => dockingFunc(sourceContext!));
-                if (LinkFollowBox.sourceDoc && LinkFollowBox.destinationDoc) {
-                    if (guid) {
-                        let views = DocumentManager.Instance.getDocumentViews(jumpToDoc);
-                        views.length && (views[0].props.Document.scrollToLinkID = guid);
-                    } else {
-                        jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.linkDoc[Id]));
-                    }
-                }
-            }
-            else if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
-                DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom);
-
-            }
-            else {
-                DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, dockingFunc);
-            }
+        if (LinkFollowBox.sourceDoc && LinkFollowBox.linkDoc) {
+            let focus = (document: Doc) => { (LinkFollowBox._addDocTab || this.props.addDocTab)(document, undefined, "inTab"); SelectionManager.DeselectAll(); };
+            //let focus = (doc: Doc, maxLocation: string) => this.props.focus(docthis.props.focus(LinkFollowBox.destinationDoc, true, 1, () => this.props.addDocTab(doc, undefined, maxLocation));
 
-            this.highlightDoc();
-            SelectionManager.DeselectAll();
+            DocumentManager.Instance.FollowLink(LinkFollowBox.linkDoc, LinkFollowBox.sourceDoc, focus, options && options.shouldZoom, false, undefined);
         }
     }
 
@@ -310,20 +263,23 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
     openLinkInPlace = (options: { shouldZoom: boolean }) => {
 
         if (LinkFollowBox.destinationDoc && LinkFollowBox.sourceDoc) {
-            let alias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
-            let y = NumCast(LinkFollowBox.sourceDoc.y);
-            let x = NumCast(LinkFollowBox.sourceDoc.x);
+            if (this.sourceView && this.sourceView.props.addDocument) {
+                let destViews = DocumentManager.Instance.getDocumentViews(LinkFollowBox.destinationDoc);
+                if (!destViews.find(dv => dv.props.ContainingCollectionView === this.sourceView!.props.ContainingCollectionView)) {
+                    let alias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
+                    let y = NumCast(LinkFollowBox.sourceDoc.y);
+                    let x = NumCast(LinkFollowBox.sourceDoc.x);
 
-            let width = NumCast(LinkFollowBox.sourceDoc.width);
-            let height = NumCast(LinkFollowBox.sourceDoc.height);
+                    let width = NumCast(LinkFollowBox.sourceDoc.width);
+                    let height = NumCast(LinkFollowBox.sourceDoc.height);
 
-            alias.x = x + width + 30;
-            alias.y = y;
-            alias.width = width;
-            alias.height = height;
+                    alias.x = x + width + 30;
+                    alias.y = y;
+                    alias.width = width;
+                    alias.height = height;
 
-            if (this.sourceView && this.sourceView.props.addDocument) {
-                this.sourceView.props.addDocument(alias, false);
+                    this.sourceView.props.addDocument(alias, false);
+                }
             }
 
             this.jumpToLink({ shouldZoom: options.shouldZoom });
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 4ea200e6d..b3e7898c1 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -5,6 +5,7 @@
   top: 0;
   left:0;
   border-radius: inherit;
+  transition : outline .3s linear;
                     
  // background: $light-color; //overflow: hidden;
   transform-origin: left top;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3273abc1d..67c3fe6e7 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -224,7 +224,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             }
         }
         else if (linkDocs.length) {
-            DocumentManager.Instance.FollowLink(this.props.Document,
+            DocumentManager.Instance.FollowLink(undefined, this.props.Document,
                 (doc: Doc, maxLocation: string) => this.props.focus(this.props.Document, true, 1, () => this.props.addDocTab(doc, undefined, maxLocation)),
                 ctrlKey, altKey, this.props.ContainingCollectionDoc);
         }
@@ -658,6 +658,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let animheight = animDims ? animDims[1] : nativeHeight;
         let animwidth = animDims ? animDims[0] : nativeWidth;
 
+        const highlightColors = ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"];
+        const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid", "solid"];
         return (
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
@@ -665,10 +667,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     transition: this.props.Document.isAnimating !== undefined ? ".5s linear" : StrCast(this.Document.transition),
                     pointerEvents: this.Document.isBackground && !this.isSelected() ? "none" : "all",
                     color: StrCast(this.Document.color),
-                    outlineColor: ["transparent", "maroon", "maroon", "yellow"][fullDegree],
-                    outlineStyle: ["none", "dashed", "solid", "solid"][fullDegree],
-                    outlineWidth: fullDegree && !borderRounding ? `${localScale}px` : "0px",
-                    border: fullDegree && borderRounding ? `${["none", "dashed", "solid", "solid"][fullDegree]} ${["transparent", "maroon", "maroon", "yellow"][fullDegree]} ${localScale}px` : undefined,
+                    outline: fullDegree && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px",
+                    border: fullDegree && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined,
                     background: backgroundColor,
                     width: animwidth,
                     height: animheight,
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 749886d9a..9347868b3 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1,47 +1,46 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faEdit, faSmile, faTextHeight, faUpload } from '@fortawesome/free-solid-svg-icons';
+import _ from "lodash";
 import { action, computed, IReactionDisposer, Lambda, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { baseKeymap } from "prosemirror-commands";
 import { history } from "prosemirror-history";
+import { inputRules } from 'prosemirror-inputrules';
 import { keymap } from "prosemirror-keymap";
-import { Fragment, Node, Node as ProsNode, NodeType, Slice, Mark, ResolvedPos } from "prosemirror-model";
-import { EditorState, Plugin, Transaction, TextSelection, NodeSelection } from "prosemirror-state";
+import { Fragment, Mark, Node, Node as ProsNode, NodeType, Slice } from "prosemirror-model";
+import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from "prosemirror-state";
+import { ReplaceStep } from 'prosemirror-transform';
 import { EditorView } from "prosemirror-view";
 import { DateField } from '../../../new_fields/DateField';
-import { Doc, DocListCast, Opt, WidthSym, DocListCastAsync } from "../../../new_fields/Doc";
+import { Doc, DocListCastAsync, Opt, WidthSym } from "../../../new_fields/Doc";
 import { Copy, Id } from '../../../new_fields/FieldSymbols';
-import { List } from '../../../new_fields/List';
 import { RichTextField } from "../../../new_fields/RichTextField";
-import { BoolCast, Cast, NumCast, StrCast, DateCast, PromiseValue } from "../../../new_fields/Types";
+import { RichTextUtils } from '../../../new_fields/RichTextUtils';
 import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { Utils, numberRange, timenow } from '../../../Utils';
+import { Cast, DateCast, NumCast, StrCast } from "../../../new_fields/Types";
+import { numberRange, timenow, Utils } from '../../../Utils';
+import { GoogleApiClientUtils, Pulls, Pushes } from '../../apis/google_docs/GoogleApiClientUtils';
 import { DocServer } from "../../DocServer";
 import { Docs, DocUtils } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { DictationManager } from '../../util/DictationManager';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager } from "../../util/DragManager";
 import buildKeymap from "../../util/ProsemirrorExampleTransfer";
 import { inpRules } from "../../util/RichTextRules";
-import { ImageResizeView, schema, SummarizedView, OrderedListView, FootnoteView } from "../../util/RichTextSchema";
+import { FootnoteView, ImageResizeView, OrderedListView, schema, SummarizedView } from "../../util/RichTextSchema";
 import { SelectionManager } from "../../util/SelectionManager";
 import { TooltipLinkingMenu } from "../../util/TooltipLinkingMenu";
 import { TooltipTextMenu } from "../../util/TooltipTextMenu";
 import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { DocComponent } from "../DocComponent";
+import { DocumentButtonBar } from '../DocumentButtonBar';
+import { DocumentDecorations } from '../DocumentDecorations';
 import { InkingControl } from "../InkingControl";
 import { FieldView, FieldViewProps } from "./FieldView";
 import "./FormattedTextBox.scss";
+import { FormattedTextBoxComment, formattedTextBoxCommentPlugin } from './FormattedTextBoxComment';
 import React = require("react");
-import { GoogleApiClientUtils, Pulls, Pushes } from '../../apis/google_docs/GoogleApiClientUtils';
-import { DocumentDecorations } from '../DocumentDecorations';
-import { DictationManager } from '../../util/DictationManager';
-import { ReplaceStep } from 'prosemirror-transform';
-import { DocumentType } from '../../documents/DocumentTypes';
-import { RichTextUtils } from '../../../new_fields/RichTextUtils';
-import _ from "lodash";
-import { formattedTextBoxCommentPlugin, FormattedTextBoxComment } from './FormattedTextBoxComment';
-import { inputRules } from 'prosemirror-inputrules';
-import { DocumentButtonBar } from '../DocumentButtonBar';
 
 library.add(faEdit);
 library.add(faSmile, faTextHeight, faUpload);
@@ -142,51 +141,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             DragManager.StartDragFunctions.push(() => FormattedTextBox.InputBoxOverlay = undefined);
         }
         FormattedTextBox.Instance = this;
-        this._scrollToRegionReactionDisposer = reaction(
-            () => StrCast(this.props.Document.scrollToLinkID),
-            async (scrollToLinkID) => {
-                let findLinkFrag = (frag: Fragment, editor: EditorView) => {
-                    const nodes: Node[] = [];
-                    frag.forEach((node, index) => {
-                        let examinedNode = findLinkNode(node, editor);
-                        if (examinedNode && examinedNode.textContent) {
-                            nodes.push(examinedNode);
-                            start += index;
-                        }
-                    });
-                    return { frag: Fragment.fromArray(nodes), start: start };
-                };
-                let findLinkNode = (node: Node, editor: EditorView) => {
-                    if (!node.isText) {
-                        const content = findLinkFrag(node.content, editor);
-                        return node.copy(content.frag);
-                    }
-                    const marks = [...node.marks];
-                    const linkIndex = marks.findIndex(mark => mark.type === editor.state.schema.marks.link);
-                    return linkIndex !== -1 && scrollToLinkID === marks[linkIndex].attrs.href.replace(/.*\/doc\//, "") ? node : undefined;
-                };
-
-                let start = -1;
-                if (this._editorView && scrollToLinkID) {
-                    let editor = this._editorView;
-                    let ret = findLinkFrag(editor.state.doc.content, editor);
-
-                    if (ret.frag.size > 2 && ((!this.props.isOverlay && !this.props.isSelected()) || (this.props.isSelected() && this.props.isOverlay))) {
-                        let selection = TextSelection.near(editor.state.doc.resolve(ret.start)); // default to near the start
-                        if (ret.frag.firstChild) {
-                            selection = TextSelection.between(editor.state.doc.resolve(ret.start + 2), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected
-                        }
-                        editor.dispatch(editor.state.tr.setSelection(new TextSelection(selection.$from, selection.$from)).scrollIntoView());
-                        const mark = editor.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
-                        setTimeout(() => editor.dispatch(editor.state.tr.addMark(selection.from, selection.to, mark)), 0);
-                        setTimeout(() => this.unhighlightSearchTerms(), 2000);
-                    }
-                    this.props.Document.scrollToLinkID = undefined;
-                }
-
-            },
-            { fireImmediately: true }
-        );
     }
 
     public get CurrentDiv(): HTMLDivElement { return this._ref.current!; }
@@ -341,8 +295,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             let url = de.data.urlField.url.href;
             let model: NodeType = [".mov", ".mp4"].includes(url) ? schema.nodes.video : schema.nodes.image;
             let pos = this._editorView!.posAtCoords({ left: de.x, top: de.y });
-            this._editorView!.dispatch(this._editorView!.state.tr.insert(pos!.pos, model.create({ src: url, docid: target[Id] })));
-            DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: target }, "ImgRef:" + target.title);
+            let link = DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: target }, "ImgRef:" + target.title);
+            link && this._editorView!.dispatch(this._editorView!.state.tr.insert(pos!.pos, model.create({ src: url, docid: link[Id] })));
             this.tryUpdateHeight();
             e.stopPropagation();
         } else if (de.data instanceof DragManager.DocumentDragData) {
@@ -424,6 +378,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
     _keymap: any = undefined;
     @computed get config() {
         this._keymap = buildKeymap(schema);
+        (schema as any).Document = this.props.Document;
         return {
             schema,
             plugins: this.props.isOverlay ? [
@@ -563,6 +518,51 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 }, 0);
             }), { fireImmediately: true }
         );
+        this._scrollToRegionReactionDisposer = reaction(
+            () => StrCast(this.props.Document.scrollToLinkID),
+            async (scrollToLinkID) => {
+                let findLinkFrag = (frag: Fragment, editor: EditorView) => {
+                    const nodes: Node[] = [];
+                    frag.forEach((node, index) => {
+                        let examinedNode = findLinkNode(node, editor);
+                        if (examinedNode && examinedNode.textContent) {
+                            nodes.push(examinedNode);
+                            start += index;
+                        }
+                    });
+                    return { frag: Fragment.fromArray(nodes), start: start };
+                };
+                let findLinkNode = (node: Node, editor: EditorView) => {
+                    if (!node.isText) {
+                        const content = findLinkFrag(node.content, editor);
+                        return node.copy(content.frag);
+                    }
+                    const marks = [...node.marks];
+                    const linkIndex = marks.findIndex(mark => mark.type === editor.state.schema.marks.link);
+                    return linkIndex !== -1 && scrollToLinkID === marks[linkIndex].attrs.href.replace(/.*\/doc\//, "") ? node : undefined;
+                };
+
+                let start = -1;
+                if (this._editorView && scrollToLinkID) {
+                    let editor = this._editorView;
+                    let ret = findLinkFrag(editor.state.doc.content, editor);
+
+                    if (ret.frag.size > 2 && ((!this.props.isOverlay && !this.props.isSelected()) || (this.props.isSelected() && this.props.isOverlay))) {
+                        let selection = TextSelection.near(editor.state.doc.resolve(ret.start)); // default to near the start
+                        if (ret.frag.firstChild) {
+                            selection = TextSelection.between(editor.state.doc.resolve(ret.start + 2), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected
+                        }
+                        editor.dispatch(editor.state.tr.setSelection(new TextSelection(selection.$from, selection.$from)).scrollIntoView());
+                        const mark = editor.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
+                        setTimeout(() => editor.dispatch(editor.state.tr.addMark(selection.from, selection.to, mark)), 0);
+                        setTimeout(() => this.unhighlightSearchTerms(), 2000);
+                    }
+                    this.props.Document.scrollToLinkID = undefined;
+                }
+
+            },
+            { fireImmediately: true }
+        );
 
         setTimeout(() => this.tryUpdateHeight(), 0);
     }
@@ -858,27 +858,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 if (href.indexOf(Utils.prepend("/doc/")) === 0) {
                     this._linkClicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
                     if (this._linkClicked) {
-                        DocServer.GetRefField(this._linkClicked).then(async linkDoc => {
-                            if (linkDoc instanceof Doc) {
-                                let proto = Doc.GetProto(linkDoc);
-                                let targetContext = await Cast(proto.targetContext, Doc);
-                                let jumpToDoc = await Cast(linkDoc.anchor2, Doc);
-
-                                if (jumpToDoc) {
-                                    if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
-                                        DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey);
-                                        return;
-                                    }
-                                }
-                                if (targetContext && (!jumpToDoc || targetContext !== await jumpToDoc.annotationOn)) {
-                                    DocumentManager.Instance.jumpToDocument(jumpToDoc || targetContext, ctrlKey, document => this.props.addDocTab(document, undefined, location ? location : "inTab"), targetContext);
-                                } else if (jumpToDoc) {
-                                    DocumentManager.Instance.jumpToDocument(jumpToDoc, ctrlKey, document => this.props.addDocTab(document, undefined, location ? location : "inTab"));
-                                } else {
-                                    DocumentManager.Instance.jumpToDocument(linkDoc, ctrlKey, document => this.props.addDocTab(document, undefined, location ? location : "inTab"));
-                                }
-                            }
-                        });
+                        DocServer.GetRefField(this._linkClicked).then(async linkDoc =>
+                            (linkDoc instanceof Doc) &&
+                            DocumentManager.Instance.FollowLink(linkDoc, this.props.Document, document => this.props.addDocTab(document, undefined, location ? location : "inTab"), false));
                         e.stopPropagation();
                         e.preventDefault();
                     }
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index fe4f75cad..a198a0764 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -299,7 +299,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
                 let rotation = NumCast(this.dataDoc.rotation) % 180;
                 let realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size;
                 let aspect = realsize.height / realsize.width;
-                if (layoutdoc.nativeHeight !== 0 && layoutdoc.nativeWidth !== 0 && (Math.abs(1 - NumCast(layoutdoc.nativeHeight) / NumCast(layoutdoc.nativeWidth) / (realsize.height / realsize.width)) > 0.1)) {
+                if (layoutdoc.width && (Math.abs(1 - NumCast(layoutdoc.height) / NumCast(layoutdoc.width) / (realsize.height / realsize.width)) > 0.1)) {
                     setTimeout(action(() => {
                         layoutdoc.height = layoutdoc[WidthSym]() * aspect;
                         layoutdoc.nativeHeight = realsize.height;
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index f7f52b3ef..98e04d93e 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -98,7 +98,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
         else if (e.button === 0) {
             let annoGroup = await Cast(this.props.document.group, Doc);
             if (annoGroup) {
-                DocumentManager.Instance.FollowLink(annoGroup,
+                DocumentManager.Instance.FollowLink(undefined, annoGroup,
                     (doc: Doc, maxLocation: string) => this.props.addDocTab(doc, undefined, e.ctrlKey ? "onRight" : "inTab"),
                     false, false, undefined);
             }
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 58304cebb..6acc6e1ca 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -637,7 +637,7 @@ export namespace Doc {
 
     export function isBrushedHighlightedDegree(doc: Doc) {
         if (Doc.IsHighlighted(doc)) {
-            return 3;
+            return 6;
         }
         else {
             return Doc.IsBrushedDegree(doc);
@@ -673,6 +673,21 @@ export namespace Doc {
         return doc;
     }
 
+    export function linkFollowUnhighlight() {
+        Doc.UnhighlightAll();
+        document.removeEventListener("pointerdown", linkFollowUnhighlight);
+    }
+
+    let dt = 0;
+    export function linkFollowHighlight(destDoc: Doc) {
+        linkFollowUnhighlight();
+        Doc.HighlightDoc(destDoc);
+        document.removeEventListener("pointerdown", linkFollowUnhighlight);
+        document.addEventListener("pointerdown", linkFollowUnhighlight);
+        let x = dt = Date.now();
+        window.setTimeout(() => dt == x && linkFollowUnhighlight(), 5000);
+    }
+
     export class HighlightBrush {
         @observable HighlightedDoc: Map<Doc, boolean> = new Map();
     }
-- 
cgit v1.2.3-70-g09d2


From 9085a369738fbe9ee1a9f74c9aba9a9ca1e5c962 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 3 Oct 2019 11:55:47 -0400
Subject: changed brushing of annotations.

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

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

diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 98e04d93e..26de12a0d 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -51,10 +51,10 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
         );
 
         this._brushDisposer = reaction(
-            () => FieldValue(Cast(this.props.document.group, Doc)) && Doc.IsBrushed(FieldValue(Cast(this.props.document.group, Doc))!),
+            () => FieldValue(Cast(this.props.document.group, Doc)) && Doc.isBrushedHighlightedDegree(FieldValue(Cast(this.props.document.group, Doc))!),
             (brushed) => {
                 if (brushed !== undefined) {
-                    runInAction(() => this._brushed = brushed);
+                    runInAction(() => this._brushed = brushed !== 0);
                 }
             }
         );
@@ -122,7 +122,9 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
                 left: this.props.x,
                 width: this.props.width,
                 height: this.props.height,
-                backgroundColor: this._brushed ? "green" : StrCast(this.props.document.color)
+                transition: "background-color 0.5s, opacity 0.5s",
+                opacity: this._brushed ? 0.5 : undefined,
+                backgroundColor: this._brushed ? "orange" : StrCast(this.props.document.color)
             }} />);
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 2413d93a31ad4c97e09f79b97bc19346e72a1537 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 3 Oct 2019 16:31:31 -0400
Subject: improved search results to avoid showing aliases.  improved Pdf
 results display.

---
 src/client/util/SearchUtil.ts               | 34 ++++++++++++++++++++++++-----
 src/client/views/nodes/FormattedTextBox.tsx |  5 -----
 src/client/views/pdf/Annotation.tsx         |  2 +-
 src/client/views/pdf/PDFViewer.scss         | 17 ++++++++++-----
 src/client/views/pdf/PDFViewer.tsx          | 33 +++++++++++++++++++---------
 src/client/views/search/SearchItem.scss     | 13 +++++++++--
 src/client/views/search/SearchItem.tsx      | 19 ++++++++++------
 7 files changed, 87 insertions(+), 36 deletions(-)

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

diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index e37f1f90d..6706dcb89 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -41,23 +41,45 @@ export namespace SearchUtil {
         }
 
         let { ids, numFound, highlighting } = result;
-        let lines: string[][] = ids.map(i => []);
 
         let txtresult = query !== "*" && JSON.parse(await rp.get(Utils.prepend("/textsearch"), {
             qs: { ...options, q: query },
         }));
+
         let fileids = txtresult ? txtresult.ids : [];
+        let newIds: string[] = [];
+        let newLines: string[][] = [];
         await Promise.all(fileids.map(async (tr: string, i: number) => {
             let docQuery = "fileUpload_t:" + tr.substr(0, 7); //If we just have a filter query, search for * as the query
             let docResult = JSON.parse(await rp.get(Utils.prepend("/search"), { qs: { ...options, q: docQuery } }));
-            ids.push(...docResult.ids);
-            lines.push(...docResult.ids.map((dr: any) => txtresult.lines[i]));
-            numFound += docResult.numFound;
+            newIds.push(...docResult.ids);
+            newLines.push(...docResult.ids.map((dr: any) => txtresult.lines[i]));
         }));
 
+
+        let theDocs: Doc[] = [];
+        let theLines: string[][] = [];
+        const textDocMap = await DocServer.GetRefFields(newIds);
+        const textDocs = newIds.map((id: string) => textDocMap[id]).map(doc => doc as Doc);
+        for (let i = 0; i < textDocs.length; i++) {
+            let testDoc = textDocs[i];
+            if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1) {
+                theDocs.push(Doc.GetProto(testDoc));
+                theLines.push(newLines[i].map(line => line.replace(query, query.toUpperCase())));
+            }
+        }
+
         const docMap = await DocServer.GetRefFields(ids);
-        const docs = ids.map((id: string) => docMap[id]).filter((doc: any) => doc instanceof Doc && doc.type !== DocumentType.KVP);
-        return { docs, numFound, highlighting, lines };
+        const docs = ids.map((id: string) => docMap[id]).map(doc => doc as Doc);
+        for (let i = 0; i < ids.length; i++) {
+            let testDoc = docs[i];
+            if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1) {
+                theDocs.push(testDoc);
+                theLines.push([]);
+            }
+        }
+
+        return { docs: theDocs, numFound: theDocs.length, highlighting, lines: theLines };
     }
 
     export async function GetAliasesOfDocument(doc: Doc): Promise<Doc[]>;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 9347868b3..c37258f50 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -474,11 +474,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         this._searchReactionDisposer = reaction(() => {
             return StrCast(this.props.Document.search_string);
         }, searchString => {
-            const fieldkey = 'preview';
-            let preview = false;
-            // if (!this._editorView && Object.keys(this.props.Document).indexOf(fieldkey) !== -1) {
-            //     preview = true;
-            // }
             if (searchString) {
                 this.highlightSearchTerms([searchString]);
             }
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 26de12a0d..134e757d1 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -122,7 +122,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
                 left: this.props.x,
                 width: this.props.width,
                 height: this.props.height,
-                transition: "background-color 0.5s, opacity 0.5s",
+                transition: "opacity 0.5s",
                 opacity: this._brushed ? 0.5 : undefined,
                 backgroundColor: this._brushed ? "orange" : StrCast(this.props.document.color)
             }} />);
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 8027e93a3..a71e4f81e 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -11,10 +11,17 @@
     //     transform: scale(0.75);
     //     transform-origin: top left;
     // }
-    // .textLayer {
-    //     transform: scale(0.75);
-    //     transform-origin: top left;
-    // }
+    .textLayer {
+        
+        mix-blend-mode: multiply;
+        opacity: 0.9;
+    }
+    .textLayer .highlight {
+        background-color: yellow;
+    }
+    .textLayer .highlight.selected {
+        background-color: orange;
+    }
 
     .page {
         position: relative;
@@ -30,7 +37,6 @@
     }
 
     .pdfViewer-overlay {
-        transform: scale(2.14359);
         transform-origin: left top;
         position: absolute;
         top: 0px;
@@ -43,6 +49,7 @@
         top: 0;
         width: 100%;
         pointer-events: none;
+        mix-blend-mode: multiply;
 
         .pdfPage-annotationBox {
             position: absolute;
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 20dfc4d8c..9ff3e1bd1 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -9,7 +9,7 @@ import { List } from "../../../new_fields/List";
 import { listSpec } from "../../../new_fields/Schema";
 import { ScriptField } from "../../../new_fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import smoothScroll, { Utils, emptyFunction, returnOne } from "../../../Utils";
+import smoothScroll, { Utils, emptyFunction, returnOne, intersectRect } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
 import { CompiledScript, CompileScript } from "../../util/Scripting";
@@ -85,6 +85,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     private _selectionReactionDisposer?: IReactionDisposer;
     private _annotationReactionDisposer?: IReactionDisposer;
     private _filterReactionDisposer?: IReactionDisposer;
+    private _searchReactionDisposer?: IReactionDisposer;
     private _viewer: React.RefObject<HTMLDivElement> = React.createRef();
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
     private _selectionText: string = "";
@@ -103,12 +104,24 @@ export class PDFViewer extends React.Component<IViewerProps> {
         return this._annotations.filter(anno => this._script.run({ this: anno }, console.log, true).result);
     }
 
+    _lastSearch: string = "";
     componentDidMount = async () => {
         // change the address to be the file address of the PNG version of each page
         // file address of the pdf
         this._coverPath = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${this.props.url.substring("files/".length, this.props.url.length - ".pdf".length)}-${NumCast(this.props.Document.curPage, 1)}.PNG`)));
         runInAction(() => this._showWaiting = this._showCover = true);
         this.props.startupLive && this.setupPdfJsViewer();
+        this._searchReactionDisposer = reaction(() => StrCast(this.props.Document.search_string), searchString => {
+            if (searchString) {
+                this.search(searchString, true);
+                this._lastSearch = searchString;
+            }
+            else {
+                setTimeout(() => this._lastSearch === "mxytzlaf" && this.search("mxytzlaf", true), 200); // bcz: how do we clear search highlights?
+                this._lastSearch && (this._lastSearch = "mxytzlaf");
+            }
+        }, { fireImmediately: true });
+
         this._selectionReactionDisposer = reaction(() => this.props.isSelected(), () => (this.props.isSelected() && SelectionManager.SelectedDocuments().length === 1) && this.setupPdfJsViewer(), { fireImmediately: true });
         this._reactionDisposer = reaction(
             () => this.props.Document.scrollY,
@@ -130,6 +143,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         this._annotationReactionDisposer && this._annotationReactionDisposer();
         this._filterReactionDisposer && this._filterReactionDisposer();
         this._selectionReactionDisposer && this._selectionReactionDisposer();
+        this._searchReactionDisposer && this._searchReactionDisposer();
         document.removeEventListener("copy", this.copy);
     }
 
@@ -298,12 +312,9 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
     @action
     scrollToAnnotation = (scrollToAnnotation: Doc) => {
-        this.allAnnotations.forEach(d => Doc.UnBrushDoc(d));
-        let windowHgt = this.props.PanelHeight() / this.props.ContentScaling();
-        let scrollRange = this._mainCont.current!.scrollHeight - windowHgt;
-        let pgScroll = scrollRange / this._pageSizes.length;
-        this._mainCont.current!.scrollTo(0, NumCast(scrollToAnnotation.y) - pgScroll / 2);
-        Doc.BrushDoc(scrollToAnnotation);
+        let offset = this.visibleHeight() / 2 * 96 / 72;
+        this._mainCont.current && smoothScroll(500, this._mainCont.current, NumCast(scrollToAnnotation.y) - offset);
+        Doc.linkFollowHighlight(scrollToAnnotation);
     }
 
     sendAnnotations = (page: number) => {
@@ -454,7 +465,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 if (rect/* && rect.width !== this._mainCont.current.getBoundingClientRect().width && rect.height !== this._mainCont.current.getBoundingClientRect().height / this.props.pdf.numPages*/) {
                     let scaleY = this._mainCont.current.offsetHeight / boundingRect.height;
                     let scaleX = this._mainCont.current.offsetWidth / boundingRect.width;
-                    if (rect.width !== this._mainCont.current.clientWidth) {
+                    if (rect.width !== this._mainCont.current.clientWidth &&
+                        (i == 0 || !intersectRect(clientRects[i], clientRects[i - 1]))) {
                         let annoBox = document.createElement("div");
                         annoBox.className = "pdfPage-annotationBox";
                         // transforms the positions from screen onto the pdf div
@@ -659,17 +671,18 @@ export class PDFViewer extends React.Component<IViewerProps> {
     marqueeX = () => this._marqueeX;
     marqueeY = () => this._marqueeY;
     marqueeing = () => this._marqueeing;
+    visibleHeight = () => this.props.PanelHeight() / this.props.ContentScaling() * 72 / 96;
     render() {
         return (<div className={"pdfViewer-viewer" + (this._zoomed !== 1 ? "-zoomed" : "")} onScroll={this.onScroll} onWheel={this.onZoomWheel} onPointerDown={this.onPointerDown} onClick={this.onClick} ref={this._mainCont}>
             {this.pdfViewerDiv}
             <PdfViewerMarquee isMarqueeing={this.marqueeing} width={this.marqueeWidth} height={this.marqueeHeight} x={this.marqueeX} y={this.marqueeY} />
+            {this.annotationLayer}
             <div className="pdfViewer-overlay" style={{ transform: `scale(${this._zoomed})` }}>
-                {this.annotationLayer}
                 <CollectionFreeFormView {...this.props}
                     setPreviewCursor={this.setPreviewCursor}
                     PanelHeight={() => NumCast(this.props.Document.scrollHeight, NumCast(this.props.Document.nativeHeight))}
                     PanelWidth={() => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : NumCast(this.props.Document.nativeWidth)}
-                    VisibleHeight={() => this.props.PanelHeight() / this.props.ContentScaling() * 72 / 96}
+                    VisibleHeight={this.visibleHeight}
                     focus={this.props.focus}
                     isSelected={this.props.isSelected}
                     select={emptyFunction}
diff --git a/src/client/views/search/SearchItem.scss b/src/client/views/search/SearchItem.scss
index 273d49349..62715c5eb 100644
--- a/src/client/views/search/SearchItem.scss
+++ b/src/client/views/search/SearchItem.scss
@@ -4,7 +4,6 @@
     display: flex;
     flex-direction: row-reverse;
     justify-content: flex-end;
-    height: 70px;
     z-index: 0;
 }
 
@@ -15,9 +14,12 @@
     border-color: $intermediate-color;
     border-bottom-style: solid;
     padding: 10px;
-    height: 70px;
+    min-height: 70px;
+    max-height: 150px;
+    height: auto;
     z-index: 0;
     display: inline-block;
+    overflow: auto;
 
     .main-search-info {
         display: flex;
@@ -26,6 +28,7 @@
 
         .search-title-container {
             width: 100%;
+            overflow: hidden;
 
             .search-title {
                 text-transform: uppercase;
@@ -181,6 +184,12 @@
     background: $lighter-alt-accent;
 }
 
+.search-highlighting {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: pre;
+}
+
 .searchBox-instances {
     float: left;
     opacity: 1;
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 4d021216d..a7822ed46 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -4,13 +4,10 @@ import { faCaretUp, faChartBar, faFile, faFilePdf, faFilm, faFingerprint, faGlob
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, WidthSym } from "../../../new_fields/Doc";
+import { Doc } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
-import { ObjectField } from "../../../new_fields/ObjectField";
-import { RichTextField } from "../../../new_fields/RichTextField";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils } from "../../../Utils";
-import { DocServer } from "../../DocServer";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager, SetupDrag } from "../../util/DragManager";
@@ -228,6 +225,12 @@ export class SearchItem extends React.Component<SearchItemProps> {
     @action
     pointerDown = (e: React.PointerEvent) => { e.preventDefault(); e.button === 0 && SearchBox.Instance.openSearch(e); }
 
+    nextHighlight = (e: React.PointerEvent) => {
+        e.preventDefault(); e.button === 0 && SearchBox.Instance.openSearch(e);
+        let sstring = StrCast(this.props.doc.search_string);
+        this.props.doc.search_string = "";
+        setTimeout(() => this.props.doc.search_string = sstring, 0);
+    }
     highlightDoc = (e: React.PointerEvent) => {
         if (this.props.doc.type === DocumentType.LINK) {
             if (this.props.doc.anchor1 && this.props.doc.anchor2) {
@@ -240,6 +243,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
         } else {
             Doc.BrushDoc(this.props.doc);
         }
+        e.stopPropagation();
     }
 
     unHighlightDoc = (e: React.PointerEvent) => {
@@ -283,13 +287,14 @@ export class SearchItem extends React.Component<SearchItemProps> {
         const doc2 = Cast(this.props.doc.anchor2, Doc);
         return (
             <div className="search-overview" onPointerDown={this.pointerDown} onContextMenu={this.onContextMenu}>
-                <div className="search-item" onPointerEnter={this.highlightDoc} onPointerLeave={this.unHighlightDoc} id="result"
-                    onClick={this.onClick} onPointerDown={this.pointerDown} >
+                <div className="search-item" onPointerDown={this.nextHighlight} onPointerEnter={this.highlightDoc} onPointerLeave={this.unHighlightDoc} id="result"
+                    onClick={this.onClick}>
                     <div className="main-search-info">
                         <div title="Drag as document" onPointerDown={this.onPointerDown} style={{ marginRight: "7px" }}> <FontAwesomeIcon icon="file" size="lg" /> </div>
                         <div className="search-title-container">
                             <div className="search-title">{StrCast(this.props.doc.title)}</div>
-                            <div className="search-highlighting">{this.props.highlighting.length ? "Matched fields:" + this.props.highlighting.join(", ") : this.props.lines.length ? "Text:" + this.props.lines[0] : ""}</div>
+                            <div className="search-highlighting">{this.props.highlighting.length ? "Matched fields:" + this.props.highlighting.join(", ") : this.props.lines.length ? this.props.lines[0] : ""}</div>
+                            {this.props.lines.filter((m, i) => i).map((l, i) => <div id={i.toString()} className="search-highlighting">`${l}`</div>)}
                         </div>
                         <div className="search-info" style={{ width: this._useIcons ? "15%" : "400px" }}>
                             <div className={`icon-${this._useIcons ? "icons" : "live"}`}>
-- 
cgit v1.2.3-70-g09d2


From d611773fc805082a935cae49723d516ce66e1a14 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 4 Oct 2019 23:45:01 -0400
Subject: more pdf cleanup.   fix to mix-multiply-mode for better
 highlighters/opacity.  small text box fixes.

---
 src/client/util/DocumentManager.ts                 |   2 +-
 src/client/util/RichTextSchema.tsx                 |  26 ++-
 src/client/util/SelectionManager.ts                |   2 -
 src/client/views/DocumentButtonBar.tsx             |   1 -
 src/client/views/DocumentDecorations.tsx           |   1 -
 src/client/views/MainView.tsx                      |   3 +-
 .../collectionFreeForm/MarqueeView.scss            |   2 +-
 src/client/views/nodes/Annotation.tsx              | 117 ------------
 src/client/views/nodes/FormattedTextBox.tsx        |   7 +-
 src/client/views/pdf/Annotation.scss               |   3 +-
 src/client/views/pdf/Annotation.tsx                |   4 +-
 src/client/views/pdf/PDFMenu.tsx                   |  10 +-
 src/client/views/pdf/PDFViewer.scss                |   6 +-
 src/client/views/pdf/PDFViewer.tsx                 | 198 +++++++++------------
 src/new_fields/Doc.ts                              |   2 +-
 .../authentication/models/current_user_utils.ts    |   2 +-
 16 files changed, 117 insertions(+), 269 deletions(-)
 delete mode 100644 src/client/views/nodes/Annotation.tsx

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index ffd311665..6fe97edbb 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -139,7 +139,7 @@ export class DocumentManager {
             const finalDocView = DocumentManager.Instance.getFirstDocumentView(targetDoc);
             finalDocView && (finalDocView.Document.scrollToLinkID = linkId);
             finalDocView && Doc.linkFollowHighlight(finalDocView.props.Document);
-        }
+        };
         const docView = DocumentManager.Instance.getFirstDocumentView(targetDoc);
         const annotatedDoc = await Cast(targetDoc.annotationOn, Doc);
         if (docView) {  // we have a docView already and aren't forced to create a new one ... just focus on the document.  TODO move into view if necessary otherwise just highlight?
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 53eaf9ce2..528c0000b 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -654,17 +654,17 @@ export class ImageResizeView {
         this._img.onclick = function (e: any) {
             e.stopPropagation();
             e.preventDefault();
-            if (view.state.selection.node && view.state.selection.node.type !== view.state.schema.nodes.image)
-                view.dispatch(
-                    view.state.tr.setSelection(new NodeSelection(view.state.doc.resolve(view.state.selection.from - 2))));
-        }
+            if (view.state.selection.node && view.state.selection.node.type !== view.state.schema.nodes.image) {
+                view.dispatch(view.state.tr.setSelection(new NodeSelection(view.state.doc.resolve(view.state.selection.from - 2))));
+            }
+        };
         this._img.onpointerdown = function (e: any) {
             if (e.ctrlKey) {
                 e.preventDefault();
                 e.stopPropagation();
                 DocServer.GetRefField(node.attrs.docid).then(async linkDoc =>
                     (linkDoc instanceof Doc) &&
-                    DocumentManager.Instance.FollowLink(linkDoc, (view.state.schema as any).Document,
+                    DocumentManager.Instance.FollowLink(linkDoc, view.state.schema.Document,
                         document => addDocTab(document, undefined, node.attrs.location ? node.attrs.location : "inTab"), false));
             }
         };
@@ -730,7 +730,7 @@ export class DashDocView {
         this._dashSpan.style.width = node.attrs.width;
         this._dashSpan.style.height = node.attrs.height;
         this._dashSpan.style.position = "absolute";
-        this._dashSpan.style.display = "inline-block"
+        this._dashSpan.style.display = "inline-block";
         this._handle.style.position = "absolute";
         this._handle.style.width = "20px";
         this._handle.style.height = "20px";
@@ -771,16 +771,10 @@ export class DashDocView {
         this._dashSpan.onclick = function (e: any) {
             FormattedTextBox.firstTarget && FormattedTextBox.firstTarget();
             e.stopPropagation();
-        }
-        this._dashSpan.onkeydown = function (e: any) {
-            e.stopPropagation();
-        }
-        this._dashSpan.onkeypress = function (e: any) {
-            e.stopPropagation();
-        }
-        this._dashSpan.onkeyup = function (e: any) {
-            e.stopPropagation();
-        }
+        };
+        this._dashSpan.onkeydown = function (e: any) { e.stopPropagation(); };
+        this._dashSpan.onkeypress = function (e: any) { e.stopPropagation(); };
+        this._dashSpan.onkeyup = function (e: any) { e.stopPropagation(); };
         this._handle.onpointerdown = function (e: any) {
             e.preventDefault();
             e.stopPropagation();
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index a02a270ee..df1b46b33 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -27,7 +27,6 @@ export namespace SelectionManager {
             } else if (!ctrlPressed && manager.SelectedDocuments.length > 1) {
                 manager.SelectedDocuments.map(dv => dv !== docView && dv.props.whenActiveChanged(false));
                 manager.SelectedDocuments = [docView];
-                FormattedTextBox.InputBoxOverlay = undefined;
             }
         }
         @action
@@ -42,7 +41,6 @@ export namespace SelectionManager {
         DeselectAll(): void {
             manager.SelectedDocuments.map(dv => dv.props.whenActiveChanged(false));
             manager.SelectedDocuments = [];
-            FormattedTextBox.InputBoxOverlay = undefined;
         }
     }
 
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index e57745b86..9e2d41621 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -140,7 +140,6 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[],
             let selDoc = this.props.views[0];
             let container = selDoc.props.ContainingCollectionDoc ? selDoc.props.ContainingCollectionDoc.proto : undefined;
             let dragData = new DragManager.LinkDragData(selDoc.props.Document, container ? [container] : []);
-            FormattedTextBox.InputBoxOverlay = undefined;
             this._linkDrag = UndoManager.StartBatch("Drag Link");
             DragManager.StartLinkDrag(this._linkerButton.current, dragData, e.pageX, e.pageY, {
                 handlers: {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 26ffaf3a6..9acb77ce2 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -466,7 +466,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 break;
         }
 
-        if (!this._resizing) runInAction(() => FormattedTextBox.InputBoxOverlay = undefined);
         SelectionManager.SelectedDocuments().forEach(element => {
             if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
                 let doc = PositionDocument(element.props.Document);
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 545f99a41..3b0457dff 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,6 +1,5 @@
 import { IconName, library } from '@fortawesome/fontawesome-svg-core';
-import { faLink, faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faUndoAlt, faTv, faChevronRight, faEllipsisV } from '@fortawesome/free-solid-svg-icons';
-import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt } from '@fortawesome/free-solid-svg-icons';
+import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faUndoAlt, faTv, faChevronRight, faEllipsisV } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
index 7dc54ea79..04f6ec2ad 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
@@ -11,7 +11,7 @@
 }
 
 .marqueeView:focus-within {
-    overflow: visible;
+    overflow: hidden;
 }
 .marquee {
     border-style: dashed;
diff --git a/src/client/views/nodes/Annotation.tsx b/src/client/views/nodes/Annotation.tsx
deleted file mode 100644
index 3e4ed6bf1..000000000
--- a/src/client/views/nodes/Annotation.tsx
+++ /dev/null
@@ -1,117 +0,0 @@
-import "./ImageBox.scss";
-import React = require("react");
-import { observer } from "mobx-react";
-import { observable, action } from 'mobx';
-import 'react-pdf/dist/Page/AnnotationLayer.css';
-
-interface IProps {
-    Span: HTMLSpanElement;
-    X: number;
-    Y: number;
-    Highlights: any[];
-    Annotations: any[];
-    CurrAnno: any[];
-
-}
-
-/**
- * Annotation class is used to take notes on a particular highlight. You can also change highlighted span's color
- * Improvements to be made: Removing the annotation when onRemove is called. (Removing this, not just the highlighted span). 
- * Also need to support multiline highlighting
- * 
- * Written by: Andrew Kim
- */
-@observer
-export class Annotation extends React.Component<IProps> {
-
-    /**
-     * changes color of the span (highlighted section)
-     */
-    onColorChange = (e: React.PointerEvent) => {
-        if (e.currentTarget.innerHTML === "r") {
-            this.props.Span.style.backgroundColor = "rgba(255,0,0, 0.3)";
-        } else if (e.currentTarget.innerHTML === "b") {
-            this.props.Span.style.backgroundColor = "rgba(0,255, 255, 0.3)";
-        } else if (e.currentTarget.innerHTML === "y") {
-            this.props.Span.style.backgroundColor = "rgba(255,255,0, 0.3)";
-        } else if (e.currentTarget.innerHTML === "g") {
-            this.props.Span.style.backgroundColor = "rgba(76, 175, 80, 0.3)";
-        }
-
-    }
-
-    /**
-     * removes the highlighted span. Supposed to remove Annotation too, but I don't know how to unmount this
-     */
-    @action
-    onRemove = (e: any) => {
-        let index: number = -1;
-        //finding the highlight in the highlight array 
-        this.props.Highlights.forEach((e) => {
-            for (const span of e.spans) {
-                if (span === this.props.Span) {
-                    index = this.props.Highlights.indexOf(e);
-                    this.props.Highlights.splice(index, 1);
-                }
-            }
-        });
-
-        //removing from CurrAnno and Annotation array 
-        this.props.Annotations.splice(index, 1);
-        this.props.CurrAnno.pop();
-
-        //removing span from div
-        if (this.props.Span.parentElement) {
-            let nodesArray = this.props.Span.parentElement.childNodes;
-            nodesArray.forEach((e) => {
-                if (e === this.props.Span) {
-                    if (this.props.Span.parentElement) {
-                        this.props.Highlights.forEach((item) => {
-                            if (item === e) {
-                                item.remove();
-                            }
-                        });
-                        e.remove();
-                    }
-                }
-            });
-        }
-
-
-    }
-
-    render() {
-        return (
-            <div
-                style={{
-                    position: "absolute",
-                    top: "20px",
-                    left: "0px",
-                    zIndex: 1,
-                    transform: `translate(${this.props.X}px, ${this.props.Y}px)`,
-
-                }}>
-                <div style={{ width: "200px", height: "50px", backgroundColor: "orange" }}>
-                    <button
-                        style={{ borderRadius: "25px", width: "25%", height: "100%" }}
-                        onClick={this.onRemove}
-                    >x</button>
-                    <div style={{ width: "75%", height: "100%", display: "inline-block" }}>
-                        <button onPointerDown={this.onColorChange} style={{ backgroundColor: "red", borderRadius: "50%", color: "transparent" }}>r</button>
-                        <button onPointerDown={this.onColorChange} style={{ backgroundColor: "blue", borderRadius: "50%", color: "transparent" }}>b</button>
-                        <button onPointerDown={this.onColorChange} style={{ backgroundColor: "yellow", borderRadius: "50%", color: "transparent" }}>y</button>
-                        <button onPointerDown={this.onColorChange} style={{ backgroundColor: "green", borderRadius: "50%", color: "transparent" }}>g</button>
-                    </div>
-
-                </div>
-                <div style={{ width: "200px", height: "200" }}>
-                    <textarea style={{ width: "100%", height: "100%" }}
-                        defaultValue="Enter Text Here..."
-
-                    ></textarea>
-                </div>
-            </div>
-
-        );
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 05904e1e7..2b6a86aed 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -291,7 +291,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             if (de.data.urlField && link) {
                 let url: string = de.data.urlField.url.href;
                 let model: NodeType = [".mov", ".mp4"].includes(url) ? schema.nodes.video : schema.nodes.image;
-                node = model.create({ src: url, docid: link[Id] })
+                node = model.create({ src: url, docid: link[Id] });
             } else {
                 node = schema.nodes.dashDoc.create({
                     width: target[WidthSym](), height: target[HeightSym](),
@@ -798,7 +798,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                     this._editorView!.focus();
                 }
             }
-        }
+        };
     }
 
     onPointerUp = (e: React.PointerEvent): void => {
@@ -807,9 +807,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             e.stopPropagation();
         }
     }
-
+    static InputBoxOverlay: any = null;
     @action
     onFocused = (e: React.FocusEvent): void => {
+        FormattedTextBox.InputBoxOverlay = this;
         document.removeEventListener("keypress", this.recordKeyHandler);
         document.addEventListener("keypress", this.recordKeyHandler);
         this.tryUpdateHeight();
diff --git a/src/client/views/pdf/Annotation.scss b/src/client/views/pdf/Annotation.scss
index 0c6df74f0..cc326eb93 100644
--- a/src/client/views/pdf/Annotation.scss
+++ b/src/client/views/pdf/Annotation.scss
@@ -2,6 +2,5 @@
     pointer-events: all;
     user-select: none;
     position: absolute;
-    background-color: red;
-    opacity: 0.1;
+    background-color: rgba(146, 245, 95, 0.467);
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 134e757d1..4bb166ffe 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -122,9 +122,9 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
                 left: this.props.x,
                 width: this.props.width,
                 height: this.props.height,
-                transition: "opacity 0.5s",
                 opacity: this._brushed ? 0.5 : undefined,
-                backgroundColor: this._brushed ? "orange" : StrCast(this.props.document.color)
+                backgroundColor: this._brushed ? "orange" : StrCast(this.props.document.backgroundColor),
+                transition: "opacity 0.5s",
             }} />);
     }
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index e62542014..1e3320069 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -4,7 +4,7 @@ import { observable, action, } from "mobx";
 import { observer } from "mobx-react";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { emptyFunction, returnFalse } from "../../../Utils";
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, Opt } from "../../../new_fields/Doc";
 
 @observer
 export default class PDFMenu extends React.Component {
@@ -31,7 +31,7 @@ export default class PDFMenu extends React.Component {
     @observable public Pinned: boolean = false;
 
     public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = emptyFunction;
-    public Highlight: (color: string) => void = emptyFunction;
+    public Highlight: (color: string) => Opt<Doc> = (color: string) => undefined;
     public Delete: () => void = emptyFunction;
     public Snippet: (marquee: { left: number, top: number, width: number, height: number }) => void = emptyFunction;
     public AddTag: (key: string, value: string) => boolean = returnFalse;
@@ -155,12 +155,8 @@ export default class PDFMenu extends React.Component {
 
     @action
     highlightClicked = (e: React.MouseEvent) => {
-        if (!this.Pinned) {
-            this.Highlight("#f4f442");
-        }
-        else {
+        if (!this.Highlight("rgba(245, 230, 95, 0.616)") && this.Pinned) {
             this.Highlighting = !this.Highlighting;
-            this.Highlight("#f4f442");
         }
     }
 
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index a71e4f81e..c77cee792 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -16,6 +16,7 @@
         mix-blend-mode: multiply;
         opacity: 0.9;
     }
+    .textLayer ::selection { background: yellow; } // should match the backgroundColor in createAnnotation()
     .textLayer .highlight {
         background-color: yellow;
     }
@@ -51,10 +52,9 @@
         pointer-events: none;
         mix-blend-mode: multiply;
 
-        .pdfPage-annotationBox {
+        .pdfViewer-annotationBox {
             position: absolute;
-            background-color: red;
-            opacity: 0.1;
+            background-color: rgba(245, 230, 95, 0.616);
         }
     }
     .pdfViewer-waiting {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 1b76ddbdc..4516e9904 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -75,8 +75,9 @@ export class PDFViewer extends React.Component<IViewerProps> {
     @observable private _showWaiting = true;
     @observable private _showCover = false;
     @observable private _zoomed = 1;
+    @observable private _scrollTop = 0;
 
-    public pdfViewer: any;
+    private _pdfViewer: any;
     private _retries = 0; // number of times tried to create the PDF viewer 
     private _isChildActive = false;
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
@@ -149,15 +150,16 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
     copy = (e: ClipboardEvent) => {
         if (this.props.active() && e.clipboardData) {
-            e.clipboardData.setData("text/plain", this._selectionText);
-            e.clipboardData.setData("dash/pdfOrigin", this.props.Document[Id]);
-            e.clipboardData.setData("dash/pdfRegion", this.makeAnnotationDocument("#0390fc")[Id]);
+            let annoDoc = this.makeAnnotationDocument("#0390fc");
+            if (annoDoc) {
+                e.clipboardData.setData("text/plain", this._selectionText);
+                e.clipboardData.setData("dash/pdfOrigin", this.props.Document[Id]);
+                e.clipboardData.setData("dash/pdfRegion", annoDoc[Id]);
+            }
             e.preventDefault();
         }
     }
 
-    setSelectionText = (text: string) => this._selectionText = text;
-
     @action
     initialLoad = async () => {
         if (this._pageSizes.length === 0) {
@@ -215,7 +217,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         document.removeEventListener("copy", this.copy);
         document.addEventListener("copy", this.copy);
         document.addEventListener("pagesinit", action(() => {
-            this.pdfViewer.currentScaleValue = this._zoomed = 1;
+            this._pdfViewer.currentScaleValue = this._zoomed = 1;
             this.gotoPage(NumCast(this.props.Document.curPage, 1));
         }));
         document.addEventListener("pagerendered", action(() => this._showCover = this._showWaiting = false));
@@ -223,36 +225,35 @@ export class PDFViewer extends React.Component<IViewerProps> {
         let pdfFindController = new PDFJSViewer.PDFFindController({
             linkService: pdfLinkService,
         });
-        this.pdfViewer = new PDFJSViewer.PDFViewer({
+        this._pdfViewer = new PDFJSViewer.PDFViewer({
             container: this._mainCont.current,
             viewer: this._viewer.current,
             linkService: pdfLinkService,
             findController: pdfFindController,
             renderer: "canvas",
         });
-        pdfLinkService.setViewer(this.pdfViewer);
+        pdfLinkService.setViewer(this._pdfViewer);
         pdfLinkService.setDocument(this.props.pdf, null);
-        this.pdfViewer.setDocument(this.props.pdf);
+        this._pdfViewer.setDocument(this.props.pdf);
     }
 
     @action
-    makeAnnotationDocument = (color: string): Doc => {
+    makeAnnotationDocument = (color: string): Opt<Doc> => {
+        if (this._savedAnnotations.size() === 0) return undefined;
         let mainAnnoDoc = Docs.Create.InstanceFromProto(new Doc(), "", {});
         let mainAnnoDocProto = Doc.GetProto(mainAnnoDoc);
         let annoDocs: Doc[] = [];
         let minY = Number.MAX_VALUE;
-        if (this._savedAnnotations.size() === 1 && this._savedAnnotations.values()[0].length === 1) {
+        if ((this._savedAnnotations.values()[0][0] as any).marqueeing) {
             let anno = this._savedAnnotations.values()[0][0];
-            let annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: "rgba(255, 0, 0, 0.1)", title: "Annotation on " + StrCast(this.props.Document.title) });
+            let annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, title: "Annotation on " + StrCast(this.props.Document.title) });
             if (anno.style.left) annoDoc.x = parseInt(anno.style.left);
             if (anno.style.top) annoDoc.y = parseInt(anno.style.top);
             if (anno.style.height) annoDoc.height = parseInt(anno.style.height);
             if (anno.style.width) annoDoc.width = parseInt(anno.style.width);
             annoDoc.group = mainAnnoDoc;
-            annoDoc.color = color;
-            annoDoc.type = AnnotationTypes.Region;
-            annoDocs.push(annoDoc);
             annoDoc.isButton = true;
+            annoDocs.push(annoDoc);
             anno.remove();
             mainAnnoDoc = annoDoc;
             mainAnnoDocProto = Doc.GetProto(mainAnnoDoc);
@@ -265,8 +266,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 if (anno.style.height) annoDoc.height = parseInt(anno.style.height);
                 if (anno.style.width) annoDoc.width = parseInt(anno.style.width);
                 annoDoc.group = mainAnnoDoc;
-                annoDoc.color = color;
-                annoDoc.type = AnnotationTypes.Region;
+                annoDoc.backgroundColor = color;
                 annoDocs.push(annoDoc);
                 anno.remove();
                 (annoDoc.y !== undefined) && (minY = Math.min(NumCast(annoDoc.y), minY));
@@ -307,7 +307,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
     @action
     gotoPage = (p: number) => {
-        this.pdfViewer && this.pdfViewer.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) });
+        this._pdfViewer && this._pdfViewer.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) });
     }
 
     @action
@@ -317,25 +317,11 @@ export class PDFViewer extends React.Component<IViewerProps> {
         Doc.linkFollowHighlight(scrollToAnnotation);
     }
 
-    sendAnnotations = (page: number) => {
-        return this._savedAnnotations.getValue(page);
-    }
-
-    receiveAnnotations = (annotations: HTMLDivElement[], page: number) => {
-        if (page === -1) {
-            this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
-            this._savedAnnotations.keys().forEach(k => this._savedAnnotations.setValue(k, annotations));
-        }
-        else {
-            this._savedAnnotations.setValue(page, annotations);
-        }
-    }
 
-    @observable scrollTop = 0;
     @action
     onScroll = (e: React.UIEvent<HTMLElement>) => {
-        this.scrollTop = this._mainCont.current!.scrollTop;
-        this.pdfViewer && (this.props.Document.curPage = this.pdfViewer.currentPageNumber);
+        this._scrollTop = this._mainCont.current!.scrollTop;
+        this._pdfViewer && (this.props.Document.curPage = this._pdfViewer.currentPageNumber);
     }
 
     // get the page index that the vertical offset passed in is on
@@ -355,6 +341,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 div.style.top = (parseInt(div.style.top)/*+ this.getScrollFromPage(page)*/).toString();
             }
             this._annotationLayer.current.append(div);
+            div.style.backgroundColor = "yellow";
+            div.style.opacity = "0.5";
             let savedPage = this._savedAnnotations.getValue(page);
             if (savedPage) {
                 savedPage.push(div);
@@ -371,8 +359,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
         if (!searchString) {
             fwd ? this.nextAnnotation() : this.prevAnnotation();
         }
-        else if (this.pdfViewer._pageViewsReady) {
-            this.pdfViewer.findController.executeCommand('findagain', {
+        else if (this._pdfViewer._pageViewsReady) {
+            this._pdfViewer.findController.executeCommand('findagain', {
                 caseSensitive: false,
                 findPrevious: !fwd,
                 highlightAll: true,
@@ -382,7 +370,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         }
         else if (this._mainCont.current) {
             let executeFind = () => {
-                this.pdfViewer.findController.executeCommand('find', {
+                this._pdfViewer.findController.executeCommand('find', {
                     caseSensitive: false,
                     findPrevious: !fwd,
                     highlightAll: true,
@@ -406,30 +394,24 @@ export class PDFViewer extends React.Component<IViewerProps> {
         }
         this._marqueeing = false;
         if (!e.altKey && e.button === 0 && this.active()) {
+            // clear out old marquees and initialize menu for new selection
             PDFMenu.Instance.StartDrag = this.startDrag;
             PDFMenu.Instance.Highlight = this.highlight;
             PDFMenu.Instance.Snippet = this.createSnippet;
             PDFMenu.Instance.Status = "pdf";
             PDFMenu.Instance.fadeOut(true);
+            this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
+            this._savedAnnotations.keys().forEach(k => this._savedAnnotations.setValue(k, []));
             if (e.target && (e.target as any).parentElement.className === "textLayer") {
-                if (!e.ctrlKey) {
-                    this.receiveAnnotations([], -1);
-                }
+                // start selecting text if mouse down on textLayer spans
             }
-            else {
+            else if (this._mainCont.current) {
                 // set marquee x and y positions to the spatially transformed position
-                if (this._mainCont.current) {
-                    let boundingRect = this._mainCont.current.getBoundingClientRect();
-                    this._startX = this._marqueeX = (e.clientX - boundingRect.left) * (this._mainCont.current.offsetWidth / boundingRect.width);
-                    this._startY = this._marqueeY = (e.clientY - boundingRect.top) * (this._mainCont.current.offsetHeight / boundingRect.height) + this._mainCont.current.scrollTop;
-                }
+                let boundingRect = this._mainCont.current.getBoundingClientRect();
+                this._startX = this._marqueeX = (e.clientX - boundingRect.left) * (this._mainCont.current.offsetWidth / boundingRect.width);
+                this._startY = this._marqueeY = (e.clientY - boundingRect.top) * (this._mainCont.current.offsetHeight / boundingRect.height) + this._mainCont.current.scrollTop;
+                this._marqueeHeight = this._marqueeWidth = 0;
                 this._marqueeing = true;
-                let marquees = this._mainCont.current!.getElementsByClassName("pdfViewer-dragAnnotationBox");
-                if (marquees && marquees.length) { // make a copy of the marquee
-                    let marquee = marquees[0] as HTMLDivElement;
-                    marquee.style.opacity = "0.2";
-                }
-                this.receiveAnnotations([], -1);
             }
             document.removeEventListener("pointermove", this.onSelectMove);
             document.addEventListener("pointermove", this.onSelectMove);
@@ -464,13 +446,13 @@ export class PDFViewer extends React.Component<IViewerProps> {
             let clientRects = selRange.getClientRects();
             for (let i = 0; i < clientRects.length; i++) {
                 let rect = clientRects.item(i);
-                if (rect/* && rect.width !== this._mainCont.current.getBoundingClientRect().width && rect.height !== this._mainCont.current.getBoundingClientRect().height / this.props.pdf.numPages*/) {
+                if (rect) {
                     let scaleY = this._mainCont.current.offsetHeight / boundingRect.height;
                     let scaleX = this._mainCont.current.offsetWidth / boundingRect.width;
                     if (rect.width !== this._mainCont.current.clientWidth &&
-                        (i == 0 || !intersectRect(clientRects[i], clientRects[i - 1]))) {
+                        (i === 0 || !intersectRect(clientRects[i], clientRects[i - 1]))) {
                         let annoBox = document.createElement("div");
-                        annoBox.className = "pdfPage-annotationBox";
+                        annoBox.className = "pdfViewer-annotationBox";
                         // transforms the positions from screen onto the pdf div
                         annoBox.style.top = ((rect.top - boundingRect.top) * scaleY + this._mainCont.current.scrollTop).toString();
                         annoBox.style.left = ((rect.left - boundingRect.left) * scaleX).toString();
@@ -481,8 +463,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 }
             }
         }
-        let text = selRange.cloneContents().textContent;
-        text && this.setSelectionText(text);
+        this._selectionText = selRange.cloneContents().textContent || "";
 
         // clear selection
         if (sel.empty) {  // Chrome
@@ -494,22 +475,22 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
     @action
     onSelectEnd = (e: PointerEvent): void => {
+        this._savedAnnotations.clear();
         if (this._marqueeing) {
             if (this._marqueeWidth > 10 || this._marqueeHeight > 10) {
                 let marquees = this._mainCont.current!.getElementsByClassName("pdfViewer-dragAnnotationBox");
-                if (marquees && marquees.length) { // make a copy of the marquee
+                if (marquees && marquees.length) { // copy the marquee and convert it to a permanent annotation. 
+                    let style = (marquees[0] as HTMLDivElement).style;
                     let copy = document.createElement("div");
-                    let marquee = marquees[0] as HTMLDivElement;
-                    let style = marquee.style;
                     copy.style.left = style.left;
                     copy.style.top = style.top;
                     copy.style.width = style.width;
                     copy.style.height = style.height;
                     copy.style.border = style.border;
                     copy.style.opacity = style.opacity;
-                    copy.className = "pdfPage-annotationBox";
+                    (copy as any).marqueeing = true;
+                    copy.className = "pdfViewer-annotationBox";
                     this.createAnnotation(copy, this.getPageFromScroll(this._marqueeY));
-                    marquee.style.opacity = "0";
                 }
 
                 if (!e.ctrlKey) {
@@ -518,8 +499,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 }
                 PDFMenu.Instance.jumpTo(e.clientX, e.clientY);
             }
-
-            this._marqueeHeight = this._marqueeWidth = 0;
+            this._marqueeing = false;
         }
         else {
             let sel = window.getSelection();
@@ -531,7 +511,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         }
 
         if (PDFMenu.Instance.Highlighting) {
-            this.highlight("goldenrod");
+            this.highlight("rgba(245, 230, 95, 0.616)");  // when highlighter has been toggled when menu is pinned, we auto-highlight immediately on mouse up
         }
         else {
             PDFMenu.Instance.StartDrag = this.startDrag;
@@ -545,7 +525,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     highlight = (color: string) => {
         // creates annotation documents for current highlights
         let annotationDoc = this.makeAnnotationDocument(color);
-        Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, annotationDoc);
+        annotationDoc && Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, annotationDoc);
         return annotationDoc;
     }
 
@@ -558,16 +538,18 @@ export class PDFViewer extends React.Component<IViewerProps> {
         e.preventDefault();
         e.stopPropagation();
         let targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "Note linked to " + this.props.Document.title });
-        let annotationDoc = this.highlight("red");
-        let dragData = new DragManager.AnnotationDragData(this.props.Document, annotationDoc, targetDoc);
-        DragManager.StartAnnotationDrag([ele], dragData, e.pageX, e.pageY, {
-            handlers: {
-                dragComplete: () => !(dragData as any).linkedToDoc &&
-                    DocUtils.MakeLink({ doc: annotationDoc }, { doc: dragData.dropDocument, ctx: dragData.targetContext }, `Annotation from ${StrCast(this.props.Document.title)}`, "link from PDF")
-
-            },
-            hideSource: false
-        });
+        const annotationDoc = this.highlight("rgba(146, 245, 95, 0.467)");
+        if (annotationDoc) {
+            let dragData = new DragManager.AnnotationDragData(this.props.Document, annotationDoc, targetDoc);
+            DragManager.StartAnnotationDrag([ele], dragData, e.pageX, e.pageY, {
+                handlers: {
+                    dragComplete: () => !(dragData as any).linkedToDoc &&
+                        DocUtils.MakeLink({ doc: annotationDoc }, { doc: dragData.dropDocument, ctx: dragData.targetContext }, `Annotation from ${StrCast(this.props.Document.title)}`, "link from PDF")
+
+                },
+                hideSource: false
+            });
+        }
     }
 
     createSnippet = (marquee: { left: number, top: number, width: number, height: number }): void => {
@@ -609,7 +591,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         return true;
     }
     scrollXf = () => {
-        return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, this.scrollTop) : this.props.ScreenToLocalTransform();
+        return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, this._scrollTop) : this.props.ScreenToLocalTransform();
     }
     setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => {
         this._setPreviewCursor = func;
@@ -647,9 +629,9 @@ export class PDFViewer extends React.Component<IViewerProps> {
     onZoomWheel = (e: React.WheelEvent) => {
         e.stopPropagation();
         if (e.ctrlKey) {
-            let curScale = Number(this.pdfViewer.currentScaleValue);
-            this.pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale + curScale * e.deltaY / 1000));
-            this._zoomed = Number(this.pdfViewer.currentScaleValue);
+            let curScale = Number(this._pdfViewer.currentScaleValue);
+            this._pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale + curScale * e.deltaY / 1000));
+            this._zoomed = Number(this._pdfViewer.currentScaleValue);
         }
     }
 
@@ -657,29 +639,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         return <div className="pdfViewer-annotationLayer" style={{ height: NumCast(this.props.Document.nativeHeight) }} ref={this._annotationLayer}>
             {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) =>
                 <Annotation {...this.props} focus={this.props.focus} anno={anno} key={`${anno[Id]}-annotation`} />)}
-        </div>;
-    }
-    @computed get pdfViewerDiv() {
-        return <div className="pdfViewer-text" ref={this._viewer} style={{ transformOrigin: "left top" }} />;
-    }
-    @computed get standinViews() {
-        return <>
-            {this._showCover ? this.getCoverImage() : (null)}
-            {this._showWaiting ? <img className="pdfViewer-waiting" key="waiting" src={"/assets/loading.gif"} /> : (null)}
-        </>;
-    }
-    marqueeWidth = () => this._marqueeWidth;
-    marqueeHeight = () => this._marqueeHeight;
-    marqueeX = () => this._marqueeX;
-    marqueeY = () => this._marqueeY;
-    marqueeing = () => this._marqueeing;
-    visibleHeight = () => this.props.PanelHeight() / this.props.ContentScaling() * 72 / 96;
-    render() {
-        return (<div className={"pdfViewer-viewer" + (this._zoomed !== 1 ? "-zoomed" : "")} onScroll={this.onScroll} onWheel={this.onZoomWheel} onPointerDown={this.onPointerDown} onClick={this.onClick} ref={this._mainCont}>
-            {this.pdfViewerDiv}
-            <PdfViewerMarquee isMarqueeing={this.marqueeing} width={this.marqueeWidth} height={this.marqueeHeight} x={this.marqueeX} y={this.marqueeY} />
-            {this.annotationLayer}
-            <div className="pdfViewer-overlay" style={{ transform: `scale(${this._zoomed})` }}>
+            <div className="pdfViewer-overlay" id="overlay" style={{ transform: `scale(${this._zoomed})` }}>
                 <CollectionFreeFormView {...this.props}
                     setPreviewCursor={this.setPreviewCursor}
                     PanelHeight={() => NumCast(this.props.Document.scrollHeight, NumCast(this.props.Document.nativeHeight))}
@@ -702,7 +662,29 @@ export class PDFViewer extends React.Component<IViewerProps> {
                     chromeCollapsed={true}>
                 </CollectionFreeFormView>
             </div>
+        </div>;
+    }
+    @computed get pdfViewerDiv() {
+        return <div className="pdfViewer-text" ref={this._viewer} style={{ transformOrigin: "left top" }} />;
+    }
+    @computed get standinViews() {
+        return <>
+            {this._showCover ? this.getCoverImage() : (null)}
+            {this._showWaiting ? <img className="pdfViewer-waiting" key="waiting" src={"/assets/loading.gif"} /> : (null)}
+        </>;
+    }
+    marqueeWidth = () => this._marqueeWidth;
+    marqueeHeight = () => this._marqueeHeight;
+    marqueeX = () => this._marqueeX;
+    marqueeY = () => this._marqueeY;
+    marqueeing = () => this._marqueeing;
+    visibleHeight = () => this.props.PanelHeight() / this.props.ContentScaling() * 72 / 96;
+    render() {
+        return (<div className={"pdfViewer-viewer" + (this._zoomed !== 1 ? "-zoomed" : "")} onScroll={this.onScroll} onWheel={this.onZoomWheel} onPointerDown={this.onPointerDown} onClick={this.onClick} ref={this._mainCont}>
+            {this.pdfViewerDiv}
+            {this.annotationLayer}
             {this.standinViews}
+            <PdfViewerMarquee isMarqueeing={this.marqueeing} width={this.marqueeWidth} height={this.marqueeHeight} x={this.marqueeX} y={this.marqueeY} />
         </div >);
     }
 }
@@ -722,11 +704,9 @@ class PdfViewerMarquee extends React.Component<PdfViewerMarqueeProps> {
             style={{
                 left: `${this.props.x()}px`, top: `${this.props.y()}px`,
                 width: `${this.props.width()}px`, height: `${this.props.height()}px`,
-                border: `${this.props.width() === 0 ? "" : "2px dashed black"}`
+                border: `${this.props.width() === 0 ? "" : "2px dashed black"}`,
+                opacity: 0.2
             }}>
         </div>;
     }
-}
-
-
-export enum AnnotationTypes { Region }
+}
\ No newline at end of file
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 6acc6e1ca..7e37eba84 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -685,7 +685,7 @@ export namespace Doc {
         document.removeEventListener("pointerdown", linkFollowUnhighlight);
         document.addEventListener("pointerdown", linkFollowUnhighlight);
         let x = dt = Date.now();
-        window.setTimeout(() => dt == x && linkFollowUnhighlight(), 5000);
+        window.setTimeout(() => dt === x && linkFollowUnhighlight(), 5000);
     }
 
     export class HighlightBrush {
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index b2509a4f1..0fbfbf2f3 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -112,7 +112,7 @@ export class CurrentUserUtils {
             if (sidebar) {
                 sidebar.backgroundColor = "lightgrey";
             }
-        })
+        });
 
         if (doc.overlays === undefined) {
             const overlays = Docs.Create.FreeformDocument([], { title: "Overlays" });
-- 
cgit v1.2.3-70-g09d2


From 89a8ec9a899dc991a598f97ff5cb3a67eff640ac Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 4 Oct 2019 23:51:20 -0400
Subject: from last

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

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

diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 4bb166ffe..5a07b88d9 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -94,6 +94,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
             PDFMenu.Instance.AddTag = this.addTag.bind(this);
             PDFMenu.Instance.PinToPres = this.pinToPres;
             PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true);
+            e.stopPropagation();
         }
         else if (e.button === 0) {
             let annoGroup = await Cast(this.props.document.group, Doc);
@@ -105,6 +106,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
         }
     }
 
+
     addTag = (key: string, value: string): boolean => {
         let group = FieldValue(Cast(this.props.document.group, Doc));
         if (group) {
-- 
cgit v1.2.3-70-g09d2

