From af2346983eae1145167b70faf96a9aec0ca82427 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Mon, 20 May 2019 00:09:47 -0400
Subject: Fixed a bunch of demo bugs Moved Field Symbols to separate file
 Editing is mostly working in debug viewer

---
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 9cb8443f4..ee6f4821f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -22,9 +22,9 @@ import { createSchema, makeInterface, listSpec } from "../../../../new_fields/Sc
 import { Doc, WidthSym, HeightSym } from "../../../../new_fields/Doc";
 import { FieldValue, Cast, NumCast, BoolCast } from "../../../../new_fields/Types";
 import { pageSchema } from "../../nodes/ImageBox";
-import { Id } from "../../../../new_fields/RefField";
 import { InkField, StrokeData } from "../../../../new_fields/InkField";
 import { HistoryUtil } from "../../../util/History";
+import { Id } from "../../../../new_fields/FieldSymbols";
 
 export const panZoomSchema = createSchema({
     panX: "number",
-- 
cgit v1.2.3-70-g09d2


From 060455feec89c94073636f612ccca54a0aaabb90 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 20 May 2019 21:29:17 -0400
Subject: fixed a few things for Firefox - dragging images, typing spaces,
 scrool speed.  and focusing proeprly with zoomBasis.

---
 .vscode/launch.json                                              | 9 +++++++++
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx    | 9 +++------
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx        | 3 +++
 src/client/views/nodes/FormattedTextBox.tsx                      | 4 ++--
 4 files changed, 17 insertions(+), 8 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/.vscode/launch.json b/.vscode/launch.json
index d245be2ad..3b6549767 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -13,6 +13,15 @@
             "url": "http://localhost:1050/login",
             "webRoot": "${workspaceFolder}",
         },
+        {
+            "type": "firefox",
+            "request": "launch",
+            "name": "Launch Firefox against localhost",
+            "sourceMaps": "client",
+            "reAttach": true,
+            "url": "http://localhost:1050/login",
+            "webRoot": "${workspaceFolder}",
+        },
         {
             "type": "chrome",
             "request": "launch",
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ee6f4821f..3ec7184d2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -183,7 +183,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             return;
         }
         e.stopPropagation();
-        const coefficient = 1000;
+        const coefficient = 100;
 
         if (e.ctrlKey) {
             let deltaScale = (1 - (e.deltaY / coefficient));
@@ -215,7 +215,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     @action
     setPan(panX: number, panY: number) {
-        this.panDisposer && clearTimeout(this.panDisposer);
         this.props.Document.panTransformType = "None";
         var scale = this.getLocalTransform().inverse().Scale;
         const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
@@ -243,7 +242,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         doc.zIndex = docs.length + 1;
     }
 
-    panDisposer?: NodeJS.Timeout;
     focusDocument = (doc: Doc) => {
         const panX = this.Document.panX;
         const panY = this.Document.panY;
@@ -265,15 +263,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             }
         }
         SelectionManager.DeselectAll();
-        const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2;
-        const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2;
+        const newPanX = NumCast(doc.x) + NumCast(doc.width) / NumCast(doc.zoomBasis, 1) / 2;
+        const newPanY = NumCast(doc.y) + NumCast(doc.height) / NumCast(doc.zoomBasis, 1) / 2;
         const newState = HistoryUtil.getState();
         newState.initializers[id] = { panX: newPanX, panY: newPanY };
         HistoryUtil.pushState(newState);
         this.setPan(newPanX, newPanY);
         this.props.Document.panTransformType = "Ease";
         this.props.focus(this.props.Document);
-        this.panDisposer = setTimeout(() => this.props.Document.panTransformType = "None", 2000);  // wait 3 seconds, then reset to false
     }
 
 
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index fa2b09ba7..7c3180496 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -177,6 +177,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         } else {
             this._lastTap = Date.now();
         }
+        if (e.button === 0) {
+            e.preventDefault();  // prevents Firefox from dragging images (we want to do that ourself)
+        }
     }
     onPointerUp = (e: PointerEvent): void => {
         document.removeEventListener("pointerup", this.onPointerUp);
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index d15813f9a..5afef221c 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -366,7 +366,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 style={{
                     pointerEvents: interactive ? "all" : "none",
                 }}
-                onKeyDown={this.onKeyPress}
+                // onKeyDown={this.onKeyPress}
                 onKeyPress={this.onKeyPress}
                 onFocus={this.onFocused}
                 onClick={this.onClick}
@@ -378,7 +378,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 // tfs: do we need this event handler
                 onWheel={this.onPointerWheel}
             >
-                <div className={`formattedTextBox-inner${rounded}`} style={{ pointerEvents: this.props.Document.isButton && !this.props.isSelected() ? "none" : "all" }} ref={this._proseRef} />
+                <div className={`formattedTextBox-inner${rounded}`} style={{ whiteSpace: "pre-wrap", pointerEvents: this.props.Document.isButton && !this.props.isSelected() ? "none" : "all" }} ref={this._proseRef} />
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From 505676028698f22e46854fdd8ea25ea3118b5d6f Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 20 May 2019 22:31:37 -0400
Subject: fixed annotation pages for videos

---
 src/Utils.ts                                                | 13 +++++++++++++
 src/client/views/InkingCanvas.tsx                           |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx           |  2 +-
 src/client/views/nodes/VideoBox.tsx                         |  6 ++++--
 4 files changed, 19 insertions(+), 4 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/Utils.ts b/src/Utils.ts
index 24878a368..611c61135 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -39,6 +39,19 @@ export class Utils {
         document.body.removeChild(textArea);
     }
 
+    public static GetClipboardText(): string {
+        var textArea = document.createElement("textarea");
+        document.body.appendChild(textArea);
+        textArea.focus();
+        textArea.select();
+
+        try { document.execCommand('paste'); } catch (err) { }
+
+        const val = textArea.value;
+        document.body.removeChild(textArea);
+        return val;
+    }
+
     public static loggingEnabled: Boolean = false;
     public static logFilter: number | undefined = undefined;
     private static log(prefix: string, messageName: string, message: any, receiving: boolean) {
diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx
index afe3e3ecb..42ab08001 100644
--- a/src/client/views/InkingCanvas.tsx
+++ b/src/client/views/InkingCanvas.tsx
@@ -146,7 +146,7 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
     get drawnPaths() {
         let curPage = NumCast(this.props.Document.curPage, -1);
         let paths = Array.from(this.inkData).reduce((paths, [id, strokeData]) => {
-            if (strokeData.page === -1 || strokeData.page === curPage) {
+            if (strokeData.page === -1 || Math.round(strokeData.page) === Math.round(curPage)) {
                 paths.push(<InkingStroke key={id} id={id}
                     line={strokeData.pathData}
                     count={strokeData.pathData.length}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 3ec7184d2..ba6a4bbab 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -302,7 +302,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         let docviews = this.childDocs.reduce((prev, doc) => {
             if (!(doc instanceof Doc)) return prev;
             var page = NumCast(doc.page, -1);
-            if (page === curPage || page === -1) {
+            if (Math.round(page) === Math.round(curPage) || page === -1) {
                 let minim = BoolCast(doc.isMinimized, false);
                 if (minim === undefined || !minim) {
                     prev.push(<CollectionFreeFormDocumentView key={doc[Id]} {...this.getDocumentViewProps(doc)} />);
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 81c429a02..6ae55d151 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -52,7 +52,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
     @action public Play() {
         this.Playing = true;
         if (this.player) this.player.play();
-        if (!this._playTimer) this._playTimer = setInterval(this.updateTimecode, 1000);
+        if (!this._playTimer) this._playTimer = setInterval(this.updateTimecode, 500);
     }
 
     @action public Pause() {
@@ -70,7 +70,9 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
     }
 
     @action
-    updateTimecode = () => this.player && (this.props.Document.curPage = this.player.currentTime)
+    updateTimecode = () => {
+        this.player && (this.props.Document.curPage = this.player.currentTime);
+    }
 
     componentDidMount() {
         if (this.props.setVideoBox) this.props.setVideoBox(this);
-- 
cgit v1.2.3-70-g09d2


From 6eb5234a0def9e5714319e512e8dd927bd894b82 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Tue, 21 May 2019 11:44:51 -0400
Subject: Fixed various scripting things

---
 src/client/util/type_decls.d                       | 33 ++++++++++++++++------
 .../views/collections/CollectionSchemaView.tsx     | 10 +++----
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/nodes/KeyValuePair.tsx            |  8 ++----
 src/debug/Viewer.tsx                               |  2 +-
 src/new_fields/Doc.ts                              |  7 +++--
 src/new_fields/URLField.ts                         |  9 ++++--
 7 files changed, 46 insertions(+), 25 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/util/type_decls.d b/src/client/util/type_decls.d
index 51114d0e2..557f6f574 100644
--- a/src/client/util/type_decls.d
+++ b/src/client/util/type_decls.d
@@ -140,33 +140,50 @@ declare const ToScriptString: unique symbol;
 declare abstract class RefField {
     readonly [Id]: FieldId;
 
-    constructor(id?: FieldId);
-    protected [HandleUpdate]?(diff: any): void;
+    constructor();
+    // protected [HandleUpdate]?(diff: any): void;
 
-    abstract [ToScriptString](): string;
+    // abstract [ToScriptString](): string;
 }
 
 declare abstract class ObjectField {
     protected [OnUpdate](diff?: any): void;
     private [Parent]?: RefField | ObjectField;
-    abstract [Copy](): ObjectField;
+    // abstract [Copy](): ObjectField;
 
-    abstract [ToScriptString](): string;
+    // abstract [ToScriptString](): string;
 }
+
+declare abstract class URLField extends ObjectField {
+    readonly url: URL;
+
+    constructor(url: string);
+    constructor(url: URL);
+}
+
+declare class AudioField extends URLField { }
+declare class VideoField extends URLField { }
+declare class ImageField extends URLField { }
+declare class WebField extends URLField { }
+declare class PdfField extends URLField { }
+
 declare type FieldId = string;
 
 declare type Field = number | string | boolean | ObjectField | RefField;
 
 declare type Opt<T> = T | undefined;
 declare class Doc extends RefField {
+    constructor();
+
     [key: string]: Field | undefined;
-    [ToScriptString](): string;
+    // [ToScriptString](): string;
 }
 
 declare class ListImpl<T extends Field> extends ObjectField {
+    constructor(fields?: T[]);
     [index: number]: T | (T extends RefField ? Promise<T> : never);
-    [ToScriptString](): string;
-    [Copy](): ObjectField;
+    // [ToScriptString](): string;
+    // [Copy](): ObjectField;
 }
 
 // @ts-ignore
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index b25b48339..488f7d6cb 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -115,22 +115,20 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                     height={Number(MAX_ROW_HEIGHT)}
                     GetValue={() => {
                         let field = props.Document[props.fieldKey];
-                        if (field) {
-                            //TODO Types
-                            // return field.ToScriptString();
-                            return String(field);
+                        if (Field.IsField(field)) {
+                            return Field.toScriptString(field);
                         }
                         return "";
                     }}
                     SetValue={(value: string) => {
-                        let script = CompileScript(value, { addReturn: true, params: { this: Document.name } });
+                        let script = CompileScript(value, { addReturn: true, params: { this: Doc.name } });
                         if (!script.compiled) {
                             return false;
                         }
                         return applyToDoc(props.Document, script.run);
                     }}
                     OnFillDown={async (value: string) => {
-                        let script = CompileScript(value, { addReturn: true, params: { this: Document.name } });
+                        let script = CompileScript(value, { addReturn: true, params: { this: Doc.name } });
                         if (!script.compiled) {
                             return;
                         }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ba6a4bbab..7a0a02318 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -183,7 +183,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             return;
         }
         e.stopPropagation();
-        const coefficient = 100;
+        const coefficient = 1000;
 
         if (e.ctrlKey) {
             let deltaScale = (1 - (e.deltaY / coefficient));
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 7a88985c0..2363553df 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -60,10 +60,8 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
                     <EditableView contents={contents} height={36} GetValue={() => {
 
                         let field = FieldValue(props.Document[props.fieldKey]);
-                        if (field) {
-                            //TODO Types
-                            return String(field);
-                            // return field.ToScriptString();
+                        if (Field.IsField(field)) {
+                            return Field.toScriptString(field);
                         }
                         return "";
                     }}
@@ -75,7 +73,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
                             let res = script.run();
                             if (!res.success) return false;
                             const field = res.result;
-                            if (Field.IsField(field)) {
+                            if (Field.IsField(field, true)) {
                                 props.Document[props.fieldKey] = field;
                                 return true;
                             }
diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx
index 4314e2132..b22300d0b 100644
--- a/src/debug/Viewer.tsx
+++ b/src/debug/Viewer.tsx
@@ -20,7 +20,7 @@ function applyToDoc(doc: any, key: string | number, scriptString: string): boole
     }
     const res = script.run({ this: doc });
     if (!res.success) return false;
-    if (!Field.IsField(res.result)) return false;
+    if (!Field.IsField(res.result, true)) return false;
     doc[key] = res.result;
     return true;
 }
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 08bc2ec4d..b0237d04d 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -19,12 +19,15 @@ export namespace Field {
             return field[ToScriptString]();
         }
     }
-    export function IsField(field: any): field is Field {
+    export function IsField(field: any): field is Field;
+    export function IsField(field: any, includeUndefined: true): field is Field | undefined;
+    export function IsField(field: any, includeUndefined: boolean = false): field is Field | undefined {
         return (typeof field === "string")
             || (typeof field === "number")
             || (typeof field === "boolean")
             || (field instanceof ObjectField)
-            || (field instanceof RefField);
+            || (field instanceof RefField)
+            || (includeUndefined && field === undefined);
     }
 }
 export type Field = number | string | boolean | ObjectField | RefField;
diff --git a/src/new_fields/URLField.ts b/src/new_fields/URLField.ts
index a6f8f1cc5..4a2841fb6 100644
--- a/src/new_fields/URLField.ts
+++ b/src/new_fields/URLField.ts
@@ -18,13 +18,18 @@ export abstract class URLField extends ObjectField {
     @serializable(url())
     readonly url: URL;
 
-    constructor(url: URL) {
+    constructor(url: string);
+    constructor(url: URL);
+    constructor(url: URL | string) {
         super();
+        if (typeof url === "string") {
+            url = new URL(url);
+        }
         this.url = url;
     }
 
     [ToScriptString]() {
-        return `new ${this.constructor.name}(new URL(${this.url.href}))`;
+        return `new ${this.constructor.name}("${this.url.href}")`;
     }
 
     [Copy](): this {
-- 
cgit v1.2.3-70-g09d2


From b1de1ab5757330950fe0f79749f098ca2a9f14e0 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 21 May 2019 21:13:58 -0400
Subject: fix to annotations/controls for videos?

---
 src/client/views/InkingCanvas.scss                                  | 6 ++++++
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +-
 2 files changed, 7 insertions(+), 1 deletion(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/InkingCanvas.scss b/src/client/views/InkingCanvas.scss
index 2c550051c..8277ff785 100644
--- a/src/client/views/InkingCanvas.scss
+++ b/src/client/views/InkingCanvas.scss
@@ -2,6 +2,12 @@
 
 .inkingCanvas {
     opacity:0.99;
+    .jsx-parser {
+        position:absolute;
+        z-index: -1;
+        width:100%;
+        height:100%;
+    }
 }
 .inkingCanvas-paths-ink, .inkingCanvas-paths-markers, .inkingCanvas-noSelect, .inkingCanvas-canSelect {
     position: absolute;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 7a0a02318..ebe1df4de 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -321,7 +321,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
     }
 
-    private childViews = () => [...this.views, <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />];
+    private childViews = () => [<CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />, ...this.views];
     render() {
         const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
         const easing = () => this.props.Document.panTransformType === "Ease";
-- 
cgit v1.2.3-70-g09d2


From d3ed10ff3ff9f2e7dec577d81a98318c941c9bdc Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 21 May 2019 22:49:23 -0400
Subject: fixed problems with link drawing to doc centers, and wraparound
 panning caused by zoomBasis

---
 .../CollectionFreeFormLinkView.tsx                 |  8 ++++----
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 22 ++++++++++++----------
 2 files changed, 16 insertions(+), 14 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 63d2f7642..f646c7bde 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -40,10 +40,10 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         let l = this.props.LinkDocs;
         let a = this.props.A;
         let b = this.props.B;
-        let x1 = NumCast(a.x) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.width) / 2);
-        let y1 = NumCast(a.y) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.height) / 2);
-        let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.width) / 2);
-        let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.height) / 2);
+        let x1 = NumCast(a.x) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.width) / NumCast(a.zoomBasis, 1) / 2);
+        let y1 = NumCast(a.y) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.height) / NumCast(a.zoomBasis, 1) / 2);
+        let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.width) / NumCast(b.zoomBasis, 1) / 2);
+        let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.height) / NumCast(b.zoomBasis, 1) / 2);
         return (
             <>
                 <line key={Utils.GenerateGuid()} className="collectionfreeformlinkview-linkLine"
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ebe1df4de..778071126 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -135,14 +135,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
             if (!this.isAnnotationOverlay) {
                 let minx = docs.length ? NumCast(docs[0].x) : 0;
-                let maxx = docs.length ? NumCast(docs[0].width) / NumCast(docs[0].zoomBasis) + minx : minx;
+                let maxx = docs.length ? NumCast(docs[0].width) / NumCast(docs[0].zoomBasis, 1) + minx : minx;
                 let miny = docs.length ? NumCast(docs[0].y) : 0;
-                let maxy = docs.length ? NumCast(docs[0].height) / NumCast(docs[0].zoomBasis) + miny : miny;
+                let maxy = docs.length ? NumCast(docs[0].height) / NumCast(docs[0].zoomBasis, 1) + miny : miny;
                 let ranges = docs.filter(doc => doc).reduce((range, doc) => {
                     let x = NumCast(doc.x);
-                    let xe = x + NumCast(doc.width) / NumCast(doc.zoomBasis);
+                    let xe = x + NumCast(doc.width) / NumCast(doc.zoomBasis, 1);
                     let y = NumCast(doc.y);
-                    let ye = y + NumCast(doc.height) / NumCast(doc.zoomBasis);
+                    let ye = y + NumCast(doc.height) / NumCast(doc.zoomBasis, 1);
                     return [[range[0][0] > x ? x : range[0][0], range[0][1] < xe ? xe : range[0][1]],
                     [range[1][0] > y ? y : range[1][0], range[1][1] < ye ? ye : range[1][1]]];
                 }, [[minx, maxx], [miny, maxy]]);
@@ -155,12 +155,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                     });
                 }
 
-                let panelwidth = this._pwidth / this.zoomScaling() / 2;
-                let panelheight = this._pheight / this.zoomScaling() / 2;
-                if (x - dx < ranges[0][0] - panelwidth) x = ranges[0][1] + panelwidth + dx;
-                if (x - dx > ranges[0][1] + panelwidth) x = ranges[0][0] - panelwidth + dx;
-                if (y - dy < ranges[1][0] - panelheight) y = ranges[1][1] + panelheight + dy;
-                if (y - dy > ranges[1][1] + panelheight) y = ranges[1][0] - panelheight + dy;
+                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;
             }
             this.setPan(x - dx, y - dy);
             this._lastX = e.pageX;
-- 
cgit v1.2.3-70-g09d2


From 54e41cfe7f3538e9cc4b1871a6692b38d21c8c46 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 22 May 2019 10:58:19 -0400
Subject: added shift+ctrl dragging of images natively.  changed z-index &
 ordering to allow videos to swho controls & ink. fixed dragging schema docs
 to copy

---
 src/client/documents/Documents.ts                  |  5 +++--
 src/client/views/InkingCanvas.scss                 |  1 -
 src/client/views/Main.scss                         |  2 +-
 .../views/collections/CollectionSchemaView.tsx     | 12 +++++------
 .../views/collections/CollectionVideoView.scss     |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.scss |  1 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../collections/collectionFreeForm/MarqueeView.tsx | 14 +++++++-----
 src/client/views/nodes/ImageBox.tsx                | 25 ++++++++++++----------
 9 files changed, 36 insertions(+), 28 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index b17544229..323534b2b 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -60,6 +60,7 @@ export interface DocumentOptions {
     borderRounding?: number;
     schemaColumns?: List<string>;
     dockingConfig?: string;
+    dbDoc?: Doc;
     // [key: string]: Opt<Field>;
 }
 const delegateKeys = ["x", "y", "width", "height", "panX", "panY"];
@@ -241,7 +242,7 @@ export namespace Docs {
         return CreateInstance(pdfProto, new PdfField(new URL(url)), options);
     }
 
-    export async function DBDocument(url: string, options: DocumentOptions = {}) {
+    export async function DBDocument(url: string, options: DocumentOptions = {}, columnOptions: DocumentOptions = {}) {
         let schemaName = options.title ? options.title : "-no schema-";
         let ctlog = await Gateway.Instance.GetSchema(url, schemaName);
         if (ctlog && ctlog.schemas) {
@@ -263,7 +264,7 @@ export namespace Docs {
                             new AttributeTransformationModel(atmod, AggregateFunction.None),
                             new AttributeTransformationModel(atmod, AggregateFunction.Count),
                             new AttributeTransformationModel(atmod, AggregateFunction.Count));
-                        docs.push(Docs.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! }));
+                        docs.push(Docs.HistogramDocument(histoOp, { ...columnOptions, width: 200, height: 200, title: attr.displayName! }));
                     }
                 }));
             });
diff --git a/src/client/views/InkingCanvas.scss b/src/client/views/InkingCanvas.scss
index 8277ff785..8bcaf41dd 100644
--- a/src/client/views/InkingCanvas.scss
+++ b/src/client/views/InkingCanvas.scss
@@ -4,7 +4,6 @@
     opacity:0.99;
     .jsx-parser {
         position:absolute;
-        z-index: -1;
         width:100%;
         height:100%;
     }
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 302c49c10..385298e18 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -40,7 +40,7 @@ p {
 
 ::-webkit-scrollbar {
     -webkit-appearance: none;
-    height: 5px;
+    height: 10px;
     width: 10px;
 }
 
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index bfd70ceae..b793447ff 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -76,9 +76,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     }
 
     onHeaderDrag = (columnName: string) => {
-        let dbDoc = Cast(this.props.Document.DBDoc, Doc);
-        if (dbDoc instanceof Doc) {
-            let columnDocs = DocListCast(dbDoc.data);
+        let schemaDoc = Cast(this.props.Document.schemaDoc, Doc);
+        if (schemaDoc instanceof Doc) {
+            let columnDocs = DocListCast(schemaDoc.data);
             if (columnDocs) {
                 let ddoc = columnDocs.find(doc => doc.title === columnName);
                 if (ddoc)
@@ -109,7 +109,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
         let reference = React.createRef<HTMLDivElement>();
         let onItemDown = (e: React.PointerEvent) =>
             (this.props.CollectionView.props.isSelected() ?
-                SetupDrag(reference, () => props.Document, this.props.moveDocument)(e) : undefined);
+                SetupDrag(reference, () => props.Document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e) : undefined);
         let applyToDoc = (doc: Doc, run: (args?: { [name: string]: any }) => any) => {
             const res = run({ this: doc });
             if (!res.success) return false;
@@ -251,10 +251,10 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
         let dbName = StrCast(this.props.Document.title);
         let res = await Gateway.Instance.PostSchema(csv, dbName);
         if (self.props.CollectionView.props.addDocument) {
-            let schemaDoc = await Docs.DBDocument("https://www.cs.brown.edu/" + dbName, { title: dbName });
+            let schemaDoc = await Docs.DBDocument("https://www.cs.brown.edu/" + dbName, { title: dbName }, { dbDoc: self.props.Document });
             if (schemaDoc) {
                 //self.props.CollectionView.props.addDocument(schemaDoc, false);
-                self.props.Document.DBDoc = schemaDoc;
+                self.props.Document.schemaDoc = schemaDoc;
             }
         }
     }
diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss
index db8b84832..9d2c23d3e 100644
--- a/src/client/views/collections/CollectionVideoView.scss
+++ b/src/client/views/collections/CollectionVideoView.scss
@@ -5,7 +5,7 @@
     position: inherit;
     top: 0;
     left:0;
- 
+    z-index: -1;
 }
 .collectionVideoView-time{
     color : white;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 063c9e2cf..8557a3db4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -63,6 +63,7 @@
     border-radius: $border-radius;
     box-sizing: border-box;
     position:absolute;
+    z-index: -1;
     .marqueeView {
         overflow: hidden;
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 778071126..8be0766eb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -323,7 +323,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
     }
 
-    private childViews = () => [<CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />, ...this.views];
+    private childViews = () => [...this.views, <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />];
     render() {
         const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
         const easing = () => this.props.Document.panTransformType === "Ease";
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index f0ccda140..6cdb117ff 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -99,19 +99,23 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                     let docList: Doc[] = [];
                     let groupAttr: string | number = "";
                     let rowProto = new Doc();
+                    rowProto.title = rowProto.Id;
                     rowProto.width = 200;
+                    rowProto.isPrototype = true;
                     for (let i = 1; i < ns.length - 1; i++) {
                         let values = ns[i].split("\t");
                         if (values.length === 1 && columns.length > 1) {
                             groupAttr = values[0];
                             continue;
                         }
-                        let doc = Doc.MakeDelegate(rowProto);
-                        columns.forEach((col, i) => doc[columns[i]] = (values.length > i ? ((values[i].indexOf(Number(values[i]).toString()) !== -1) ? Number(values[i]) : values[i]) : undefined));
+                        let docDataProto = Doc.MakeDelegate(rowProto);
+                        docDataProto.isPrototype = true;
+                        columns.forEach((col, i) => docDataProto[columns[i]] = (values.length > i ? ((values[i].indexOf(Number(values[i]).toString()) !== -1) ? Number(values[i]) : values[i]) : undefined));
                         if (groupAttr) {
-                            doc._group = groupAttr;
+                            docDataProto._group = groupAttr;
                         }
-                        doc.title = i.toString();
+                        docDataProto.title = i.toString();
+                        let doc = Doc.MakeDelegate(docDataProto);
                         doc.width = 200;
                         docList.push(doc);
                     }
@@ -269,7 +273,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                     d.page = -1;
                     return d;
                 });
-                let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" });
+                let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
                 // summary.proto!.thumbnail = new ImageField(new URL(dataUrl));
                 // summary.proto!.templates = new List<string>([Templates.ImageOverlay(Math.min(50, bounds.width), bounds.height * Math.min(50, bounds.width) / bounds.width, "thumbnail")]);
                 newCollection.proto!.summaryDoc = summary;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index d9d964ee9..71bda6fea 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -93,16 +93,19 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
     }
 
     onPointerDown = (e: React.PointerEvent): void => {
-        if (Date.now() - this._lastTap < 300) {
-            if (e.buttons === 1) {
-                this._downX = e.clientX;
-                this._downY = e.clientY;
-                document.removeEventListener("pointerup", this.onPointerUp);
-                document.addEventListener("pointerup", this.onPointerUp);
-            }
-        } else {
-            this._lastTap = Date.now();
-        }
+        if (e.shiftKey && e.ctrlKey)
+            e.stopPropagation();
+
+        // if (Date.now() - this._lastTap < 300) {
+        //     if (e.buttons === 1) {
+        //         this._downX = e.clientX;
+        //         this._downY = e.clientY;
+        //         document.removeEventListener("pointerup", this.onPointerUp);
+        //         document.addEventListener("pointerup", this.onPointerUp);
+        //     }
+        // } else {
+        //     this._lastTap = Date.now();
+        // }
     }
     @action
     onPointerUp = (e: PointerEvent): void => {
@@ -204,7 +207,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
         let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive";
         return (
             <div id={id} className={`imageBox-cont${interactive}`}
-                // onPointerDown={this.onPointerDown}
+                onPointerDown={this.onPointerDown}
                 onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
                 <img id={id}
                     key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
-- 
cgit v1.2.3-70-g09d2


From 01d172a91d9a140129d2dd938c3289d7a0d25f16 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 24 May 2019 14:10:32 -0400
Subject: cleaned up nativewidth/height for Image and Video and some PDF stuff.

---
 package.json                                       |  1 +
 src/client/documents/Documents.ts                  | 12 +++-
 .../CollectionFreeFormLinkView.tsx                 |  4 +-
 .../CollectionFreeFormLinksView.tsx                |  7 ++-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  5 +-
 src/client/views/nodes/ImageBox.tsx                | 15 +----
 src/client/views/nodes/PDFBox.scss                 |  4 ++
 src/client/views/nodes/PDFBox.tsx                  |  2 +-
 src/client/views/nodes/VideoBox.tsx                | 68 +++++++---------------
 9 files changed, 53 insertions(+), 65 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/package.json b/package.json
index aa4abb0a5..d5abc808e 100644
--- a/package.json
+++ b/package.json
@@ -167,6 +167,7 @@
         "react-split-pane": "^0.1.85",
         "react-table": "^6.9.2",
         "request": "^2.88.0",
+        "request-image-size": "^2.1.0",
         "request-promise": "^4.2.4",
         "serializr": "^1.5.1",
         "sharp": "^0.22.1",
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index ae190a989..1f4b76384 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -34,6 +34,7 @@ import { StrokeData, InkField } from "../../new_fields/InkField";
 import { dropActionType } from "../util/DragManager";
 import { DateField } from "../../new_fields/DateField";
 import { UndoManager } from "../util/UndoManager";
+var requestImageSize = require('request-image-size');
 
 export interface DocumentOptions {
     x?: number;
@@ -216,7 +217,16 @@ export namespace Docs {
     }
 
     export function ImageDocument(url: string, options: DocumentOptions = {}) {
-        return CreateInstance(imageProto, new ImageField(new URL(url)), options);
+        let inst = CreateInstance(imageProto, new ImageField(new URL(url)), options);
+        requestImageSize(url)
+            .then((size: any) => {
+                if (!inst.proto!.nativeWidth) {
+                    inst.proto!.nativeWidth = size.width;
+                }
+                inst.proto!.nativeHeight = Number(inst.proto!.nativeWidth!) * size.height / size.width;
+            })
+            .catch((err: any) => console.log(err));
+        return inst;
         // let doc = SetInstanceOptions(GetImagePrototype(), { ...options, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] },
         //     [new URL(url), ImageField]);
         // doc.SetText(KeyStore.Caption, "my caption...");
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index d6097b9a3..61de83f57 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -46,11 +46,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.height) / NumCast(b.zoomBasis, 1) / 2);
         return (
             <>
-                <line key={Utils.GenerateGuid()} className="collectionfreeformlinkview-linkLine"
+                <line key={"linkLine"} className="collectionfreeformlinkview-linkLine"
                     style={{ strokeWidth: `${35 * l.length / 2}` }}
                     x1={`${x1}`} y1={`${y1}`}
                     x2={`${x2}`} y2={`${y2}`} />
-                <circle key={Utils.GenerateGuid()} className="collectionfreeformlinkview-linkCircle"
+                <circle key={"linkCircle"} className="collectionfreeformlinkview-linkCircle"
                     cx={(x1 + x2) / 2} cy={(y1 + y2) / 2} r={8} onPointerDown={this.onPointerDown} />
             </>
         );
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index c5f7ad0d1..85556fb90 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -113,8 +113,11 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
             );
             return drawnPairs;
         }, [] as { a: Doc, b: Doc, l: Doc[] }[]);
-        return connections.map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l}
-            removeDocument={this.props.removeDocument} addDocument={this.props.addDocument} />);
+        return connections.map(c => {
+            let x = c.l.reduce((p, l) => p + l[Id], "");
+            return <CollectionFreeFormLinkView key={x} A={c.a} B={c.b} LinkDocs={c.l}
+                removeDocument={this.props.removeDocument} addDocument={this.props.addDocument} />;
+        });
     }
 
     render() {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8be0766eb..d1c4fb72d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -323,7 +323,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
     }
 
-    private childViews = () => [...this.views, <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />];
+    private childViews = () => [
+        <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />,
+        ...this.views
+    ];
     render() {
         const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
         const easing = () => this.props.Document.panTransformType === "Ease";
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index cc0dc8220..128f3c6f8 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -12,7 +12,7 @@ import React = require("react");
 import { createSchema, makeInterface, listSpec } from '../../../new_fields/Schema';
 import { DocComponent } from '../DocComponent';
 import { positionSchema } from './DocumentView';
-import { FieldValue, Cast, StrCast } from '../../../new_fields/Types';
+import { FieldValue, Cast, StrCast, PromiseValue } from '../../../new_fields/Types';
 import { ImageField } from '../../../new_fields/URLField';
 import { List } from '../../../new_fields/List';
 import { InkingControl } from '../InkingControl';
@@ -21,6 +21,7 @@ import { faImage } from '@fortawesome/free-solid-svg-icons';
 import { library } from '@fortawesome/fontawesome-svg-core';
 var path = require('path');
 
+
 library.add(faImage);
 
 
@@ -43,15 +44,6 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
     @observable private _isOpen: boolean = false;
     private dropDisposer?: DragManager.DragDropDisposer;
 
-    @action
-    onLoad = (target: any) => {
-        var h = this._imgRef.current!.naturalHeight;
-        var w = this._imgRef.current!.naturalWidth;
-        if (this._photoIndex === 0 && (this.props as any).id !== "isExpander" && (!this.Document.nativeWidth || !this.Document.nativeHeight || Math.abs(this.Document.nativeWidth / this.Document.nativeHeight - w / h) > 0.05)) {
-            Doc.SetOnPrototype(this.Document, "nativeHeight", FieldValue(this.Document.nativeWidth, 0) * h / w);
-            this.Document.height = FieldValue(this.Document.width, 0) * h / w;
-        }
-    }
 
 
     protected createDropTarget = (ele: HTMLDivElement) => {
@@ -217,8 +209,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
                     // style={{ objectFit: (this._photoIndex === 0 ? undefined : "contain") }}
                     width={nativeWidth}
                     ref={this._imgRef}
-                    onError={this.onError}
-                    onLoad={this.onLoad} />
+                    onError={this.onError} />
                 {paths.length > 1 ? this.dots(paths) : (null)}
                 {/* {this.lightbox(paths)} */}
             </div>);
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 3760e378a..449408a61 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -17,6 +17,10 @@
     z-index: 25;
     pointer-events: all;
 }
+.pdfBox-thumbnail {
+    position: absolute;
+    width: 100%;
+}
 .pdfButton {
     pointer-events: all;
     width: 100px;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 733bc920f..aa29a7170 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -375,7 +375,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
             // else if (this._largeRetryCount < 10) this._curSuffix = "_l";
             if (field instanceof ImageField) path = this.choosePath(field.url);
             // }
-            return <img key={path + (this._mediumRetryCount).toString()} style={{ position: "absolute", width: "100%" }} src={path} onError={this.onError} />;
+            return <img className="pdfBox-thumbnail" key={path + (this._mediumRetryCount).toString()} src={path} onError={this.onError} />;
         }
         return (null);
     }
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index ab57b4b04..35ecf12f6 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -10,10 +10,10 @@ import { makeInterface } from "../../../new_fields/Schema";
 import { pageSchema } from "./ImageBox";
 import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
 import { VideoField } from "../../../new_fields/URLField";
-import Measure from "react-measure";
 import "./VideoBox.scss";
 import { RouteStore } from "../../../server/RouteStore";
 import { DocServer } from "../../DocServer";
+import { actionFieldDecorator } from "mobx/lib/internal";
 
 type VideoDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
 const VideoDocument = makeInterface(positionSchema, pageSchema);
@@ -22,30 +22,23 @@ const VideoDocument = makeInterface(positionSchema, pageSchema);
 export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoDocument) {
     private _reactionDisposer?: IReactionDisposer;
     private _videoRef: HTMLVideoElement | null = null;
-    private _loaded: boolean = false;
     @observable _playTimer?: NodeJS.Timeout = undefined;
     @observable _fullScreen = false;
     @observable public Playing: boolean = false;
     public static LayoutString() { return FieldView.LayoutString(VideoBox); }
 
-    public get player(): HTMLVideoElement | undefined {
-        if (this._videoRef) {
-            return this._videoRef;
-        }
+    public get player(): HTMLVideoElement | null {
+        return this._videoRef;
     }
-    @action
-    setScaling = (r: any) => {
-        if (this._loaded) {
-            // bcz: the nativeHeight should really be set when the document is imported.
-            var nativeWidth = FieldValue(this.Document.nativeWidth, 0);
-            var nativeHeight = FieldValue(this.Document.nativeHeight, 0);
-            var newNativeHeight = nativeWidth * r.offset.height / r.offset.width;
-            if (!nativeHeight && newNativeHeight !== nativeHeight && !isNaN(newNativeHeight)) {
-                this.Document.height = newNativeHeight / nativeWidth * FieldValue(this.Document.width, 0);
-                this.Document.nativeHeight = newNativeHeight;
-            }
-        } else {
-            this._loaded = true;
+
+    videoLoad = () => {
+        let aspect = this.player!.videoWidth / this.player!.videoHeight;
+        var nativeWidth = FieldValue(this.Document.nativeWidth, 0);
+        var nativeHeight = FieldValue(this.Document.nativeHeight, 0);
+        if (!nativeWidth || !nativeHeight) {
+            if (!this.Document.nativeWidth) this.Document.nativeWidth = this.player!.videoWidth;
+            this.Document.nativeHeight = this.Document.nativeWidth / aspect;
+            this.Document.height = FieldValue(this.Document.width, 0) / aspect;
         }
     }
 
@@ -88,22 +81,12 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         if (vref) {
             vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen);
             if (this._reactionDisposer) this._reactionDisposer();
-            this._reactionDisposer = reaction(() => this.props.Document.curPage, () => {
-                if (!this.Playing) {
-                    vref.currentTime = NumCast(this.props.Document.curPage, 0);
-                }
-            }, { fireImmediately: true });
+            this._reactionDisposer = reaction(() => this.props.Document.curPage, () =>
+                !this.Playing && (vref.currentTime = this.Document.curPage || 0)
+                , { fireImmediately: true });
         }
     }
 
-    videoContent(path: string) {
-        let style = "videoBox-cont" + (this._fullScreen ? "-fullScreen" : "");
-        return <video className={`${style}`} ref={this.setVideoRef} onPointerDown={this.onPointerDown}>
-            <source src={path} type="video/mp4" />
-            Not supported.
-        </video>;
-    }
-
     getMp4ForVideo(videoId: string = "JN5beCVArMs") {
         return new Promise(async (resolve, reject) => {
             const videoInfoRequestConfig = {
@@ -111,7 +94,6 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
                     connection: 'keep-alive',
                     "user-agent": 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:43.0) Gecko/20100101 Firefox/46.0',
                 },
-
             };
             try {
                 let responseSchema: any = {};
@@ -145,9 +127,6 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
 
     render() {
         let field = Cast(this.Document[this.props.fieldKey], VideoField);
-        if (!field) {
-            return <div>Loading</div>;
-        }
 
         // this.getMp4ForVideo().then((mp4) => {
         //     console.log(mp4);
@@ -155,15 +134,12 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         //     console.log("")
         // });
         // //
-        let content = this.videoContent(field.url.href);
-        return NumCast(this.props.Document.nativeHeight) ?
-            content :
-            <Measure offset onResize={this.setScaling}>
-                {({ measureRef }) =>
-                    <div style={{ width: "100%", height: "auto" }} ref={measureRef}>
-                        {content}
-                    </div>
-                }
-            </Measure>;
+
+        let style = "videoBox-cont" + (this._fullScreen ? "-fullScreen" : "");
+        return !field ? <div>Loading</div> :
+            <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} onPointerDown={this.onPointerDown}>
+                <source src={field.url.href} type="video/mp4" />
+                Not supported.
+            </video>;
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 37e16bb59a38c000ba80312a0661e1f54c93f3c6 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 30 May 2019 10:51:00 -0400
Subject: cleaned up event handling for documentviews.   fixed context menu
 closing.

---
 src/client/documents/Documents.ts                  |   2 +-
 src/client/views/ContextMenu.tsx                   |   7 +-
 src/client/views/ContextMenuItem.tsx               |  34 +++--
 src/client/views/DocumentDecorations.tsx           |   2 +-
 src/client/views/MainView.tsx                      |   1 -
 .../views/collections/CollectionBaseView.tsx       |   1 +
 .../views/collections/CollectionDockingView.tsx    |   1 -
 .../views/collections/CollectionSchemaView.tsx     |   1 -
 .../views/collections/CollectionStackingView.tsx   |  28 +----
 .../views/collections/CollectionTreeView.tsx       |  22 ++--
 .../CollectionFreeFormLinksView.tsx                |   1 -
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   1 -
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 119 ------------------
 src/client/views/nodes/DocumentView.tsx            | 140 ++++++++++++++++-----
 src/client/views/nodes/FieldView.tsx               |   1 -
 15 files changed, 153 insertions(+), 208 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index fffada459..ab61b915c 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -295,7 +295,7 @@ export namespace Docs {
         return CreateInstance(webProto, new HtmlField(html), options);
     }
     export function KVPDocument(document: Doc, options: DocumentOptions = {}) {
-        return CreateInstance(kvpProto, document, options);
+        return CreateInstance(kvpProto, document, { title: document.title + ".kvp", ...options });
     }
     export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, makePrototype: boolean = true) {
         if (!makePrototype) {
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 542d259d6..da374455e 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -81,6 +81,11 @@ export class ContextMenu extends React.Component {
         return false;
     }
 
+    @action
+    closeMenu = () => {
+        this.clearItems();
+    }
+
     render() {
         let style = this._yRelativeToTop ? { left: this._pageX, top: this._pageY, display: this._display } :
             { left: this._pageX, bottom: this._pageY, display: this._display };
@@ -95,7 +100,7 @@ export class ContextMenu extends React.Component {
                     <input className="contextMenu-item contextMenu-description" type="text" placeholder="Search . . ." value={this._searchString} onChange={this.onChange} />
                 </span>
                 {this._items.filter(prop => prop.description.toLowerCase().indexOf(this._searchString.toLowerCase()) !== -1).
-                    map(prop => <ContextMenuItem {...prop} key={prop.description} />)}
+                    map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.closeMenu} />)}
             </div>
         );
     }
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index b9239dac9..63347e076 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -8,11 +8,13 @@ export interface OriginalMenuProps {
     description: string;
     event: (e: React.MouseEvent<HTMLDivElement>) => void;
     icon?: IconProp; //maybe should be optional (icon?)
+    closeMenu?: () => void;
 }
 
 export interface SubmenuProps {
     description: string;
     subitems: ContextMenuProps[];
+    closeMenu?: () => void;
 }
 
 export interface ContextMenuItemProps {
@@ -32,30 +34,36 @@ export class ContextMenuItem extends React.Component<ContextMenuProps> {
         }
     }
 
+    handleEvent = (e: React.MouseEvent<HTMLDivElement>) => {
+        if ("event" in this.props) {
+            this.props.event(e);
+            this.props.closeMenu && this.props.closeMenu();
+        }
+    }
+
     render() {
         if ("event" in this.props) {
             return (
-                <div className="contextMenu-item" onClick={this.props.event}>
+                <div className="contextMenu-item" onClick={this.handleEvent}>
                     <span className="icon-background">
                         {this.props.icon ? <FontAwesomeIcon icon={this.props.icon} size="sm" /> : <FontAwesomeIcon icon="circle" size="sm" />}
                     </span>
-                    <div className="contextMenu-description"> {this.props.description}</div>
+                    <div className="contextMenu-description">
+                        {this.props.description}
+                    </div>
                 </div>
             );
         }
         else {
-            let submenu = null;
-            if (this.overItem) {
-                submenu = (
-                    <div className="subMenu-cont" style={{ marginLeft: "100.5%", left: "0px" }}>
-                        {this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} />)}
-                    </div>
-                );
-            }
+            let submenu = !this.overItem ? (null) :
+                <div className="subMenu-cont" style={{ marginLeft: "100.5%", left: "0px" }}>
+                    {this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)}
+                </div>;
             return (
-                <div className="contextMenu-item" onMouseEnter={action(() => { this.overItem = true; })}
-                    onMouseLeave={action(() => this.overItem = false)}>
-                    <div className="contextMenu-description"> {this.props.description}</div>
+                <div className="contextMenu-item" onMouseEnter={action(() => { this.overItem = true; })} onMouseLeave={action(() => this.overItem = false)}>
+                    <div className="contextMenu-description">
+                        {this.props.description}
+                    </div>
                     {submenu}
                 </div>
             );
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 319e91337..e06fd6119 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -251,7 +251,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             }
             if (!this._removeIcon) {
                 if (selectedDocs.length === 1) {
-                    this.getIconDoc(selectedDocs[0]).then(icon => selectedDocs[0].props.toggleMinimized());
+                    this.getIconDoc(selectedDocs[0]).then(icon => selectedDocs[0].toggleMinimized());
                 } else if (Math.abs(e.pageX - this._downX) < Utils.DRAG_THRESHOLD &&
                     Math.abs(e.pageY - this._downY) < Utils.DRAG_THRESHOLD) {
                     let docViews = SelectionManager.ViewsSortedVertically();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 438607157..74dafadc4 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -184,7 +184,6 @@ export class MainView extends React.Component {
         let mainCont = this.mainContainer;
         let content = !mainCont ? (null) :
             <DocumentView Document={mainCont}
-                toggleMinimized={emptyFunction}
                 addDocument={undefined}
                 addDocTab={emptyFunction}
                 removeDocument={undefined}
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 2bccde241..e89b8be0f 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -128,6 +128,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
 
     @action.bound
     removeDocument(doc: Doc): boolean {
+        SelectionManager.DeselectAll();
         const props = this.props;
         //TODO This won't create the field if it doesn't already exist
         const value = Cast(props.Document[props.fieldKey], listSpec(Doc), []);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 180a8be46..b6076b5f7 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -466,7 +466,6 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             <div className="collectionDockingView-content" ref={this._mainCont}
                 style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px) scale(${this.scaleToFitMultiplier}, ${this.scaleToFitMultiplier})` }}>
                 <DocumentView key={this._document[Id]} Document={this._document}
-                    toggleMinimized={emptyFunction}
                     bringToFront={emptyFunction}
                     addDocument={undefined}
                     removeDocument={undefined}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 1af13765b..b17d5b865 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -412,7 +412,6 @@ class CollectionSchemaPreview extends React.Component<CollectionSchemaPreviewPro
             {!this.props.Document || !this.props.width ? (null) : (
                 <div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.centeringOffset}px, 0px)` }}>
                     <DocumentView Document={this.props.Document} isTopMost={false} selectOnLoad={false}
-                        toggleMinimized={emptyFunction}
                         addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
                         ScreenToLocalTransform={this.getTransform}
                         ContentScaling={this.contentScaling}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 943e8dd5f..bf246d4ec 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -14,15 +14,11 @@ import { Id } from "../../../new_fields/FieldSymbols";
 
 @observer
 export class CollectionStackingView extends CollectionSubView(doc => doc) {
-
+    _masonryGridRef: HTMLDivElement | null = null;
     get gridGap() { return 10; }
     get gridSize() { return 20; }
     get itemWidth() { return NumCast(this.props.Document.itemWidth, 250); }
 
-    constructor(props: SubCollectionViewProps) {
-        super(props);
-    }
-
     @action
     moveDocument = (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean): boolean => {
         this.props.removeDocument(doc);
@@ -31,13 +27,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
     getDocTransform(doc: Doc, dref: HTMLDivElement) {
         let { scale, translateX, translateY } = Utils.GetScreenTransform(dref);
-        let outerXf = Utils.GetScreenTransform(this.masonryGridRef!);
+        let outerXf = Utils.GetScreenTransform(this._masonryGridRef!);
         let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
         return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]).scale(NumCast(doc.width, 1) / this.itemWidth);
     }
-    masonryGridRef: HTMLDivElement | null = null;
     createRef = (ele: HTMLDivElement | null) => {
-        this.masonryGridRef = ele;
+        this._masonryGridRef = ele;
         this.createDropTarget(ele!);
     }
     @computed
@@ -77,32 +72,17 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                     parentActive={this.props.active}
                     addDocTab={this.props.addDocTab}
                     bringToFront={emptyFunction}
-                    toggleMinimized={emptyFunction}
                     whenActiveChanged={this.props.whenActiveChanged} />
             </div>);
         })
     }
-    onClick = (e: React.MouseEvent) => {
-        let hitBackground = (e.target as any).className === "collectionStackingView-masonryGrid" ||
-            (e.target as any).className === "collectionStackingView";
-        if (this.props.active()) {
-            if (!hitBackground)
-                e.stopPropagation();
-        }
-        if (hitBackground) {
-            if (!this.props.active() && this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.active()) {
-                e.stopPropagation();
-            }
-            this.props.select(false);
-        }
-    }
     render() {
         let leftMargin = 20;
         let topMargin = 20;
         let itemCols = Math.ceil(this.itemWidth / (this.gridSize + this.gridGap));
         let cells = Math.floor((this.props.PanelWidth() - leftMargin) / (itemCols * (this.gridSize + this.gridGap)));
         return (
-            <div className="collectionStackingView" ref={this.createRef} onClick={this.onClick} onWheel={(e: React.WheelEvent) => e.stopPropagation()}>
+            <div className="collectionStackingView" ref={this.createRef} onWheel={(e: React.WheelEvent) => e.stopPropagation()}>
                 <div className="collectionStackingView-masonryGrid"
                     style={{
                         padding: `${topMargin}px 0px 0px ${leftMargin}px`,
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 8ad495762..85aab96d3 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -26,6 +26,7 @@ export interface TreeViewProps {
     deleteDoc: (doc: Doc) => void;
     moveDocument: DragManager.MoveFunction;
     dropAction: "alias" | "copy" | undefined;
+    addDocTab: (doc: Doc, where: string) => void;
 }
 
 export enum BulletType {
@@ -140,19 +141,12 @@ class TreeView extends React.Component<TreeViewProps> {
     onWorkspaceContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped() && this.props.document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
             ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.props.document)) });
-            ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.document) });
-            ContextMenu.Instance.addItem({
-                description: "Open Fields", event: () => CollectionDockingView.Instance.AddRightSplit(Docs.KVPDocument(this.props.document,
-                    { title: this.props.document.title + ".kvp", width: 300, height: 300 }))
-            });
+            ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, "onRight") });
+            ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.KVPDocument(this.props.document, { width: 300, height: 300 }), "onRight") });
             if (DocumentManager.Instance.getDocumentViews(this.props.document).length) {
                 ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.props.document).map(view => view.props.focus(this.props.document)) });
             }
-            ContextMenu.Instance.addItem({
-                description: "Delete", event: undoBatch(() => {
-                    this.props.deleteDoc(this.props.document);
-                })
-            });
+            ContextMenu.Instance.addItem({ description: "Delete", event: undoBatch(() => this.props.deleteDoc(this.props.document)) });
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
             e.stopPropagation();
         }
@@ -180,7 +174,7 @@ class TreeView extends React.Component<TreeViewProps> {
                         {(key === "data") ? (null) :
                             <span className="collectionTreeView-keyHeader" style={{ display: "block", marginTop: "7px" }} key={key}>{key}</span>}
                         <div style={{ display: "block", marginTop: `${spacing}px` }}>
-                            {TreeView.GetChildElements(doc instanceof Doc ? [doc] : docList, key !== "data", (doc: Doc) => this.remove(doc, key), this.move, this.props.dropAction)}
+                            {TreeView.GetChildElements(doc instanceof Doc ? [doc] : docList, key !== "data", (doc: Doc) => this.remove(doc, key), this.move, this.props.dropAction, this.props.addDocTab)}
                         </div>
                     </ul >);
                 } else {
@@ -197,9 +191,9 @@ class TreeView extends React.Component<TreeViewProps> {
             </li>
         </div>;
     }
-    public static GetChildElements(docs: Doc[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) {
+    public static GetChildElements(docs: Doc[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType, addDocTab: (doc: Doc, where: string) => void) {
         return docs.filter(child => !child.excludeFromLibrary && (allowMinimized || !child.isMinimized)).map(child =>
-            <TreeView document={child} key={child[Id]} deleteDoc={remove} moveDocument={move} dropAction={dropAction} />);
+            <TreeView document={child} key={child[Id]} deleteDoc={remove} moveDocument={move} dropAction={dropAction} addDocTab={addDocTab} />);
     }
 }
 
@@ -225,7 +219,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
         if (!this.childDocs) {
             return (null);
         }
-        let childElements = TreeView.GetChildElements(this.childDocs, false, this.remove, this.props.moveDocument, dropAction);
+        let childElements = TreeView.GetChildElements(this.childDocs, false, this.remove, this.props.moveDocument, dropAction, this.props.addDocTab);
 
         return (
             <div id="body" className="collectionTreeView-dropTarget"
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 6207dc8af..a43c5f241 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -95,7 +95,6 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
 
     @computed
     get uniqueConnections() {
-        console.log("-----");
         let connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => {
             let srcViews = this.documentAnchors(connection.a);
             let targetViews = this.documentAnchors(connection.b);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d1c4fb72d..1a1614ac7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -279,7 +279,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     getDocumentViewProps(document: Doc): DocumentViewProps {
         return {
             Document: document,
-            toggleMinimized: emptyFunction,
             addDocument: this.props.addDocument,
             removeDocument: this.props.removeDocument,
             moveDocument: this.props.moveDocument,
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 5d11e051d..411e8c059 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -30,9 +30,6 @@ const FreeformDocument = makeInterface(schema, positionSchema);
 @observer
 export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, FreeformDocument>(FreeformDocument) {
     private _mainCont = React.createRef<HTMLDivElement>();
-    private _downX: number = 0;
-    private _downY: number = 0;
-    private _doubleTap = false;
     _bringToFrontDisposer?: IReactionDisposer;
 
     @computed get transform() {
@@ -62,7 +59,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     contentScaling = () => this.nativeWidth > 0 ? this.width / this.nativeWidth : 1;
     panelWidth = () => this.props.PanelWidth();
     panelHeight = () => this.props.PanelHeight();
-    toggleMinimized = async () => this.toggleIcon(await DocListCastAsync(this.props.Document.maximizedDocs));
     getTransform = (): Transform => this.props.ScreenToLocalTransform()
         .translate(-this.X, -this.Y)
         .scale(1 / this.contentScaling()).scale(1 / this.zoom)
@@ -70,7 +66,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     @computed
     get docView() {
         return <DocumentView {...OmitKeys(this.props, ['zoomFade']).omit}
-            toggleMinimized={this.toggleMinimized}
             ContentScaling={this.contentScaling}
             ScreenToLocalTransform={this.getTransform}
             PanelWidth={this.panelWidth}
@@ -124,118 +119,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         },
             2);
     }
-    @action
-    public toggleIcon = async (maximizedDocs: Doc[] | undefined): Promise<void> => {
-        SelectionManager.DeselectAll();
-        let isMinimized: boolean | undefined;
-        let minimizedDoc: Doc | undefined = this.props.Document;
-        if (!maximizedDocs) {
-            minimizedDoc = await Cast(this.props.Document.minimizedDoc, Doc);
-            if (minimizedDoc) maximizedDocs = await DocListCastAsync(minimizedDoc.maximizedDocs);
-        }
-        if (minimizedDoc && maximizedDocs) {
-            let minimizedTarget = minimizedDoc;
-            if (!CollectionFreeFormDocumentView._undoBatch) {
-                CollectionFreeFormDocumentView._undoBatch = UndoManager.StartBatch("iconAnimating");
-            }
-            maximizedDocs.map(d => Doc.GetProto(d)).map(maximizedDoc => {
-                let iconAnimating = Cast(maximizedDoc.isIconAnimating, List);
-                if (!iconAnimating || (Date.now() - iconAnimating[2] > 1000)) {
-                    if (isMinimized === undefined) {
-                        isMinimized = BoolCast(maximizedDoc.isMinimized, false);
-                    }
-                    let minx = NumCast(minimizedTarget.x, undefined) + NumCast(minimizedTarget.width, undefined) / 2;
-                    let miny = NumCast(minimizedTarget.y, undefined) + NumCast(minimizedTarget.height, undefined) / 2;
-                    if (minx !== undefined && miny !== undefined) {
-                        let scrpt = this.props.ScreenToLocalTransform().inverse().transformPoint(minx, miny);
-                        maximizedDoc.willMaximize = isMinimized;
-                        maximizedDoc.isMinimized = false;
-                        maximizedDoc.isIconAnimating = new List<number>([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0]);
-                    }
-                }
-            });
-            setTimeout(() => {
-                CollectionFreeFormDocumentView._undoBatch && CollectionFreeFormDocumentView._undoBatch.end();
-                CollectionFreeFormDocumentView._undoBatch = undefined;
-            }, 500);
-        }
-    }
-    static _undoBatch?: UndoManager.Batch = undefined;
-    onPointerDown = (e: React.PointerEvent): void => {
-        this._downX = e.clientX;
-        this._downY = e.clientY;
-        this._doubleTap = false;
-        if (e.button === 0 && e.altKey) {
-            e.stopPropagation(); // prevents panning from happening on collection if shift is pressed after a document drag has started
-        } // allow pointer down to go through otherwise so that marquees can be drawn starting over a document 
-
-        if (e.button === 0) {
-            e.preventDefault();  // prevents Firefox from dragging images (we want to do that ourself)
-        }
-    }
-    onClick = async (e: React.MouseEvent) => {
-        e.stopPropagation();
-        let altKey = e.altKey;
-        let ctrlKey = e.ctrlKey;
-        if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
-            Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
-            let isExpander = (e.target as any).id === "isExpander";
-            if (BoolCast(this.props.Document.isButton, false) || isExpander) {
-                SelectionManager.DeselectAll();
-                let subBulletDocs = await DocListCastAsync(this.props.Document.subBulletDocs);
-                let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs);
-                let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs);
-                let linkedToDocs = await DocListCastAsync(this.props.Document.linkedToDocs, []);
-                let linkedFromDocs = await DocListCastAsync(this.props.Document.linkedFromDocs, []);
-                let expandedDocs: Doc[] = [];
-                expandedDocs = subBulletDocs ? [...subBulletDocs, ...expandedDocs] : expandedDocs;
-                expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs;
-                expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs;
-                // let expandedDocs = [...(subBulletDocs ? subBulletDocs : []), ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : []),];
-                if (expandedDocs.length) {   // bcz: need a better way to associate behaviors with click events on widget-documents
-                    let expandedProtoDocs = expandedDocs.map(doc => Doc.GetProto(doc));
-                    let maxLocation = StrCast(this.props.Document.maximizeLocation, "inPlace");
-                    let getDispDoc = (target: Doc) => Object.getOwnPropertyNames(target).indexOf("isPrototype") === -1 ? target : Doc.MakeDelegate(target);
-                    if (altKey) {
-                        maxLocation = this.props.Document.maximizeLocation = (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace");
-                        if (!maxLocation || maxLocation === "inPlace") {
-                            let hadView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedProtoDocs[0], this.props.ContainingCollectionView);
-                            let wasMinimized = !hadView && expandedDocs.reduce((min, d) => !min && !BoolCast(d.IsMinimized, false), false);
-                            expandedDocs.forEach(maxDoc => Doc.GetProto(maxDoc).isMinimized = false);
-                            let hasView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedProtoDocs[0], this.props.ContainingCollectionView);
-                            if (!hasView) {
-                                this.props.addDocument && expandedDocs.forEach(async maxDoc => this.props.addDocument!(getDispDoc(maxDoc), false));
-                            }
-                            expandedProtoDocs.forEach(maxDoc => maxDoc.isMinimized = wasMinimized);
-                        }
-                    }
-                    if (maxLocation && maxLocation !== "inPlace") {
-                        let dataDocs = DocListCast(CollectionDockingView.Instance.props.Document.data);
-                        if (dataDocs) {
-                            expandedDocs.forEach(maxDoc =>
-                                (!CollectionDockingView.Instance.CloseRightSplit(Doc.GetProto(maxDoc)) &&
-                                    this.props.addDocTab(getDispDoc(maxDoc), maxLocation)));
-                        }
-                    } else {
-                        this.toggleIcon(expandedProtoDocs);
-                    }
-                }
-                else if (linkedToDocs.length || linkedFromDocs.length) {
-                    let linkedFwdDocs = [
-                        linkedToDocs.length ? linkedToDocs[0].linkedTo as Doc : linkedFromDocs.length ? linkedFromDocs[0].linkedFrom as Doc : expandedDocs[0],
-                        linkedFromDocs.length ? linkedFromDocs[0].linkedFrom as Doc : linkedToDocs.length ? linkedToDocs[0].linkedTo as Doc : expandedDocs[0]];
-
-                    let linkedFwdPage = [
-                        linkedToDocs.length ? NumCast(linkedToDocs[0].linkedToPage, undefined) : linkedFromDocs.length ? NumCast(linkedFromDocs[0].linkedFromPage, undefined) : undefined,
-                        linkedFromDocs.length ? NumCast(linkedFromDocs[0].linkedFromPage, undefined) : linkedToDocs.length ? NumCast(linkedToDocs[0].linkedToPage, undefined) : undefined];
-                    if (!linkedFwdDocs.some(l => l instanceof Promise)) {
-                        let maxLocation = StrCast(linkedFwdDocs[altKey ? 1 : 0].maximizeLocation, "inTab");
-                        DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, document => this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey ? 1 : 0]);
-                    }
-                }
-            }
-        }
-    }
 
     borderRounding = () => {
         let br = NumCast(this.props.Document.borderRounding);
@@ -261,8 +144,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
 
         return (
             <div className="collectionFreeFormDocumentView-container" ref={this._mainCont}
-                onPointerDown={this.onPointerDown}
-                onClick={this.onClick}
                 style={{
                     opacity: zoomFade,
                     borderRadius: `${this.borderRounding()}px`,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 36c14fbf2..a16a52ac6 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -2,11 +2,11 @@ import { library } from '@fortawesome/fontawesome-svg-core';
 import { faAlignCenter, faCaretSquareRight, faCompressArrowsAlt, faExpandArrowsAlt, faLayerGroup, faSquare, faTrash, faConciergeBell, faFolder, faMapPin, faLink, faFingerprint, faCrosshairs, faDesktop } from '@fortawesome/free-solid-svg-icons';
 import { action, computed, IReactionDisposer, reaction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
+import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync } from "../../../new_fields/Doc";
 import { List } from "../../../new_fields/List";
 import { ObjectField } from "../../../new_fields/ObjectField";
 import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { BoolCast, Cast, FieldValue, StrCast } from "../../../new_fields/Types";
+import { BoolCast, Cast, FieldValue, StrCast, NumCast } from "../../../new_fields/Types";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { emptyFunction, Utils } from "../../../Utils";
 import { DocServer } from "../../DocServer";
@@ -16,7 +16,7 @@ import { DragManager, dropActionType } from "../../util/DragManager";
 import { SearchUtil } from "../../util/SearchUtil";
 import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
-import { undoBatch } from "../../util/UndoManager";
+import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
 import { CollectionPDFView } from "../collections/CollectionPDFView";
 import { CollectionVideoView } from "../collections/CollectionVideoView";
@@ -72,9 +72,9 @@ export interface DocumentViewProps {
     selectOnLoad: boolean;
     parentActive: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
-    toggleMinimized: () => void;
     bringToFront: (doc: Doc) => void;
     addDocTab: (doc: Doc, where: string) => void;
+    whenClicked?: () => void;
 }
 
 const schema = createSchema({
@@ -103,6 +103,9 @@ const Document = makeInterface(schema);
 export class DocumentView extends DocComponent<DocumentViewProps, Document>(Document) {
     private _downX: number = 0;
     private _downY: number = 0;
+    private _lastTap: number = 0;
+    private _doubleTap = false;
+    private _hitExpander = false;
     private _mainCont = React.createRef<HTMLDivElement>();
     private _dropDisposer?: DragManager.DragDropDisposer;
 
@@ -182,23 +185,112 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             });
         }
     }
+    toggleMinimized = async () => {
+        let minimizedDoc = await Cast(this.props.Document.minimizedDoc, Doc);
+        if (minimizedDoc) {
+            let scrpt = this.props.ScreenToLocalTransform().inverse().transformPoint(
+                NumCast(minimizedDoc.x) - NumCast(this.Document.x), NumCast(minimizedDoc.y) - NumCast(this.Document.y));
+            this.collapseToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs));
+        }
+    }
 
-    _doubleTap = false;
-    onClick = (e: React.MouseEvent): void => {
+    static _undoBatch?: UndoManager.Batch = undefined;
+    @action
+    public collapseToPoint = async (scrpt: number[], expandedDocs: Doc[] | undefined): Promise<void> => {
+        SelectionManager.DeselectAll();
+        if (expandedDocs) {
+            if (!DocumentView._undoBatch) {
+                DocumentView._undoBatch = UndoManager.StartBatch("iconAnimating");
+            }
+            let isMinimized: boolean | undefined;
+            expandedDocs.map(d => Doc.GetProto(d)).map(maximizedDoc => {
+                let iconAnimating = Cast(maximizedDoc.isIconAnimating, List);
+                if (!iconAnimating || (Date.now() - iconAnimating[2] > 1000)) {
+                    if (isMinimized === undefined) {
+                        isMinimized = BoolCast(maximizedDoc.isMinimized, false);
+                    }
+                    maximizedDoc.willMaximize = isMinimized;
+                    maximizedDoc.isMinimized = false;
+                    maximizedDoc.isIconAnimating = new List<number>([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0]);
+                }
+            });
+            setTimeout(() => {
+                DocumentView._undoBatch && DocumentView._undoBatch.end();
+                DocumentView._undoBatch = undefined;
+            }, 500);
+        }
+    }
+    onClick = async (e: React.MouseEvent) => {
+        e.stopPropagation();
+        let altKey = e.altKey;
+        let ctrlKey = e.ctrlKey;
         if (this._doubleTap && !this.props.isTopMost) {
             this.props.addDocTab(this.props.Document, "inTab");
             SelectionManager.DeselectAll();
             this.props.Document.libraryBrush = false;
-            e.stopPropagation();
         }
         else if (CurrentUserUtils.MainDocId !== this.props.Document[Id] &&
             (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
                 Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
             SelectionManager.SelectDoc(this, e.ctrlKey);
+            let isExpander = (e.target as any).id === "isExpander";
+            if (BoolCast(this.props.Document.isButton, false) || isExpander) {
+                SelectionManager.DeselectAll();
+                let subBulletDocs = await DocListCastAsync(this.props.Document.subBulletDocs);
+                let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs);
+                let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs);
+                let linkedToDocs = await DocListCastAsync(this.props.Document.linkedToDocs, []);
+                let linkedFromDocs = await DocListCastAsync(this.props.Document.linkedFromDocs, []);
+                let expandedDocs: Doc[] = [];
+                expandedDocs = subBulletDocs ? [...subBulletDocs, ...expandedDocs] : expandedDocs;
+                expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs;
+                expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs;
+                // let expandedDocs = [...(subBulletDocs ? subBulletDocs : []), ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : []),];
+                if (expandedDocs.length) {   // bcz: need a better way to associate behaviors with click events on widget-documents
+                    let expandedProtoDocs = expandedDocs.map(doc => Doc.GetProto(doc));
+                    let maxLocation = StrCast(this.props.Document.maximizeLocation, "inPlace");
+                    let getDispDoc = (target: Doc) => Object.getOwnPropertyNames(target).indexOf("isPrototype") === -1 ? target : Doc.MakeDelegate(target);
+                    if (altKey) {
+                        maxLocation = this.props.Document.maximizeLocation = (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace");
+                        if (!maxLocation || maxLocation === "inPlace") {
+                            let hadView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedProtoDocs[0], this.props.ContainingCollectionView);
+                            let wasMinimized = !hadView && expandedDocs.reduce((min, d) => !min && !BoolCast(d.IsMinimized, false), false);
+                            expandedDocs.forEach(maxDoc => Doc.GetProto(maxDoc).isMinimized = false);
+                            let hasView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedProtoDocs[0], this.props.ContainingCollectionView);
+                            if (!hasView) {
+                                this.props.addDocument && expandedDocs.forEach(async maxDoc => this.props.addDocument!(getDispDoc(maxDoc), false));
+                            }
+                            expandedProtoDocs.forEach(maxDoc => maxDoc.isMinimized = wasMinimized);
+                        }
+                    }
+                    if (maxLocation && maxLocation !== "inPlace") {
+                        let dataDocs = DocListCast(CollectionDockingView.Instance.props.Document.data);
+                        if (dataDocs) {
+                            expandedDocs.forEach(maxDoc =>
+                                (!CollectionDockingView.Instance.CloseRightSplit(Doc.GetProto(maxDoc)) &&
+                                    this.props.addDocTab(getDispDoc(maxDoc), maxLocation)));
+                        }
+                    } else {
+                        let scrpt = this.props.ScreenToLocalTransform().inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2);
+                        this.collapseToPoint(scrpt, expandedProtoDocs);
+                    }
+                }
+                else if (linkedToDocs.length || linkedFromDocs.length) {
+                    let linkedFwdDocs = [
+                        linkedToDocs.length ? linkedToDocs[0].linkedTo as Doc : linkedFromDocs.length ? linkedFromDocs[0].linkedFrom as Doc : expandedDocs[0],
+                        linkedFromDocs.length ? linkedFromDocs[0].linkedFrom as Doc : linkedToDocs.length ? linkedToDocs[0].linkedTo as Doc : expandedDocs[0]];
+
+                    let linkedFwdPage = [
+                        linkedToDocs.length ? NumCast(linkedToDocs[0].linkedToPage, undefined) : linkedFromDocs.length ? NumCast(linkedFromDocs[0].linkedFromPage, undefined) : undefined,
+                        linkedFromDocs.length ? NumCast(linkedFromDocs[0].linkedFromPage, undefined) : linkedToDocs.length ? NumCast(linkedToDocs[0].linkedToPage, undefined) : undefined];
+                    if (!linkedFwdDocs.some(l => l instanceof Promise)) {
+                        let maxLocation = StrCast(linkedFwdDocs[altKey ? 1 : 0].maximizeLocation, "inTab");
+                        DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, document => this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey ? 1 : 0]);
+                    }
+                }
+            }
         }
     }
-    private _lastTap: number = 0;
-    _hitExpander = false;
     onPointerDown = (e: React.PointerEvent): void => {
         this._downX = e.clientX;
         this._downY = e.clientY;
@@ -206,8 +298,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (e.shiftKey && e.buttons === 1) {
             CollectionDockingView.Instance.StartOtherDrag([Doc.MakeAlias(this.props.Document)], e);
             e.stopPropagation();
-        } else if (this.active) {
-            //e.stopPropagation(); // bcz: doing this will block click events from CollectionFreeFormDocumentView which are needed for iconifying,etc
+        } else {
+            if (this.active) e.stopPropagation(); // events stop at the lowest document that is active.  
             document.removeEventListener("pointermove", this.onPointerMove);
             document.addEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
@@ -215,7 +307,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
     }
     onPointerMove = (e: PointerEvent): void => {
-        if (!e.cancelBubble) {
+        if (!e.cancelBubble && this.active) {
             if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
                 document.removeEventListener("pointermove", this.onPointerMove);
                 document.removeEventListener("pointerup", this.onPointerUp);
@@ -234,32 +326,22 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         this._lastTap = Date.now();
     }
 
-    deleteClicked = (): void => {
-        this.props.removeDocument && this.props.removeDocument(this.props.Document);
-    }
-    fieldsClicked = (e: React.MouseEvent): void => {
-        let kvp = Docs.KVPDocument(this.props.Document, { title: this.props.Document.title + ".kvp", width: 300, height: 300 });
-        CollectionDockingView.Instance.AddRightSplit(kvp);
-    }
-    makeButton = (e: React.MouseEvent): void => {
-        let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
+    deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); }
+    fieldsClicked = (): void => { this.props.addDocTab(Docs.KVPDocument(this.Document, { width: 300, height: 300 }), "onRight") };
+    makeBtnClicked = (): void => {
+        let doc = Doc.GetProto(this.props.Document);
         doc.isButton = !BoolCast(doc.isButton, false);
         if (StrCast(doc.layout).indexOf("Formatted") !== -1) { // only need to freeze the dimensions of text boxes since they don't have a native width and height naturally
             if (doc.isButton && !doc.nativeWidth) {
                 doc.nativeWidth = this.props.Document[WidthSym]();
                 doc.nativeHeight = this.props.Document[HeightSym]();
             } else {
-
                 doc.nativeWidth = doc.nativeHeight = undefined;
             }
         }
     }
-    fullScreenClicked = (e: React.MouseEvent): void => {
-        const doc = Doc.MakeCopy(this.props.Document, false);
-        if (doc) {
-            CollectionDockingView.Instance.OpenFullScreen(doc);
-        }
-        ContextMenu.Instance.clearItems();
+    fullScreenClicked = (): void => {
+        CollectionDockingView.Instance.OpenFullScreen(Doc.MakeCopy(this.props.Document, false));
         SelectionManager.DeselectAll();
     }
 
@@ -332,7 +414,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         cm.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document), icon: "caret-square-right" });
         cm.addItem({ description: "Fields", event: this.fieldsClicked, icon: "layer-group" });
         cm.addItem({ description: "Pin to Pres", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" });
-        cm.addItem({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeButton, icon: "concierge-bell" });
+        cm.addItem({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeBtnClicked, icon: "concierge-bell" });
         cm.addItem({
             description: "Find aliases", event: async () => {
                 const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document);
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index d3d765eed..7b642b299 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -102,7 +102,6 @@ export class FieldView extends React.Component<FieldViewProps> {
                     layoutKey={"layout"}
                     ContainingCollectionView={this.props.ContainingCollectionView}
                     parentActive={this.props.active}
-                    toggleMinimized={emptyFunction}
                     whenActiveChanged={this.props.whenActiveChanged}
                     bringToFront={emptyFunction} />
             );
-- 
cgit v1.2.3-70-g09d2


From 7d3ef1c914cc1cc0b6c05b14773a8b83e1b95c96 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 4 Jun 2019 13:55:10 -0400
Subject: drop prosemirror text w/ links

---
 src/client/views/collections/CollectionSubView.tsx   |  8 ++++++++
 .../CollectionFreeFormLinkView.tsx                   |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx    | 20 +++++++++++++++++++-
 3 files changed, 28 insertions(+), 2 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index fe9e12640..be37efd3d 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -17,6 +17,7 @@ import { Cast, PromiseValue, FieldValue, ListSpec } from "../../../new_fields/Ty
 import { List } from "../../../new_fields/List";
 import { DocServer } from "../../DocServer";
 import CursorField from "../../../new_fields/CursorField";
+import { DocumentManager } from "../../util/DocumentManager";
 
 export interface CollectionViewProps extends FieldViewProps {
     addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
@@ -166,6 +167,13 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
             e.stopPropagation();
             e.preventDefault();
 
+            if (html && html.indexOf(document.location.origin)) { // prosemirror text containing link to dash document
+                let start = html.indexOf(window.location.origin);
+                let path = html.substr(start, html.length - start);
+                let docid = path.substr(0, path.indexOf("\">")).replace(DocServer.prepend("/doc/"), "").split("?")[0];
+                DocServer.GetRefField(docid).then(f => (f instanceof Doc) && this.props.addDocument(f, false));
+                return;
+            }
             if (html && html.indexOf("<img") !== 0 && !html.startsWith("<a")) {
                 let htmlDoc = Docs.HtmlDocument(html, { ...options, width: 300, height: 300, documentText: text });
                 this.props.addDocument(htmlDoc, false);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 7af4f1682..301b769af 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -45,7 +45,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.width) / NumCast(b.zoomBasis, 1) / 2);
         let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.height) / NumCast(b.zoomBasis, 1) / 2);
         let text = "";
-        this.props.LinkDocs.map(l => text += StrCast(l.title) + ", ");
+        this.props.LinkDocs.map(l => text += StrCast(l.title) + "(" + StrCast(l.linkDescription) + "), ");
         text = text.substr(0, text.length - 2);
         return (
             <>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 1a1614ac7..9d19df540 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -25,6 +25,7 @@ import { pageSchema } from "../../nodes/ImageBox";
 import { InkField, StrokeData } from "../../../../new_fields/InkField";
 import { HistoryUtil } from "../../../util/History";
 import { Id } from "../../../../new_fields/FieldSymbols";
+import { DocServer } from "../../../DocServer";
 
 export const panZoomSchema = createSchema({
     panX: "number",
@@ -228,7 +229,24 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     @action
     onDrop = (e: React.DragEvent): void => {
         var pt = this.getTransform().transformPoint(e.pageX, e.pageY);
-        super.onDrop(e, { x: pt[0], y: pt[1] });
+        let html = e.dataTransfer.getData("text/html");
+        if (html && html.indexOf(document.location.origin)) {  // prosemirror text containing link to dash document
+            e.stopPropagation();
+            e.preventDefault();
+            let start = html.indexOf(window.location.origin);
+            let path = html.substr(start, html.length - start);
+            let docid = path.substr(0, path.indexOf("\">")).replace(DocServer.prepend("/doc/"), "").split("?")[0];
+            DocServer.GetRefField(docid).then(f => {
+                if (f instanceof Doc) {
+                    f.x = pt[0];
+                    f.y = pt[1];
+                    (f instanceof Doc) && this.props.addDocument(f, false);
+                }
+            });
+            return;
+        } else {
+            super.onDrop(e, { x: pt[0], y: pt[1] });
+        }
     }
 
     onDragOver = (): void => {
-- 
cgit v1.2.3-70-g09d2

