From 9e3bfb7308247af8766bff73d02d566a746735b9 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 14 Apr 2019 00:43:10 -0400
Subject: playing with cyclic panning

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 85 +++++++++++-----------
 1 file changed, 42 insertions(+), 43 deletions(-)

(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 e19dc98fa..d0b1e7f2c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -37,20 +37,13 @@ export class CollectionFreeFormView extends CollectionSubView {
         this.addDocument(newBox, false);
     }
 
-    public addDocument = (newBox: Document, allowDuplicates: boolean) => {
-        let added = this.props.addDocument(newBox, false);
-        this.bringToFront(newBox);
-        return added;
-    }
+    public addDocument = (newBox: Document, allowDuplicates: boolean) =>
+        this.props.addDocument(this.bringToFront(newBox), false);
 
     public selectDocuments = (docs: Document[]) => {
         SelectionManager.DeselectAll;
-        docs.map(doc => {
-            const dv = DocumentManager.Instance.getDocumentView(doc);
-            if (dv) {
-                SelectionManager.SelectDoc(dv, true);
-            }
-        });
+        docs.map(doc => DocumentManager.Instance.getDocumentView(doc)).filter(dv => dv).map(dv =>
+            SelectionManager.SelectDoc(dv!, true));
     }
 
     public getActiveDocuments = () => {
@@ -84,41 +77,28 @@ export class CollectionFreeFormView extends CollectionSubView {
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
-        if (super.drop(e, de)) {
-            if (de.data instanceof DragManager.DocumentDragData) {
-                let droppedDocs = de.data.droppedDocuments;
-                let xoff = de.data.xOffset as number || 0;
-                let yoff = de.data.yOffset as number || 0;
-                if (droppedDocs.length) {
-                    let screenX = de.x - xoff;
-                    let screenY = de.y - yoff;
-                    const [x, y] = this.getTransform().transformPoint(screenX, screenY);
-                    let dragDoc = droppedDocs[0];
-                    let dragX = dragDoc.GetNumber(KeyStore.X, 0);
-                    let dragY = dragDoc.GetNumber(KeyStore.Y, 0);
-                    droppedDocs.map(async d => {
-                        let docX = d.GetNumber(KeyStore.X, 0);
-                        let docY = d.GetNumber(KeyStore.Y, 0);
-                        d.SetNumber(KeyStore.X, x + (docX - dragX));
-                        d.SetNumber(KeyStore.Y, y + (docY - dragY));
-                        let docW = await d.GetTAsync(KeyStore.Width, NumberField);
-                        let docH = await d.GetTAsync(KeyStore.Height, NumberField);
-                        if (!docW) {
-                            d.SetNumber(KeyStore.Width, 300);
-                        }
-                        if (!docH) {
-                            d.SetNumber(KeyStore.Height, 300);
-                        }
-                        this.bringToFront(d);
-                    });
-                }
+        if (super.drop(e, de) && de.data instanceof DragManager.DocumentDragData) {
+            const [x, y] = this.getTransform().transformPoint(de.x - de.data.xOffset, de.y - de.data.yOffset);
+            if (de.data.droppedDocuments.length) {
+                let dropX = de.data.droppedDocuments[0].GetNumber(KeyStore.X, 0);
+                let dropY = de.data.droppedDocuments[0].GetNumber(KeyStore.Y, 0);
+                de.data.droppedDocuments.map(d => {
+                    d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0) - dropX));
+                    d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0) - dropY));
+                    if (!d.GetNumber(KeyStore.Width, 0)) {
+                        d.SetNumber(KeyStore.Width, 300);
+                    }
+                    if (!d.GetNumber(KeyStore.Height, 0)) {
+                        d.SetNumber(KeyStore.Height, 300);
+                    }
+                    this.bringToFront(d);
+                });
             }
             return true;
         }
         return false;
     }
 
-
     @action
     cleanupInteractions = () => {
         document.removeEventListener("pointermove", this.onPointerMove);
@@ -150,7 +130,26 @@ export class CollectionFreeFormView extends CollectionSubView {
             if ((!this.isAnnotationOverlay || this.zoomScaling !== 1) && !e.shiftKey) {
                 let x = this.props.Document.GetNumber(KeyStore.PanX, 0);
                 let y = this.props.Document.GetNumber(KeyStore.PanY, 0);
+                let docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
+                let minx = docs.length ? docs[0].GetNumber(KeyStore.X, 0) : 0;
+                let maxx = docs.length ? docs[0].GetNumber(KeyStore.Width, 0) + minx : minx;
+                let miny = docs.length ? docs[0].GetNumber(KeyStore.Y, 0) : 0;
+                let maxy = docs.length ? docs[0].GetNumber(KeyStore.Height, 0) + miny : miny;
+                let ranges = docs.filter(doc => doc).reduce((range, doc) => {
+                    let x = doc.GetNumber(KeyStore.X, 0);
+                    let xe = x + doc.GetNumber(KeyStore.Width, 0);
+                    let y = doc.GetNumber(KeyStore.Y, 0);
+                    let ye = y + doc.GetNumber(KeyStore.Height, 0);
+                    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]]);
+                let panelwidth = this._pwidth / this.scale / 2;
+                let panelheight = this._pheight / this.scale / 2;
                 let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
+                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;
                 this.SetPan(x - dx, y - dy);
                 this._lastX = e.pageX;
                 this._lastY = e.pageY;
@@ -227,9 +226,9 @@ export class CollectionFreeFormView extends CollectionSubView {
                 return -1;
             }
             return doc1.GetNumber(KeyStore.ZIndex, 0) - doc2.GetNumber(KeyStore.ZIndex, 0);
-        }).map((doc, index) => {
-            doc.SetNumber(KeyStore.ZIndex, index + 1);
-        });
+        }).map((doc, index) =>
+            doc.SetNumber(KeyStore.ZIndex, index + 1));
+        return doc;
     }
 
     @computed get backgroundLayout(): string | undefined {
-- 
cgit v1.2.3-70-g09d2


From d0379269b3261f846c42e30f306419044b6025a7 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 14 Apr 2019 10:52:27 -0400
Subject: fixed some scrolling artifacts

---
 build/index.html                                         |  6 +++---
 deploy/index.html                                        | 16 ++++++++--------
 src/Utils.ts                                             |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx        |  8 ++++++--
 src/client/views/nodes/FormattedTextBox.tsx              |  5 ++++-
 5 files changed, 22 insertions(+), 15 deletions(-)

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

diff --git a/build/index.html b/build/index.html
index fda212af4..a738d1092 100644
--- a/build/index.html
+++ b/build/index.html
@@ -1,12 +1,12 @@
 <html>
 
 <head>
-  <title>Dash Web</title>
+    <title>Dash Web Build</title>
 </head>
 
 <body>
-  <div id="root"></div>
-  <script src="./bundle.js"></script>
+    <div id="root"></div>
+    <script src="./bundle.js"></script>
 </body>
 
 </html>
\ No newline at end of file
diff --git a/deploy/index.html b/deploy/index.html
index ca5c13e98..532b995f8 100644
--- a/deploy/index.html
+++ b/deploy/index.html
@@ -1,16 +1,16 @@
-<html>
+<html style="overflow: hidden;">
 
 <head>
-  <title>Dash Web</title>
-  <link href="https://fonts.googleapis.com/css?family=Fjalla+One|Hind+Siliguri:300" rel="stylesheet">
-  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
-    integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
-  <script src="https://cdnjs.cloudflare.com/ajax/libs/typescript/3.3.1/typescript.min.js"></script>
+    <title>Dash Web</title>
+    <link href="https://fonts.googleapis.com/css?family=Fjalla+One|Hind+Siliguri:300" rel="stylesheet">
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
+        integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/typescript/3.3.1/typescript.min.js"></script>
 </head>
 
 <body>
-  <div id="root"></div>
-  <script src="/bundle.js"></script>
+    <div id="root"></div>
+    <script src="/bundle.js"></script>
 </body>
 
 </html>
\ No newline at end of file
diff --git a/src/Utils.ts b/src/Utils.ts
index 2e672db9a..e07d4e82d 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -48,7 +48,7 @@ export class Utils {
         if (this.logFilter !== undefined && this.logFilter !== message.type) {
             return;
         }
-        let idString = (message.id || message.id || "").padStart(36, ' ');
+        let idString = (message.id || "").padStart(36, ' ');
         prefix = prefix.padEnd(16, ' ');
         console.log(`${prefix}: ${idString}, ${receiving ? 'receiving' : 'sending'} ${messageName} with data ${JSON.stringify(message)}`);
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d0b1e7f2c..0da6fe49c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -38,7 +38,7 @@ export class CollectionFreeFormView extends CollectionSubView {
     }
 
     public addDocument = (newBox: Document, allowDuplicates: boolean) =>
-        this.props.addDocument(this.bringToFront(newBox), false);
+        this.props.addDocument(this.bringToFront(newBox), false)
 
     public selectDocuments = (docs: Document[]) => {
         SelectionManager.DeselectAll;
@@ -161,7 +161,9 @@ export class CollectionFreeFormView extends CollectionSubView {
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        this.props.select(false);
+        if (!this.props.active()) {
+            return;
+        }
         e.stopPropagation();
         let coefficient = 1000;
 
@@ -191,6 +193,8 @@ export class CollectionFreeFormView extends CollectionSubView {
 
             this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale);
             this.SetPan(-localTransform.TranslateX / localTransform.Scale, -localTransform.TranslateY / localTransform.Scale);
+            e.stopPropagation();
+            e.preventDefault();
         }
     }
 
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index a49497f8f..2e6272836 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -216,7 +216,9 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
     }
 
     onPointerWheel = (e: React.WheelEvent): void => {
-        e.stopPropagation();
+        if (this.props.isSelected()) {
+            e.stopPropagation();
+        }
     }
 
     tooltipMenuPlugin() {
@@ -236,6 +238,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
     render() {
         return (
             <div
+                style={{ overflowY: this.props.isSelected() || this.props.isOverlay ? "scroll" : "hidden" }}
                 className={`formattedTextBox-cont`}
                 onKeyDown={this.onKeyPress}
                 onKeyPress={this.onKeyPress}
-- 
cgit v1.2.3-70-g09d2


From 7e05ea0058766a3afa2ec82f6312f2df87178883 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 14 Apr 2019 12:23:10 -0400
Subject: starting to add LOD

---
 src/client/views/Main.tsx                          |  1 +
 .../views/collections/CollectionBaseView.tsx       |  6 +-
 .../views/collections/CollectionDockingView.tsx    |  1 +
 .../views/collections/CollectionSchemaView.tsx     |  1 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 72 +++++++++++++---------
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 13 ++--
 src/client/views/nodes/DocumentView.tsx            |  1 +
 src/client/views/nodes/FieldView.tsx               |  1 +
 src/fields/KeyStore.ts                             |  1 +
 9 files changed, 57 insertions(+), 40 deletions(-)

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

diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index ed61aa5a7..babd3be2a 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -295,6 +295,7 @@ export class Main extends React.Component {
             <DocumentView Document={this.mainContainer}
                 addDocument={undefined}
                 removeDocument={undefined}
+                opacity={1}
                 ScreenToLocalTransform={Transform.Identity}
                 ContentScaling={this.noScaling}
                 PanelWidth={this.pwidthFunc}
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index b5eaab349..bff56e8c3 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -1,4 +1,4 @@
-import { action } from 'mobx';
+import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Document } from '../../../fields/Document';
@@ -80,12 +80,16 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         }
         return false;
     }
+    @computed get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey.Id === KeyStore.Annotations.Id; } // bcz: ? Why do we need to compare Id's?
 
     @action.bound
     addDocument(doc: Document, allowDuplicates: boolean = false): boolean {
         let props = this.props;
         var curPage = props.Document.GetNumber(KeyStore.CurPage, -1);
         doc.SetOnPrototype(KeyStore.Page, new NumberField(curPage));
+        if (this.isAnnotationOverlay) {
+            doc.SetOnPrototype(KeyStore.Zoom, new NumberField(this.props.Document.GetNumber(KeyStore.Scale, 1)));
+        }
         if (curPage >= 0) {
             doc.SetOnPrototype(KeyStore.AnnotationOn, props.Document);
         }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index eb1cd1c09..6d772b90e 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -343,6 +343,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                 <DocumentView key={this._document.Id} Document={this._document}
                     addDocument={undefined}
                     removeDocument={undefined}
+                    opacity={1}
                     ContentScaling={this._contentScaling}
                     PanelWidth={this._nativeWidth}
                     PanelHeight={this._nativeHeight}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index fdb82690a..2a1d1dd94 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -307,6 +307,7 @@ export class CollectionSchemaView extends CollectionSubView {
                             <DocumentView Document={doc}
                                 addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
                                 isTopMost={false}
+                                opacity={1}
                                 selectOnLoad={false}
                                 ScreenToLocalTransform={this.getPreviewTransform}
                                 ContentScaling={this.getContentScaling}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 0da6fe49c..94945bde6 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable, runInAction } from "mobx";
+import { action, computed, observable, runInAction, untracked } from "mobx";
 import { observer } from "mobx-react";
 import Measure from "react-measure";
 import { Document } from "../../../../fields/Document";
@@ -37,8 +37,12 @@ export class CollectionFreeFormView extends CollectionSubView {
         this.addDocument(newBox, false);
     }
 
-    public addDocument = (newBox: Document, allowDuplicates: boolean) =>
-        this.props.addDocument(this.bringToFront(newBox), false)
+    public addDocument = (newBox: Document, allowDuplicates: boolean) => {
+        if (this.isAnnotationOverlay) {
+            newBox.SetNumber(KeyStore.Zoom, this.props.Document.GetNumber(KeyStore.Scale, 1));
+        }
+        return this.props.addDocument(this.bringToFront(newBox), false);
+    }
 
     public selectDocuments = (docs: Document[]) => {
         SelectionManager.DeselectAll;
@@ -131,25 +135,27 @@ export class CollectionFreeFormView extends CollectionSubView {
                 let x = this.props.Document.GetNumber(KeyStore.PanX, 0);
                 let y = this.props.Document.GetNumber(KeyStore.PanY, 0);
                 let docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
-                let minx = docs.length ? docs[0].GetNumber(KeyStore.X, 0) : 0;
-                let maxx = docs.length ? docs[0].GetNumber(KeyStore.Width, 0) + minx : minx;
-                let miny = docs.length ? docs[0].GetNumber(KeyStore.Y, 0) : 0;
-                let maxy = docs.length ? docs[0].GetNumber(KeyStore.Height, 0) + miny : miny;
-                let ranges = docs.filter(doc => doc).reduce((range, doc) => {
-                    let x = doc.GetNumber(KeyStore.X, 0);
-                    let xe = x + doc.GetNumber(KeyStore.Width, 0);
-                    let y = doc.GetNumber(KeyStore.Y, 0);
-                    let ye = y + doc.GetNumber(KeyStore.Height, 0);
-                    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]]);
-                let panelwidth = this._pwidth / this.scale / 2;
-                let panelheight = this._pheight / this.scale / 2;
                 let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
-                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;
+                if (!this.isAnnotationOverlay) {
+                    let minx = docs.length ? docs[0].GetNumber(KeyStore.X, 0) : 0;
+                    let maxx = docs.length ? docs[0].GetNumber(KeyStore.Width, 0) + minx : minx;
+                    let miny = docs.length ? docs[0].GetNumber(KeyStore.Y, 0) : 0;
+                    let maxy = docs.length ? docs[0].GetNumber(KeyStore.Height, 0) + miny : miny;
+                    let ranges = docs.filter(doc => doc).reduce((range, doc) => {
+                        let x = doc.GetNumber(KeyStore.X, 0);
+                        let xe = x + doc.GetNumber(KeyStore.Width, 0);
+                        let y = doc.GetNumber(KeyStore.Y, 0);
+                        let ye = y + doc.GetNumber(KeyStore.Height, 0);
+                        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]]);
+                    let panelwidth = this._pwidth / this.scale / 2;
+                    let panelheight = this._pheight / this.scale / 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;
+                }
                 this.SetPan(x - dx, y - dy);
                 this._lastX = e.pageX;
                 this._lastY = e.pageY;
@@ -161,9 +167,9 @@ export class CollectionFreeFormView extends CollectionSubView {
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        if (!this.props.active()) {
-            return;
-        }
+        // if (!this.props.active()) {
+        //     return;
+        // }
         e.stopPropagation();
         let coefficient = 1000;
 
@@ -255,9 +261,10 @@ export class CollectionFreeFormView extends CollectionSubView {
         this.props.focus(this.props.Document);
     }
 
-    getDocumentViewProps(document: Document): DocumentViewProps {
+    getDocumentViewProps(document: Document, opacity: number): DocumentViewProps {
         return {
             Document: document,
+            opacity: opacity,
             addDocument: this.props.addDocument,
             removeDocument: this.props.removeDocument,
             moveDocument: this.props.moveDocument,
@@ -277,25 +284,30 @@ export class CollectionFreeFormView extends CollectionSubView {
     @computed
     get views() {
         var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
-        return this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((prev, doc) => {
+        let docviews = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((prev, doc) => {
             var page = doc.GetNumber(KeyStore.Page, -1);
-            if (page === curPage || page === -1) {
-                prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
+            var zoom = doc.GetNumber(KeyStore.Zoom, 1);
+            var dv = DocumentManager.Instance.getDocumentView(doc);
+            let opacity = this.isAnnotationOverlay && (!dv || !SelectionManager.IsSelected(dv)) ? 1 - Math.abs(zoom - this.scale) : 1;
+            if ((page === curPage || page === -1)) {
+                prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc, opacity)} />);
             }
             return prev;
         }, [] as JSX.Element[]);
+        untracked(() => this._selectOnLoaded = "");
+        return docviews;
     }
 
     @computed
     get backgroundView() {
         return !this.backgroundLayout ? (null) :
-            (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document)}
+            (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document, 1)}
                 layoutKey={KeyStore.BackgroundLayout} isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
     }
     @computed
     get overlayView() {
         return !this.overlayLayout ? (null) :
-            (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document)}
+            (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document, 1)}
                 layoutKey={KeyStore.OverlayLayout} isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
     }
 
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 77f41105f..0aa152209 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -6,7 +6,6 @@ import { Transform } from "../../util/Transform";
 import { DocumentView, DocumentViewProps } from "./DocumentView";
 import "./DocumentView.scss";
 import React = require("react");
-import { thisExpression } from "babel-types";
 
 
 @observer
@@ -16,18 +15,13 @@ export class CollectionFreeFormDocumentView extends React.Component<DocumentView
     constructor(props: DocumentViewProps) {
         super(props);
     }
-    get screenRect(): ClientRect | DOMRect {
-        if (this._mainCont.current) {
-            return this._mainCont.current.getBoundingClientRect();
-        }
-        return new DOMRect();
-    }
 
     @computed
     get transform(): string {
-        return `scale(${this.props.ContentScaling()}, ${this.props.ContentScaling()}) translate(${this.props.Document.GetNumber(KeyStore.X, 0)}px, ${this.props.Document.GetNumber(KeyStore.Y, 0)}px)`;
+        return `scale(${this.props.ContentScaling()}, ${this.props.ContentScaling()}) translate(${this.props.Document.GetNumber(KeyStore.X, 0)}px, ${this.props.Document.GetNumber(KeyStore.Y, 0)}px) scale(${this.zoom}, ${this.zoom}) `;
     }
 
+    @computed get zoom(): number { return 1 / this.props.Document.GetNumber(KeyStore.Zoom, 1); }
     @computed get zIndex(): number { return this.props.Document.GetNumber(KeyStore.ZIndex, 0); }
     @computed get width(): number { return this.props.Document.Width(); }
     @computed get height(): number { return this.props.Document.Height(); }
@@ -57,7 +51,7 @@ export class CollectionFreeFormDocumentView extends React.Component<DocumentView
     getTransform = (): Transform =>
         this.props.ScreenToLocalTransform()
             .translate(-this.props.Document.GetNumber(KeyStore.X, 0), -this.props.Document.GetNumber(KeyStore.Y, 0))
-            .scale(1 / this.contentScaling())
+            .scale(1 / this.contentScaling()).scale(1 / this.zoom)
 
     @computed
     get docView() {
@@ -74,6 +68,7 @@ export class CollectionFreeFormDocumentView extends React.Component<DocumentView
     render() {
         return (
             <div className="collectionFreeFormDocumentView-container" ref={this._mainCont} style={{
+                opacity: this.props.opacity,
                 transformOrigin: "left top",
                 transform: this.transform,
                 pointerEvents: "all",
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4d7a85316..f0f9af89f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -26,6 +26,7 @@ import { undoBatch, UndoManager } from "../../util/UndoManager";
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView>;
     Document: Document;
+    opacity: number;
     addDocument?: (doc: Document, allowDuplicates?: boolean) => boolean;
     removeDocument?: (doc: Document) => boolean;
     moveDocument?: (doc: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 0037d7b28..e0c5e19fb 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -79,6 +79,7 @@ export class FieldView extends React.Component<FieldViewProps> {
                 <DocumentContentsView Document={field}
                     addDocument={undefined}
                     removeDocument={undefined}
+                    opacity={1}
                     ScreenToLocalTransform={Transform.Identity}
                     ContentScaling={() => 1}
                     PanelWidth={() => 100}
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index da2d7268f..19431bbe3 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -15,6 +15,7 @@ export namespace KeyStore {
     export const Width = new Key("Width");
     export const Height = new Key("Height");
     export const ZIndex = new Key("ZIndex");
+    export const Zoom = new Key("Zoom");
     export const Data = new Key("Data");
     export const Annotations = new Key("Annotations");
     export const ViewType = new Key("ViewType");
-- 
cgit v1.2.3-70-g09d2


From a12bc4e408119ca2c310c04598c3aa2b424751c5 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 14 Apr 2019 15:10:12 -0400
Subject: restored ContainingCollectionView for use in drawings links (maybe
 brushing later)

---
 src/client/views/Main.tsx                          |  2 +-
 .../views/collections/CollectionSchemaView.tsx     |  4 +++-
 src/client/views/collections/CollectionSubView.tsx |  2 ++
 src/client/views/collections/CollectionView.tsx    |  8 +++----
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |  5 ++--
 src/client/views/nodes/DocumentContentsView.tsx    | 28 ++++++++++++----------
 src/client/views/nodes/FieldView.tsx               |  7 ++++--
 src/client/views/nodes/KeyValuePair.tsx            |  2 ++
 9 files changed, 36 insertions(+), 24 deletions(-)

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

diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index babd3be2a..03cfbe9da 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -282,7 +282,7 @@ export class Main extends React.Component {
             return <div className="mainDiv-textInput" style={{ pointerEvents: "none", transform: `translate(${x}px, ${y}px) scale(${1 / s[0]},${1 / s[0]})`, width: "auto", height: "auto" }} >
                 <div className="mainDiv-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll} style={{ pointerEvents: "none", transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}>
                     <FormattedTextBox fieldKey={this._textFieldKey!} isOverlay={true} Document={this._textDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
-                        selectOnLoad={true} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={emptyDocFunction} />
+                        selectOnLoad={true} ContainingCollectionView={undefined} CollectionView={undefined} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={emptyDocFunction} />
                 </div>
             </ div>;
         }
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 2a1d1dd94..63346a4c6 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -75,6 +75,8 @@ export class CollectionSchemaView extends CollectionSubView {
         let props: FieldViewProps = {
             Document: rowProps.value[0],
             fieldKey: rowProps.value[1],
+            CollectionView: this.props.CollectionView,
+            ContainingCollectionView: this.props.CollectionView,
             isSelected: returnFalse,
             select: emptyFunction,
             isTopMost: false,
@@ -313,7 +315,7 @@ export class CollectionSchemaView extends CollectionSubView {
                                 ContentScaling={this.getContentScaling}
                                 PanelWidth={this.getPanelWidth}
                                 PanelHeight={this.getPanelHeight}
-                                ContainingCollectionView={undefined}
+                                ContainingCollectionView={this.props.CollectionView}
                                 focus={emptyDocFunction}
                                 parentActive={this.props.active}
                                 onActiveChanged={this.props.onActiveChanged} /> : null}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d91db68bb..61b7787e7 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -16,6 +16,7 @@ import { Server } from "../../Server";
 import { FieldViewProps } from "../nodes/FieldView";
 import * as rp from 'request-promise';
 import { emptyFunction } from "../../../Utils";
+import { CollectionView } from "./CollectionView";
 
 export interface CollectionViewProps extends FieldViewProps {
     addDocument: (document: Document, allowDuplicates?: boolean) => boolean;
@@ -24,6 +25,7 @@ export interface CollectionViewProps extends FieldViewProps {
 }
 
 export interface SubCollectionViewProps extends CollectionViewProps {
+    CollectionView: Opt<CollectionView>;
 }
 
 export type CursorEntry = TupleField<[string, string], [number, number]>;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 8abd0a02d..1f69a69e8 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -18,12 +18,12 @@ export class CollectionView extends React.Component<FieldViewProps> {
     private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
         let props = { ...this.props, ...renderProps };
         switch (type) {
-            case CollectionViewType.Schema: return (<CollectionSchemaView {...props} />);
-            case CollectionViewType.Docking: return (<CollectionDockingView {...props} />);
-            case CollectionViewType.Tree: return (<CollectionTreeView {...props} />);
+            case CollectionViewType.Schema: return (<CollectionSchemaView {...props} CollectionView={this} />);
+            case CollectionViewType.Docking: return (<CollectionDockingView {...props} CollectionView={this} />);
+            case CollectionViewType.Tree: return (<CollectionTreeView {...props} CollectionView={this} />);
             case CollectionViewType.Freeform:
             default:
-                return (<CollectionFreeFormView {...props} />);
+                return (<CollectionFreeFormView {...props} CollectionView={this} />);
         }
         return (null);
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 94945bde6..e9e942b78 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -274,7 +274,7 @@ export class CollectionFreeFormView extends CollectionSubView {
             PanelWidth: document.Width,
             PanelHeight: document.Height,
             ContentScaling: this.noScaling,
-            ContainingCollectionView: undefined,
+            ContainingCollectionView: this.props.CollectionView,
             focus: this.focusDocument,
             parentActive: this.props.active,
             onActiveChanged: this.props.active,
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index a4722a6f8..cf75aef40 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -209,10 +209,11 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         let selRect = this.Bounds;
         let selection: Document[] = [];
         this.props.activeDocuments().map(doc => {
+            var z = doc.GetNumber(KeyStore.Zoom, 1);
             var x = doc.GetNumber(KeyStore.X, 0);
             var y = doc.GetNumber(KeyStore.Y, 0);
-            var w = doc.GetNumber(KeyStore.Width, 0);
-            var h = doc.GetNumber(KeyStore.Height, 0);
+            var w = doc.GetNumber(KeyStore.Width, 0) / z;
+            var h = doc.GetNumber(KeyStore.Height, 0) / z;
             if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
                 selection.push(doc);
             }
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 5836da396..a967db000 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -44,19 +44,19 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
 
 
     CreateBindings(): JsxBindings {
-        let
-            {
-                Document,
-                isSelected,
-                select,
-                isTopMost,
-                selectOnLoad,
-                ScreenToLocalTransform,
-                addDocument,
-                removeDocument,
-                onActiveChanged,
-                parentActive: active,
-            } = this.props;
+        let {
+            Document,
+            isSelected,
+            select,
+            isTopMost,
+            selectOnLoad,
+            ScreenToLocalTransform,
+            ContainingCollectionView,
+            addDocument,
+            removeDocument,
+            onActiveChanged,
+            parentActive: active,
+        } = this.props;
         let bindings: JsxBindings = {
             props: {
                 Document,
@@ -65,6 +65,8 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
                 isTopMost,
                 selectOnLoad,
                 ScreenToLocalTransform,
+                CollectionView: undefined,
+                ContainingCollectionView,
                 active,
                 onActiveChanged,
                 addDocument,
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index e0c5e19fb..755367d23 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -1,7 +1,7 @@
 import React = require("react");
 import { observer } from "mobx-react";
 import { computed } from "mobx";
-import { Field, FieldWaiting, FieldValue } from "../../../fields/Field";
+import { Field, FieldWaiting, FieldValue, Opt } from "../../../fields/Field";
 import { Document } from "../../../fields/Document";
 import { TextField } from "../../../fields/TextField";
 import { NumberField } from "../../../fields/NumberField";
@@ -20,6 +20,7 @@ import { DocumentContentsView } from "./DocumentContentsView";
 import { Transform } from "../../util/Transform";
 import { KeyStore } from "../../../fields/KeyStore";
 import { returnFalse, emptyDocFunction } from "../../../Utils";
+import { CollectionView } from "../collections/CollectionView";
 
 
 //
@@ -29,6 +30,8 @@ import { returnFalse, emptyDocFunction } from "../../../Utils";
 //
 export interface FieldViewProps {
     fieldKey: Key;
+    ContainingCollectionView: Opt<CollectionView>;
+    CollectionView: Opt<CollectionView>;
     Document: Document;
     isSelected: () => boolean;
     select: (isCtrlPressed: boolean) => void;
@@ -90,7 +93,7 @@ export class FieldView extends React.Component<FieldViewProps> {
                     isSelected={returnFalse}
                     select={returnFalse}
                     layoutKey={KeyStore.Layout}
-                    ContainingCollectionView={undefined}
+                    ContainingCollectionView={this.props.ContainingCollectionView}
                     parentActive={this.props.active}
                     onActiveChanged={this.props.onActiveChanged} />
             );
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 3e0b61c3d..1b9dd26bd 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -47,6 +47,8 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
         }
         let props: FieldViewProps = {
             Document: this.props.doc,
+            CollectionView: undefined,
+            ContainingCollectionView: undefined,
             fieldKey: this.key,
             isSelected: returnFalse,
             select: emptyFunction,
-- 
cgit v1.2.3-70-g09d2


From 7f0b7a3e02d2d5ea3bd9f8554f1828d0470b1d04 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 14 Apr 2019 17:46:54 -0400
Subject: tweaks to text input

---
 src/client/documents/Documents.ts                                | 2 +-
 src/client/views/collections/CollectionSubView.tsx               | 2 +-
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx    | 9 ++++++---
 3 files changed, 8 insertions(+), 5 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 3c36fe500..4febfa7eb 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -127,7 +127,7 @@ export namespace Documents {
 
     function CreateImagePrototype(): Document {
         let imageProto = setupPrototypeOptions(imageProtoId, "IMAGE_PROTO", CollectionView.LayoutString("AnnotationsKey"),
-            { x: 0, y: 0, nativeWidth: 300, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] });
+            { x: 0, y: 0, nativeWidth: 600, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] });
         imageProto.SetText(KeyStore.BackgroundLayout, ImageBox.LayoutString());
         imageProto.SetNumber(KeyStore.CurPage, 0);
         return imageProto;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 1c927c78d..d3d69b1af 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -197,7 +197,7 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
                 }).then(async (res: Response) => {
                     (await res.json()).map(action((file: any) => {
                         let path = window.location.origin + file;
-                        let docPromise = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300, title: dropFileName });
+                        let docPromise = this.getDocumentFromType(type, path, { ...options, nativeWidth: 600, width: 300, title: dropFileName });
 
                         docPromise.then(action((doc?: Document) => {
                             let docs = this.props.Document.GetT(KeyStore.Data, ListField);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index e9e942b78..6c382a353 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -4,7 +4,6 @@ import Measure from "react-measure";
 import { Document } from "../../../../fields/Document";
 import { FieldWaiting } from "../../../../fields/Field";
 import { KeyStore } from "../../../../fields/KeyStore";
-import { NumberField } from "../../../../fields/NumberField";
 import { TextField } from "../../../../fields/TextField";
 import { emptyFunction, returnFalse } from "../../../../Utils";
 import { DocumentManager } from "../../../util/DocumentManager";
@@ -18,7 +17,7 @@ import { Main } from "../../Main";
 import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
 import { DocumentContentsView } from "../../nodes/DocumentContentsView";
 import { DocumentViewProps } from "../../nodes/DocumentView";
-import { CollectionSubView } from "../CollectionSubView";
+import { CollectionSubView, SubCollectionViewProps } from "../CollectionSubView";
 import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView";
 import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
 import "./CollectionFreeFormView.scss";
@@ -294,7 +293,11 @@ export class CollectionFreeFormView extends CollectionSubView {
             }
             return prev;
         }, [] as JSX.Element[]);
-        untracked(() => this._selectOnLoaded = "");
+
+        setTimeout(() => { // bcz: surely there must be a better way ....
+            this._selectOnLoaded = "";
+        }, 600);
+
         return docviews;
     }
 
-- 
cgit v1.2.3-70-g09d2


From 845057ef78f272faf488b5bbc2fe79d64fb64120 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 14 Apr 2019 19:44:38 -0400
Subject: mostly northstar cleanup, plus cleaning up main.tsx

---
 src/client/northstar/dash-nodes/HistogramBox.tsx   |   2 +-
 src/client/northstar/manager/Gateway.ts            |  16 +-
 .../model/binRanges/VisualBinRangeHelper.ts        |   2 +-
 src/client/util/DragManager.ts                     |   9 +-
 src/client/util/SelectionManager.ts                |   3 +-
 src/client/views/DocumentDecorations.tsx           |  16 +-
 src/client/views/Main.scss                         |  18 --
 src/client/views/Main.tsx                          | 279 ++++++---------------
 src/client/views/MainOverlayTextBox.scss           |  19 ++
 src/client/views/MainOverlayTextBox.tsx            | 110 ++++++++
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   3 +-
 src/client/views/nodes/FormattedTextBox.tsx        |   9 +-
 src/fields/KeyStore.ts                             |   2 +-
 .../authentication/models/current_user_utils.ts    |  99 +++-----
 src/server/authentication/models/user_model.ts     |  21 +-
 15 files changed, 286 insertions(+), 322 deletions(-)
 create mode 100644 src/client/views/MainOverlayTextBox.scss
 create mode 100644 src/client/views/MainOverlayTextBox.tsx

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

diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index 2084fc346..3e94fed81 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -8,7 +8,7 @@ import { KeyStore } from "../../../fields/KeyStore";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { ChartType, VisualBinRange } from '../../northstar/model/binRanges/VisualBinRange';
 import { VisualBinRangeHelper } from "../../northstar/model/binRanges/VisualBinRangeHelper";
-import { AggregateBinRange, AggregateFunction, BinRange, Catalog, DoubleValueAggregateResult, HistogramResult, Result } from "../../northstar/model/idea/idea";
+import { AggregateBinRange, AggregateFunction, BinRange, Catalog, DoubleValueAggregateResult, HistogramResult } from "../../northstar/model/idea/idea";
 import { ModelHelpers } from "../../northstar/model/ModelHelpers";
 import { HistogramOperation } from "../../northstar/operations/HistogramOperation";
 import { SizeConverter } from "../../northstar/utils/SizeConverter";
diff --git a/src/client/northstar/manager/Gateway.ts b/src/client/northstar/manager/Gateway.ts
index 207a9ad19..d26f2724f 100644
--- a/src/client/northstar/manager/Gateway.ts
+++ b/src/client/northstar/manager/Gateway.ts
@@ -36,7 +36,7 @@ export class Gateway {
 
     public async ClearCatalog(): Promise<void> {
         try {
-            const json = await this.MakePostJsonRequest("Datamart/ClearAllAugmentations", {});
+            await this.MakePostJsonRequest("Datamart/ClearAllAugmentations", {});
         }
         catch (error) {
             throw new Error("can not reach northstar's backend");
@@ -180,18 +180,18 @@ export class Gateway {
 
 
     public static ConstructUrl(appendix: string): string {
-        let base = Settings.Instance.ServerUrl;
+        let base = NorthstarSettings.Instance.ServerUrl;
         if (base.slice(-1) === "/") {
             base = base.slice(0, -1);
         }
-        let url = base + "/" + Settings.Instance.ServerApiPath + "/" + appendix;
+        let url = base + "/" + NorthstarSettings.Instance.ServerApiPath + "/" + appendix;
         return url;
     }
 }
 
 declare var ENV: any;
 
-export class Settings {
+export class NorthstarSettings {
     private _environment: any;
 
     @observable
@@ -248,10 +248,10 @@ export class Settings {
         return window.location.origin + "/";
     }
 
-    private static _instance: Settings;
+    private static _instance: NorthstarSettings;
 
     @action
-    public Update(environment: any): void {
+    public UpdateEnvironment(environment: any): void {
         /*let serverParam = new URL(document.URL).searchParams.get("serverUrl");
         if (serverParam) {
             if (serverParam === "debug") {
@@ -278,9 +278,9 @@ export class Settings {
         this.DegreeOfParallelism = environment.DEGREE_OF_PARALLISM;
     }
 
-    public static get Instance(): Settings {
+    public static get Instance(): NorthstarSettings {
         if (!this._instance) {
-            this._instance = new Settings();
+            this._instance = new NorthstarSettings();
         }
         return this._instance;
     }
diff --git a/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts b/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts
index 9671e55f8..a92412686 100644
--- a/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts
+++ b/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts
@@ -4,7 +4,7 @@ import { NominalVisualBinRange } from "./NominalVisualBinRange";
 import { QuantitativeVisualBinRange } from "./QuantitativeVisualBinRange";
 import { AlphabeticVisualBinRange } from "./AlphabeticVisualBinRange";
 import { DateTimeVisualBinRange } from "./DateTimeVisualBinRange";
-import { Settings } from "../../manager/Gateway";
+import { NorthstarSettings } from "../../manager/Gateway";
 import { ModelHelpers } from "../ModelHelpers";
 import { AttributeTransformationModel } from "../../core/attribute/AttributeTransformationModel";
 
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 2ee36d2ec..4bd654e15 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,13 +1,12 @@
 import { action } from "mobx";
 import { Document } from "../../fields/Document";
+import { FieldWaiting } from "../../fields/Field";
+import { KeyStore } from "../../fields/KeyStore";
 import { emptyFunction } from "../../Utils";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import { DocumentDecorations } from "../views/DocumentDecorations";
-import { Main } from "../views/Main";
-import { DocumentView } from "../views/nodes/DocumentView";
 import * as globalCssVariables from "../views/globalCssVariables.scss";
-import { KeyStore } from "../../fields/KeyStore";
-import { FieldWaiting } from "../../fields/Field";
+import { MainOverlayTextBox } from "../views/MainOverlayTextBox";
 
 export function SetupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) {
     let onRowMove = action((e: PointerEvent): void => {
@@ -177,7 +176,7 @@ export namespace DragManager {
             dragDiv.className = "dragManager-dragDiv";
             DragManager.Root().appendChild(dragDiv);
         }
-        Main.Instance.SetTextDoc();
+        MainOverlayTextBox.Instance.SetTextDoc();
 
         let scaleXs: number[] = [];
         let scaleYs: number[] = [];
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 2fa45a086..c56f6a4ff 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -2,6 +2,7 @@ import { observable, action } from "mobx";
 import { DocumentView } from "../views/nodes/DocumentView";
 import { Document } from "../../fields/Document";
 import { Main } from "../views/Main";
+import { MainOverlayTextBox } from "../views/MainOverlayTextBox";
 
 export namespace SelectionManager {
     class Manager {
@@ -25,7 +26,7 @@ export namespace SelectionManager {
         DeselectAll(): void {
             manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false));
             manager.SelectedDocuments = [];
-            Main.Instance.SetTextDoc();
+            MainOverlayTextBox.Instance.SetTextDoc();
         }
     }
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 2dc496bc1..b97a47a3c 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -1,24 +1,20 @@
-import { action, computed, observable, trace, runInAction } from "mobx";
+import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Key } from "../../fields/Key";
 //import ContentEditable from 'react-contenteditable'
 import { KeyStore } from "../../fields/KeyStore";
 import { ListField } from "../../fields/ListField";
 import { NumberField } from "../../fields/NumberField";
-import { Document } from "../../fields/Document";
 import { TextField } from "../../fields/TextField";
-import { DragManager, DragLinksAsDocuments } from "../util/DragManager";
+import { emptyFunction } from "../../Utils";
+import { DragLinksAsDocuments, DragManager } from "../util/DragManager";
 import { SelectionManager } from "../util/SelectionManager";
-import { CollectionView } from "./collections/CollectionView";
+import { undoBatch } from "../util/UndoManager";
 import './DocumentDecorations.scss';
+import { MainOverlayTextBox } from "./MainOverlayTextBox";
 import { DocumentView } from "./nodes/DocumentView";
 import { LinkMenu } from "./nodes/LinkMenu";
 import React = require("react");
-import { FieldWaiting } from "../../fields/Field";
-import { emptyFunction } from "../../Utils";
-import { Main } from "./Main";
-import { undo } from "prosemirror-history";
-import { undoBatch } from "../util/UndoManager";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -304,7 +300,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 break;
         }
 
-        Main.Instance.SetTextDoc();
+        MainOverlayTextBox.Instance.SetTextDoc();
         SelectionManager.SelectedDocuments().forEach(element => {
             const rect = element.screenRect();
             if (rect.width !== 0) {
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 13cadb10d..4373534b2 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -168,24 +168,6 @@ button:hover {
     left:0;
     overflow: scroll;
 }
-.mainDiv-textInput {
-    background-color: rgba(248, 6, 6, 0.001);
-    width: 200px;
-    height: 200px;
-    position:absolute;
-    overflow: visible;
-    top: 0;
-    left: 0;
-    z-index: $mainTextInput-zindex;
-    .formattedTextBox-cont {
-        background-color: rgba(248, 6, 6, 0.001);
-        width: 100%;
-        height: 100%;
-        position:absolute;
-        top: 0;
-        left: 0;
-    }
-}
 #mainContent-div {
     width:100%;
     height:100%;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index f8c6ec0e2..51c076b14 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -15,20 +15,19 @@ import { ListField } from '../../fields/ListField';
 import { WorkspacesMenu } from '../../server/authentication/controllers/WorkspacesMenu';
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import { MessageStore } from '../../server/Message';
-import { Utils, returnTrue, emptyFunction, emptyDocFunction } from '../../Utils';
-import * as rp from 'request-promise';
 import { RouteStore } from '../../server/RouteStore';
 import { ServerUtils } from '../../server/ServerUtil';
+import { emptyDocFunction, emptyFunction, returnTrue, Utils } from '../../Utils';
 import { Documents } from '../documents/Documents';
 import { ColumnAttributeModel } from '../northstar/core/attribute/AttributeModel';
 import { AttributeTransformationModel } from '../northstar/core/attribute/AttributeTransformationModel';
-import { Gateway, Settings } from '../northstar/manager/Gateway';
-import { AggregateFunction, Catalog, Point } from '../northstar/model/idea/idea';
+import { Gateway, NorthstarSettings } from '../northstar/manager/Gateway';
+import { AggregateFunction, Catalog } from '../northstar/model/idea/idea';
 import '../northstar/model/ModelExtensions';
 import { HistogramOperation } from '../northstar/operations/HistogramOperation';
 import '../northstar/utils/Extensions';
 import { Server } from '../Server';
-import { SetupDrag, DragManager } from '../util/DragManager';
+import { SetupDrag } from '../util/DragManager';
 import { Transform } from '../util/Transform';
 import { UndoManager } from '../util/UndoManager';
 import { CollectionDockingView } from './collections/CollectionDockingView';
@@ -36,41 +35,28 @@ import { ContextMenu } from './ContextMenu';
 import { DocumentDecorations } from './DocumentDecorations';
 import { InkingControl } from './InkingControl';
 import "./Main.scss";
+import { MainOverlayTextBox } from './MainOverlayTextBox';
 import { DocumentView } from './nodes/DocumentView';
-import { FormattedTextBox } from './nodes/FormattedTextBox';
-import { REPLCommand } from 'repl';
-import { Key } from '../../fields/Key';
 import { PreviewCursor } from './PreviewCursor';
 
 
 @observer
 export class Main extends React.Component {
-    // dummy initializations keep the compiler happy
-    @observable private mainfreeform?: Document;
+    public static Instance: Main;
+    @observable private _workspacesShown: boolean = false;
     @observable public pwidth: number = 0;
     @observable public pheight: number = 0;
-    private _northstarSchemas: Document[] = [];
 
     @computed private get mainContainer(): Document | undefined {
-        let doc = this.userDocument.GetT(KeyStore.ActiveWorkspace, Document);
+        let doc = CurrentUserUtils.UserDocument.GetT(KeyStore.ActiveWorkspace, Document);
         return doc === FieldWaiting ? undefined : doc;
     }
-
     private set mainContainer(doc: Document | undefined) {
-        if (doc) {
-            this.userDocument.Set(KeyStore.ActiveWorkspace, doc);
-        }
+        doc && CurrentUserUtils.UserDocument.Set(KeyStore.ActiveWorkspace, doc);
     }
 
-    private get userDocument(): Document {
-        return CurrentUserUtils.UserDocument;
-    }
-
-    public static Instance: Main;
-
     constructor(props: Readonly<{}>) {
         super(props);
-        this._textProxyDiv = React.createRef();
         Main.Instance = this;
         // causes errors to be generated when modifying an observable outside of an action
         configure({ enforceActions: "observed" });
@@ -102,6 +88,10 @@ export class Main extends React.Component {
         this.initializeNorthstar();
     }
 
+    componentDidMount() { window.onpopstate = this.onHistory; }
+
+    componentWillUnmount() { window.onpopstate = null; }
+
     onHistory = () => {
         if (window.location.pathname !== RouteStore.home) {
             let pathname = window.location.pathname.split("/");
@@ -114,14 +104,6 @@ export class Main extends React.Component {
         }
     }
 
-    componentDidMount() {
-        window.onpopstate = this.onHistory;
-    }
-
-    componentWillUnmount() {
-        window.onpopstate = null;
-    }
-
     initEventListeners = () => {
         // window.addEventListener("pointermove", (e) => this.reportLocation(e))
         window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler
@@ -137,7 +119,7 @@ export class Main extends React.Component {
     initAuthenticationRouters = () => {
         // Load the user's active workspace, or create a new one if initial session after signup
         if (!CurrentUserUtils.MainDocId) {
-            this.userDocument.GetTAsync(KeyStore.ActiveWorkspace, Document).then(doc => {
+            CurrentUserUtils.UserDocument.GetTAsync(KeyStore.ActiveWorkspace, Document).then(doc => {
                 if (doc) {
                     CurrentUserUtils.MainDocId = doc.Id;
                     this.openWorkspace(doc);
@@ -158,7 +140,7 @@ export class Main extends React.Component {
 
     @action
     createNewWorkspace = (id?: string): void => {
-        this.userDocument.GetTAsync<ListField<Document>>(KeyStore.Workspaces, ListField).then(action((list: Opt<ListField<Document>>) => {
+        CurrentUserUtils.UserDocument.GetTAsync<ListField<Document>>(KeyStore.Workspaces, ListField).then(action((list: Opt<ListField<Document>>) => {
             if (list) {
                 let freeformDoc = Documents.FreeformDocument([], { x: 0, y: 400, title: "mini collection" });
                 var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc)] }] };
@@ -179,7 +161,7 @@ export class Main extends React.Component {
     openWorkspace = (doc: Document, fromHistory = false): void => {
         this.mainContainer = doc;
         fromHistory || window.history.pushState(null, doc.Title, "/doc/" + doc.Id);
-        this.userDocument.GetTAsync(KeyStore.OptionalRightCollection, Document).then(col => {
+        CurrentUserUtils.UserDocument.GetTAsync(KeyStore.OptionalRightCollection, Document).then(col => {
             // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized)
             setTimeout(() => {
                 if (col) {
@@ -193,119 +175,33 @@ export class Main extends React.Component {
         });
     }
 
-    @observable
-    workspacesShown: boolean = false;
-
-    areWorkspacesShown = () => this.workspacesShown;
-    @action
-    toggleWorkspaces = () => {
-        this.workspacesShown = !this.workspacesShown;
-    }
-
-    pwidthFunc = () => this.pwidth;
-    pheightFunc = () => this.pheight;
-    noScaling = () => 1;
-
-    @observable _textDoc?: Document = undefined;
-    _textRect: any;
-    _textXf: Transform = Transform.Identity();
-    _textScroll: number = 0;
-    _textFieldKey: Key = KeyStore.Data;
-    _textColor: string | null = null;
-    _textTargetDiv: HTMLDivElement | undefined;
-    _textProxyDiv: React.RefObject<HTMLDivElement>;
-    @action
-    SetTextDoc(textDoc?: Document, textFieldKey?: Key, div?: HTMLDivElement, tx?: Transform) {
-        if (this._textTargetDiv) {
-            this._textTargetDiv.style.color = this._textColor;
-        }
-
-        this._textDoc = undefined;
-        this._textDoc = textDoc;
-        this._textFieldKey = textFieldKey!;
-        this._textXf = tx ? tx : Transform.Identity();
-        this._textTargetDiv = div;
-        if (div) {
-            this._textColor = div.style.color;
-            div.style.color = "transparent";
-            this._textRect = div.getBoundingClientRect();
-            this._textScroll = div.scrollTop;
-        }
-    }
-
-    @action
-    textScroll = (e: React.UIEvent) => {
-        if (this._textProxyDiv.current && this._textTargetDiv) {
-            this._textTargetDiv.scrollTop = this._textScroll = this._textProxyDiv.current.children[0].scrollTop;
-        }
-    }
-
-    textBoxDown = (e: React.PointerEvent) => {
-        if (e.button !== 0 || e.metaKey || e.altKey) {
-            document.addEventListener("pointermove", this.textBoxMove);
-            document.addEventListener('pointerup', this.textBoxUp);
-        }
-    }
-    textBoxMove = (e: PointerEvent) => {
-        if (e.movementX > 1 || e.movementY > 1) {
-            document.removeEventListener("pointermove", this.textBoxMove);
-            document.removeEventListener('pointerup', this.textBoxUp);
-            let dragData = new DragManager.DocumentDragData([this._textDoc!]);
-            const [left, top] = this._textXf
-                .inverse()
-                .transformPoint(0, 0);
-            dragData.xOffset = e.clientX - left;
-            dragData.yOffset = e.clientY - top;
-            DragManager.StartDocumentDrag([this._textTargetDiv!], dragData, e.clientX, e.clientY, {
-                handlers: {
-                    dragComplete: action(emptyFunction),
-                },
-                hideSource: false
-            });
-        }
-    }
-    textBoxUp = (e: PointerEvent) => {
-        document.removeEventListener("pointermove", this.textBoxMove);
-        document.removeEventListener('pointerup', this.textBoxUp);
-    }
-
-    @computed
-    get activeTextBox() {
-        if (this._textDoc) {
-            let x: number = this._textRect.x;
-            let y: number = this._textRect.y;
-            let w: number = this._textRect.width;
-            let h: number = this._textRect.height;
-            let t = this._textXf.transformPoint(0, 0);
-            let s = this._textXf.transformPoint(1, 0);
-            s[0] = Math.sqrt((s[0] - t[0]) * (s[0] - t[0]) + (s[1] - t[1]) * (s[1] - t[1]));
-            return <div className="mainDiv-textInput" style={{ pointerEvents: "none", transform: `translate(${x}px, ${y}px) scale(${1 / s[0]},${1 / s[0]})`, width: "auto", height: "auto" }} >
-                <div className="mainDiv-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll} style={{ pointerEvents: "none", transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}>
-                    <FormattedTextBox fieldKey={this._textFieldKey!} isOverlay={true} Document={this._textDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
-                        selectOnLoad={true} ContainingCollectionView={undefined} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={emptyDocFunction} />
-                </div>
-            </ div>;
-        }
-        else return (null);
-    }
-
     @computed
     get mainContent() {
-        return !this.mainContainer ? (null) :
-            <DocumentView Document={this.mainContainer}
-                addDocument={undefined}
-                removeDocument={undefined}
-                opacity={1}
-                ScreenToLocalTransform={Transform.Identity}
-                ContentScaling={this.noScaling}
-                PanelWidth={this.pwidthFunc}
-                PanelHeight={this.pheightFunc}
-                isTopMost={true}
-                selectOnLoad={false}
-                focus={emptyDocFunction}
-                parentActive={returnTrue}
-                onActiveChanged={emptyFunction}
-                ContainingCollectionView={undefined} />;
+        trace();
+        let pwidthFunc = () => this.pwidth;
+        let pheightFunc = () => this.pheight;
+        let noScaling = () => 1;
+        return <Measure onResize={action((r: any) => { this.pwidth = r.entry.width; this.pheight = r.entry.height; })}>
+            {({ measureRef }) =>
+                <div ref={measureRef} id="mainContent-div">
+                    {!this.mainContainer ? (null) :
+                        <DocumentView Document={this.mainContainer}
+                            addDocument={undefined}
+                            removeDocument={undefined}
+                            opacity={1}
+                            ScreenToLocalTransform={Transform.Identity}
+                            ContentScaling={noScaling}
+                            PanelWidth={pwidthFunc}
+                            PanelHeight={pheightFunc}
+                            isTopMost={true}
+                            selectOnLoad={false}
+                            focus={emptyDocFunction}
+                            parentActive={returnTrue}
+                            onActiveChanged={emptyFunction}
+                            ContainingCollectionView={undefined} />}
+                </div>
+            }
+        </Measure>;
     }
 
     /* for the expandable add nodes menu. Not included with the miscbuttons because once it expands it expands the whole div with it, making canvas interactions limited. */
@@ -361,6 +257,7 @@ export class Main extends React.Component {
     get miscButtons() {
         let workspacesRef = React.createRef<HTMLDivElement>();
         let logoutRef = React.createRef<HTMLDivElement>();
+        let toggleWorkspaces = () => runInAction(() => { this._workspacesShown = !this._workspacesShown; });
 
         let clearDatabase = action(() => Utils.Emit(Server.Socket, MessageStore.DeleteAll, {}));
         return [
@@ -371,55 +268,50 @@ export class Main extends React.Component {
                 <button className="toolbar-button round-button" title="Ink" onClick={() => InkingControl.Instance.toggleDisplay()}><FontAwesomeIcon icon="pen-nib" size="sm" /></button>
             </div >,
             <div className="main-buttonDiv" key="workspaces" style={{ top: '34px', left: '2px', position: 'absolute' }} ref={workspacesRef}>
-                <button onClick={this.toggleWorkspaces}>Workspaces</button></div>,
+                <button onClick={toggleWorkspaces}>Workspaces</button></div>,
             <div className="main-buttonDiv" key="logout" style={{ top: '34px', right: '1px', position: 'absolute' }} ref={logoutRef}>
                 <button onClick={() => request.get(ServerUtils.prepend(RouteStore.logout), emptyFunction)}>Log Out</button></div>
         ];
     }
 
+    @computed
+    get workspaceMenu() {
+        let areWorkspacesShown = () => this._workspacesShown;
+        let toggleWorkspaces = () => runInAction(() => { this._workspacesShown = !this._workspacesShown; });
+        let workspaces = CurrentUserUtils.UserDocument.GetT<ListField<Document>>(KeyStore.Workspaces, ListField);
+        return (!workspaces || workspaces === FieldWaiting) ? (null) :
+            <WorkspacesMenu active={this.mainContainer} open={this.openWorkspace}
+                new={this.createNewWorkspace} allWorkspaces={workspaces.Data}
+                isShown={areWorkspacesShown} toggle={toggleWorkspaces} />;
+    }
+
     render() {
-        let workspaceMenu: any = null;
-        let workspaces = this.userDocument.GetT<ListField<Document>>(KeyStore.Workspaces, ListField);
-        if (workspaces && workspaces !== FieldWaiting) {
-            workspaceMenu = <WorkspacesMenu active={this.mainContainer} open={this.openWorkspace} new={this.createNewWorkspace} allWorkspaces={workspaces.Data}
-                isShown={this.areWorkspacesShown} toggle={this.toggleWorkspaces} />;
-        }
         return (
-            <>
-                <div id="main-div">
-                    <DocumentDecorations />
-                    <Measure onResize={(r: any) => runInAction(() => {
-                        this.pwidth = r.entry.width;
-                        this.pheight = r.entry.height;
-                    })}>
-                        {({ measureRef }) =>
-                            <div ref={measureRef} id="mainContent-div">
-                                {this.mainContent}
-                                <PreviewCursor />
-                            </div>
-                        }
-                    </Measure>
-                    <ContextMenu />
-                    {this.nodesMenu}
-                    {this.miscButtons}
-                    {workspaceMenu}
-                    <InkingControl />
-                </div>
-                {this.activeTextBox}
-            </>
+            <div id="main-div">
+                <DocumentDecorations />
+                {this.mainContent}
+                <PreviewCursor />
+                <ContextMenu />
+                {this.nodesMenu}
+                {this.miscButtons}
+                {this.workspaceMenu}
+                <InkingControl />
+                <MainOverlayTextBox />
+            </div>
         );
     }
 
     // --------------- Northstar hooks ------------- /
+    private _northstarSchemas: Document[] = [];
 
-    @action AddToNorthstarCatalog(ctlog: Catalog) {
-        CurrentUserUtils.NorthstarDBCatalog = CurrentUserUtils.NorthstarDBCatalog ? CurrentUserUtils.NorthstarDBCatalog : ctlog;
+    @action SetNorthstarCatalog(ctlog: Catalog) {
+        CurrentUserUtils.NorthstarDBCatalog = ctlog;
         if (ctlog && ctlog.schemas) {
             ctlog.schemas.map(schema => {
-                let promises: Promise<void>[] = [];
                 let schemaDocuments: Document[] = [];
-                CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => {
-                    let prom = Server.GetField(attr.displayName! + ".alias").then(action((field: Opt<Field>) => {
+                let attributesToBecomeDocs = CurrentUserUtils.GetAllNorthstarColumnAttributes(schema);
+                Promise.all(attributesToBecomeDocs.reduce((promises, attr) => {
+                    promises.push(Server.GetField(attr.displayName! + ".alias").then(action((field: Opt<Field>) => {
                         if (field instanceof Document) {
                             schemaDocuments.push(field);
                         } else {
@@ -430,32 +322,17 @@ export class Main extends React.Component {
                                 new AttributeTransformationModel(atmod, AggregateFunction.Count));
                             schemaDocuments.push(Documents.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! }, undefined, attr.displayName! + ".alias"));
                         }
-                    }));
-                    promises.push(prom);
-                });
-                Promise.all(promises).finally(() => {
-                    let schemaDoc = Documents.TreeDocument(schemaDocuments, { width: 50, height: 100, title: schema.displayName! });
-                    this._northstarSchemas.push(schemaDoc);
-                });
+                    })));
+                    return promises;
+                }, [] as Promise<void>[])).finally(() =>
+                    this._northstarSchemas.push(Documents.TreeDocument(schemaDocuments, { width: 50, height: 100, title: schema.displayName! })));
             });
         }
     }
     async initializeNorthstar(): Promise<void> {
-        let envPath = "/assets/env.json";
-        const response = await fetch(envPath, {
-            redirect: "follow",
-            method: "GET",
-            credentials: "include"
-        });
-        const env = await response.json();
-        Settings.Instance.Update(env);
-        let cat = Gateway.Instance.ClearCatalog();
-        cat.then(async () => {
-            this.AddToNorthstarCatalog(await Gateway.Instance.GetCatalog());
-            // if (!CurrentUserUtils.GetNorthstarSchema("Book1"))
-            //     this.AddToNorthstarCatalog(await Gateway.Instance.GetSchema("http://www.cs.brown.edu/~bcz/Book1.csv", "Book1"));
-        });
-
+        const getEnvironment = await fetch("/assets/env.json", { redirect: "follow", method: "GET", credentials: "include" });
+        NorthstarSettings.Instance.UpdateEnvironment(await getEnvironment.json());
+        Gateway.Instance.ClearCatalog().then(async () => this.SetNorthstarCatalog(await Gateway.Instance.GetCatalog()));
     }
 }
 
diff --git a/src/client/views/MainOverlayTextBox.scss b/src/client/views/MainOverlayTextBox.scss
new file mode 100644
index 000000000..697d68c8c
--- /dev/null
+++ b/src/client/views/MainOverlayTextBox.scss
@@ -0,0 +1,19 @@
+@import "globalCssVariables";
+.mainOverlayTextBox-textInput {
+    background-color: rgba(248, 6, 6, 0.001);
+    width: 200px;
+    height: 200px;
+    position:absolute;
+    overflow: visible;
+    top: 0;
+    left: 0;
+    z-index: $mainTextInput-zindex;
+    .formattedTextBox-cont {
+        background-color: rgba(248, 6, 6, 0.001);
+        width: 100%;
+        height: 100%;
+        position:absolute;
+        top: 0;
+        left: 0;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
new file mode 100644
index 000000000..df1addbc7
--- /dev/null
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -0,0 +1,110 @@
+import { action, observable, trace } from 'mobx';
+import { observer } from 'mobx-react';
+import "normalize.css";
+import * as React from 'react';
+import { Document } from '../../fields/Document';
+import { Key } from '../../fields/Key';
+import { KeyStore } from '../../fields/KeyStore';
+import { emptyDocFunction, emptyFunction, returnTrue } from '../../Utils';
+import '../northstar/model/ModelExtensions';
+import '../northstar/utils/Extensions';
+import { DragManager } from '../util/DragManager';
+import { Transform } from '../util/Transform';
+import "./MainOverlayTextBox.scss";
+import { FormattedTextBox } from './nodes/FormattedTextBox';
+
+interface MainOverlayTextBoxProps {
+}
+
+@observer
+export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps> {
+    public static Instance: MainOverlayTextBox;
+    @observable public TextDoc?: Document = undefined;
+    public TextScroll: number = 0;
+    private _textRect: any;
+    private _textXf: Transform = Transform.Identity();
+    private _textFieldKey: Key = KeyStore.Data;
+    private _textColor: string | null = null;
+    private _textTargetDiv: HTMLDivElement | undefined;
+    private _textProxyDiv: React.RefObject<HTMLDivElement>;
+
+    constructor(props: MainOverlayTextBoxProps) {
+        super(props);
+        this._textProxyDiv = React.createRef();
+        MainOverlayTextBox.Instance = this;
+    }
+
+    @action
+    SetTextDoc(textDoc?: Document, textFieldKey?: Key, div?: HTMLDivElement, tx?: Transform) {
+        if (this._textTargetDiv) {
+            this._textTargetDiv.style.color = this._textColor;
+        }
+
+        this.TextDoc = undefined;
+        this.TextDoc = textDoc;
+        this._textFieldKey = textFieldKey!;
+        this._textXf = tx ? tx : Transform.Identity();
+        this._textTargetDiv = div;
+        if (div) {
+            this._textColor = div.style.color;
+            div.style.color = "transparent";
+            this._textRect = div.getBoundingClientRect();
+            this.TextScroll = div.scrollTop;
+        }
+    }
+
+    @action
+    textScroll = (e: React.UIEvent) => {
+        if (this._textProxyDiv.current && this._textTargetDiv) {
+            this._textTargetDiv.scrollTop = this.TextScroll = this._textProxyDiv.current.children[0].scrollTop;
+        }
+    }
+
+    textBoxDown = (e: React.PointerEvent) => {
+        if (e.button !== 0 || e.metaKey || e.altKey) {
+            document.addEventListener("pointermove", this.textBoxMove);
+            document.addEventListener('pointerup', this.textBoxUp);
+        }
+    }
+    textBoxMove = (e: PointerEvent) => {
+        if (e.movementX > 1 || e.movementY > 1) {
+            document.removeEventListener("pointermove", this.textBoxMove);
+            document.removeEventListener('pointerup', this.textBoxUp);
+            let dragData = new DragManager.DocumentDragData([this.TextDoc!]);
+            const [left, top] = this._textXf
+                .inverse()
+                .transformPoint(0, 0);
+            dragData.xOffset = e.clientX - left;
+            dragData.yOffset = e.clientY - top;
+            DragManager.StartDocumentDrag([this._textTargetDiv!], dragData, e.clientX, e.clientY, {
+                handlers: {
+                    dragComplete: action(emptyFunction),
+                },
+                hideSource: false
+            });
+        }
+    }
+    textBoxUp = (e: PointerEvent) => {
+        document.removeEventListener("pointermove", this.textBoxMove);
+        document.removeEventListener('pointerup', this.textBoxUp);
+    }
+
+    render() {
+        if (this.TextDoc) {
+            let x: number = this._textRect.x;
+            let y: number = this._textRect.y;
+            let w: number = this._textRect.width;
+            let h: number = this._textRect.height;
+            let t = this._textXf.transformPoint(0, 0);
+            let s = this._textXf.transformPoint(1, 0);
+            s[0] = Math.sqrt((s[0] - t[0]) * (s[0] - t[0]) + (s[1] - t[1]) * (s[1] - t[1]));
+            return <div className="mainOverlayTextBox-textInput" style={{ pointerEvents: "none", transform: `translate(${x}px, ${y}px) scale(${1 / s[0]},${1 / s[0]})`, width: "auto", height: "auto" }} >
+                <div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll} style={{ pointerEvents: "none", transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}>
+                    <FormattedTextBox fieldKey={this._textFieldKey!} isOverlay={true} Document={this.TextDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
+                        selectOnLoad={true} ContainingCollectionView={undefined} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={emptyDocFunction} />
+                </div>
+            </ div>;
+        }
+        else return (null);
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 6c382a353..cb4e8fb2e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -24,6 +24,7 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
+import { MainOverlayTextBox } from "../../MainOverlayTextBox";
 
 @observer
 export class CollectionFreeFormView extends CollectionSubView {
@@ -205,7 +206,7 @@ export class CollectionFreeFormView extends CollectionSubView {
 
     @action
     private SetPan(panX: number, panY: number) {
-        Main.Instance.SetTextDoc();
+        MainOverlayTextBox.Instance.SetTextDoc();
         var x1 = this.getLocalTransform().inverse().Scale;
         const newPanX = Math.min((1 - 1 / x1) * this.nativeWidth, Math.max(0, panX));
         const newPanY = Math.min((1 - 1 / x1) * this.nativeHeight, Math.max(0, panY));
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 2e6272836..87c1bcb1e 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -16,6 +16,7 @@ import "./FormattedTextBox.scss";
 import React = require("react");
 import { TextField } from "../../../fields/TextField";
 import { KeyStore } from "../../../fields/KeyStore";
+import { MainOverlayTextBox } from "../MainOverlayTextBox";
 const { buildMenuItems } = require("prosemirror-example-setup");
 const { menuBar } = require("prosemirror-menu");
 
@@ -92,7 +93,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
         };
 
         if (this.props.isOverlay) {
-            this._inputReactionDisposer = reaction(() => Main.Instance._textDoc && Main.Instance._textDoc.Id,
+            this._inputReactionDisposer = reaction(() => MainOverlayTextBox.Instance.TextDoc && MainOverlayTextBox.Instance.TextDoc.Id,
                 () => {
                     if (this._editorView) {
                         this._editorView.destroy();
@@ -103,7 +104,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
             );
         } else {
             this._proxyReactionDisposer = reaction(() => this.props.isSelected(),
-                () => this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform()));
+                () => this.props.isSelected() && MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform()));
         }
 
         this._reactionDisposer = reaction(
@@ -184,10 +185,10 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
 
     onFocused = (e: React.FocusEvent): void => {
         if (!this.props.isOverlay) {
-            Main.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform());
+            MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform());
         } else {
             if (this._ref.current) {
-                this._ref.current.scrollTop = Main.Instance._textScroll;
+                this._ref.current.scrollTop = MainOverlayTextBox.Instance.TextScroll;
             }
         }
     }
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index 19431bbe3..16a909eb8 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -49,7 +49,7 @@ export namespace KeyStore {
     export const CopyDraggedItems = new Key("CopyDraggedItems");
 
     export const KeyList: Key[] = [Prototype, X, Y, Page, Title, Author, PanX, PanY, Scale, NativeWidth, NativeHeight,
-        Width, Height, ZIndex, Data, Annotations, ViewType, Layout, BackgroundColor, BackgroundLayout, OverlayLayout, LayoutKeys,
+        Width, Height, ZIndex, Zoom, Data, Annotations, ViewType, Layout, BackgroundColor, BackgroundLayout, OverlayLayout, LayoutKeys,
         LayoutFields, ColumnsKey, SchemaSplitPercentage, Caption, ActiveWorkspace, DocumentText, BrushingDocs, LinkedToDocs, LinkedFromDocs,
         LinkDescription, LinkTags, Thumbnail, ThumbnailPage, CurPage, AnnotationOn, NumPages, Ink, Cursors, OptionalRightCollection,
         Archives, Workspaces, Minimized, CopyDraggedItems
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 13eddafbf..34454eda0 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -1,15 +1,14 @@
-import { DashUserModel } from "./user_model";
+import { computed, observable, action } from "mobx";
 import * as rp from 'request-promise';
-import { RouteStore } from "../../RouteStore";
-import { ServerUtils } from "../../ServerUtil";
+import { Documents } from "../../../client/documents/Documents";
+import { Attribute, AttributeGroup, Catalog, Schema } from "../../../client/northstar/model/idea/idea";
+import { ArrayUtil } from "../../../client/northstar/utils/ArrayUtil";
 import { Server } from "../../../client/Server";
 import { Document } from "../../../fields/Document";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ListField } from "../../../fields/ListField";
-import { Documents } from "../../../client/documents/Documents";
-import { Schema, Attribute, AttributeGroup, Catalog } from "../../../client/northstar/model/idea/idea";
-import { observable, computed, action } from "mobx";
-import { ArrayUtil } from "../../../client/northstar/utils/ArrayUtil";
+import { RouteStore } from "../../RouteStore";
+import { ServerUtils } from "../../ServerUtil";
 
 export class CurrentUserUtils {
     private static curr_email: string;
@@ -17,65 +16,22 @@ export class CurrentUserUtils {
     private static user_document: Document;
     //TODO tfs: these should be temporary...
     private static mainDocId: string | undefined;
-    @observable private static catalog?: Catalog;
-
-    public static get email(): string {
-        return this.curr_email;
-    }
-
-    public static get id(): string {
-        return this.curr_id;
-    }
-
-    public static get UserDocument(): Document {
-        return this.user_document;
-    }
 
-    public static get MainDocId(): string | undefined {
-        return this.mainDocId;
-    }
-
-    public static set MainDocId(id: string | undefined) {
-        this.mainDocId = id;
-    }
-
-    @computed public static get NorthstarDBCatalog(): Catalog | undefined {
-        return this.catalog;
-    }
-    public static set NorthstarDBCatalog(ctlog: Catalog | undefined) {
-        this.catalog = ctlog;
-    }
-    public static GetNorthstarSchema(name: string): Schema | undefined {
-        return !this.catalog || !this.catalog.schemas ? undefined :
-            ArrayUtil.FirstOrDefault<Schema>(this.catalog.schemas, (s: Schema) => s.displayName === name);
-    }
-    public static GetAllNorthstarColumnAttributes(schema: Schema) {
-        if (!schema || !schema.rootAttributeGroup) {
-            return [];
-        }
-        const recurs = (attrs: Attribute[], g: AttributeGroup) => {
-            if (g.attributes) {
-                attrs.push.apply(attrs, g.attributes);
-                if (g.attributeGroups) {
-                    g.attributeGroups.forEach(ng => recurs(attrs, ng));
-                }
-            }
-        };
-        const allAttributes: Attribute[] = new Array<Attribute>();
-        recurs(allAttributes, schema.rootAttributeGroup);
-        return allAttributes;
-    }
+    public static get email() { return this.curr_email; }
+    public static get id() { return this.curr_id; }
+    public static get UserDocument() { return this.user_document; }
+    public static get MainDocId() { return this.mainDocId; }
+    public static set MainDocId(id: string | undefined) { this.mainDocId = id; }
 
     private static createUserDocument(id: string): Document {
         let doc = new Document(id);
-
         doc.Set(KeyStore.Workspaces, new ListField<Document>());
         doc.Set(KeyStore.OptionalRightCollection, Documents.SchemaDocument([], { title: "Pending documents" }));
         return doc;
     }
 
     public static loadCurrentUser(): Promise<any> {
-        let userPromise = rp.get(ServerUtils.prepend(RouteStore.getCurrUser)).then((response) => {
+        let userPromise = rp.get(ServerUtils.prepend(RouteStore.getCurrUser)).then(response => {
             if (response) {
                 let obj = JSON.parse(response);
                 CurrentUserUtils.curr_id = obj.id as string;
@@ -86,17 +42,34 @@ export class CurrentUserUtils {
         });
         let userDocPromise = rp.get(ServerUtils.prepend(RouteStore.getUserDocumentId)).then(id => {
             if (id) {
-                return Server.GetField(id).then(field => {
-                    if (field instanceof Document) {
-                        this.user_document = field;
-                    } else {
-                        this.user_document = this.createUserDocument(id);
-                    }
-                });
+                return Server.GetField(id).then(field =>
+                    this.user_document = field instanceof Document ? field : this.createUserDocument(id));
             } else {
                 throw new Error("There should be a user id! Why does Dash think there isn't one?");
             }
         });
         return Promise.all([userPromise, userDocPromise]);
     }
+
+    /* Northstar catalog ... really just for testing so this should eventually go away */
+    @observable private static _northstarCatalog?: Catalog;
+    @computed public static get NorthstarDBCatalog() { return this._northstarCatalog; }
+    public static set NorthstarDBCatalog(ctlog: Catalog | undefined) { this._northstarCatalog = ctlog; }
+
+    public static GetNorthstarSchema(name: string): Schema | undefined {
+        return !this._northstarCatalog || !this._northstarCatalog.schemas ? undefined :
+            ArrayUtil.FirstOrDefault<Schema>(this._northstarCatalog.schemas, (s: Schema) => s.displayName === name);
+    }
+    public static GetAllNorthstarColumnAttributes(schema: Schema) {
+        const recurs = (attrs: Attribute[], g?: AttributeGroup) => {
+            if (g && g.attributes) {
+                attrs.push.apply(attrs, g.attributes);
+                if (g.attributeGroups) {
+                    g.attributeGroups.forEach(ng => recurs(attrs, ng));
+                }
+            }
+            return attrs;
+        };
+        return recurs([] as Attribute[], schema ? schema.rootAttributeGroup : undefined);
+    }
 }
\ No newline at end of file
diff --git a/src/server/authentication/models/user_model.ts b/src/server/authentication/models/user_model.ts
index 1c6926517..d5c84c311 100644
--- a/src/server/authentication/models/user_model.ts
+++ b/src/server/authentication/models/user_model.ts
@@ -18,8 +18,8 @@ mongoose.connection.on('disconnected', function () {
 export type DashUserModel = mongoose.Document & {
     email: string,
     password: string,
-    passwordResetToken: string | undefined,
-    passwordResetExpires: Date | undefined,
+    passwordResetToken?: string,
+    passwordResetExpires?: Date,
 
     userDocumentId: string;
 
@@ -67,11 +67,17 @@ const userSchema = new mongoose.Schema({
  */
 userSchema.pre("save", function save(next) {
     const user = this as DashUserModel;
-    if (!user.isModified("password")) { return next(); }
+    if (!user.isModified("password")) {
+        return next();
+    }
     bcrypt.genSalt(10, (err, salt) => {
-        if (err) { return next(err); }
+        if (err) {
+            return next(err);
+        }
         bcrypt.hash(user.password, salt, () => void {}, (err: mongoose.Error, hash) => {
-            if (err) { return next(err); }
+            if (err) {
+                return next(err);
+            }
             user.password = hash;
             next();
         });
@@ -79,9 +85,8 @@ userSchema.pre("save", function save(next) {
 });
 
 const comparePassword: comparePasswordFunction = function (this: DashUserModel, candidatePassword, cb) {
-    bcrypt.compare(candidatePassword, this.password, (err: mongoose.Error, isMatch: boolean) => {
-        cb(err, isMatch);
-    });
+    bcrypt.compare(candidatePassword, this.password, (err: mongoose.Error, isMatch: boolean) =>
+        cb(err, isMatch));
 };
 
 userSchema.methods.comparePassword = comparePassword;
-- 
cgit v1.2.3-70-g09d2

