From 1cdb04707301bc51b3ed6dd1c15c79a7b989b7f7 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Mon, 1 Apr 2019 19:54:05 -0400
Subject: Started refactoring props and collections

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

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 60fb95ff5..51ab85b71 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -231,7 +231,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
             RemoveDocument: this.props.removeDocument,
             ScreenToLocalTransform: this.getTransform,
             isTopMost: false,
-            SelectOnLoad: document.Id == this._selectOnLoaded,
+            selectOnLoad: document.Id == this._selectOnLoaded,
             PanelWidth: document.Width,
             PanelHeight: document.Height,
             ContentScaling: this.noScaling,
-- 
cgit v1.2.3-70-g09d2


From 5889bf159ee0a0f6567683b2bb8c2475feccf9ec Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Wed, 3 Apr 2019 01:38:29 -0400
Subject: Almost compiles

---
 src/Utils.ts                                       | 10 ++++++
 src/client/Server.ts                               |  2 +-
 src/client/documents/Documents.ts                  |  3 +-
 src/client/northstar/dash-nodes/HistogramBox.tsx   | 14 ++++----
 src/client/util/DragManager.ts                     |  2 +-
 src/client/util/Transform.ts                       |  2 +-
 src/client/util/type_decls.d                       |  2 +-
 src/client/views/Main.tsx                          | 10 +++---
 .../views/collections/CollectionBaseView.tsx       | 28 +++++++++++++---
 .../views/collections/CollectionDockingView.tsx    |  9 ++---
 src/client/views/collections/CollectionPDFView.tsx | 36 ++++++++++----------
 .../views/collections/CollectionSchemaView.tsx     | 12 +++++--
 .../views/collections/CollectionTreeView.tsx       |  3 +-
 .../views/collections/CollectionVideoView.tsx      | 39 +++++++++++-----------
 src/client/views/collections/CollectionView.tsx    |  4 +--
 .../views/collections/CollectionViewBase.tsx       |  7 ++--
 .../CollectionFreeFormRemoteCursors.tsx            | 17 ++--------
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 30 +++++++++++------
 src/client/views/nodes/DocumentContentsView.tsx    | 18 +++++++++-
 src/client/views/nodes/DocumentView.tsx            | 37 +++++++++-----------
 src/client/views/nodes/FieldView.tsx               | 17 ++++++----
 src/client/views/nodes/KeyValuePair.tsx            |  4 +++
 src/fields/Field.ts                                |  4 +--
 23 files changed, 181 insertions(+), 129 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index a5d9bd0ca..3eb192eb8 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -86,4 +86,14 @@ export class Utils {
     }
 }
 
+export function returnTrue() {
+    return true;
+}
+
+export function returnFalse() {
+    return false;
+}
+
+export function emptyFunction() { }
+
 export type Without<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
\ No newline at end of file
diff --git a/src/client/Server.ts b/src/client/Server.ts
index 37e3c2c0d..c301b04d3 100644
--- a/src/client/Server.ts
+++ b/src/client/Server.ts
@@ -42,7 +42,7 @@ export class Server {
                 reaction(() => {
                     return this.ClientFieldsCached.get(fieldid);
                 }, (field, reaction) => {
-                    if (field !== "<Waiting>") {
+                    if (field !== FieldWaiting) {
                         reaction.dispose()
                         cb(field)
                     }
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 0bf275df8..15f980895 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -17,7 +17,8 @@ import { HistogramOperation } from "../northstar/operations/HistogramOperation";
 import { Server } from "../Server";
 import { CollectionPDFView } from "../views/collections/CollectionPDFView";
 import { CollectionVideoView } from "../views/collections/CollectionVideoView";
-import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
+import { CollectionView } from "../views/collections/CollectionView";
+import { CollectionViewType } from "../views/collections/CollectionBaseView";
 import { AudioBox } from "../views/nodes/AudioBox";
 import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
 import { ImageBox } from "../views/nodes/ImageBox";
diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index a968def96..4457c9502 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -117,14 +117,14 @@ export class HistogramBox extends React.Component<FieldViewProps> {
 
     activateHistogramOperation(catalog?: Catalog) {
         if (catalog) {
-            this.props.doc.GetTAsync(this.props.fieldKey, HistogramField).then((histoOp: Opt<HistogramField>) => runInAction(() => {
+            this.props.Document.GetTAsync(this.props.fieldKey, HistogramField).then((histoOp: Opt<HistogramField>) => runInAction(() => {
                 this.HistoOp = histoOp ? histoOp.Data : HistogramOperation.Empty;
                 if (this.HistoOp != HistogramOperation.Empty) {
-                    reaction(() => this.props.doc.GetList(KeyStore.LinkedFromDocs, []), (docs: Document[]) => this.HistoOp.Links.splice(0, this.HistoOp.Links.length, ...docs), { fireImmediately: true });
-                    reaction(() => this.props.doc.GetList(KeyStore.BrushingDocs, []).length,
+                    reaction(() => this.props.Document.GetList(KeyStore.LinkedFromDocs, []), (docs: Document[]) => this.HistoOp.Links.splice(0, this.HistoOp.Links.length, ...docs), { fireImmediately: true });
+                    reaction(() => this.props.Document.GetList(KeyStore.BrushingDocs, []).length,
                         () => {
-                            let brushingDocs = this.props.doc.GetList(KeyStore.BrushingDocs, [] as Document[]);
-                            let proto = this.props.doc.GetPrototype() as Document;
+                            let brushingDocs = this.props.Document.GetList(KeyStore.BrushingDocs, [] as Document[]);
+                            let proto = this.props.Document.GetPrototype() as Document;
                             this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...brushingDocs.map((brush, i) => {
                                 brush.SetNumber(KeyStore.BackgroundColor, StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length]);
                                 let brushed = brush.GetList(KeyStore.BrushingDocs, [] as Document[]);
@@ -139,8 +139,8 @@ export class HistogramBox extends React.Component<FieldViewProps> {
     render() {
         let labelY = this.HistoOp && this.HistoOp.Y ? this.HistoOp.Y.PresentedName : "<...>";
         let labelX = this.HistoOp && this.HistoOp.X ? this.HistoOp.X.PresentedName : "<...>";
-        var h = this.props.isTopMost ? this.PanelHeight : this.props.doc.GetNumber(KeyStore.Height, 0);
-        var w = this.props.isTopMost ? this.PanelWidth : this.props.doc.GetNumber(KeyStore.Width, 0);
+        var h = this.props.isTopMost ? this.PanelHeight : this.props.Document.GetNumber(KeyStore.Height, 0);
+        var w = this.props.isTopMost ? this.PanelWidth : this.props.Document.GetNumber(KeyStore.Width, 0);
         let loff = this.SizeConverter.LeftOffset;
         let toff = this.SizeConverter.TopOffset;
         let roff = this.SizeConverter.RightOffset;
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 70b1c9829..53bbae18d 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -110,7 +110,7 @@ export namespace DragManager {
         xOffset?: number;
         yOffset?: number;
         aliasOnDrop?: boolean;
-        removeDocument?: (collectionDrop: CollectionView) => void;
+        moveDocument?: (targetCollection: Document, addDocument: (document: Document) => void) => void;
         [id: string]: any;
     }
     export function StartDocumentDrag(ele: HTMLElement, dragData: DocumentDragData, options?: DragOptions) {
diff --git a/src/client/util/Transform.ts b/src/client/util/Transform.ts
index 3e1039166..889134e3e 100644
--- a/src/client/util/Transform.ts
+++ b/src/client/util/Transform.ts
@@ -3,7 +3,7 @@ export class Transform {
     private _translateY: number = 0;
     private _scale: number = 1;
 
-    static get Identity(): Transform {
+    static Identity(): Transform {
         return new Transform(0, 0, 1);
     }
 
diff --git a/src/client/util/type_decls.d b/src/client/util/type_decls.d
index 4f69053b1..47c3481b2 100644
--- a/src/client/util/type_decls.d
+++ b/src/client/util/type_decls.d
@@ -181,7 +181,7 @@ declare class Key extends Field {
     Copy(): Field;
     ToScriptString(): string;
 }
-declare type FIELD_WAITING = "<Waiting>";
+declare type FIELD_WAITING = null;
 declare type Opt<T> = T | undefined;
 declare type FieldValue<T> = Opt<T> | FIELD_WAITING;
 // @ts-ignore
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 82fd1e7c9..bcbe6b987 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -6,7 +6,7 @@ import { Document } from '../../fields/Document';
 import { KeyStore } from '../../fields/KeyStore';
 import "./Main.scss";
 import { MessageStore } from '../../server/Message';
-import { Utils } from '../../Utils';
+import { Utils, returnTrue } from '../../Utils';
 import * as request from 'request'
 import * as rp from 'request-promise'
 import { Documents } from '../documents/Documents';
@@ -212,7 +212,6 @@ export class Main extends React.Component {
         this.workspacesShown = !this.workspacesShown;
     }
 
-    screenToLocalTransform = () => Transform.Identity
     pwidthFunc = () => this.pwidth;
     pheightFunc = () => this.pheight;
     focusDocument = (doc: Document) => { }
@@ -221,15 +220,16 @@ export class Main extends React.Component {
     get content() {
         return !this.mainContainer ? (null) :
             <DocumentView Document={this.mainContainer}
-                AddDocument={undefined}
-                RemoveDocument={undefined}
-                ScreenToLocalTransform={this.screenToLocalTransform}
+                addDocument={undefined}
+                removeDocument={undefined}
+                ScreenToLocalTransform={Transform.Identity}
                 ContentScaling={this.noScaling}
                 PanelWidth={this.pwidthFunc}
                 PanelHeight={this.pheightFunc}
                 isTopMost={true}
                 selectOnLoad={false}
                 focus={this.focusDocument}
+                parentActive={returnTrue}
                 ContainingCollectionView={undefined} />
     }
 
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index c8c840085..c9f640c4a 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -19,16 +19,21 @@ export enum CollectionViewType {
 }
 
 export interface CollectionRenderProps {
-    addDocument: (document: Document, allowDuplicates: boolean) => void;
+    addDocument: (document: Document, allowDuplicates?: boolean) => boolean;
     removeDocument: (document: Document) => boolean;
+    moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => void) => boolean;
     active: () => boolean;
 }
 
 export interface CollectionViewProps extends FieldViewProps {
     onContextMenu?: (e: React.MouseEvent) => void;
-    children: (type: CollectionViewType, props: CollectionRenderProps) => JSX.Element | null;
+    children: (type: CollectionViewType, props: CollectionRenderProps) => JSX.Element | JSX.Element[] | null;
+    className?: string;
+    contentRef?: React.Ref<HTMLDivElement>;
 }
 
+export const COLLECTION_BORDER_WIDTH = 1;
+
 export class CollectionBaseView extends React.Component<CollectionViewProps> {
     get collectionViewType(): CollectionViewType {
         let Document = this.props.Document;
@@ -44,7 +49,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
 
     active = (): boolean => {
         var isSelected = this.props.isSelected();
-        var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == self);
+        var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this);
         var topMost = this.props.isTopMost;
         return isSelected || childSelected || topMost;
     }
@@ -68,7 +73,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
     }
 
     @action.bound
-    addDocument(doc: Document, allowDuplicates: boolean): boolean {
+    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));
@@ -123,14 +128,27 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         return false
     }
 
+    @action.bound
+    moveDocument(doc: Document, targetCollection: Document, addDocument: (doc: Document) => void): boolean {
+        if (this.props.Document === targetCollection) {
+            return false;
+        }
+        if (this.removeDocument(doc)) {
+            addDocument(doc);
+            return true;
+        }
+        return false;
+    }
+
     render() {
         const props: CollectionRenderProps = {
             addDocument: this.addDocument,
             removeDocument: this.removeDocument,
+            moveDocument: this.moveDocument,
             active: this.active
         }
         return (
-            <div className="collectionView-cont" onContextMenu={this.props.onContextMenu}>
+            <div className={this.props.className || "collectionView-cont"} onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}>
                 {this.props.children(this.collectionViewType, props)}
             </div>
         )
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index c67354139..bb34340fb 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -8,12 +8,12 @@ import { Document } from "../../../fields/Document";
 import { KeyStore } from "../../../fields/KeyStore";
 import Measure from "react-measure";
 import { FieldId, Opt, Field } from "../../../fields/Field";
-import { Utils } from "../../../Utils";
+import { Utils, returnTrue } from "../../../Utils";
 import { Server } from "../../Server";
 import { undoBatch } from "../../util/UndoManager";
 import { DocumentView } from "../nodes/DocumentView";
 import "./CollectionDockingView.scss";
-import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
+import { COLLECTION_BORDER_WIDTH } from "./CollectionBaseView";
 import React = require("react");
 import { SubCollectionViewProps } from "./CollectionViewBase";
 import { ServerUtils } from "../../../server/ServerUtil";
@@ -310,14 +310,15 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         var content =
             <div className="collectionDockingView-content" ref={this._mainCont}>
                 <DocumentView key={this._document.Id} Document={this._document}
-                    AddDocument={undefined}
-                    RemoveDocument={undefined}
+                    addDocument={undefined}
+                    removeDocument={undefined}
                     ContentScaling={this._contentScaling}
                     PanelWidth={this._nativeWidth}
                     PanelHeight={this._nativeHeight}
                     ScreenToLocalTransform={this.ScreenToLocalTransform}
                     isTopMost={true}
                     selectOnLoad={false}
+                    parentActive={returnTrue}
                     focus={(doc: Document) => { }}
                     ContainingCollectionView={undefined} />
             </div>
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 6bb3bacc7..14ed70b8c 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -1,23 +1,20 @@
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
-import { Document } from "../../../fields/Document";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ContextMenu } from "../ContextMenu";
-import { CollectionView, CollectionViewType } from "./CollectionView";
 import { CollectionViewProps } from "./CollectionViewBase";
 import "./CollectionPDFView.scss"
 import React = require("react");
-import { FieldId } from "../../../fields/Field";
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
+import { FieldView } from "../nodes/FieldView";
+import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from "./CollectionBaseView";
 
 
 @observer
 export class CollectionPDFView extends React.Component<CollectionViewProps> {
 
     public static LayoutString(fieldKey: string = "DataKey") {
-        return `<${CollectionPDFView.name} Document={Document}
-                    ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings}
-                    isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`;
+        return FieldView.LayoutString(CollectionPDFView, fieldKey);
     }
 
     private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, -1); }
@@ -35,26 +32,27 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
         );
     }
 
-    // "inherited" CollectionView API starts here...
-    @observable
-    public SelectedDocs: FieldId[] = []
-    public active: () => boolean = () => CollectionView.Active(this);
-
-    addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); }
-    removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
-
-    specificContextMenu = (e: React.MouseEvent): void => {
+    onContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped() && this.props.Document.Id != "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
             ContextMenu.Instance.addItem({ description: "PDFOptions", event: () => { } });
         }
     }
 
-    render() {
+    private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => {
+        let props = { ...renderProps, ...this.props };
         return (
-            <div className="collectionPdfView-cont" onContextMenu={this.specificContextMenu}>
-                <CollectionFreeFormView {...CollectionView.SubViewProps(this)} />
+            <>
+                <CollectionFreeFormView {...props} />
                 {this.props.isSelected() ? this.uIButtons : (null)}
-            </div>
+            </>
+        )
+    }
+
+    render() {
+        return (
+            <CollectionBaseView {...this.props} className="collectionPdfView-cont" onContextMenu={this.onContextMenu}>
+                {this.subView}
+            </CollectionBaseView>
         )
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 0c7e28691..0eacd38b0 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -22,9 +22,11 @@ import { EditableView } from "../EditableView";
 import { DocumentView } from "../nodes/DocumentView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
-import { CollectionView, COLLECTION_BORDER_WIDTH } from "./CollectionView";
+import { CollectionView } from "./CollectionView";
 import { CollectionViewBase } from "./CollectionViewBase";
 import { TextField } from "../../../fields/TextField";
+import { COLLECTION_BORDER_WIDTH } from "./CollectionBaseView";
+import { emptyFunction, returnFalse } from "../../../Utils";
 
 
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
@@ -79,6 +81,9 @@ export class CollectionSchemaView extends CollectionViewBase {
             select: () => { },
             isTopMost: false,
             selectOnLoad: false,
+            ScreenToLocalTransform: Transform.Identity,
+            focus: emptyFunction,
+            active: returnFalse
         }
         let contents = (
             <FieldView {...props} />
@@ -295,15 +300,16 @@ export class CollectionSchemaView extends CollectionViewBase {
                 {({ measureRef }) =>
                     <div className="collectionSchemaView-content" ref={measureRef}>
                         {doc instanceof Document ? <DocumentView Document={doc}
-                            AddDocument={this.props.addDocument} RemoveDocument={this.props.removeDocument}
+                            addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
                             isTopMost={false}
                             selectOnLoad={false}
                             ScreenToLocalTransform={this.getPreviewTransform}
                             ContentScaling={this.getContentScaling}
                             PanelWidth={this.getPanelWidth}
                             PanelHeight={this.getPanelHeight}
-                            ContainingCollectionView={this.props.CollectionView}
+                            ContainingCollectionView={undefined}
                             focus={this.focusDocument}
+                            parentActive={this.props.active}
                         /> : null}
                         <input value={this.previewScript} onChange={this.onPreviewScriptChange}
                             style={{ position: 'absolute', bottom: '0px' }} />
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 70790af18..6f9ba40d2 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -10,9 +10,10 @@ import { ListField } from "../../../fields/ListField";
 import { setupDrag } from "../../util/DragManager";
 import { EditableView } from "../EditableView";
 import "./CollectionTreeView.scss";
-import { CollectionView, COLLECTION_BORDER_WIDTH } from "./CollectionView";
+import { CollectionView } from "./CollectionView";
 import { CollectionViewBase } from "./CollectionViewBase";
 import React = require("react")
+import { COLLECTION_BORDER_WIDTH } from './CollectionBaseView';
 
 
 export interface TreeViewProps {
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 1f0143d87..7cb461b4d 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -3,12 +3,14 @@ import { observer } from "mobx-react";
 import { Document } from "../../../fields/Document";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ContextMenu } from "../ContextMenu";
-import { CollectionView, CollectionViewType } from "./CollectionView";
+import { CollectionView } from "./CollectionView";
+import { CollectionViewType, CollectionBaseView, CollectionRenderProps } from "./CollectionBaseView";
 import { CollectionViewProps } from "./CollectionViewBase";
 import React = require("react");
 import { FieldId } from "../../../fields/Field";
 import "./CollectionVideoView.scss"
-import { CollectionFreeFormView } from "./CollectionFreeFormView";
+import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
+import { FieldView } from "../nodes/FieldView";
 
 
 @observer
@@ -20,9 +22,7 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
     @observable _isPlaying: boolean = false;
 
     public static LayoutString(fieldKey: string = "DataKey") {
-        return `<${CollectionVideoView.name} Document={Document}
-                    ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings}
-                    isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`;
+        return FieldView.LayoutString(CollectionVideoView, fieldKey);
     }
     private get uIButtons() {
         let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]);
@@ -102,26 +102,27 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
 
     }
 
-    // "inherited" CollectionView API starts here...
-
-    @observable
-    public SelectedDocs: FieldId[] = []
-    public active: () => boolean = () => CollectionView.Active(this);
-
-    addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); }
-    removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
-
-    specificContextMenu = (e: React.MouseEvent): void => {
+    onContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped() && this.props.Document.Id != "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
             ContextMenu.Instance.addItem({ description: "VideoOptions", event: () => { } });
         }
     }
 
+    private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => {
+        let props = { ...renderProps, ...this.props };
+        return (
+            <>
+                <CollectionFreeFormView {...props} />
+                {this.props.isSelected() ? this.uIButtons : (null)}
+            </>
+        )
+    }
+
     render() {
         trace();
-        return (<div className="collectionVideoView-cont" ref={this.mainCont} onContextMenu={this.specificContextMenu}>
-            <CollectionFreeFormView {...CollectionView.SubViewProps(this)} />
-            {this.props.isSelected() ? this.uIButtons : (null)}
-        </div>)
+        return (
+            <CollectionBaseView {...this.props} className="collectionVideoView-cont" contentRef={this.mainCont} onContextMenu={this.onContextMenu}>
+                {this.subView}
+            </CollectionBaseView>)
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 51cc99595..d440dcff9 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -12,7 +12,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
     public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(CollectionView, fieldStr) }
 
     private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
-        let props = { ...renderProps, ...this.props };
+        let props = { ...this.props, ...renderProps };
         switch (type) {
             case CollectionViewType.Freeform: return (<CollectionFreeFormView {...props} />)
             case CollectionViewType.Schema: return (<CollectionSchemaView {...props} />)
@@ -34,7 +34,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
         return (
             <CollectionBaseView {...this.props} onContextMenu={this.onContextMenu}>
                 {this.SubView}
-            </CollectionBaseView> >
+            </CollectionBaseView>
         )
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index ca4f3b12f..4a0b2312e 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -20,6 +20,9 @@ import { Server } from "../../Server";
 import { FieldViewProps } from "../nodes/FieldView";
 
 export interface CollectionViewProps extends FieldViewProps {
+    addDocument: (document: Document, allowDuplicates?: boolean) => boolean;
+    removeDocument: (document: Document) => boolean;
+    moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => void) => boolean;
 }
 
 export interface SubCollectionViewProps extends CollectionViewProps {
@@ -72,8 +75,8 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
                     de.data.draggedDocument.GetTAsync(key, NumberField, (f: Opt<NumberField>) => f ? de.data.droppedDocument.SetNumber(key, f.Data) : null));
             }
             let added = this.props.addDocument(de.data.droppedDocument, false);
-            if (added && de.data.removeDocument && !de.data.aliasOnDrop) {
-                de.data.removeDocument(this.props.CollectionView);
+            if (added && de.data.moveDocument && !de.data.aliasOnDrop) {
+                de.data.moveDocument(this.props.Document, this.props.addDocument);
             }
             e.stopPropagation();
             return added;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index 19382e66f..f664f4e65 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -1,21 +1,8 @@
-import { action, computed, observable } from "mobx";
+import { computed } from "mobx";
 import { observer } from "mobx-react";
-import { Document } from "../../../../fields/Document";
-import { FieldWaiting } from "../../../../fields/Field";
 import { KeyStore } from "../../../../fields/KeyStore";
-import { TextField } from "../../../../fields/TextField";
-import { DragManager } from "../../../util/DragManager";
-import { Transform } from "../../../util/Transform";
-import { undoBatch } from "../../../util/UndoManager";
-import { InkingCanvas } from "../../InkingCanvas";
-import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
-import { DocumentContentsView } from "../../nodes/DocumentContentsView";
-import { DocumentViewProps } from "../../nodes/DocumentView";
-import { COLLECTION_BORDER_WIDTH } from "../CollectionView";
-import { CollectionViewBase, CollectionViewProps, CursorEntry } from "../CollectionViewBase";
-import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView";
+import { CollectionViewProps, CursorEntry } from "../CollectionViewBase";
 import "./CollectionFreeFormView.scss";
-import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
 import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 51ab85b71..144d121db 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -11,7 +11,7 @@ import { InkingCanvas } from "../../InkingCanvas";
 import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
 import { DocumentContentsView } from "../../nodes/DocumentContentsView";
 import { DocumentViewProps } from "../../nodes/DocumentView";
-import { COLLECTION_BORDER_WIDTH } from "../CollectionView";
+import { COLLECTION_BORDER_WIDTH } from "../CollectionBaseView";
 import { CollectionViewBase } from "../CollectionViewBase";
 import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView";
 import "./CollectionFreeFormView.scss";
@@ -20,6 +20,8 @@ import React = require("react");
 import v5 = require("uuid/v5");
 import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
 import { PreviewCursor } from "./PreviewCursor";
+import { DocumentManager } from "../../../util/DocumentManager";
+import { SelectionManager } from "../../../util/SelectionManager";
 
 @observer
 export class CollectionFreeFormView extends CollectionViewBase {
@@ -39,8 +41,13 @@ export class CollectionFreeFormView extends CollectionViewBase {
     }
 
     public selectDocuments = (docs: Document[]) => {
-        this.props.CollectionView.SelectedDocs.length = 0;
-        docs.map(d => this.props.CollectionView.SelectedDocs.push(d.Id));
+        SelectionManager.DeselectAll;
+        docs.map(doc => {
+            const dv = DocumentManager.Instance.getDocumentView(doc);
+            if (dv) {
+                SelectionManager.SelectDoc(dv, true);
+            }
+        })
     }
 
     public getActiveDocuments = () => {
@@ -59,6 +66,8 @@ export class CollectionFreeFormView extends CollectionViewBase {
     @observable private _lastX: number = 0;
     @observable private _lastY: number = 0;
 
+    private outerElement?: HTMLDivElement;
+
     @computed get panX(): number { return this.props.Document.GetNumber(KeyStore.PanX, 0) }
     @computed get panY(): number { return this.props.Document.GetNumber(KeyStore.PanY, 0) }
     @computed get scale(): number { return this.props.Document.GetNumber(KeyStore.Scale, 1); }
@@ -66,8 +75,8 @@ export class CollectionFreeFormView extends CollectionViewBase {
     @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
     @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
     @computed get zoomScaling() { return this.props.Document.GetNumber(KeyStore.Scale, 1); }
-    @computed get centeringShiftX() { return !this.props.Document.GetNumber(KeyStore.NativeWidth, 0) ? this.props.panelWidth() / 2 : 0; }  // shift so pan position is at center of window for non-overlay collections
-    @computed get centeringShiftY() { return !this.props.Document.GetNumber(KeyStore.NativeHeight, 0) ? this.props.panelHeight() / 2 : 0; }// shift so pan position is at center of window for non-overlay collections
+    @computed get centeringShiftX() { return !this.props.Document.GetNumber(KeyStore.NativeWidth, 0) && this.outerElement ? this.outerElement.clientWidth / 2 : 0; }  // shift so pan position is at center of window for non-overlay collections
+    @computed get centeringShiftY() { return !this.props.Document.GetNumber(KeyStore.NativeHeight, 0) && this.outerElement ? this.outerElement.clientHeight / 2 : 0; }// shift so pan position is at center of window for non-overlay collections
 
     @undoBatch
     @action
@@ -227,16 +236,17 @@ export class CollectionFreeFormView extends CollectionViewBase {
     getDocumentViewProps(document: Document): DocumentViewProps {
         return {
             Document: document,
-            AddDocument: this.props.addDocument,
-            RemoveDocument: this.props.removeDocument,
+            addDocument: this.props.addDocument,
+            removeDocument: this.props.removeDocument,
             ScreenToLocalTransform: this.getTransform,
             isTopMost: false,
             selectOnLoad: document.Id == this._selectOnLoaded,
             PanelWidth: document.Width,
             PanelHeight: document.Height,
             ContentScaling: this.noScaling,
-            ContainingCollectionView: this.props.CollectionView,
-            focus: this.focusDocument
+            ContainingCollectionView: undefined,
+            focus: this.focusDocument,
+            parentActive: this.props.active,
         }
     }
 
@@ -266,7 +276,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
 
     getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform())
     getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH)
-    getLocalTransform = (): Transform => Transform.Identity.scale(1 / this.scale).translate(this.panX, this.panY);
+    getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.scale).translate(this.panX, this.panY);
     noScaling = () => 1;
     childViews = () => this.views;
 
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index a5e4b1a17..2574b374a 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -44,7 +44,18 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
 
 
     CreateBindings(): JsxBindings {
-        let { Document, isSelected, select, isTopMost, selectOnLoad } = this.props;
+        let
+            {
+                Document,
+                isSelected,
+                select,
+                isTopMost,
+                selectOnLoad,
+                ScreenToLocalTransform,
+                addDocument,
+                removeDocument,
+                parentActive: active
+            } = this.props;
         let bindings: JsxBindings = {
             props: {
                 Document,
@@ -52,6 +63,11 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
                 select,
                 isTopMost,
                 selectOnLoad,
+                ScreenToLocalTransform,
+                active,
+                addDocument,
+                removeDocument,
+                focus,
             }
         };
         for (const key of this.layoutKeys) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6530dafc6..545344024 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -13,19 +13,21 @@ import { DragManager } from "../../util/DragManager";
 import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { CollectionView, CollectionViewType } from "../collections/CollectionView";
+import { CollectionView } from "../collections/CollectionView";
 import { ContextMenu } from "../ContextMenu";
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
 import React = require("react");
 import { ServerUtils } from "../../../server/ServerUtil";
+import { CollectionViewType } from "../collections/CollectionBaseView";
 
 
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView>;
     Document: Document;
-    AddDocument?: (doc: Document, allowDuplicates: boolean) => boolean;
-    RemoveDocument?: (doc: Document) => boolean;
+    addDocument?: (doc: Document, allowDuplicates?: boolean) => boolean;
+    removeDocument?: (doc: Document) => boolean;
+    moveDocument?: (doc: Document, targetCollection: Document, addDocument: (document: Document) => void) => boolean;
     ScreenToLocalTransform: () => Transform;
     isTopMost: boolean;
     ContentScaling: () => number;
@@ -33,6 +35,7 @@ export interface DocumentViewProps {
     PanelHeight: () => number;
     focus: (doc: Document) => void;
     selectOnLoad: boolean;
+    parentActive: () => boolean;
 }
 export interface JsxArgs extends DocumentViewProps {
     Keys: { [name: string]: Key }
@@ -83,9 +86,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     private _mainCont = React.createRef<HTMLDivElement>();
     private _downX: number = 0;
     private _downY: number = 0;
-    private _reactionDisposer: Opt<IReactionDisposer>;
-    @computed get active(): boolean { return SelectionManager.IsSelected(this) || !this.props.ContainingCollectionView || this.props.ContainingCollectionView.active(); }
-    @computed get topMost(): boolean { return !this.props.ContainingCollectionView || this.props.ContainingCollectionView.collectionViewType == CollectionViewType.Docking; }
+    @computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
+    @computed get topMost(): boolean { return this.props.isTopMost; }
     @computed get layout(): string { return this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>"); }
     @computed get layoutKeys(): Key[] { return this.props.Document.GetData(KeyStore.LayoutKeys, ListField, new Array<Key>()); }
     @computed get layoutFields(): Key[] { return this.props.Document.GetData(KeyStore.LayoutFields, ListField, new Array<Key>()); }
@@ -119,12 +121,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         runInAction(() => {
             DocumentManager.Instance.DocumentViews.push(this);
         })
-        this._reactionDisposer = reaction(
-            () => this.props.ContainingCollectionView && this.props.ContainingCollectionView.SelectedDocs.slice(),
-            () => {
-                if (this.props.ContainingCollectionView && this.props.ContainingCollectionView.SelectedDocs.indexOf(this.props.Document.Id) != -1)
-                    SelectionManager.SelectDoc(this, true);
-            });
     }
 
     componentDidUpdate() {
@@ -144,9 +140,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
 
         })
-        if (this._reactionDisposer) {
-            this._reactionDisposer();
-        }
     }
 
     startDragging(x: number, y: number, dropAliasOfDraggedDoc: boolean) {
@@ -156,9 +149,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             dragData.aliasOnDrop = dropAliasOfDraggedDoc;
             dragData.xOffset = x - left;
             dragData.yOffset = y - top;
-            dragData.removeDocument = (dropCollectionView: CollectionView) => {
-                if (this.props.RemoveDocument && this.props.ContainingCollectionView !== dropCollectionView) {
-                    this.props.RemoveDocument(this.props.Document);
+            dragData.moveDocument = (targetCollection: Document, addDocument: (document: Document) => void) => {
+                if (this.props.moveDocument) {
+                    this.props.moveDocument(this.props.Document, targetCollection, addDocument)
                 }
             }
             DragManager.StartDocumentDrag(this._mainCont.current, dragData, {
@@ -197,14 +190,14 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 
     deleteClicked = (): void => {
-        if (this.props.RemoveDocument) {
-            this.props.RemoveDocument(this.props.Document);
+        if (this.props.removeDocument) {
+            this.props.removeDocument(this.props.Document);
         }
     }
 
     fieldsClicked = (e: React.MouseEvent): void => {
-        if (this.props.AddDocument) {
-            this.props.AddDocument(Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }), false);
+        if (this.props.addDocument) {
+            this.props.addDocument(Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }), false);
         }
     }
     fullScreenClicked = (e: React.MouseEvent): void => {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 2a7d3175f..57af55f9f 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -19,6 +19,7 @@ import { ListField } from "../../../fields/ListField";
 import { DocumentContentsView } from "./DocumentContentsView";
 import { Transform } from "../../util/Transform";
 import { KeyStore } from "../../../fields/KeyStore";
+import { returnFalse } from "../../../Utils";
 
 
 //
@@ -33,8 +34,9 @@ export interface FieldViewProps {
     select: (isCtrlPressed: boolean) => void;
     isTopMost: boolean;
     selectOnLoad: boolean;
-    addDocument: (document: Document, allowDuplicates: boolean) => boolean;
-    removeDocument: (document: Document) => boolean;
+    addDocument?: (document: Document, allowDuplicates?: boolean) => boolean;
+    removeDocument?: (document: Document) => boolean;
+    moveDocument?: (document: Document, targetCollection: Document, addDocument: (document: Document) => void) => boolean;
     ScreenToLocalTransform: () => Transform;
     active: () => boolean;
     focus: (doc: Document) => void;
@@ -73,19 +75,20 @@ export class FieldView extends React.Component<FieldViewProps> {
         }
         else if (field instanceof Document) {
             return (<DocumentContentsView Document={field}
-                AddDocument={undefined}
-                RemoveDocument={undefined}
-                ScreenToLocalTransform={() => Transform.Identity}
+                addDocument={undefined}
+                removeDocument={undefined}
+                ScreenToLocalTransform={Transform.Identity}
                 ContentScaling={() => 1}
                 PanelWidth={() => 100}
                 PanelHeight={() => 100}
-                isTopMost={true}
+                isTopMost={true} //TODO Why is this top most?
                 selectOnLoad={false}
                 focus={() => { }}
                 isSelected={() => false}
                 select={() => false}
                 layoutKey={KeyStore.Layout}
-                ContainingCollectionView={undefined} />)
+                ContainingCollectionView={undefined}
+                parentActive={this.props.active} />)
         }
         else if (field instanceof ListField) {
             return (<div>
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 2fc11328f..e178cbc8d 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -11,6 +11,7 @@ import { Key } from '../../../fields/Key';
 import { Server } from "../../Server"
 import { EditableView } from "../EditableView";
 import { CompileScript, ToField } from "../../util/Scripting";
+import { Transform } from '../../util/Transform';
 
 // Represents one row in a key value plane
 
@@ -49,6 +50,9 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             select: () => { },
             isTopMost: false,
             selectOnLoad: false,
+            active: () => false,
+            ScreenToLocalTransform: Transform.Identity,
+            focus: () => { },
         }
         let contents = (
             <FieldView {...props} />
diff --git a/src/fields/Field.ts b/src/fields/Field.ts
index d48509a47..0d0e56f77 100644
--- a/src/fields/Field.ts
+++ b/src/fields/Field.ts
@@ -12,8 +12,8 @@ export function Cast<T extends Field>(field: FieldValue<Field>, ctor: { new(): T
     return undefined;
 }
 
-export const FieldWaiting: FIELD_WAITING = "<Waiting>";
-export type FIELD_WAITING = "<Waiting>";
+export const FieldWaiting: FIELD_WAITING = null;
+export type FIELD_WAITING = null;
 export type FieldId = string;
 export type Opt<T> = T | undefined;
 export type FieldValue<T> = Opt<T> | FIELD_WAITING;
-- 
cgit v1.2.3-70-g09d2


From dc2a998acdae1545c8e7bf9ee9f4ca98bba82ac9 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Thu, 4 Apr 2019 01:30:01 -0400
Subject: Started adding linting

---
 package.json                                       |  2 +
 src/Utils.ts                                       |  2 +-
 src/client/Server.ts                               | 52 +++++++-------
 src/client/SocketStub.ts                           |  2 +-
 src/client/northstar/manager/Gateway.ts            | 24 +++----
 src/client/util/DocumentManager.ts                 | 10 +--
 src/client/util/DragManager.ts                     | 23 +++---
 src/client/util/Scripting.ts                       | 14 ++--
 src/client/util/SelectionManager.ts                |  5 +-
 src/client/util/Transform.ts                       | 50 ++++++-------
 src/client/util/TypedEvent.ts                      |  5 +-
 src/client/util/UndoManager.ts                     |  4 +-
 src/client/views/DocumentDecorations.tsx           |  2 +-
 src/client/views/EditableView.tsx                  |  4 +-
 src/client/views/InkingCanvas.tsx                  | 14 ++--
 src/client/views/InkingControl.tsx                 |  4 +-
 src/client/views/InkingStroke.tsx                  |  7 +-
 src/client/views/Main.tsx                          | 13 ++--
 .../views/collections/CollectionBaseView.tsx       |  4 +-
 .../views/collections/CollectionSchemaView.tsx     | 17 ++---
 .../views/collections/CollectionVideoView.tsx      |  2 +-
 .../views/collections/CollectionViewBase.tsx       |  5 +-
 .../CollectionFreeFormLinksView.tsx                | 18 ++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |  2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 12 ++--
 src/client/views/nodes/DocumentView.tsx            |  5 +-
 src/client/views/nodes/FieldView.tsx               |  5 +-
 src/client/views/nodes/ImageBox.tsx                |  2 +-
 src/client/views/nodes/KeyValueBox.tsx             |  5 +-
 src/client/views/nodes/PDFBox.tsx                  | 36 +++++-----
 src/client/views/nodes/VideoBox.tsx                |  4 +-
 src/debug/Viewer.tsx                               |  7 +-
 src/fields/Document.ts                             | 14 ++--
 src/fields/WebField.ts                             |  2 +-
 src/mobile/ImageUpload.tsx                         |  5 +-
 .../authentication/controllers/WorkspacesMenu.tsx  |  2 +-
 .../authentication/controllers/user_controller.ts  | 12 ++--
 src/server/database.ts                             |  2 +-
 src/server/index.ts                                |  8 +--
 test/test.ts                                       |  2 +-
 tslint.json                                        | 56 +++++++++++++++
 webpack.config.js                                  | 83 ++++++++++++----------
 43 files changed, 292 insertions(+), 259 deletions(-)
 create mode 100644 tslint.json

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

diff --git a/package.json b/package.json
index 27b3eead1..4fc253834 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,8 @@
     "scss-loader": "0.0.1",
     "style-loader": "^0.23.1",
     "ts-node": "^7.0.1",
+    "tslint": "^5.15.0",
+    "tslint-loader": "^3.5.4",
     "typescript": "^3.3.3333",
     "webpack": "^4.29.6",
     "webpack-cli": "^3.2.3",
diff --git a/src/Utils.ts b/src/Utils.ts
index 3eb192eb8..8bd7f2f5c 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -18,7 +18,7 @@ export class Utils {
             return { scale: 1, translateX: 1, translateY: 1 }
         }
         const rect = ele.getBoundingClientRect();
-        const scale = ele.offsetWidth == 0 && rect.width == 0 ? 1 : rect.width / ele.offsetWidth;
+        const scale = ele.offsetWidth === 0 && rect.width === 0 ? 1 : rect.width / ele.offsetWidth;
         const translateX = rect.left;
         const translateY = rect.top;
 
diff --git a/src/client/Server.ts b/src/client/Server.ts
index c301b04d3..e3f4448cb 100644
--- a/src/client/Server.ts
+++ b/src/client/Server.ts
@@ -21,11 +21,11 @@ export class Server {
         let fn = (cb: (field: Opt<Field>) => void) => {
 
             let cached = this.ClientFieldsCached.get(fieldid);
-            if (!cached) {
+            if (cached === undefined) {
                 this.ClientFieldsCached.set(fieldid, FieldWaiting);
                 SocketStub.SEND_FIELD_REQUEST(fieldid, action((field: Field | undefined) => {
                     let cached = this.ClientFieldsCached.get(fieldid);
-                    if (cached != FieldWaiting)
+                    if (cached !== FieldWaiting)
                         cb(cached);
                     else {
                         if (field) {
@@ -36,23 +36,22 @@ export class Server {
                         cb(field)
                     }
                 }));
-            } else if (cached != FieldWaiting) {
+            } else if (cached !== FieldWaiting) {
                 setTimeout(() => cb(cached as Field), 0);
             } else {
-                reaction(() => {
-                    return this.ClientFieldsCached.get(fieldid);
-                }, (field, reaction) => {
-                    if (field !== FieldWaiting) {
-                        reaction.dispose()
-                        cb(field)
-                    }
-                })
+                reaction(() =>
+                    this.ClientFieldsCached.get(fieldid), (field, reaction) => {
+                        if (field !== FieldWaiting) {
+                            reaction.dispose()
+                            cb(field)
+                        }
+                    })
             }
         }
         if (callback) {
             fn(callback);
         } else {
-            return new Promise(res => fn(res));
+            return new Promise(fn);
         }
     }
 
@@ -92,24 +91,23 @@ export class Server {
                         }
                     }
                 }
-                reaction(() => {
-                    return waitingFieldIds.map(id => this.ClientFieldsCached.get(id));
-                }, (cachedFields, reaction) => {
-                    if (!cachedFields.some(field => !field || field === FieldWaiting)) {
-                        reaction.dispose();
-                        for (let field of cachedFields) {
-                            let realField = field as Field;
-                            existingFields[realField.Id] = realField;
+                reaction(() =>
+                    waitingFieldIds.map(id => this.ClientFieldsCached.get(id)), (cachedFields, reaction) => {
+                        if (!cachedFields.some(field => !field)) {
+                            reaction.dispose();
+                            for (let field of cachedFields) {
+                                let realField = field as Field;
+                                existingFields[realField.Id] = realField;
+                            }
+                            cb({ ...fields, ...existingFields })
                         }
-                        cb({ ...fields, ...existingFields })
-                    }
-                }, { fireImmediately: true })
+                    }, { fireImmediately: true })
             }));
         };
         if (callback) {
             fn(callback);
         } else {
-            return new Promise(res => fn(res));
+            return new Promise(fn);
         }
     }
 
@@ -153,19 +151,19 @@ export class Server {
     @action
     private static cacheField(clientField: Field) {
         var cached = this.ClientFieldsCached.get(clientField.Id);
-        if (!cached || cached == FieldWaiting) {
+        if (!cached || cached === FieldWaiting) {
             this.ClientFieldsCached.set(clientField.Id, clientField);
         } else {
             // probably should overwrite the values within any field that was already here...
         }
-        return this.ClientFieldsCached.get(clientField.Id) as Field;
+        return this.ClientFieldsCached.get(clientField.Id);
     }
 
     @action
     static updateField(field: { _id: string, data: any, type: Types }) {
         if (Server.ClientFieldsCached.has(field._id)) {
             var f = Server.ClientFieldsCached.get(field._id);
-            if (f && f != FieldWaiting) {
+            if (f && f !== FieldWaiting) {
                 // console.log("Applying        : " + field._id);
                 f.UpdateFromServer(field.data);
                 f.init(() => { });
diff --git a/src/client/SocketStub.ts b/src/client/SocketStub.ts
index 5045037c5..c3cd8bee6 100644
--- a/src/client/SocketStub.ts
+++ b/src/client/SocketStub.ts
@@ -55,7 +55,7 @@ export class SocketStub {
         if (callback) {
             fn(callback);
         } else {
-            return new Promise(res => fn(res))
+            return new Promise(fn)
         }
     }
 
diff --git a/src/client/northstar/manager/Gateway.ts b/src/client/northstar/manager/Gateway.ts
index 5ae5e4f47..3e72a50ae 100644
--- a/src/client/northstar/manager/Gateway.ts
+++ b/src/client/northstar/manager/Gateway.ts
@@ -246,18 +246,18 @@ export class Settings {
         else {
             this.ServerUrl = environment["SERVER_URL"] ? environment["SERVER_URL"] : document.URL;
         }*/
-        this.ServerUrl = environment["SERVER_URL"] ? environment["SERVER_URL"] : document.URL;
-        this.ServerApiPath = environment["SERVER_API_PATH"];
-        this.SampleSize = environment["SAMPLE_SIZE"];
-        this.XBins = environment["X_BINS"];
-        this.YBins = environment["Y_BINS"];
-        this.SplashTimeInMS = environment["SPLASH_TIME_IN_MS"];
-        this.ShowFpsCounter = environment["SHOW_FPS_COUNTER"];
-        this.ShowShutdownButton = environment["SHOW_SHUTDOWN_BUTTON"];
-        this.IsMenuFixed = environment["IS_MENU_FIXED"];
-        this.IsDarpa = environment["IS_DARPA"];
-        this.IsIGT = environment["IS_IGT"];
-        this.DegreeOfParallelism = environment["DEGREE_OF_PARALLISM"];
+        this.ServerUrl = environment.SERVER_URL ? environment.SERVER_URL : document.URL;
+        this.ServerApiPath = environment.SERVER_API_PATH;
+        this.SampleSize = environment.SAMPLE_SIZE;
+        this.XBins = environment.X_BINS;
+        this.YBins = environment.Y_BINS;
+        this.SplashTimeInMS = environment.SPLASH_TIME_IN_MS;
+        this.ShowFpsCounter = environment.SHOW_FPS_COUNTER;
+        this.ShowShutdownButton = environment.SHOW_SHUTDOWN_BUTTON;
+        this.IsMenuFixed = environment.IS_MENU_FIXED;
+        this.IsDarpa = environment.IS_DARPA;
+        this.IsIGT = environment.IS_IGT;
+        this.DegreeOfParallelism = environment.DEGREE_OF_PARALLISM;
     }
 
     public static get Instance(): Settings {
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index bf59fbb43..7cb368f47 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -29,7 +29,7 @@ export class DocumentManager {
 
     public getAllDocumentViews(collection: Document) {
         return this.DocumentViews.filter(dv =>
-            dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document == collection);
+            dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document === collection);
     }
 
     public getDocumentView(toFind: Document): DocumentView | null {
@@ -47,7 +47,7 @@ export class DocumentManager {
                 return;
             }
             let docSrc = doc.GetT(KeyStore.Prototype, Document);
-            if (docSrc && docSrc != FieldWaiting && Object.is(docSrc, toFind)) {
+            if (docSrc && docSrc !== FieldWaiting && Object.is(docSrc, toFind)) {
                 toReturn = view;
             }
         })
@@ -67,7 +67,7 @@ export class DocumentManager {
                 toReturn.push(view);
             } else {
                 let docSrc = doc.GetT(KeyStore.Prototype, Document);
-                if (docSrc && docSrc != FieldWaiting && Object.is(docSrc, toFind)) {
+                if (docSrc && docSrc !== FieldWaiting && Object.is(docSrc, toFind)) {
                     toReturn.push(view);
                 }
             }
@@ -80,11 +80,11 @@ export class DocumentManager {
     public get LinkedDocumentViews() {
         return DocumentManager.Instance.DocumentViews.reduce((pairs, dv) => {
             let linksList = dv.props.Document.GetT(KeyStore.LinkedToDocs, ListField);
-            if (linksList && linksList != FieldWaiting && linksList.Data.length) {
+            if (linksList && linksList !== FieldWaiting && linksList.Data.length) {
                 pairs.push(...linksList.Data.reduce((pairs, link) => {
                     if (link instanceof Document) {
                         let linkToDoc = link.GetT(KeyStore.LinkedToDocs, Document);
-                        if (linkToDoc && linkToDoc != FieldWaiting) {
+                        if (linkToDoc && linkToDoc !== FieldWaiting) {
                             DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
                                 pairs.push({ a: dv, b: docView1, l: link })
                             })
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 96c965c23..ff778302b 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -6,6 +6,7 @@ import { ImageField } from "../../fields/ImageField";
 import { KeyStore } from "../../fields/KeyStore";
 import { CollectionView } from "../views/collections/CollectionView";
 import { DocumentView } from "../views/nodes/DocumentView";
+import { emptyFunction } from "../../Utils";
 
 export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, removeFunc: (containingCollection: CollectionView) => void = () => { }) {
     let onRowMove = action((e: PointerEvent): void => {
@@ -24,7 +25,7 @@ export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc:
     });
     let onItemDown = (e: React.PointerEvent) => {
         // if (this.props.isSelected() || this.props.isTopMost) {
-        if (e.button == 0) {
+        if (e.button === 0) {
             e.stopPropagation();
             if (e.shiftKey) {
                 CollectionDockingView.Instance.StartOtherDrag([docFunc()], e);
@@ -88,7 +89,7 @@ export namespace DragManager {
         if ("canDrop" in element.dataset) {
             throw new Error("Element is already droppable, can't make it droppable again");
         }
-        element.dataset["canDrop"] = "true";
+        element.dataset.canDrop = "true";
         const handler = (e: Event) => {
             const ce = e as CustomEvent<DropEvent>;
             options.handlers.drop(e, ce.detail);
@@ -96,7 +97,7 @@ export namespace DragManager {
         element.addEventListener("dashOnDrop", handler);
         return () => {
             element.removeEventListener("dashOnDrop", handler);
-            delete element.dataset["canDrop"]
+            delete element.dataset.canDrop
         };
     }
 
@@ -167,11 +168,11 @@ export namespace DragManager {
                 let thumbnail = docs[0].GetT(KeyStore.Thumbnail, ImageField);
                 if (pdfBox && pdfBox.childElementCount && thumbnail) {
                     let img = new Image();
-                    img!.src = thumbnail.toString();
-                    img!.style.position = "absolute";
-                    img!.style.width = `${rect.width / scaleX}px`;
-                    img!.style.height = `${rect.height / scaleY}px`;
-                    pdfBox.replaceChild(img!, pdfBox.children[0])
+                    img.src = thumbnail.toString();
+                    img.style.position = "absolute";
+                    img.style.width = `${rect.width / scaleX}px`;
+                    img.style.height = `${rect.height / scaleY}px`;
+                    pdfBox.replaceChild(img, pdfBox.children[0])
                 }
             }
 
@@ -224,9 +225,9 @@ export namespace DragManager {
         });
         const target = document.elementFromPoint(e.x, e.y);
         removed.map(r => {
-            let dragEle: HTMLElement = r[0]!;
-            let parent: HTMLElement | null = r[1];
-            if (parent)
+            let dragEle = r[0];
+            let parent = r[1];
+            if (parent && dragEle)
                 parent.appendChild(dragEle);
         });
         if (target) {
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 4e97b9401..8aef44a3a 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -26,7 +26,7 @@ export interface ExecutableScript {
 }
 
 function Compile(script: string | undefined, diagnostics: Opt<any[]>, scope: { [name: string]: any }): ExecutableScript {
-    const compiled = !(diagnostics && diagnostics.some(diag => diag.category == ts.DiagnosticCategory.Error));
+    const compiled = !(diagnostics && diagnostics.some(diag => diag.category === ts.DiagnosticCategory.Error));
 
     let func: () => Opt<Field>;
     if (compiled && script) {
@@ -40,7 +40,7 @@ function Compile(script: string | undefined, diagnostics: Opt<any[]>, scope: { [
             paramNames.push(prop);
             params.push(scope[prop]);
         }
-        let thisParam = scope["this"];
+        let thisParam = scope.this;
         let compiledFunction = new Function(...paramNames, script);
         func = function (): Opt<Field> {
             return compiledFunction.apply(thisParam, params)
@@ -49,10 +49,8 @@ function Compile(script: string | undefined, diagnostics: Opt<any[]>, scope: { [
         func = () => undefined;
     }
 
-    return Object.assign(func,
-        {
-            compiled
-        });
+    Object.assign(func, { compiled });
+    return func as ExecutableScript;
 }
 
 interface File {
@@ -125,9 +123,9 @@ export function CompileScript(script: string, scope?: { [name: string]: any }, a
 }
 
 export function ToField(data: any): Opt<Field> {
-    if (typeof data == "string") {
+    if (typeof data === "string") {
         return new TextField(data);
-    } else if (typeof data == "number") {
+    } else if (typeof data === "number") {
         return new NumberField(data);
     }
     return undefined;
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 05810b61c..494420f0b 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -40,9 +40,8 @@ export namespace SelectionManager {
     export function DeselectAll(except?: Document): void {
         let found: DocumentView | undefined = undefined;
         if (except) {
-            for (let i = 0; i < manager.SelectedDocuments.length; i++) {
-                let view = manager.SelectedDocuments[i];
-                if (view.props.Document == except)
+            for (const view of manager.SelectedDocuments) {
+                if (view.props.Document === except)
                     found = view;
             }
         }
diff --git a/src/client/util/Transform.ts b/src/client/util/Transform.ts
index 889134e3e..8608264a1 100644
--- a/src/client/util/Transform.ts
+++ b/src/client/util/Transform.ts
@@ -62,33 +62,26 @@ export class Transform {
         return this;
     }
 
-    translated = (x: number, y: number): Transform => {
-        return this.copy().translate(x, y);
-    }
+    translated = (x: number, y: number): Transform =>
+        this.copy().translate(x, y)
 
-    preTranslated = (x: number, y: number): Transform => {
-        return this.copy().preTranslate(x, y);
-    }
+    preTranslated = (x: number, y: number): Transform =>
+        this.copy().preTranslate(x, y)
 
-    scaled = (scale: number): Transform => {
-        return this.copy().scale(scale);
-    }
+    scaled = (scale: number): Transform =>
+        this.copy().scale(scale)
 
-    scaledAbout = (scale: number, x: number, y: number): Transform => {
-        return this.copy().scaleAbout(scale, x, y);
-    }
+    scaledAbout = (scale: number, x: number, y: number): Transform =>
+        this.copy().scaleAbout(scale, x, y)
 
-    preScaled = (scale: number): Transform => {
-        return this.copy().preScale(scale);
-    }
+    preScaled = (scale: number): Transform =>
+        this.copy().preScale(scale)
 
-    transformed = (transform: Transform): Transform => {
-        return this.copy().transform(transform);
-    }
+    transformed = (transform: Transform): Transform =>
+        this.copy().transform(transform)
 
-    preTransformed = (transform: Transform): Transform => {
-        return this.copy().preTransform(transform);
-    }
+    preTransformed = (transform: Transform): Transform =>
+        this.copy().preTransform(transform)
 
     transformPoint = (x: number, y: number): [number, number] => {
         x *= this._scale;
@@ -98,9 +91,8 @@ export class Transform {
         return [x, y];
     }
 
-    transformDirection = (x: number, y: number): [number, number] => {
-        return [x * this._scale, y * this._scale];
-    }
+    transformDirection = (x: number, y: number): [number, number] =>
+        [x * this._scale, y * this._scale]
 
     transformBounds(x: number, y: number, width: number, height: number): { x: number, y: number, width: number, height: number } {
         [x, y] = this.transformPoint(x, y);
@@ -108,12 +100,10 @@ export class Transform {
         return { x, y, width, height };
     }
 
-    inverse = () => {
-        return new Transform(-this._translateX / this._scale, -this._translateY / this._scale, 1 / this._scale)
-    }
+    inverse = () =>
+        new Transform(-this._translateX / this._scale, -this._translateY / this._scale, 1 / this._scale)
 
-    copy = () => {
-        return new Transform(this._translateX, this._translateY, this._scale);
-    }
+    copy = () =>
+        new Transform(this._translateX, this._translateY, this._scale)
 
 }
\ No newline at end of file
diff --git a/src/client/util/TypedEvent.ts b/src/client/util/TypedEvent.ts
index 0714a7f5c..c590d3734 100644
--- a/src/client/util/TypedEvent.ts
+++ b/src/client/util/TypedEvent.ts
@@ -36,7 +36,6 @@ export class TypedEvent<T> {
     this.listenersOncer = [];
   }
 
-  pipe = (te: TypedEvent<T>): Disposable => {
-    return this.on((e) => te.emit(e));
-  }
+  pipe = (te: TypedEvent<T>): Disposable =>
+    this.on((e) => te.emit(e))
 }
\ No newline at end of file
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index 6d1b2f1b8..1e5028375 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -159,8 +159,8 @@ export namespace UndoManager {
         }
 
         undoing = true;
-        for (let i = 0; i < commands.length; i++) {
-            commands[i].redo();
+        for (const command of commands) {
+            command.redo();
         }
         undoing = false;
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d0699e1ab..b0aa190e5 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -75,7 +75,7 @@ export class DocumentDecorations extends React.Component {
         this._dragging = true;
         document.removeEventListener("pointermove", this.onBackgroundMove);
         document.removeEventListener("pointerup", this.onBackgroundUp);
-        DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(docView => (docView as any)._mainCont!.current!), dragData, {
+        DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(docView => (docView as any)._mainCont.current), dragData, {
             handlers: {
                 dragComplete: action(() => this._dragging = false),
             },
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 29bf6add7..982aacdea 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -38,7 +38,7 @@ export class EditableView extends React.Component<EditableProps> {
 
     @action
     onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
-        if (e.key == "Enter") {
+        if (e.key === "Enter") {
             if (!e.ctrlKey) {
                 if (this.props.SetValue(e.currentTarget.value)) {
                     this.editing = false;
@@ -47,7 +47,7 @@ export class EditableView extends React.Component<EditableProps> {
                 this.props.OnFillDown(e.currentTarget.value);
                 this.editing = false;
             }
-        } else if (e.key == "Escape") {
+        } else if (e.key === "Escape") {
             this.editing = false;
         }
     }
diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx
index cad4b74b1..a2956f1b6 100644
--- a/src/client/views/InkingCanvas.tsx
+++ b/src/client/views/InkingCanvas.tsx
@@ -56,7 +56,7 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
 
     @action
     onPointerDown = (e: React.PointerEvent): void => {
-        if (e.button != 0 || e.altKey || e.ctrlKey || InkingControl.Instance.selectedTool === InkTool.None) {
+        if (e.button !== 0 || e.altKey || e.ctrlKey || InkingControl.Instance.selectedTool === InkTool.None) {
             return;
         }
         document.addEventListener("pointermove", this.onPointerMove, true);
@@ -64,7 +64,7 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
         e.stopPropagation();
         e.preventDefault();
 
-        if (InkingControl.Instance.selectedTool != InkTool.Eraser) {
+        if (InkingControl.Instance.selectedTool !== InkTool.Eraser) {
             // start the new line, saves a uuid to represent the field of the stroke
             this._currentStrokeId = Utils.GenerateGuid();
             this.inkData.set(this._currentStrokeId, {
@@ -94,7 +94,7 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
     onPointerMove = (e: PointerEvent): void => {
         e.stopPropagation()
         e.preventDefault();
-        if (InkingControl.Instance.selectedTool != InkTool.Eraser) {
+        if (InkingControl.Instance.selectedTool !== InkTool.Eraser) {
             let data = this.inkData;  // add points to new line as it is being drawn
             let strokeData = data.get(this._currentStrokeId);
             if (strokeData) {
@@ -121,7 +121,7 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
     get drawnPaths() {
         let curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1)
         let paths = Array.from(this.inkData).reduce((paths, [id, strokeData]) => {
-            if (strokeData.page == -1 || strokeData.page == curPage)
+            if (strokeData.page === -1 || strokeData.page === curPage)
                 paths.push(<InkingStroke key={id} id={id} line={strokeData.pathData}
                     offsetX={this.maxCanvasDim - this.inkMidX}
                     offsetY={this.maxCanvasDim - this.inkMidY}
@@ -131,16 +131,16 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
         }, [] as JSX.Element[]);
         return [<svg className={`inkingCanvas-paths-markers`} key="Markers"
             style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }} >
-            {paths.filter(path => path.props.tool == InkTool.Highlighter)}
+            {paths.filter(path => path.props.tool === InkTool.Highlighter)}
         </svg>,
         <svg className={`inkingCanvas-paths-ink`} key="Pens"
             style={{ left: `-${this.inkMidX - this.maxCanvasDim}px`, top: `-${this.inkMidY - this.maxCanvasDim}px` }}>
-            {paths.filter(path => path.props.tool != InkTool.Highlighter)}
+            {paths.filter(path => path.props.tool !== InkTool.Highlighter)}
         </svg>];
     }
 
     render() {
-        let svgCanvasStyle = InkingControl.Instance.selectedTool != InkTool.None ? "canSelect" : "noSelect";
+        let svgCanvasStyle = InkingControl.Instance.selectedTool !== InkTool.None ? "canSelect" : "noSelect";
         return (
             <div className="inkingCanvas" >
                 <div className={`inkingCanvas-${svgCanvasStyle}`} onPointerDown={this.onPointerDown} />
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index c1519dff8..13f0a0acc 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -36,9 +36,9 @@ export class InkingControl extends React.Component {
     @action
     switchColor = (color: ColorResult): void => {
         this._selectedColor = color.hex;
-        if (SelectionManager.SelectedDocuments().length == 1) {
+        if (SelectionManager.SelectedDocuments().length === 1) {
             var sdoc = SelectionManager.SelectedDocuments()[0];
-            if (sdoc.props.ContainingCollectionView && sdoc.props.ContainingCollectionView) {
+            if (sdoc.props.ContainingCollectionView) {
                 sdoc.props.Document.SetDataOnPrototype(KeyStore.BackgroundColor, color.hex, TextField);
             }
         }
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 615f8af7e..dbb79c0c6 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -29,9 +29,8 @@ export class InkingStroke extends React.Component<StrokeProps> {
         }
     }
 
-    parseData = (line: Array<{ x: number, y: number }>): string => {
-        return !line.length ? "" : "M " + line.map(p => (p.x + this.props.offsetX) + " " + (p.y + this.props.offsetY)).join(" L ");
-    }
+    parseData = (line: Array<{ x: number, y: number }>): string =>
+        !line.length ? "" : "M " + line.map(p => (p.x + this.props.offsetX) + " " + (p.y + this.props.offsetY)).join(" L ")
 
     createStyle() {
         switch (this._strokeTool) {
@@ -49,7 +48,7 @@ export class InkingStroke extends React.Component<StrokeProps> {
         let pathStyle = this.createStyle();
         let pathData = this.parseData(this.props.line);
 
-        let pointerEvents: any = InkingControl.Instance.selectedTool == InkTool.Eraser ? "all" : "none";
+        let pointerEvents: any = InkingControl.Instance.selectedTool === InkTool.Eraser ? "all" : "none";
         return (
             <path d={pathData} style={{ ...pathStyle, pointerEvents: pointerEvents }} strokeLinejoin="round" strokeLinecap="round"
                 onPointerOver={this.deleteStroke} onPointerDown={this.deleteStroke} />
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 8f67c006d..446c3d55f 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -204,9 +204,7 @@ export class Main extends React.Component {
     @observable
     workspacesShown: boolean = false;
 
-    areWorkspacesShown = () => {
-        return this.workspacesShown;
-    }
+    areWorkspacesShown = () => this.workspacesShown
     @action
     toggleWorkspaces = () => {
         this.workspacesShown = !this.workspacesShown;
@@ -373,8 +371,7 @@ export class Main extends React.Component {
     }
 }
 
-Documents.initProtos().then(() => {
-    return CurrentUserUtils.loadCurrentUser()
-}).then(() => {
-    ReactDOM.render(<Main />, document.getElementById('root'));
-});
+Documents.initProtos().then(() =>
+    CurrentUserUtils.loadCurrentUser()).then(() => {
+        ReactDOM.render(<Main />, document.getElementById('root'));
+    });
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 301467d99..9b68ee06c 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
 import { FieldViewProps } from '../nodes/FieldView';
 import { KeyStore } from '../../../fields/KeyStore';
 import { NumberField } from '../../../fields/NumberField';
-import { FieldWaiting, Field } from '../../../fields/Field';
+import { FieldWaiting, Field, FieldValue } from '../../../fields/Field';
 import { ContextMenu } from '../ContextMenu';
 import { SelectionManager } from '../../util/SelectionManager';
 import { Document } from '../../../fields/Document';
@@ -72,7 +72,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
             if (this.createsCycle(annots[i], containerDocument))
                 return true;
         }
-        for (let containerProto: any = containerDocument; containerProto && containerProto != FieldWaiting; containerProto = containerProto.GetPrototype()) {
+        for (let containerProto: FieldValue<Document> = containerDocument; containerProto && containerProto != FieldWaiting; containerProto = containerProto.GetPrototype()) {
             if (containerProto.Id == documentToAdd.Id)
                 return true;
         }
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 78a813a99..ced46cc25 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -122,9 +122,7 @@ export class CollectionSchemaView extends CollectionViewBase {
                         }
                         return field || "";
                     }}
-                    SetValue={(value: string) => {
-                        return applyToDoc(props.Document, value);
-                    }}
+                    SetValue={(value: string) => applyToDoc(props.Document, value)}
                     OnFillDown={(value: string) => {
                         this.props.Document.GetTAsync<ListField<Document>>(this.props.fieldKey, ListField).then((val) => {
                             if (val) {
@@ -240,12 +238,8 @@ export class CollectionSchemaView extends CollectionViewBase {
     getContentScaling = (): number => this._contentScaling;
     getPanelWidth = (): number => this._panelWidth;
     getPanelHeight = (): number => this._panelHeight;
-    getTransform = (): Transform => {
-        return this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling);
-    }
-    getPreviewTransform = (): Transform => {
-        return this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX - this._tableWidth, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling);
-    }
+    getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling)
+    getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX - this._tableWidth, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling)
 
     focusDocument = (doc: Document) => { }
 
@@ -332,9 +326,8 @@ export class CollectionSchemaView extends CollectionViewBase {
                     <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={this.splitPercentage != 0} onChange={this.toggleExpander} />  Show Preview </div>
                     <h6 className="schema-options-subHeader" >Displayed Columns</h6>
                     <ul id="schema-col-checklist" >
-                        {Array.from(Object.keys(allKeys)).map(item => {
-                            return (<KeyToggle checked={allKeys[item]} key={item} keyId={item} toggle={this.toggleKey} />)
-                        })}
+                        {Array.from(Object.keys(allKeys)).map(item =>
+                            (<KeyToggle checked={allKeys[item]} key={item} keyId={item} toggle={this.toggleKey} />))}
                     </ul>
                     <input value={this.newKeyName} onChange={this.newKeyChange} />
                     <button onClick={this.addColumn}><FontAwesomeIcon style={{ color: "white" }} icon="plus" size="lg" /></button>
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 7cb461b4d..3ab6db5ef 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -43,7 +43,7 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
     @action
     mainCont = (ele: HTMLDivElement | null) => {
         if (ele) {
-            this._player = ele!.getElementsByTagName("video")[0];
+            this._player = ele.getElementsByTagName("video")[0];
             if (this.props.Document.GetNumber(KeyStore.CurPage, -1) >= 0) {
                 this._currentTimecode = this.props.Document.GetNumber(KeyStore.CurPage, -1);
             }
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index 71a639137..51280275c 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -171,9 +171,8 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
                 fetch(upload, {
                     method: 'POST',
                     body: formData
-                }).then((res: Response) => {
-                    return res.json()
-                }).then(json => {
+                }).then((res: Response) =>
+                    res.json()).then(json => {
                     json.map((file: any) => {
                         let path = window.location.origin + file
                         runInAction(() => {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index eb20b3100..b682ab303 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -37,11 +37,11 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
                             let srcTarg = (protoSrc ? protoSrc : srcDoc);
                             let findBrush = (field: ListField<Document>) => field.Data.findIndex(brush => {
                                 let bdocs = brush.GetList(KeyStore.BrushingDocs, [] as Document[]);
-                                return (bdocs.length == 0 || (bdocs[0] == dstTarg && bdocs[1] == srcTarg) || (bdocs[0] == srcTarg && bdocs[1] == dstTarg))
+                                return (bdocs.length === 0 || (bdocs[0] === dstTarg && bdocs[1] === srcTarg) || (bdocs[0] === srcTarg && bdocs[1] === dstTarg))
                             });
                             let brushAction = (field: ListField<Document>) => {
                                 let found = findBrush(field);
-                                if (found != -1)
+                                if (found !== -1)
                                     field.Data.splice(found, 1);
                             };
                             if (Math.abs(x1 + x1w - x2) < 20 || Math.abs(x2 + x2w - x1) < 20) {
@@ -50,7 +50,7 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
                                 linkDoc.SetText(KeyStore.LinkDescription, "Brush between " + srcTarg.Title + " and " + dstTarg.Title);
                                 linkDoc.SetData(KeyStore.BrushingDocs, [dstTarg, srcTarg], ListField);
 
-                                brushAction = brushAction = (field: ListField<Document>) => (findBrush(field) == -1) && field.Data.push(linkDoc);
+                                brushAction = brushAction = (field: ListField<Document>) => (findBrush(field) === -1) && field.Data.push(linkDoc);
                             }
                             dstTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction);
                             srcTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction);
@@ -63,10 +63,10 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
     documentAnchors(view: DocumentView) {
         let equalViews = [view];
         let containerDoc = view.props.Document.GetT(KeyStore.AnnotationOn, Document);
-        if (containerDoc && containerDoc != FieldWaiting && containerDoc instanceof Document) {
-            equalViews = DocumentManager.Instance.getDocumentViews(containerDoc.GetPrototype() as Document)
+        if (containerDoc && containerDoc instanceof Document) {
+            equalViews = DocumentManager.Instance.getDocumentViews(containerDoc.GetPrototype()!)
         }
-        return equalViews.filter(sv => sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document == this.props.Document);
+        return equalViews.filter(sv => sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === this.props.Document);
     }
 
     @computed
@@ -78,14 +78,14 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
             srcViews.map(sv => targetViews.map(tv => possiblePairs.push({ a: sv.props.Document, b: tv.props.Document })));
             possiblePairs.map(possiblePair => {
                 if (!drawnPairs.reduce((found, drawnPair) => {
-                    let match = (possiblePair.a == drawnPair.a && possiblePair.b == drawnPair.b);
+                    let match = (possiblePair.a === drawnPair.a && possiblePair.b === drawnPair.b);
                     if (match) {
-                        if (!drawnPair.l.reduce((found, link) => found || link.Id == connection.l.Id, false))
+                        if (!drawnPair.l.reduce((found, link) => found || link.Id === connection.l.Id, false))
                             drawnPair.l.push(connection.l);
                     }
                     return match || found;
                 }, false)) {
-                    drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] as Document[] });
+                    drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] });
                 }
             })
             return drawnPairs
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b04438ede..8c5d3f536 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -83,8 +83,8 @@ export class CollectionFreeFormView extends CollectionViewBase {
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (super.drop(e, de)) {
             if (de.data instanceof DragManager.DocumentDragData) {
-                let screenX = de.x - (de.data.xOffset as number || 0);
-                let screenY = de.y - (de.data.yOffset as number || 0);
+                let screenX = de.x - (de.data.xOffset || 0);
+                let screenY = de.y - (de.data.yOffset || 0);
                 const [x, y] = this.getTransform().transformPoint(screenX, screenY);
                 let dragDoc = de.data.draggedDocuments[0];
                 let dragX = dragDoc.GetNumber(KeyStore.X, 0);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index e2239c8be..b068d49d0 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -146,7 +146,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             if (InkingCanvas.IntersectStrokeRect(value, this.Bounds)) {
                 idata.set(key,
                     {
-                        pathData: value.pathData.map(val => { return { x: val.x + centerShiftX, y: val.y + centerShiftY } }),
+                        pathData: value.pathData.map(val => ({ x: val.x + centerShiftX, y: val.y + centerShiftY })),
                         color: value.color,
                         width: value.width,
                         tool: value.tool,
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index d52b662bd..e6475ee2a 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -52,14 +52,12 @@ export class CollectionFreeFormDocumentView extends React.Component<DocumentView
         this.props.Document.SetData(KeyStore.ZIndex, h, NumberField)
     }
 
-    contentScaling = () => {
-        return this.nativeWidth > 0 ? this.width / this.nativeWidth : 1;
-    }
+    contentScaling = () =>
+        this.nativeWidth > 0 ? this.width / this.nativeWidth : 1
 
-    getTransform = (): Transform => {
-        return this.props.ScreenToLocalTransform().
-            translate(-this.props.Document.GetNumber(KeyStore.X, 0), -this.props.Document.GetNumber(KeyStore.Y, 0)).scale(1 / this.contentScaling());
-    }
+    getTransform = (): Transform =>
+        this.props.ScreenToLocalTransform().
+            translate(-this.props.Document.GetNumber(KeyStore.X, 0), -this.props.Document.GetNumber(KeyStore.Y, 0)).scale(1 / this.contentScaling())
 
     @computed
     get docView() {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6c05f6924..34eb8919f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -287,9 +287,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 
 
-    isSelected = () => {
-        return SelectionManager.IsSelected(this);
-    }
+    isSelected = () =>
+        SelectionManager.IsSelected(this)
 
     select = (ctrlPressed: boolean) => {
         SelectionManager.SelectDoc(this, ctrlPressed)
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 4c6062a2f..d6035a076 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -96,9 +96,8 @@ export class FieldView extends React.Component<FieldViewProps> {
         }
         else if (field instanceof ListField) {
             return (<div>
-                {(field as ListField<Field>).Data.map(f => {
-                    return f instanceof Document ? f.Title : f.GetValue().toString();
-                }).join(", ")}
+                {(field as ListField<Field>).Data.map(f =>
+                    f instanceof Document ? f.Title : f.GetValue().toString()).join(", ")}
             </div>)
         }
         // bcz: this belongs here, but it doesn't render well so taking it out for now
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 9b9dfe645..c5f29f7b0 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -104,7 +104,7 @@ export class ImageBox extends React.Component<FieldViewProps> {
 
     render() {
         let field = this.props.Document.Get(this.props.fieldKey);
-        let path = field == FieldWaiting ? "https://image.flaticon.com/icons/svg/66/66163.svg" :
+        let path = field === FieldWaiting ? "https://image.flaticon.com/icons/svg/66/66163.svg" :
             field instanceof ImageField ? field.Data.href : "http://www.cs.brown.edu/~bcz/face.gif";
         let nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 1);
         return (
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index a3478143d..9b067aeeb 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -103,14 +103,13 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         this._valueInput = e.currentTarget.value;
     }
 
-    newKeyValue = () => {
-        return (
+    newKeyValue = () =>
+        (
             <tr>
                 <td><input type="text" value={this._keyInput} placeholder="Key" onChange={this.keyChanged} /></td>
                 <td><input type="text" value={this._valueInput} placeholder="Value" onChange={this.valueChanged} onKeyPress={this.onEnterKey} /></td>
             </tr>
         )
-    }
 
     render() {
         return (<div className="keyValueBox-cont" onWheel={this.onPointerWheel}>
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 3b5e3a570..66c9f477e 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -94,7 +94,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
         this._reactionDisposer = reaction(
             () => [this.curPage, this.thumbnailPage],
             () => {
-                if (this.curPage > 0 && this.thumbnailPage > 0 && this.curPage != this.thumbnailPage) {
+                if (this.curPage > 0 && this.thumbnailPage > 0 && this.curPage !== this.thumbnailPage) {
                     this.saveThumbnail();
                     this._interactive = true;
                 }
@@ -165,16 +165,16 @@ export class PDFBox extends React.Component<FieldViewProps> {
 
             let obj: Object = { parentDivs: [], spans: [] };
             //@ts-ignore
-            if (range.commonAncestorContainer.className == 'react-pdf__Page__textContent') { //multiline highlighting case
+            if (range.commonAncestorContainer.className === 'react-pdf__Page__textContent') { //multiline highlighting case
                 obj = this.highlightNodes(range.commonAncestorContainer.childNodes)
             } else { //single line highlighting case
                 let parentDiv = range.commonAncestorContainer.parentElement
                 if (parentDiv) {
-                    if (parentDiv.className == 'react-pdf__Page__textContent') { //when highlight is overwritten
+                    if (parentDiv.className === 'react-pdf__Page__textContent') { //when highlight is overwritten
                         obj = this.highlightNodes(parentDiv.childNodes)
                     } else {
                         parentDiv.childNodes.forEach((child) => {
-                            if (child.nodeName == 'SPAN') {
+                            if (child.nodeName === 'SPAN') {
                                 //@ts-ignore
                                 obj.parentDivs.push(parentDiv)
                                 //@ts-ignore
@@ -197,7 +197,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
         let temp = { parentDivs: [], spans: [] }
         nodes.forEach((div) => {
             div.childNodes.forEach((child) => {
-                if (child.nodeName == 'SPAN') {
+                if (child.nodeName === 'SPAN') {
                     //@ts-ignore
                     temp.parentDivs.push(div)
                     //@ts-ignore
@@ -221,7 +221,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
         let index: any;
         this._pageInfo.divs.forEach((obj: any) => {
             obj.spans.forEach((element: any) => {
-                if (element == span) {
+                if (element === span) {
                     if (!index) {
                         index = this._pageInfo.divs.indexOf(obj);
                     }
@@ -230,11 +230,11 @@ export class PDFBox extends React.Component<FieldViewProps> {
         })
 
         if (this._pageInfo.anno.length >= index + 1) {
-            if (this._currAnno.length == 0) {
+            if (this._currAnno.length === 0) {
                 this._currAnno.push(this._pageInfo.anno[index]);
             }
         } else {
-            if (this._currAnno.length == 0) { //if there are no current annotation
+            if (this._currAnno.length === 0) { //if there are no current annotation
                 let div = span.offsetParent;
                 //@ts-ignore
                 let divX = div.style.left
@@ -315,7 +315,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
      * starts drawing the line when user presses down. 
      */
     onDraw = () => {
-        if (this._currTool != null) {
+        if (this._currTool !== null) {
             this._currTool.style.backgroundColor = "grey";
         }
 
@@ -340,13 +340,13 @@ export class PDFBox extends React.Component<FieldViewProps> {
      * for changing color (for ink/pen)
      */
     onColorChange = (e: React.PointerEvent) => {
-        if (e.currentTarget.innerHTML == "Red") {
+        if (e.currentTarget.innerHTML === "Red") {
             this._currColor = "red";
-        } else if (e.currentTarget.innerHTML == "Blue") {
+        } else if (e.currentTarget.innerHTML === "Blue") {
             this._currColor = "blue";
-        } else if (e.currentTarget.innerHTML == "Green") {
+        } else if (e.currentTarget.innerHTML === "Green") {
             this._currColor = "green";
-        } else if (e.currentTarget.innerHTML == "Black") {
+        } else if (e.currentTarget.innerHTML === "Black") {
             this._currColor = "black";
         }
 
@@ -358,7 +358,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
      */
     onHighlight = () => {
         this._drawToolOn = false;
-        if (this._currTool != null) {
+        if (this._currTool !== null) {
             this._currTool.style.backgroundColor = "grey";
         }
         if (this._highlightTool.current) {
@@ -394,7 +394,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
     onLoaded = (page: any) => {
         if (this._mainDiv.current) {
             this._mainDiv.current.childNodes.forEach((element) => {
-                if (element.nodeName == "DIV") {
+                if (element.nodeName === "DIV") {
                     element.childNodes[0].childNodes.forEach((e) => {
 
                         if (e instanceof HTMLCanvasElement) {
@@ -410,7 +410,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
 
         // bcz: the number of pages should really be set when the document is imported.
         this.props.Document.SetNumber(KeyStore.NumPages, page._transport.numPages);
-        if (this._perPageInfo.length == 0) { //Makes sure it only runs once
+        if (this._perPageInfo.length === 0) { //Makes sure it only runs once
             this._perPageInfo = [...Array(page._transport.numPages)]
         }
         this._loaded = true;
@@ -455,7 +455,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
     get pdfRenderer() {
         let proxy = this._loaded ? (null) : this.imageProxyRenderer;
         let pdfUrl = this.props.Document.GetT(this.props.fieldKey, PDFField);
-        if ((!this._interactive && proxy) || !pdfUrl || pdfUrl == FieldWaiting) {
+        if ((!this._interactive && proxy) || !pdfUrl || pdfUrl === FieldWaiting) {
             return proxy;
         }
         return [
@@ -470,7 +470,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
     get imageProxyRenderer() {
         let thumbField = this.props.Document.Get(KeyStore.Thumbnail);
         if (thumbField) {
-            let path = thumbField == FieldWaiting || this.thumbnailPage != this.curPage ? "https://image.flaticon.com/icons/svg/66/66163.svg" :
+            let path = thumbField === FieldWaiting || this.thumbnailPage !== this.curPage ? "https://image.flaticon.com/icons/svg/66/66163.svg" :
                 thumbField instanceof ImageField ? thumbField.Data.href : "http://cs.brown.edu/people/bcz/prairie.jpg";
             return <img src={path} width="100%" />;
         }
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 72495a964..b4590df34 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -50,8 +50,8 @@ export class VideoBox extends React.Component<FieldViewProps> {
     @action
     setVideoRef = (vref: HTMLVideoElement | null) => {
         if (this.curPage >= 0 && vref) {
-            vref!.currentTime = this.curPage;
-            (vref! as any).AHackBecauseSomethingResetsTheVideoToZero = this.curPage;
+            vref.currentTime = this.curPage;
+            (vref as any).AHackBecauseSomethingResetsTheVideoToZero = this.curPage;
         }
     }
 
diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx
index 7fdd77bf3..9f52d0ea6 100644
--- a/src/debug/Viewer.tsx
+++ b/src/debug/Viewer.tsx
@@ -87,7 +87,7 @@ class DocumentViewer extends React.Component<{ field: Document }> {
             return (
                 <div key={kv[0]}>
                     <b>({key ? key.Name : kv[0]}): </b>
-                    <DebugViewer fieldId={kv[1]!}></DebugViewer>
+                    <DebugViewer fieldId={kv[1]}></DebugViewer>
                 </div>
             )
         })
@@ -177,9 +177,8 @@ class Viewer extends React.Component {
                     onChange={this.inputOnChange}
                     onKeyDown={this.onKeyPress} />
                 <div>
-                    {this.ids.map(id => {
-                        return <DebugViewer fieldId={id} key={id}></DebugViewer>
-                    })}
+                    {this.ids.map(id =>
+                        <DebugViewer fieldId={id} key={id}></DebugViewer>)}
                 </div>
             </>
         )
diff --git a/src/fields/Document.ts b/src/fields/Document.ts
index 85ff6ddcb..45e4f93f6 100644
--- a/src/fields/Document.ts
+++ b/src/fields/Document.ts
@@ -30,9 +30,9 @@ export class Document extends Field {
         }
     }
 
-    public Width = () => { return this.GetNumber(KeyStore.Width, 0) }
-    public Height = () => { return this.GetNumber(KeyStore.Height, this.GetNumber(KeyStore.NativeWidth, 0) ? this.GetNumber(KeyStore.NativeHeight, 0) / this.GetNumber(KeyStore.NativeWidth, 0) * this.GetNumber(KeyStore.Width, 0) : 0) }
-    public Scale = () => { return this.GetNumber(KeyStore.Scale, 1) }
+    public Width = () => this.GetNumber(KeyStore.Width, 0)
+    public Height = () => this.GetNumber(KeyStore.Height, this.GetNumber(KeyStore.NativeWidth, 0) ? this.GetNumber(KeyStore.NativeHeight, 0) / this.GetNumber(KeyStore.NativeWidth, 0) * this.GetNumber(KeyStore.Width, 0) : 0)
+    public Scale = () => this.GetNumber(KeyStore.Scale, 1)
 
     @computed
     public get Title(): string {
@@ -90,7 +90,7 @@ export class Document extends Field {
             }
         } else {
             let doc: FieldValue<Document> = this;
-            while (doc && doc != FieldWaiting && field != FieldWaiting) {
+            while (doc && doc !== FieldWaiting && field !== FieldWaiting) {
                 let curField = doc.fields.get(key.Id);
                 let curProxy = doc._proxies.get(key.Id);
                 if (!curField || (curProxy && curField.field.Id !== curProxy)) {
@@ -118,7 +118,7 @@ export class Document extends Field {
                     break;
                 }
             }
-            if (doc == FieldWaiting)
+            if (doc === FieldWaiting)
                 field = FieldWaiting;
         }
 
@@ -165,7 +165,7 @@ export class Document extends Field {
         if (callback) {
             fn(callback);
         } else {
-            return new Promise(res => fn(res));
+            return new Promise(fn);
         }
     }
 
@@ -356,7 +356,7 @@ export class Document extends Field {
         let fields: [string, string][] = []
         this._proxies.forEach((field, key) => {
             if (field) {
-                fields.push([key, field as string])
+                fields.push([key, field])
             }
         });
 
diff --git a/src/fields/WebField.ts b/src/fields/WebField.ts
index 6c4de5000..0cbcc6d33 100644
--- a/src/fields/WebField.ts
+++ b/src/fields/WebField.ts
@@ -4,7 +4,7 @@ import { Types } from "../server/Message";
 
 export class WebField extends BasicField<URL> {
     constructor(data: URL | undefined = undefined, id?: FieldId, save: boolean = true) {
-        super(data == undefined ? new URL("https://crossorigin.me/" + "https://cs.brown.edu/") : data, save, id);
+        super(data === undefined ? new URL("https://crossorigin.me/" + "https://cs.brown.edu/") : data, save, id);
     }
 
     toString(): string {
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
index ae48dd2c6..c4d346876 100644
--- a/src/mobile/ImageUpload.tsx
+++ b/src/mobile/ImageUpload.tsx
@@ -33,9 +33,8 @@ const onFileLoad = (file: any) => {
             fetch(upload, {
                 method: 'POST',
                 body: formData
-            }).then((res: Response) => {
-                return res.json()
-            }).then(json => {
+            }).then((res: Response) =>
+                res.json()).then(json => {
                 json.map((file: any) => {
                     let path = window.location.origin + file
                     var doc: Document = Documents.ImageDocument(path, { nativeWidth: 200, width: 200 })
diff --git a/src/server/authentication/controllers/WorkspacesMenu.tsx b/src/server/authentication/controllers/WorkspacesMenu.tsx
index 8e14cf98e..835432c8e 100644
--- a/src/server/authentication/controllers/WorkspacesMenu.tsx
+++ b/src/server/authentication/controllers/WorkspacesMenu.tsx
@@ -73,7 +73,7 @@ export class WorkspacesMenu extends React.Component<WorkspaceMenuProps> {
                         <span>{i + 1} - </span>
                         <EditableView
                             display={"inline"}
-                            GetValue={() => { return s.Title }}
+                            GetValue={() => s.Title}
                             SetValue={(title: string): boolean => {
                                 s.SetText(KeyStore.Title, title);
                                 return true;
diff --git a/src/server/authentication/controllers/user_controller.ts b/src/server/authentication/controllers/user_controller.ts
index e365b8dce..2bbb334b5 100644
--- a/src/server/authentication/controllers/user_controller.ts
+++ b/src/server/authentication/controllers/user_controller.ts
@@ -4,7 +4,7 @@ import * as passport from "passport";
 import { IVerifyOptions } from "passport-local";
 import "../config/passport";
 import * as request from "express-validator";
-const flash = require("express-flash");
+import flash = require("express-flash");
 import * as session from "express-session";
 import * as pug from 'pug';
 import * as async from 'async';
@@ -109,12 +109,12 @@ export let postLogin = (req: Request, res: Response, next: NextFunction) => {
     }
 
     passport.authenticate("local", (err: Error, user: DashUserModel, info: IVerifyOptions) => {
-        if (err) { return next(err); }
+        if (err) { next(err); return }
         if (!user) {
             return res.redirect(RouteStore.signup);
         }
         req.logIn(user, (err) => {
-            if (err) { return next(err); }
+            if (err) { next(err); return }
             res.redirect(RouteStore.home);
         });
     })(req, res, next);
@@ -158,7 +158,8 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio
             User.findOne({ email }, function (err, user: DashUserModel) {
                 if (!user) {
                     // NO ACCOUNT WITH SUBMITTED EMAIL
-                    return res.redirect(RouteStore.forgot);
+                    res.redirect(RouteStore.forgot);
+                    return
                 }
                 user.passwordResetToken = token;
                 user.passwordResetExpires = new Date(Date.now() + 3600000); // 1 HOUR
@@ -228,7 +229,8 @@ export let postReset = function (req: Request, res: Response) {
 
                 user.save(function (err) {
                     if (err) {
-                        return res.redirect(RouteStore.login);
+                        res.redirect(RouteStore.login);
+                        return;
                     }
                     req.logIn(user, function (err) {
                         if (err) {
diff --git a/src/server/database.ts b/src/server/database.ts
index a42d29aac..87a0b3c70 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -27,7 +27,7 @@ export class Database {
                     console.log(err.errmsg);
                 }
                 if (res) {
-                    console.log(JSON.stringify(res.result));
+                    // console.log(JSON.stringify(res.result));
                 }
                 callback()
             });
diff --git a/src/server/index.ts b/src/server/index.ts
index 17d7432e0..0c61f6885 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -264,11 +264,9 @@ function deleteFields() {
 }
 
 function deleteAll() {
-    return Database.Instance.deleteAll().then(() => {
-        return Database.Instance.deleteAll('sessions')
-    }).then(() => {
-        return Database.Instance.deleteAll('users')
-    });
+    return Database.Instance.deleteAll().then(() =>
+        Database.Instance.deleteAll('sessions')).then(() =>
+            Database.Instance.deleteAll('users'));
 }
 
 function barReceived(guid: String) {
diff --git a/test/test.ts b/test/test.ts
index 0fa1ea15b..db24cae5f 100644
--- a/test/test.ts
+++ b/test/test.ts
@@ -152,7 +152,7 @@ describe("Reference", () => {
         let ran = false;
         reaction(() => {
             let field = doc2.GetT(key, NumberField);
-            if (field && field != FieldWaiting) {
+            if (field && field !== FieldWaiting) {
                 return field.Data;
             }
             return undefined;
diff --git a/tslint.json b/tslint.json
new file mode 100644
index 000000000..54876916e
--- /dev/null
+++ b/tslint.json
@@ -0,0 +1,56 @@
+{
+    "rules": {
+        // "no-non-null-assertion": true,
+        "no-return-await": true,
+        "no-string-literal": true,
+        // "no-var-keyword": true,
+        // "no-var-requires": true,
+        "prefer-object-spread": true,
+        "prefer-for-of": true,
+        "no-unnecessary-type-assertion": true,
+        // "no-void-expression": [
+        //     true,
+        //     "ignore-arrow-function-shorthand"
+        // ],
+        "triple-equals": true,
+        // "prefer-const": true,
+        "no-unnecessary-callback-wrapper": true,
+        // "align": [
+        //     true,
+        //     "parameters",
+        //     "arguments",
+        //     "statements",
+        //     "members",
+        //     "elements"
+        // ],
+        "class-name": true,
+        "arrow-return-shorthand": true,
+        // "object-literal-shorthand": true,
+        // "object-literal-sort-keys": true,
+        // "semicolon": [
+        //     true,
+        //     "always"
+        // ],
+        // "curly": [
+        //     true,
+        //     "ignore-same-line"
+        // ],
+        // "quotemark": [
+        //     true,
+        //     "double",
+        //     "jsx-double",
+        //     "avoid-template",
+        //     "avoid-escape"
+        // ],
+        "no-tautology-expression": true,
+        "unnecessary-constructor": true
+        // "trailing-comma": [
+        //     true,
+        //     {
+        //         "multiline": "always",
+        //         "singleline": "never"
+        //     }
+        // ],
+        // "ordered-imports": true
+    }
+}
\ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
index 5ba9dd4b5..50079255f 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -28,44 +28,53 @@ module.exports = {
     extensions: ['.js', '.ts', '.tsx']
   },
   module: {
-    rules: [{
-      test: [/\.tsx?$/, /\.ts?$/,],
-      loader: "awesome-typescript-loader",
-      include: path.join(__dirname, 'src')
-    },
-    {
-      test: /\.scss|css$/,
-      use: [
-        {
-          loader: "style-loader"
-        },
-        {
-          loader: "css-loader"
-        },
-        {
-          loader: "sass-loader"
-        }
-      ]
-    },
-    {
-      test: /\.(jpg|png|pdf)$/,
-      use: [
-        {
-          loader: 'file-loader'
-        }
-      ]
-    },
-    {
-      test: /\.(png|jpg|gif)$/i,
-      use: [
-        {
-          loader: 'url-loader',
-          options: {
-            limit: 8192
+    rules: [
+      {
+        test: [/\.tsx?$/, /\.ts?$/,],
+        enforce: 'pre',
+        use: [
+          {
+            loader: "tslint-loader",
           }
-        }
-      ]
-    }]
+        ]
+      }, {
+        test: [/\.tsx?$/, /\.ts?$/,],
+        loader: "awesome-typescript-loader",
+        include: path.join(__dirname, 'src')
+      },
+      {
+        test: /\.scss|css$/,
+        use: [
+          {
+            loader: "style-loader"
+          },
+          {
+            loader: "css-loader"
+          },
+          {
+            loader: "sass-loader"
+          }
+        ]
+      },
+      {
+        test: /\.(jpg|png|pdf)$/,
+        use: [
+          {
+            loader: 'file-loader'
+          }
+        ]
+      },
+      {
+        test: /\.(png|jpg|gif)$/i,
+        use: [
+          {
+            loader: 'url-loader',
+            options: {
+              limit: 8192
+            }
+          }
+        ]
+      }]
   },
   plugins: [
     new CopyWebpackPlugin([{ from: "deploy", to: path.join(__dirname, "build") }]),
-- 
cgit v1.2.3-70-g09d2


From b78aff5115862cbfa5e704422cb738aa7fd73c64 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 4 Apr 2019 14:13:29 -0400
Subject: fixed a number of smaller issues related to linking

---
 src/client/northstar/dash-nodes/HistogramBox.scss  |   5 +
 src/client/util/DragManager.ts                     | 533 +++++++-------
 src/client/util/SelectionManager.ts                |  74 +-
 src/client/views/DocumentDecorations.scss          |  61 +-
 src/client/views/DocumentDecorations.tsx           |  81 ++-
 src/client/views/InkingCanvas.scss                 |   2 +
 src/client/views/Main.scss                         |   7 +
 .../views/collections/CollectionDockingView.scss   |   2 +-
 .../views/collections/CollectionPDFView.scss       |   2 +
 .../views/collections/CollectionVideoView.scss     |   2 +
 .../views/collections/CollectionViewBase.tsx       |  20 +
 .../CollectionFreeFormLinkView.tsx                 |   8 +-
 .../CollectionFreeFormLinksView.scss               |   2 +
 .../collectionFreeForm/CollectionFreeFormView.scss |   6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  13 +-
 .../collectionFreeForm/MarqueeView.scss            |   2 +
 .../collectionFreeForm/PreviewCursor.scss          |   4 +
 .../views/nodes/CollectionFreeFormDocumentView.tsx |   4 +
 src/client/views/nodes/DocumentView.scss           |   2 +
 src/client/views/nodes/PDFBox.scss                 |   4 +
 src/client/views/nodes/WebBox.scss                 |   2 +
 src/fields/Document.ts                             | 773 +++++++++++----------
 22 files changed, 882 insertions(+), 727 deletions(-)

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

diff --git a/src/client/northstar/dash-nodes/HistogramBox.scss b/src/client/northstar/dash-nodes/HistogramBox.scss
index b11840a65..94da36e29 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.scss
+++ b/src/client/northstar/dash-nodes/HistogramBox.scss
@@ -1,6 +1,8 @@
 .histogrambox-container {
     padding: 0vw;
     position: absolute;
+    top: 0;
+    left:0;
     text-align: center;
     width: 100%;
     height: 100%;
@@ -8,6 +10,7 @@
   }
   .histogrambox-xaxislabel {
     position:absolute;
+    left:0;
     width:100%;
     text-align: center;
     bottom:0;
@@ -19,11 +22,13 @@
     position:absolute;
     height:100%;
     width: 25px;
+    left:0;
     bottom:0;
     background: lightgray;
   }
   .histogrambox-yaxislabel-text {
     position:absolute;
+    left:0;
     transform-origin: left;
     transform: rotate(-90deg);
     text-align: center;
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index c0f482e18..e8f8cce7c 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -8,304 +8,307 @@ import { CollectionView } from "../views/collections/CollectionView";
 import { DocumentView } from "../views/nodes/DocumentView";
 
 export function setupDrag(
-  _reference: React.RefObject<HTMLDivElement>,
-  docFunc: () => Document,
-  removeFunc: (containingCollection: CollectionView) => void = () => {}
+    _reference: React.RefObject<HTMLDivElement>,
+    docFunc: () => Document,
+    removeFunc: (containingCollection: CollectionView) => void = () => { }
 ) {
-  let onRowMove = action(
-    (e: PointerEvent): void => {
-      e.stopPropagation();
-      e.preventDefault();
+    let onRowMove = action(
+        (e: PointerEvent): void => {
+            e.stopPropagation();
+            e.preventDefault();
 
-      document.removeEventListener("pointermove", onRowMove);
-      document.removeEventListener("pointerup", onRowUp);
-      var dragData = new DragManager.DocumentDragData([docFunc()]);
-      dragData.removeDocument = removeFunc;
-      DragManager.StartDocumentDrag([_reference.current!], dragData);
-    }
-  );
-  let onRowUp = action(
-    (e: PointerEvent): void => {
-      document.removeEventListener("pointermove", onRowMove);
-      document.removeEventListener("pointerup", onRowUp);
-    }
-  );
-  let onItemDown = (e: React.PointerEvent) => {
-    // if (this.props.isSelected() || this.props.isTopMost) {
-    if (e.button == 0) {
-      e.stopPropagation();
-      if (e.shiftKey) {
-        CollectionDockingView.Instance.StartOtherDrag([docFunc()], e);
-      } else {
-        document.addEventListener("pointermove", onRowMove);
-        document.addEventListener("pointerup", onRowUp);
-      }
-    }
-    //}
-  };
-  return onItemDown;
+            document.removeEventListener("pointermove", onRowMove);
+            document.removeEventListener("pointerup", onRowUp);
+            var dragData = new DragManager.DocumentDragData([docFunc()]);
+            dragData.removeDocument = removeFunc;
+            DragManager.StartDocumentDrag([_reference.current!], dragData);
+        }
+    );
+    let onRowUp = action(
+        (e: PointerEvent): void => {
+            document.removeEventListener("pointermove", onRowMove);
+            document.removeEventListener("pointerup", onRowUp);
+        }
+    );
+    let onItemDown = (e: React.PointerEvent) => {
+        // if (this.props.isSelected() || this.props.isTopMost) {
+        if (e.button == 0) {
+            e.stopPropagation();
+            if (e.shiftKey) {
+                CollectionDockingView.Instance.StartOtherDrag([docFunc()], e);
+            } else {
+                document.addEventListener("pointermove", onRowMove);
+                document.addEventListener("pointerup", onRowUp);
+            }
+        }
+        //}
+    };
+    return onItemDown;
 }
 
 export namespace DragManager {
-  export function Root() {
-    const root = document.getElementById("root");
-    if (!root) {
-      throw new Error("No root element found");
+    export function Root() {
+        const root = document.getElementById("root");
+        if (!root) {
+            throw new Error("No root element found");
+        }
+        return root;
     }
-    return root;
-  }
 
-  let dragDiv: HTMLDivElement;
+    let dragDiv: HTMLDivElement;
 
-  export enum DragButtons {
-    Left = 1,
-    Right = 2,
-    Both = Left | Right
-  }
+    export enum DragButtons {
+        Left = 1,
+        Right = 2,
+        Both = Left | Right
+    }
 
-  interface DragOptions {
-    handlers: DragHandlers;
+    interface DragOptions {
+        handlers: DragHandlers;
 
-    hideSource: boolean | (() => boolean);
-  }
+        hideSource: boolean | (() => boolean);
+    }
 
-  export interface DragDropDisposer {
-    (): void;
-  }
+    export interface DragDropDisposer {
+        (): void;
+    }
 
-  export class DragCompleteEvent {}
+    export class DragCompleteEvent { }
 
-  export interface DragHandlers {
-    dragComplete: (e: DragCompleteEvent) => void;
-  }
+    export interface DragHandlers {
+        dragComplete: (e: DragCompleteEvent) => void;
+    }
 
-  export interface DropOptions {
-    handlers: DropHandlers;
-  }
-  export class DropEvent {
-    constructor(
-      readonly x: number,
-      readonly y: number,
-      readonly data: { [id: string]: any }
-    ) {}
-  }
+    export interface DropOptions {
+        handlers: DropHandlers;
+    }
+    export class DropEvent {
+        constructor(
+            readonly x: number,
+            readonly y: number,
+            readonly data: { [id: string]: any }
+        ) { }
+    }
 
-  export interface DropHandlers {
-    drop: (e: Event, de: DropEvent) => void;
-  }
+    export interface DropHandlers {
+        drop: (e: Event, de: DropEvent) => void;
+    }
 
-  export function MakeDropTarget(
-    element: HTMLElement,
-    options: DropOptions
-  ): DragDropDisposer {
-    if ("canDrop" in element.dataset) {
-      throw new Error(
-        "Element is already droppable, can't make it droppable again"
-      );
+    export function MakeDropTarget(
+        element: HTMLElement,
+        options: DropOptions
+    ): DragDropDisposer {
+        if ("canDrop" in element.dataset) {
+            throw new Error(
+                "Element is already droppable, can't make it droppable again"
+            );
+        }
+        element.dataset["canDrop"] = "true";
+        const handler = (e: Event) => {
+            const ce = e as CustomEvent<DropEvent>;
+            options.handlers.drop(e, ce.detail);
+        };
+        element.addEventListener("dashOnDrop", handler);
+        return () => {
+            element.removeEventListener("dashOnDrop", handler);
+            delete element.dataset["canDrop"];
+        };
     }
-    element.dataset["canDrop"] = "true";
-    const handler = (e: Event) => {
-      const ce = e as CustomEvent<DropEvent>;
-      options.handlers.drop(e, ce.detail);
-    };
-    element.addEventListener("dashOnDrop", handler);
-    return () => {
-      element.removeEventListener("dashOnDrop", handler);
-      delete element.dataset["canDrop"];
-    };
-  }
 
-  export class DocumentDragData {
-    constructor(dragDoc: Document[]) {
-      this.draggedDocuments = dragDoc;
-      this.droppedDocuments = dragDoc;
+    export class DocumentDragData {
+        constructor(dragDoc: Document[]) {
+            this.draggedDocuments = dragDoc;
+            this.droppedDocuments = dragDoc;
+        }
+        draggedDocuments: Document[];
+        droppedDocuments: Document[];
+        xOffset?: number;
+        yOffset?: number;
+        aliasOnDrop?: boolean;
+        removeDocument?: (collectionDrop: CollectionView) => void;
+        [id: string]: any;
     }
-    draggedDocuments: Document[];
-    droppedDocuments: Document[];
-    xOffset?: number;
-    yOffset?: number;
-    aliasOnDrop?: boolean;
-    removeDocument?: (collectionDrop: CollectionView) => void;
-    [id: string]: any;
-  }
 
-  export function StartDocumentDrag(
-    eles: HTMLElement[],
-    dragData: DocumentDragData,
-    options?: DragOptions
-  ) {
-    StartDrag(
-      eles,
-      dragData,
-      options,
-      (dropData: { [id: string]: any }) =>
-        (dropData.droppedDocuments = dragData.aliasOnDrop
-          ? dragData.draggedDocuments.map(d => d.CreateAlias())
-          : dragData.draggedDocuments)
-    );
-  }
+    export function StartDocumentDrag(
+        eles: HTMLElement[],
+        dragData: DocumentDragData,
+        options?: DragOptions
+    ) {
+        StartDrag(
+            eles,
+            dragData,
+            options,
+            (dropData: { [id: string]: any }) =>
+                (dropData.droppedDocuments = dragData.aliasOnDrop
+                    ? dragData.draggedDocuments.map(d => d.CreateAlias())
+                    : dragData.draggedDocuments)
+        );
+    }
 
-  export class LinkDragData {
-    constructor(linkSourceDoc: DocumentView) {
-      this.linkSourceDocumentView = linkSourceDoc;
+    export class LinkDragData {
+        constructor(linkSourceDoc: DocumentView) {
+            this.linkSourceDocumentView = linkSourceDoc;
+        }
+        droppedDocuments: Document[] = [];
+        linkSourceDocumentView: DocumentView;
+        [id: string]: any;
     }
-    linkSourceDocumentView: DocumentView;
-    [id: string]: any;
-  }
-  export function StartLinkDrag(
-    ele: HTMLElement,
-    dragData: LinkDragData,
-    options?: DragOptions
-  ) {
-    StartDrag([ele], dragData, options);
-  }
-  function StartDrag(
-    eles: HTMLElement[],
-    dragData: { [id: string]: any },
-    options?: DragOptions,
-    finishDrag?: (dropData: { [id: string]: any }) => void
-  ) {
-    if (!dragDiv) {
-      dragDiv = document.createElement("div");
-      dragDiv.className = "dragManager-dragDiv";
-      DragManager.Root().appendChild(dragDiv);
+    export function StartLinkDrag(
+        ele: HTMLElement,
+        dragData: LinkDragData,
+        options?: DragOptions
+    ) {
+        StartDrag([ele], dragData, options);
     }
+    function StartDrag(
+        eles: HTMLElement[],
+        dragData: { [id: string]: any },
+        options?: DragOptions,
+        finishDrag?: (dropData: { [id: string]: any }) => void
+    ) {
+        if (!dragDiv) {
+            dragDiv = document.createElement("div");
+            dragDiv.className = "dragManager-dragDiv";
+            DragManager.Root().appendChild(dragDiv);
+        }
 
-    let scaleXs: number[] = [];
-    let scaleYs: number[] = [];
-    let xs: number[] = [];
-    let ys: number[] = [];
+        let scaleXs: number[] = [];
+        let scaleYs: number[] = [];
+        let xs: number[] = [];
+        let ys: number[] = [];
 
-    const docs: Document[] =
-      dragData instanceof DocumentDragData ? dragData.draggedDocuments : [];
-    let dragElements = eles.map(ele => {
-      const w = ele.offsetWidth,
-        h = ele.offsetHeight;
-      const rect = ele.getBoundingClientRect();
-      const scaleX = rect.width / w,
-        scaleY = rect.height / h;
-      let x = rect.left,
-        y = rect.top;
-      xs.push(x);
-      ys.push(y);
-      scaleXs.push(scaleX);
-      scaleYs.push(scaleY);
-      let dragElement = ele.cloneNode(true) as HTMLElement;
-      dragElement.style.opacity = "0.7";
-      dragElement.style.position = "absolute";
-      dragElement.style.bottom = "";
-      dragElement.style.left = "";
-      dragElement.style.transformOrigin = "0 0";
-      dragElement.style.zIndex = "1000";
-      dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
-      dragElement.style.width = `${rect.width / scaleX}px`;
-      dragElement.style.height = `${rect.height / scaleY}px`;
+        const docs: Document[] =
+            dragData instanceof DocumentDragData ? dragData.draggedDocuments : [];
+        let dragElements = eles.map(ele => {
+            const w = ele.offsetWidth,
+                h = ele.offsetHeight;
+            const rect = ele.getBoundingClientRect();
+            const scaleX = rect.width / w,
+                scaleY = rect.height / h;
+            let x = rect.left,
+                y = rect.top;
+            xs.push(x);
+            ys.push(y);
+            scaleXs.push(scaleX);
+            scaleYs.push(scaleY);
+            let dragElement = ele.cloneNode(true) as HTMLElement;
+            dragElement.style.opacity = "0.7";
+            dragElement.style.position = "absolute";
+            dragElement.style.margin = "0";
+            dragElement.style.top = "0";
+            dragElement.style.bottom = "";
+            dragElement.style.left = "0";
+            dragElement.style.transformOrigin = "0 0";
+            dragElement.style.zIndex = "1000";
+            dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
+            dragElement.style.width = `${rect.width / scaleX}px`;
+            dragElement.style.height = `${rect.height / scaleY}px`;
 
-      // bcz: PDFs don't show up if you clone them because they contain a canvas.
-      //      however, PDF's have a thumbnail field that contains an image of their canvas.
-      //      So we replace the pdf's canvas with the image thumbnail
-      if (docs.length) {
-        var pdfBox = dragElement.getElementsByClassName(
-          "pdfBox-cont"
-        )[0] as HTMLElement;
-        let thumbnail = docs[0].GetT(KeyStore.Thumbnail, ImageField);
-        if (pdfBox && pdfBox.childElementCount && thumbnail) {
-          let img = new Image();
-          img!.src = thumbnail.toString();
-          img!.style.position = "absolute";
-          img!.style.width = `${rect.width / scaleX}px`;
-          img!.style.height = `${rect.height / scaleY}px`;
-          pdfBox.replaceChild(img!, pdfBox.children[0]);
+            // bcz: PDFs don't show up if you clone them because they contain a canvas.
+            //      however, PDF's have a thumbnail field that contains an image of their canvas.
+            //      So we replace the pdf's canvas with the image thumbnail
+            if (docs.length) {
+                var pdfBox = dragElement.getElementsByClassName(
+                    "pdfBox-cont"
+                )[0] as HTMLElement;
+                let thumbnail = docs[0].GetT(KeyStore.Thumbnail, ImageField);
+                if (pdfBox && pdfBox.childElementCount && thumbnail) {
+                    let img = new Image();
+                    img!.src = thumbnail.toString();
+                    img!.style.position = "absolute";
+                    img!.style.width = `${rect.width / scaleX}px`;
+                    img!.style.height = `${rect.height / scaleY}px`;
+                    pdfBox.replaceChild(img!, pdfBox.children[0]);
+                }
+            }
+
+            dragDiv.appendChild(dragElement);
+            return dragElement;
+        });
+
+        let hideSource = false;
+        if (options) {
+            if (typeof options.hideSource === "boolean") {
+                hideSource = options.hideSource;
+            } else {
+                hideSource = options.hideSource();
+            }
         }
-      }
+        eles.map(ele => (ele.hidden = hideSource));
 
-      dragDiv.appendChild(dragElement);
-      return dragElement;
-    });
+        const moveHandler = (e: PointerEvent) => {
+            e.stopPropagation();
+            e.preventDefault();
+            if (dragData instanceof DocumentDragData)
+                dragData.aliasOnDrop = e.ctrlKey || e.altKey;
+            if (e.shiftKey) {
+                abortDrag();
+                CollectionDockingView.Instance.StartOtherDrag(docs, {
+                    pageX: e.pageX,
+                    pageY: e.pageY,
+                    preventDefault: () => { },
+                    button: 0
+                });
+            }
+            dragElements.map(
+                (dragElement, i) =>
+                    (dragElement.style.transform = `translate(${(xs[i] +=
+                        e.movementX)}px, ${(ys[i] += e.movementY)}px) scale(${
+                        scaleXs[i]
+                        }, ${scaleYs[i]})`)
+            );
+        };
 
-    let hideSource = false;
-    if (options) {
-      if (typeof options.hideSource === "boolean") {
-        hideSource = options.hideSource;
-      } else {
-        hideSource = options.hideSource();
-      }
+        const abortDrag = () => {
+            document.removeEventListener("pointermove", moveHandler, true);
+            document.removeEventListener("pointerup", upHandler);
+            dragElements.map(dragElement => dragDiv.removeChild(dragElement));
+            eles.map(ele => (ele.hidden = false));
+        };
+        const upHandler = (e: PointerEvent) => {
+            abortDrag();
+            FinishDrag(eles, e, dragData, options, finishDrag);
+        };
+        document.addEventListener("pointermove", moveHandler, true);
+        document.addEventListener("pointerup", upHandler);
     }
-    eles.map(ele => (ele.hidden = hideSource));
 
-    const moveHandler = (e: PointerEvent) => {
-      e.stopPropagation();
-      e.preventDefault();
-      if (dragData instanceof DocumentDragData)
-        dragData.aliasOnDrop = e.ctrlKey || e.altKey;
-      if (e.shiftKey) {
-        abortDrag();
-        CollectionDockingView.Instance.StartOtherDrag(docs, {
-          pageX: e.pageX,
-          pageY: e.pageY,
-          preventDefault: () => {},
-          button: 0
+    function FinishDrag(
+        dragEles: HTMLElement[],
+        e: PointerEvent,
+        dragData: { [index: string]: any },
+        options?: DragOptions,
+        finishDrag?: (dragData: { [index: string]: any }) => void
+    ) {
+        let removed = dragEles.map(dragEle => {
+            let parent = dragEle.parentElement;
+            if (parent) parent.removeChild(dragEle);
+            return [dragEle, parent];
         });
-      }
-      dragElements.map(
-        (dragElement, i) =>
-          (dragElement.style.transform = `translate(${(xs[i] +=
-            e.movementX)}px, ${(ys[i] += e.movementY)}px) scale(${
-            scaleXs[i]
-          }, ${scaleYs[i]})`)
-      );
-    };
-
-    const abortDrag = () => {
-      document.removeEventListener("pointermove", moveHandler, true);
-      document.removeEventListener("pointerup", upHandler);
-      dragElements.map(dragElement => dragDiv.removeChild(dragElement));
-      eles.map(ele => (ele.hidden = false));
-    };
-    const upHandler = (e: PointerEvent) => {
-      abortDrag();
-      FinishDrag(eles, e, dragData, options, finishDrag);
-    };
-    document.addEventListener("pointermove", moveHandler, true);
-    document.addEventListener("pointerup", upHandler);
-  }
-
-  function FinishDrag(
-    dragEles: HTMLElement[],
-    e: PointerEvent,
-    dragData: { [index: string]: any },
-    options?: DragOptions,
-    finishDrag?: (dragData: { [index: string]: any }) => void
-  ) {
-    let removed = dragEles.map(dragEle => {
-      let parent = dragEle.parentElement;
-      if (parent) parent.removeChild(dragEle);
-      return [dragEle, parent];
-    });
-    const target = document.elementFromPoint(e.x, e.y);
-    removed.map(r => {
-      let dragEle: HTMLElement = r[0]!;
-      let parent: HTMLElement | null = r[1];
-      if (parent) parent.appendChild(dragEle);
-    });
-    if (target) {
-      if (finishDrag) finishDrag(dragData);
+        const target = document.elementFromPoint(e.x, e.y);
+        removed.map(r => {
+            let dragEle: HTMLElement = r[0]!;
+            let parent: HTMLElement | null = r[1];
+            if (parent) parent.appendChild(dragEle);
+        });
+        if (target) {
+            if (finishDrag) finishDrag(dragData);
 
-      target.dispatchEvent(
-        new CustomEvent<DropEvent>("dashOnDrop", {
-          bubbles: true,
-          detail: {
-            x: e.x,
-            y: e.y,
-            data: dragData
-          }
-        })
-      );
+            target.dispatchEvent(
+                new CustomEvent<DropEvent>("dashOnDrop", {
+                    bubbles: true,
+                    detail: {
+                        x: e.x,
+                        y: e.y,
+                        data: dragData
+                    }
+                })
+            );
 
-      if (options) {
-        options.handlers.dragComplete({});
-      }
+            if (options) {
+                options.handlers.dragComplete({});
+            }
+        }
+        DocumentDecorations.Instance.Hidden = false;
     }
-    DocumentDecorations.Instance.Hidden = false;
-  }
 }
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 438659108..958c14491 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -3,48 +3,46 @@ import { DocumentView } from "../views/nodes/DocumentView";
 import { Document } from "../../fields/Document";
 
 export namespace SelectionManager {
-  class Manager {
-    @observable
-    SelectedDocuments: Array<DocumentView> = [];
-
-    @action
-    SelectDoc(doc: DocumentView, ctrlPressed: boolean): void {
-      // if doc is not in SelectedDocuments, add it
-      if (!ctrlPressed) {
-        manager.SelectedDocuments = [];
-      }
-
-      if (manager.SelectedDocuments.indexOf(doc) === -1) {
-        manager.SelectedDocuments.push(doc);
-      }
+    class Manager {
+        @observable
+        SelectedDocuments: Array<DocumentView> = [];
+
+        @action
+        SelectDoc(doc: DocumentView, ctrlPressed: boolean): void {
+            // if doc is not in SelectedDocuments, add it
+            if (!ctrlPressed) {
+                manager.SelectedDocuments = [];
+            }
+
+            if (manager.SelectedDocuments.indexOf(doc) === -1) {
+                manager.SelectedDocuments.push(doc);
+            }
+        }
     }
-  }
 
-  const manager = new Manager();
+    const manager = new Manager();
 
-  export function SelectDoc(doc: DocumentView, ctrlPressed: boolean): void {
-    if (!doc.isMinimized()) {
-      manager.SelectDoc(doc, ctrlPressed);
+    export function SelectDoc(doc: DocumentView, ctrlPressed: boolean): void {
+        manager.SelectDoc(doc, ctrlPressed);
     }
-  }
-
-  export function IsSelected(doc: DocumentView): boolean {
-    return manager.SelectedDocuments.indexOf(doc) !== -1;
-  }
-
-  export function DeselectAll(except?: Document): void {
-    let found: DocumentView | undefined = undefined;
-    if (except) {
-      for (let i = 0; i < manager.SelectedDocuments.length; i++) {
-        let view = manager.SelectedDocuments[i];
-        if (view.props.Document == except) found = view;
-      }
+
+    export function IsSelected(doc: DocumentView): boolean {
+        return manager.SelectedDocuments.indexOf(doc) !== -1;
+    }
+
+    export function DeselectAll(except?: Document): void {
+        let found: DocumentView | undefined = undefined;
+        if (except) {
+            for (let i = 0; i < manager.SelectedDocuments.length; i++) {
+                let view = manager.SelectedDocuments[i];
+                if (view.props.Document == except) found = view;
+            }
+        }
+        manager.SelectedDocuments.length = 0;
+        if (found) manager.SelectedDocuments.push(found);
     }
-    manager.SelectedDocuments.length = 0;
-    if (found) manager.SelectedDocuments.push(found);
-  }
 
-  export function SelectedDocuments(): Array<DocumentView> {
-    return manager.SelectedDocuments;
-  }
+    export function SelectedDocuments(): Array<DocumentView> {
+        return manager.SelectedDocuments;
+    }
 }
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index befe175b5..080c9fa1e 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -2,13 +2,16 @@
 
 #documentDecorations-container {
     position: absolute;
+    top: 0;
+    left:0;
     display: grid;
     z-index: 1000;
     grid-template-rows: 20px 8px 1fr 8px;
-    grid-template-columns: 8px 1fr 8px;
+    grid-template-columns: 8px 8px 1fr 8px;
     pointer-events: none;
 
     #documentDecorations-centerCont {
+        grid-column:3;
         background: none;
     }
 
@@ -18,6 +21,24 @@
         opacity: 0.8;
     }
 
+    #documentDecorations-topLeftResizer,
+    #documentDecorations-leftResizer,
+    #documentDecorations-bottomLeftResizer {
+        grid-column: 1
+    }
+
+    #documentDecorations-topResizer,
+    #documentDecorations-bottomResizer {
+        grid-column-start: 2;
+        grid-column-end: 4;
+    }
+
+    #documentDecorations-bottomRightResizer,
+    #documentDecorations-topRightResizer,
+    #documentDecorations-rightResizer {
+        grid-column:4
+    }
+
     #documentDecorations-topLeftResizer,
     #documentDecorations-bottomRightResizer {
         cursor: nwse-resize;
@@ -39,15 +60,19 @@
     }
     .title{
         background: lightblue;
-        grid-column-start:2;
-        grid-column-end: 4;
+        grid-column-start:3;
+        grid-column-end: 5;
         pointer-events: auto;
     }
 }
 
 .documentDecorations-minimizeButton {
-    background: rgb(250, 57, 9);
+    background:$alt-accent;
+    opacity: 0.8;
+    grid-column-start: 1;
+    grid-column-end: 3;
     pointer-events: all;
+    text-align: center;
 }
 .documentDecorations-background {
     background: lightblue;
@@ -87,7 +112,8 @@
 //     }
 // }
 .linkFlyout {
-    grid-column: 1/4
+    grid-column: 1/4;
+    margin-left: 25px;
 }
 
 .linkButton-empty:hover {
@@ -102,6 +128,31 @@
     cursor: pointer;
 }
 
+.linkButton-linker {
+    position:absolute;
+    bottom:0px;
+    left: 0px;
+    height: 20px;
+    width: 20px;
+    margin-top: 10px;
+    margin-right: 5px;
+    border-radius: 50%;
+    opacity: 0.9;
+    pointer-events: auto;
+    color: $dark-color;
+    border: $dark-color 1px solid;
+    text-transform: uppercase;
+    letter-spacing: 2px;
+    font-size: 75%;
+    transition: transform 0.2s;
+    text-align: center;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+}
+.linkButton-tray {
+    grid-column: 1/4;
+}
 .linkButton-empty {
     height: 20px;
     width: 20px;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index b74737ec9..a46087c9a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -1,10 +1,11 @@
-import { action, computed, observable } from "mobx";
+import { action, computed, observable, trace, runInAction } 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 } from "../util/DragManager";
 import { SelectionManager } from "../util/SelectionManager";
@@ -13,6 +14,7 @@ import './DocumentDecorations.scss';
 import { DocumentView } from "./nodes/DocumentView";
 import { LinkMenu } from "./nodes/LinkMenu";
 import React = require("react");
+import { FieldWaiting } from "../../fields/Field";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -28,6 +30,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     private _linkBoxHeight = 30;
     private _titleHeight = 20;
     private _linkButton = React.createRef<HTMLDivElement>();
+    private _linkerButton = React.createRef<HTMLDivElement>();
     //@observable private _title: string = this._documents[0].props.Document.Title;
     @observable private _title: string = this._documents.length > 0 ? this._documents[0].props.Document.Title : "";
     @observable private _fieldKey: Key = KeyStore.Title;
@@ -174,18 +177,40 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         }
     }
 
-    onLinkButtonDown = (e: React.PointerEvent): void => {
-        // if ()
-        // let linkMenu = new LinkMenu(SelectionManager.SelectedDocuments()[0]);
-        // linkMenu.Hidden = false;
-        console.log("down");
+    onLinkerButtonDown = (e: React.PointerEvent): void => {
+        e.stopPropagation();
+        document.removeEventListener("pointermove", this.onLinkerButtonMoved)
+        document.addEventListener("pointermove", this.onLinkerButtonMoved);
+        document.removeEventListener("pointerup", this.onLinkerButtonUp)
+        document.addEventListener("pointerup", this.onLinkerButtonUp);
+    }
+    onLinkerButtonUp = (e: PointerEvent): void => {
+        document.removeEventListener("pointermove", this.onLinkerButtonMoved)
+        document.removeEventListener("pointerup", this.onLinkerButtonUp)
+        e.stopPropagation();
+    }
 
+    onLinkerButtonMoved = (e: PointerEvent): void => {
+        if (this._linkerButton.current != null) {
+            document.removeEventListener("pointermove", this.onLinkerButtonMoved)
+            document.removeEventListener("pointerup", this.onLinkerButtonUp)
+            let dragData = new DragManager.LinkDragData(SelectionManager.SelectedDocuments()[0]);
+            DragManager.StartLinkDrag(this._linkerButton.current, dragData, {
+                handlers: {
+                    dragComplete: action(() => { }),
+                },
+                hideSource: false
+            })
+        }
+        e.stopPropagation();
+    }
+
+    onLinkButtonDown = (e: React.PointerEvent): void => {
         e.stopPropagation();
         document.removeEventListener("pointermove", this.onLinkButtonMoved)
         document.addEventListener("pointermove", this.onLinkButtonMoved);
         document.removeEventListener("pointerup", this.onLinkButtonUp)
         document.addEventListener("pointerup", this.onLinkButtonUp);
-
     }
 
     onLinkButtonUp = (e: PointerEvent): void => {
@@ -194,18 +219,32 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         e.stopPropagation();
     }
 
-
     onLinkButtonMoved = (e: PointerEvent): void => {
         if (this._linkButton.current != null) {
             document.removeEventListener("pointermove", this.onLinkButtonMoved)
-            document.removeEventListener("pointerup", this.onLinkButtonUp)
-            let dragData = new DragManager.LinkDragData(SelectionManager.SelectedDocuments()[0]);
-            DragManager.StartLinkDrag(this._linkButton.current, dragData, {
-                handlers: {
-                    dragComplete: action(() => { }),
-                },
-                hideSource: false
-            })
+            document.removeEventListener("pointerup", this.onLinkButtonUp);
+
+            let sourceDoc = SelectionManager.SelectedDocuments()[0].props.Document;
+            let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document)
+            let draggedDocs = (srcTarg && srcTarg != FieldWaiting) ?
+                srcTarg.GetList(KeyStore.LinkedToDocs, [] as Document[]).map(linkDoc =>
+                    (linkDoc.GetT(KeyStore.LinkedToDocs, Document)) as Document) : [];
+            let draggedFromDocs = (srcTarg && srcTarg != FieldWaiting) ?
+                srcTarg.GetList(KeyStore.LinkedFromDocs, [] as Document[]).map(linkDoc =>
+                    (linkDoc.GetT(KeyStore.LinkedFromDocs, Document)) as Document) : [];
+            draggedDocs.push(...draggedFromDocs);
+            if (draggedDocs.length) {
+                let dragData = new DragManager.DocumentDragData(draggedDocs.map(d => {
+                    let annot = d.GetT(KeyStore.AnnotationOn, Document); // bcz: ... needs to change ... the annotationOn document may not have been retrieved yet...
+                    return annot && annot != FieldWaiting ? annot : d;
+                }));
+                DragManager.StartDocumentDrag([this._linkButton.current], dragData, {
+                    handlers: {
+                        dragComplete: action(() => { }),
+                    },
+                    hideSource: false
+                })
+            }
         }
         e.stopPropagation();
     }
@@ -298,7 +337,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     }
 
     getValue = (): string => {
-        console.log("value watched");
         if (this._title === "changed" && this._documents.length > 0) {
             let field = this._documents[0].props.Document.Get(this._fieldKey);
             if (field instanceof TextField) {
@@ -338,8 +376,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             linkButton = (<Flyout
                 anchorPoint={anchorPoints.RIGHT_TOP}
                 content={
-                    <LinkMenu docView={selFirst} changeFlyout={this.changeFlyoutContent}>
-                    </LinkMenu>
+                    <LinkMenu docView={selFirst} changeFlyout={this.changeFlyoutContent} />
                 }>
                 <div className={"linkButton-" + (selFirst.props.Document.GetData(KeyStore.LinkedToDocs, ListField, []).length ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >{linkCount}</div>
             </Flyout>);
@@ -361,7 +398,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
                 opacity: this._opacity
             }}>
-                <div className="documentDecorations-minimizeButton" onPointerDown={this.onMinimizeDown}>v</div>
+                <div className="documentDecorations-minimizeButton" onPointerDown={this.onMinimizeDown}>...</div>
                 <input ref={this.keyinput} className="title" type="text" name="dynbox" value={this.getValue()} onChange={this.handleChange} onPointerDown={this.onPointerDown} onKeyPress={this.enterPressed} />
                 <div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
                 <div id="documentDecorations-topResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
@@ -373,8 +410,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 <div id="documentDecorations-bottomResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
                 <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
 
-                <div title="View Links" className="linkFlyout" ref={this._linkButton}>{linkButton}</div>
-
+                <div title="View Links" className="linkFlyout" ref={this._linkButton}> {linkButton}  </div>
+                <div className="linkButton-linker" ref={this._linkerButton} onPointerDown={this.onLinkerButtonDown}>∞</div>
             </div >
         </div>
         )
diff --git a/src/client/views/InkingCanvas.scss b/src/client/views/InkingCanvas.scss
index 35c8ee942..c5a2a50cb 100644
--- a/src/client/views/InkingCanvas.scss
+++ b/src/client/views/InkingCanvas.scss
@@ -2,6 +2,8 @@
 
 .inkingCanvas-paths-ink, .inkingCanvas-paths-markers, .inkingCanvas-noSelect, .inkingCanvas-canSelect {
     position: absolute;
+    top: 0;
+    left:0;
     width: 8192px;
     height: 8192px;
     cursor:"crosshair";
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 698a9c617..594803aab 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -7,6 +7,9 @@ body {
     overflow: hidden;
     font-family: $sans-serif;
     margin: 0;
+    position:absolute;
+    top: 0;
+    left:0;
 }
 
 #dash-title {
@@ -158,11 +161,15 @@ button:hover {
     width:100%;
     height:100%;
     position:absolute;
+    top: 0;
+    left:0;
 }
 #mainContent-div {
     width:100%;
     height:100%;
     position:absolute;
+    top: 0;
+    left:0;
 }
 #add-options-content {
     display: table;
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 2706c3272..583d50c5b 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -3,7 +3,7 @@
 }
 
 .collectiondockingview-container {
-    position: relative;
+    position: absolute;
     top: 0;
     left: 0;
     overflow: hidden;
diff --git a/src/client/views/collections/CollectionPDFView.scss b/src/client/views/collections/CollectionPDFView.scss
index 0144625c1..0eca3f1cd 100644
--- a/src/client/views/collections/CollectionPDFView.scss
+++ b/src/client/views/collections/CollectionPDFView.scss
@@ -9,6 +9,8 @@
     width: 100%;
     height: 100%;
     position: absolute; 
+    top: 0;
+    left:0;
     
 }
 .collectionPdfView-backward {
diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss
index cbb981b13..ed56ad268 100644
--- a/src/client/views/collections/CollectionVideoView.scss
+++ b/src/client/views/collections/CollectionVideoView.scss
@@ -3,6 +3,8 @@
     width: 100%;
     height: 100%;
     position: absolute;
+    top: 0;
+    left:0;
  
 }
 .collectionVideoView-time{
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index 2b0689800..daeca69e2 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -17,6 +17,8 @@ import { NumberField } from "../../../fields/NumberField";
 import request = require("request");
 import { ServerUtils } from "../../../server/ServerUtil";
 import { Server } from "../../Server";
+import { CollectionDockingView } from "./CollectionDockingView";
+import { runReactions } from "mobx/lib/internal";
 
 export interface CollectionViewProps {
     fieldKey: Key;
@@ -92,6 +94,24 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
             e.stopPropagation();
             return added;
         }
+        if (de.data instanceof DragManager.LinkDragData) {
+            let sourceDoc: Document = de.data.linkSourceDocumentView.props.Document;
+            if (sourceDoc) runInAction(() => {
+                let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document)
+                if (srcTarg && srcTarg != FieldWaiting) {
+                    let linkDocs = srcTarg.GetList(KeyStore.LinkedToDocs, [] as Document[]);
+                    linkDocs.map(linkDoc => {
+                        let targDoc = linkDoc.GetT(KeyStore.LinkedToDocs, Document);
+                        if (targDoc && targDoc != FieldWaiting) {
+                            let dropdoc = targDoc.MakeDelegate();
+                            de.data.droppedDocuments.push(dropdoc);
+                            this.props.addDocument(dropdoc, false);
+                        }
+                    })
+                }
+            })
+            return true;
+        }
         return false;
     }
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index e84f0c5ad..3dfd74ec8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -23,10 +23,10 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         let l = this.props.LinkDocs;
         let a = this.props.A;
         let b = this.props.B;
-        let x1 = a.GetNumber(KeyStore.X, 0) + a.GetNumber(KeyStore.Width, 0) / 2;
-        let y1 = a.GetNumber(KeyStore.Y, 0) + a.GetNumber(KeyStore.Height, 0) / 2;
-        let x2 = b.GetNumber(KeyStore.X, 0) + b.GetNumber(KeyStore.Width, 0) / 2;
-        let y2 = b.GetNumber(KeyStore.Y, 0) + b.GetNumber(KeyStore.Height, 0) / 2;
+        let x1 = a.GetNumber(KeyStore.X, 0) + (a.GetBoolean(KeyStore.Minimized, false) ? 5 : a.GetNumber(KeyStore.Width, 0) / 2);
+        let y1 = a.GetNumber(KeyStore.Y, 0) + (a.GetBoolean(KeyStore.Minimized, false) ? 5 : a.GetNumber(KeyStore.Height, 0) / 2);
+        let x2 = b.GetNumber(KeyStore.X, 0) + (b.GetBoolean(KeyStore.Minimized, false) ? 5 : b.GetNumber(KeyStore.Width, 0) / 2);
+        let y2 = b.GetNumber(KeyStore.Y, 0) + (b.GetBoolean(KeyStore.Minimized, false) ? 5 : b.GetNumber(KeyStore.Height, 0) / 2);
         return (
             <line key={Utils.GenerateGuid()} className="collectionfreeformlinkview-linkLine" onPointerDown={this.onPointerDown}
                 style={{ strokeWidth: `${l.length * 5}` }}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss
index 4341c82f7..30e158603 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss
@@ -1,6 +1,8 @@
 .collectionfreeformlinksview-svgCanvas{
     transform: translate(-10000px,-10000px);
     position: absolute;
+    top: 0;
+    left: 0;
     width: 20000px; 
     height: 20000px;
     pointer-events: none;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 81d21d89a..79d520069 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -26,7 +26,7 @@
   border: 0px solid $light-color-secondary;
   border-radius: $border-radius;
   box-sizing: border-box;
-  position: relative;
+  position: absolute;
   overflow: hidden;
   top: 0;
   left: 0;
@@ -41,12 +41,12 @@
   .formattedTextBox-cont {
     background: $light-color-secondary;
   }
-
+ 
   opacity: 0.99;
   border: 0px solid transparent;
   border-radius: $border-radius;
   box-sizing: border-box;
-  position:relative;
+  position:absolute;
   overflow: hidden;
   top: 0;
   left: 0;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c5178f69d..3484e963e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -73,14 +73,17 @@ export class CollectionFreeFormView extends CollectionViewBase {
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (super.drop(e, de)) {
-            if (de.data instanceof DragManager.DocumentDragData) {
-                let screenX = de.x - (de.data.xOffset as number || 0);
-                let screenY = de.y - (de.data.yOffset as number || 0);
+            let droppedDocs = de.data.droppedDocuments as Document[];
+            let xoff = de.data.xOffset as number || 0;
+            let yoff = de.data.yOffset as number || 0;
+            if (droppedDocs && droppedDocs.length) {
+                let screenX = de.x - xoff;
+                let screenY = de.y - yoff;
                 const [x, y] = this.getTransform().transformPoint(screenX, screenY);
-                let dragDoc = de.data.draggedDocuments[0];
+                let dragDoc = de.data.droppedDocuments[0];
                 let dragX = dragDoc.GetNumber(KeyStore.X, 0);
                 let dragY = dragDoc.GetNumber(KeyStore.Y, 0);
-                de.data.draggedDocuments.map(d => {
+                droppedDocs.map(d => {
                     let docX = d.GetNumber(KeyStore.X, 0);
                     let docY = d.GetNumber(KeyStore.Y, 0);
                     d.SetNumber(KeyStore.X, x + (docX - dragX));
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
index 1ee3b244b..0b406e722 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
@@ -1,6 +1,8 @@
 
 .marqueeView {
     position: absolute;
+    top:0;
+    left:0;
     width:100%;
     height:100%;
 }
diff --git a/src/client/views/collections/collectionFreeForm/PreviewCursor.scss b/src/client/views/collections/collectionFreeForm/PreviewCursor.scss
index 21210be2b..7a67c29bf 100644
--- a/src/client/views/collections/collectionFreeForm/PreviewCursor.scss
+++ b/src/client/views/collections/collectionFreeForm/PreviewCursor.scss
@@ -3,9 +3,13 @@
     color: black;
     position: absolute;
     transform-origin: left top;
+    top: 0;
+    left:0;
     pointer-events: none;
 }
 .previewCursorView {
+    top: 0;
+    left:0;
     position: absolute;
     width:100%;
     height:100%;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index d52b662bd..1a0f0cbbd 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -66,8 +66,12 @@ export class CollectionFreeFormDocumentView extends React.Component<DocumentView
         return <DocumentView {...this.props}
             ContentScaling={this.contentScaling}
             ScreenToLocalTransform={this.getTransform}
+            PanelWidth={this.panelWidth}
+            PanelHeight={this.panelHeight}
         />
     }
+    panelWidth = () => this.props.Document.GetBoolean(KeyStore.Minimized, false) ? 10 : this.props.PanelWidth();
+    panelHeight = () => this.props.Document.GetBoolean(KeyStore.Minimized, false) ? 10 : this.props.PanelHeight();
 
     render() {
         return (
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 127a6b535..5126e69f9 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -2,6 +2,8 @@
 
 .documentView-node {
   position: absolute;
+  top: 0;
+  left:0;
   background: $light-color; //overflow: hidden;
 
   &.minimized {
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index ad947afd5..830dfe6c6 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -1,12 +1,16 @@
 .react-pdf__Page {
     transform-origin: left top;
     position: absolute;
+    top: 0;
+    left:0;
 }
 .react-pdf__Document {
     position: absolute;
 }
 .pdfBox-buttonTray {
     position:absolute;
+    top: 0;
+    left:0;
     z-index: 25;
 }
 .pdfBox-contentContainer {
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index a535b2638..c73bc0c47 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -2,6 +2,8 @@
 .webBox-cont {
     padding: 0vw;
     position: absolute;
+    top: 0;
+    left:0;
     width: 100%;
     height: 100%;
     overflow: scroll;
diff --git a/src/fields/Document.ts b/src/fields/Document.ts
index 8e71019a4..92d03d765 100644
--- a/src/fields/Document.ts
+++ b/src/fields/Document.ts
@@ -9,415 +9,420 @@ import { Server } from "../client/Server";
 import { Types } from "../server/Message";
 import { UndoManager } from "../client/util/UndoManager";
 import { HtmlField } from "./HtmlField";
+import { BooleanField } from "./BooleanField";
 
 export class Document extends Field {
-  //TODO tfs: We should probably store FieldWaiting in fields when we request it from the server so that we don't set up multiple server gets for the same document and field
-  public fields: ObservableMap<
-    string,
-    { key: Key; field: Field }
-  > = new ObservableMap();
-  public _proxies: ObservableMap<string, FieldId> = new ObservableMap();
-
-  constructor(id?: string, save: boolean = true) {
-    super(id);
-
-    if (save) {
-      Server.UpdateField(this);
+    //TODO tfs: We should probably store FieldWaiting in fields when we request it from the server so that we don't set up multiple server gets for the same document and field
+    public fields: ObservableMap<
+        string,
+        { key: Key; field: Field }
+    > = new ObservableMap();
+    public _proxies: ObservableMap<string, FieldId> = new ObservableMap();
+
+    constructor(id?: string, save: boolean = true) {
+        super(id);
+
+        if (save) {
+            Server.UpdateField(this);
+        }
     }
-  }
 
-  UpdateFromServer(data: [string, string][]) {
-    for (const key in data) {
-      const element = data[key];
-      this._proxies.set(element[0], element[1]);
-    }
-  }
-
-  public Width = () => {
-    return this.GetNumber(KeyStore.Width, 0);
-  };
-  public Height = () => {
-    return this.GetNumber(
-      KeyStore.Height,
-      this.GetNumber(KeyStore.NativeWidth, 0)
-        ? (this.GetNumber(KeyStore.NativeHeight, 0) /
-            this.GetNumber(KeyStore.NativeWidth, 0)) *
-            this.GetNumber(KeyStore.Width, 0)
-        : 0
-    );
-  };
-  public Scale = () => {
-    return this.GetNumber(KeyStore.Scale, 1);
-  };
-
-  @computed
-  public get Title(): string {
-    let title = this.Get(KeyStore.Title, true);
-    if (title)
-      if (title != FieldWaiting && title instanceof TextField)
-        return title.Data;
-      else return "-waiting-";
-    let parTitle = this.GetT(KeyStore.Title, TextField);
-    if (parTitle)
-      if (parTitle != FieldWaiting) return parTitle.Data + ".alias";
-      else return "-waiting-.alias";
-    return "-untitled-";
-  }
-
-  @computed
-  public get Fields() {
-    return this.fields;
-  }
-
-  /**
-   * Get the field in the document associated with the given key. If the
-   * associated field has not yet been filled in from the server, a request
-   * to the server will automatically be sent, the value will be filled in
-   * when the request is completed, and {@link Field.ts#FieldWaiting} will be returned.
-   * @param key - The key of the value to get
-   * @param ignoreProto - If true, ignore any prototype this document
-   * might have and only search for the value on this immediate document.
-   * If false (default), search up the prototype chain, starting at this document,
-   * for a document that has a field associated with the given key, and return the first
-   * one found.
-   *
-   * @returns If the document does not have a field associated with the given key, returns `undefined`.
-   * If the document does have an associated field, but the field has not been fetched from the server, returns {@link Field.ts#FieldWaiting}.
-   * If the document does have an associated field, and the field has not been fetched from the server, returns the associated field.
-   */
-  Get(key: Key, ignoreProto: boolean = false): FieldValue<Field> {
-    let field: FieldValue<Field>;
-    if (ignoreProto) {
-      if (this.fields.has(key.Id)) {
-        field = this.fields.get(key.Id)!.field;
-      } else if (this._proxies.has(key.Id)) {
-        Server.GetDocumentField(this, key);
-        /*
-                The field might have been instantly filled from the cache
-                Maybe we want to just switch back to returning the value
-                from Server.GetDocumentField if it's in the cache
-                */
-        if (this.fields.has(key.Id)) {
-          field = this.fields.get(key.Id)!.field;
-        } else {
-          field = FieldWaiting;
+    UpdateFromServer(data: [string, string][]) {
+        for (const key in data) {
+            const element = data[key];
+            this._proxies.set(element[0], element[1]);
         }
-      }
-    } else {
-      let doc: FieldValue<Document> = this;
-      while (doc && doc != FieldWaiting && field != FieldWaiting) {
-        let curField = doc.fields.get(key.Id);
-        let curProxy = doc._proxies.get(key.Id);
-        if (!curField || (curProxy && curField.field.Id !== curProxy)) {
-          if (curProxy) {
-            Server.GetDocumentField(doc, key);
-            /*
+    }
+
+    public Width = () => {
+        return this.GetNumber(KeyStore.Width, 0);
+    };
+    public Height = () => {
+        return this.GetNumber(
+            KeyStore.Height,
+            this.GetNumber(KeyStore.NativeWidth, 0)
+                ? (this.GetNumber(KeyStore.NativeHeight, 0) /
+                    this.GetNumber(KeyStore.NativeWidth, 0)) *
+                this.GetNumber(KeyStore.Width, 0)
+                : 0
+        );
+    };
+    public Scale = () => {
+        return this.GetNumber(KeyStore.Scale, 1);
+    };
+
+    @computed
+    public get Title(): string {
+        let title = this.Get(KeyStore.Title, true);
+        if (title)
+            if (title != FieldWaiting && title instanceof TextField)
+                return title.Data;
+            else return "-waiting-";
+        let parTitle = this.GetT(KeyStore.Title, TextField);
+        if (parTitle)
+            if (parTitle != FieldWaiting) return parTitle.Data + ".alias";
+            else return "-waiting-.alias";
+        return "-untitled-";
+    }
+
+    @computed
+    public get Fields() {
+        return this.fields;
+    }
+
+    /**
+     * Get the field in the document associated with the given key. If the
+     * associated field has not yet been filled in from the server, a request
+     * to the server will automatically be sent, the value will be filled in
+     * when the request is completed, and {@link Field.ts#FieldWaiting} will be returned.
+     * @param key - The key of the value to get
+     * @param ignoreProto - If true, ignore any prototype this document
+     * might have and only search for the value on this immediate document.
+     * If false (default), search up the prototype chain, starting at this document,
+     * for a document that has a field associated with the given key, and return the first
+     * one found.
+     *
+     * @returns If the document does not have a field associated with the given key, returns `undefined`.
+     * If the document does have an associated field, but the field has not been fetched from the server, returns {@link Field.ts#FieldWaiting}.
+     * If the document does have an associated field, and the field has not been fetched from the server, returns the associated field.
+     */
+    Get(key: Key, ignoreProto: boolean = false): FieldValue<Field> {
+        let field: FieldValue<Field>;
+        if (ignoreProto) {
+            if (this.fields.has(key.Id)) {
+                field = this.fields.get(key.Id)!.field;
+            } else if (this._proxies.has(key.Id)) {
+                Server.GetDocumentField(this, key);
+                /*
                         The field might have been instantly filled from the cache
                         Maybe we want to just switch back to returning the value
                         from Server.GetDocumentField if it's in the cache
                         */
-            if (this.fields.has(key.Id)) {
-              field = this.fields.get(key.Id)!.field;
-            } else {
-              field = FieldWaiting;
+                if (this.fields.has(key.Id)) {
+                    field = this.fields.get(key.Id)!.field;
+                } else {
+                    field = FieldWaiting;
+                }
             }
-            break;
-          }
-          if (
-            doc.fields.has(KeyStore.Prototype.Id) ||
-            doc._proxies.has(KeyStore.Prototype.Id)
-          ) {
-            doc = doc.GetPrototype();
-          } else {
-            break;
-          }
         } else {
-          field = curField.field;
-          break;
+            let doc: FieldValue<Document> = this;
+            while (doc && doc != FieldWaiting && field != FieldWaiting) {
+                let curField = doc.fields.get(key.Id);
+                let curProxy = doc._proxies.get(key.Id);
+                if (!curField || (curProxy && curField.field.Id !== curProxy)) {
+                    if (curProxy) {
+                        Server.GetDocumentField(doc, key);
+                        /*
+                                    The field might have been instantly filled from the cache
+                                    Maybe we want to just switch back to returning the value
+                                    from Server.GetDocumentField if it's in the cache
+                                    */
+                        if (this.fields.has(key.Id)) {
+                            field = this.fields.get(key.Id)!.field;
+                        } else {
+                            field = FieldWaiting;
+                        }
+                        break;
+                    }
+                    if (
+                        doc.fields.has(KeyStore.Prototype.Id) ||
+                        doc._proxies.has(KeyStore.Prototype.Id)
+                    ) {
+                        doc = doc.GetPrototype();
+                    } else {
+                        break;
+                    }
+                } else {
+                    field = curField.field;
+                    break;
+                }
+            }
+            if (doc == FieldWaiting) field = FieldWaiting;
         }
-      }
-      if (doc == FieldWaiting) field = FieldWaiting;
+
+        return field;
     }
 
-    return field;
-  }
-
-  /**
-   * Tries to get the field associated with the given key, and if there is an
-   * associated field, calls the given callback with that field.
-   * @param key - The key of the value to get
-   * @param callback - A function that will be called with the associated field, if it exists,
-   * once it is fetched from the server (this may be immediately if the field has already been fetched).
-   * Note: The callback will not be called if there is no associated field.
-   * @returns `true` if the field exists on the document and `callback` will be called, and `false` otherwise
-   */
-  GetAsync(key: Key, callback: (field: Opt<Field>) => void): void {
-    //TODO: This currently doesn't deal with prototypes
-    let field = this.fields.get(key.Id);
-    if (field && field.field) {
-      callback(field.field);
-    } else if (this._proxies.has(key.Id)) {
-      Server.GetDocumentField(this, key, callback);
-    } else if (this._proxies.has(KeyStore.Prototype.Id)) {
-      this.GetTAsync(KeyStore.Prototype, Document, proto => {
-        if (proto) {
-          proto.GetAsync(key, callback);
+    /**
+     * Tries to get the field associated with the given key, and if there is an
+     * associated field, calls the given callback with that field.
+     * @param key - The key of the value to get
+     * @param callback - A function that will be called with the associated field, if it exists,
+     * once it is fetched from the server (this may be immediately if the field has already been fetched).
+     * Note: The callback will not be called if there is no associated field.
+     * @returns `true` if the field exists on the document and `callback` will be called, and `false` otherwise
+     */
+    GetAsync(key: Key, callback: (field: Opt<Field>) => void): void {
+        //TODO: This currently doesn't deal with prototypes
+        let field = this.fields.get(key.Id);
+        if (field && field.field) {
+            callback(field.field);
+        } else if (this._proxies.has(key.Id)) {
+            Server.GetDocumentField(this, key, callback);
+        } else if (this._proxies.has(KeyStore.Prototype.Id)) {
+            this.GetTAsync(KeyStore.Prototype, Document, proto => {
+                if (proto) {
+                    proto.GetAsync(key, callback);
+                } else {
+                    callback(undefined);
+                }
+            });
         } else {
-          callback(undefined);
+            callback(undefined);
         }
-      });
-    } else {
-      callback(undefined);
     }
-  }
-
-  GetTAsync<T extends Field>(key: Key, ctor: { new (): T }): Promise<Opt<T>>;
-  GetTAsync<T extends Field>(
-    key: Key,
-    ctor: { new (): T },
-    callback: (field: Opt<T>) => void
-  ): void;
-  GetTAsync<T extends Field>(
-    key: Key,
-    ctor: { new (): T },
-    callback?: (field: Opt<T>) => void
-  ): Promise<Opt<T>> | void {
-    let fn = (cb: (field: Opt<T>) => void) => {
-      return this.GetAsync(key, field => {
-        cb(Cast(field, ctor));
-      });
-    };
-    if (callback) {
-      fn(callback);
-    } else {
-      return new Promise(res => fn(res));
+
+    GetTAsync<T extends Field>(key: Key, ctor: { new(): T }): Promise<Opt<T>>;
+    GetTAsync<T extends Field>(
+        key: Key,
+        ctor: { new(): T },
+        callback: (field: Opt<T>) => void
+    ): void;
+    GetTAsync<T extends Field>(
+        key: Key,
+        ctor: { new(): T },
+        callback?: (field: Opt<T>) => void
+    ): Promise<Opt<T>> | void {
+        let fn = (cb: (field: Opt<T>) => void) => {
+            return this.GetAsync(key, field => {
+                cb(Cast(field, ctor));
+            });
+        };
+        if (callback) {
+            fn(callback);
+        } else {
+            return new Promise(res => fn(res));
+        }
     }
-  }
-
-  /**
-   * Same as {@link Document#GetAsync}, except a field of the given type
-   * will be created if there is no field associated with the given key,
-   * or the field associated with the given key is not of the given type.
-   * @param ctor - Constructor of the field type to get. E.g., TextField, ImageField, etc.
-   */
-  GetOrCreateAsync<T extends Field>(
-    key: Key,
-    ctor: { new (): T },
-    callback: (field: T) => void
-  ): void {
-    //This currently doesn't deal with prototypes
-    if (this._proxies.has(key.Id)) {
-      Server.GetDocumentField(this, key, field => {
-        if (field && field instanceof ctor) {
-          callback(field);
+
+    /**
+     * Same as {@link Document#GetAsync}, except a field of the given type
+     * will be created if there is no field associated with the given key,
+     * or the field associated with the given key is not of the given type.
+     * @param ctor - Constructor of the field type to get. E.g., TextField, ImageField, etc.
+     */
+    GetOrCreateAsync<T extends Field>(
+        key: Key,
+        ctor: { new(): T },
+        callback: (field: T) => void
+    ): void {
+        //This currently doesn't deal with prototypes
+        if (this._proxies.has(key.Id)) {
+            Server.GetDocumentField(this, key, field => {
+                if (field && field instanceof ctor) {
+                    callback(field);
+                } else {
+                    let newField = new ctor();
+                    this.Set(key, newField);
+                    callback(newField);
+                }
+            });
         } else {
-          let newField = new ctor();
-          this.Set(key, newField);
-          callback(newField);
+            let newField = new ctor();
+            this.Set(key, newField);
+            callback(newField);
         }
-      });
-    } else {
-      let newField = new ctor();
-      this.Set(key, newField);
-      callback(newField);
     }
-  }
-
-  /**
-   * Same as {@link Document#Get}, except that it will additionally
-   * check if the field is of the given type.
-   * @param ctor - Constructor of the field type to get. E.g., `TextField`, `ImageField`, etc.
-   * @returns Same as {@link Document#Get}, except will return `undefined`
-   * if there is an associated field but it is of the wrong type.
-   */
-  GetT<T extends Field = Field>(
-    key: Key,
-    ctor: { new (...args: any[]): T },
-    ignoreProto: boolean = false
-  ): FieldValue<T> {
-    var getfield = this.Get(key, ignoreProto);
-    if (getfield != FieldWaiting) {
-      return Cast(getfield, ctor);
+
+    /**
+     * Same as {@link Document#Get}, except that it will additionally
+     * check if the field is of the given type.
+     * @param ctor - Constructor of the field type to get. E.g., `TextField`, `ImageField`, etc.
+     * @returns Same as {@link Document#Get}, except will return `undefined`
+     * if there is an associated field but it is of the wrong type.
+     */
+    GetT<T extends Field = Field>(
+        key: Key,
+        ctor: { new(...args: any[]): T },
+        ignoreProto: boolean = false
+    ): FieldValue<T> {
+        var getfield = this.Get(key, ignoreProto);
+        if (getfield != FieldWaiting) {
+            return Cast(getfield, ctor);
+        }
+        return FieldWaiting;
     }
-    return FieldWaiting;
-  }
-
-  GetOrCreate<T extends Field>(
-    key: Key,
-    ctor: { new (): T },
-    ignoreProto: boolean = false
-  ): T {
-    const field = this.GetT(key, ctor, ignoreProto);
-    if (field && field != FieldWaiting) {
-      return field;
+
+    GetOrCreate<T extends Field>(
+        key: Key,
+        ctor: { new(): T },
+        ignoreProto: boolean = false
+    ): T {
+        const field = this.GetT(key, ctor, ignoreProto);
+        if (field && field != FieldWaiting) {
+            return field;
+        }
+        const newField = new ctor();
+        this.Set(key, newField);
+        return newField;
     }
-    const newField = new ctor();
-    this.Set(key, newField);
-    return newField;
-  }
-
-  GetData<T, U extends Field & { Data: T }>(
-    key: Key,
-    ctor: { new (): U },
-    defaultVal: T
-  ): T {
-    let val = this.Get(key);
-    let vval = val && val instanceof ctor ? val.Data : defaultVal;
-    return vval;
-  }
-
-  GetHtml(key: Key, defaultVal: string): string {
-    return this.GetData(key, HtmlField, defaultVal);
-  }
-
-  GetNumber(key: Key, defaultVal: number): number {
-    return this.GetData(key, NumberField, defaultVal);
-  }
-
-  GetText(key: Key, defaultVal: string): string {
-    return this.GetData(key, TextField, defaultVal);
-  }
-
-  GetList<T extends Field>(key: Key, defaultVal: T[]): T[] {
-    return this.GetData<T[], ListField<T>>(key, ListField, defaultVal);
-  }
-
-  @action
-  Set(key: Key, field: Field | undefined, setOnPrototype = false): void {
-    let old = this.fields.get(key.Id);
-    let oldField = old ? old.field : undefined;
-    if (setOnPrototype) {
-      this.SetOnPrototype(key, field);
-    } else {
-      if (field) {
-        this.fields.set(key.Id, { key, field });
-        this._proxies.set(key.Id, field.Id);
-        // Server.AddDocumentField(this, key, field);
-      } else {
-        this.fields.delete(key.Id);
-        this._proxies.delete(key.Id);
-        // Server.DeleteDocumentField(this, key);
-      }
-      Server.UpdateField(this);
+
+    GetData<T, U extends Field & { Data: T }>(
+        key: Key,
+        ctor: { new(): U },
+        defaultVal: T
+    ): T {
+        let val = this.Get(key);
+        let vval = val && val instanceof ctor ? val.Data : defaultVal;
+        return vval;
     }
-    if (oldField || field) {
-      UndoManager.AddEvent({
-        undo: () => this.Set(key, oldField, setOnPrototype),
-        redo: () => this.Set(key, field, setOnPrototype)
-      });
+
+    GetHtml(key: Key, defaultVal: string): string {
+        return this.GetData(key, HtmlField, defaultVal);
     }
-  }
-
-  @action
-  SetOnPrototype(key: Key, field: Field | undefined): void {
-    this.GetTAsync(KeyStore.Prototype, Document, (f: Opt<Document>) => {
-      f && f.Set(key, field);
-    });
-  }
-
-  @action
-  SetDataOnPrototype<T, U extends Field & { Data: T }>(
-    key: Key,
-    value: T,
-    ctor: { new (): U },
-    replaceWrongType = true
-  ) {
-    this.GetTAsync(KeyStore.Prototype, Document, (f: Opt<Document>) => {
-      f && f.SetData(key, value, ctor);
-    });
-  }
-
-  @action
-  SetData<T, U extends Field & { Data: T }>(
-    key: Key,
-    value: T,
-    ctor: { new (data: T): U },
-    replaceWrongType = true
-  ) {
-    let field = this.Get(key, true);
-    if (field instanceof ctor) {
-      field.Data = value;
-    } else if (!field || replaceWrongType) {
-      let newField = new ctor(value);
-      // newField.Data = value;
-      this.Set(key, newField);
+
+    GetBoolean(key: Key, defaultVal: boolean): boolean {
+        return this.GetData(key, BooleanField, defaultVal);
     }
-  }
-
-  @action
-  SetText(key: Key, value: string, replaceWrongType = true) {
-    this.SetData(key, value, TextField, replaceWrongType);
-  }
-
-  @action
-  SetNumber(key: Key, value: number, replaceWrongType = true) {
-    this.SetData(key, value, NumberField, replaceWrongType);
-  }
-
-  GetPrototype(): FieldValue<Document> {
-    return this.GetT(KeyStore.Prototype, Document, true);
-  }
-
-  GetAllPrototypes(): Document[] {
-    let protos: Document[] = [];
-    let doc: FieldValue<Document> = this;
-    while (doc && doc != FieldWaiting) {
-      protos.push(doc);
-      doc = doc.GetPrototype();
+
+    GetNumber(key: Key, defaultVal: number): number {
+        return this.GetData(key, NumberField, defaultVal);
+    }
+
+    GetText(key: Key, defaultVal: string): string {
+        return this.GetData(key, TextField, defaultVal);
+    }
+
+    GetList<T extends Field>(key: Key, defaultVal: T[]): T[] {
+        return this.GetData<T[], ListField<T>>(key, ListField, defaultVal);
+    }
+
+    @action
+    Set(key: Key, field: Field | undefined, setOnPrototype = false): void {
+        let old = this.fields.get(key.Id);
+        let oldField = old ? old.field : undefined;
+        if (setOnPrototype) {
+            this.SetOnPrototype(key, field);
+        } else {
+            if (field) {
+                this.fields.set(key.Id, { key, field });
+                this._proxies.set(key.Id, field.Id);
+                // Server.AddDocumentField(this, key, field);
+            } else {
+                this.fields.delete(key.Id);
+                this._proxies.delete(key.Id);
+                // Server.DeleteDocumentField(this, key);
+            }
+            Server.UpdateField(this);
+        }
+        if (oldField || field) {
+            UndoManager.AddEvent({
+                undo: () => this.Set(key, oldField, setOnPrototype),
+                redo: () => this.Set(key, field, setOnPrototype)
+            });
+        }
+    }
+
+    @action
+    SetOnPrototype(key: Key, field: Field | undefined): void {
+        this.GetTAsync(KeyStore.Prototype, Document, (f: Opt<Document>) => {
+            f && f.Set(key, field);
+        });
+    }
+
+    @action
+    SetDataOnPrototype<T, U extends Field & { Data: T }>(
+        key: Key,
+        value: T,
+        ctor: { new(): U },
+        replaceWrongType = true
+    ) {
+        this.GetTAsync(KeyStore.Prototype, Document, (f: Opt<Document>) => {
+            f && f.SetData(key, value, ctor);
+        });
+    }
+
+    @action
+    SetData<T, U extends Field & { Data: T }>(
+        key: Key,
+        value: T,
+        ctor: { new(data: T): U },
+        replaceWrongType = true
+    ) {
+        let field = this.Get(key, true);
+        if (field instanceof ctor) {
+            field.Data = value;
+        } else if (!field || replaceWrongType) {
+            let newField = new ctor(value);
+            // newField.Data = value;
+            this.Set(key, newField);
+        }
+    }
+
+    @action
+    SetText(key: Key, value: string, replaceWrongType = true) {
+        this.SetData(key, value, TextField, replaceWrongType);
+    }
+
+    @action
+    SetNumber(key: Key, value: number, replaceWrongType = true) {
+        this.SetData(key, value, NumberField, replaceWrongType);
+    }
+
+    GetPrototype(): FieldValue<Document> {
+        return this.GetT(KeyStore.Prototype, Document, true);
+    }
+
+    GetAllPrototypes(): Document[] {
+        let protos: Document[] = [];
+        let doc: FieldValue<Document> = this;
+        while (doc && doc != FieldWaiting) {
+            protos.push(doc);
+            doc = doc.GetPrototype();
+        }
+        return protos;
+    }
+
+    CreateAlias(id?: string): Document {
+        let alias = new Document(id);
+        this.GetTAsync(KeyStore.Prototype, Document, (f: Opt<Document>) => {
+            f && alias.Set(KeyStore.Prototype, f);
+        });
+
+        return alias;
+    }
+
+    MakeDelegate(id?: string): Document {
+        let delegate = new Document(id);
+
+        delegate.Set(KeyStore.Prototype, this);
+
+        return delegate;
+    }
+
+    ToScriptString(): string {
+        return "";
+    }
+
+    TrySetValue(value: any): boolean {
+        throw new Error("Method not implemented.");
+    }
+    GetValue() {
+        return this.Title;
+        var title =
+            (this._proxies.has(KeyStore.Title.Id) ? "???" : this.Title) +
+            "(" +
+            this.Id +
+            ")";
+        return title;
+        //throw new Error("Method not implemented.");
+    }
+    Copy(): Field {
+        throw new Error("Method not implemented.");
+    }
+
+    ToJson(): { type: Types; data: [string, string][]; _id: string } {
+        let fields: [string, string][] = [];
+        this._proxies.forEach((field, key) => {
+            if (field) {
+                fields.push([key, field as string]);
+            }
+        });
+
+        return {
+            type: Types.Document,
+            data: fields,
+            _id: this.Id
+        };
     }
-    return protos;
-  }
-
-  CreateAlias(id?: string): Document {
-    let alias = new Document(id);
-    this.GetTAsync(KeyStore.Prototype, Document, (f: Opt<Document>) => {
-      f && alias.Set(KeyStore.Prototype, f);
-    });
-
-    return alias;
-  }
-
-  MakeDelegate(id?: string): Document {
-    let delegate = new Document(id);
-
-    delegate.Set(KeyStore.Prototype, this);
-
-    return delegate;
-  }
-
-  ToScriptString(): string {
-    return "";
-  }
-
-  TrySetValue(value: any): boolean {
-    throw new Error("Method not implemented.");
-  }
-  GetValue() {
-    return this.Title;
-    var title =
-      (this._proxies.has(KeyStore.Title.Id) ? "???" : this.Title) +
-      "(" +
-      this.Id +
-      ")";
-    return title;
-    //throw new Error("Method not implemented.");
-  }
-  Copy(): Field {
-    throw new Error("Method not implemented.");
-  }
-
-  ToJson(): { type: Types; data: [string, string][]; _id: string } {
-    let fields: [string, string][] = [];
-    this._proxies.forEach((field, key) => {
-      if (field) {
-        fields.push([key, field as string]);
-      }
-    });
-
-    return {
-      type: Types.Document,
-      data: fields,
-      _id: this.Id
-    };
-  }
 }
-- 
cgit v1.2.3-70-g09d2


From e70be930dc7c8ff1e999bf163bcbb511a60bae6f Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 4 Apr 2019 17:46:33 -0400
Subject: added close button and fixed some async stuff.

---
 src/client/views/DocumentDecorations.scss          | 17 +++++++---
 src/client/views/DocumentDecorations.tsx           | 38 +++++++++++++++++++---
 .../views/collections/CollectionViewBase.tsx       |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  9 +++--
 .../collections/collectionFreeForm/MarqueeView.tsx |  1 +
 src/client/views/nodes/PDFBox.tsx                  | 10 +++---
 6 files changed, 60 insertions(+), 17 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 080c9fa1e..d7137d7a2 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -7,7 +7,7 @@
     display: grid;
     z-index: 1000;
     grid-template-rows: 20px 8px 1fr 8px;
-    grid-template-columns: 8px 8px 1fr 8px;
+    grid-template-columns: 8px 8px 1fr 8px 8px;
     pointer-events: none;
 
     #documentDecorations-centerCont {
@@ -30,13 +30,14 @@
     #documentDecorations-topResizer,
     #documentDecorations-bottomResizer {
         grid-column-start: 2;
-        grid-column-end: 4;
+        grid-column-end: 5;
     }
 
     #documentDecorations-bottomRightResizer,
     #documentDecorations-topRightResizer,
     #documentDecorations-rightResizer {
-        grid-column:4
+        grid-column-start:5;
+        grid-column-end:7;
     }
 
     #documentDecorations-topLeftResizer,
@@ -61,11 +62,19 @@
     .title{
         background: lightblue;
         grid-column-start:3;
-        grid-column-end: 5;
+        grid-column-end: 4;
         pointer-events: auto;
     }
 }
 
+.documentDecorations-closeButton {
+    background:$alt-accent;
+    opacity: 0.8;
+    grid-column-start: 4;
+    grid-column-end: 6;
+    pointer-events: all;
+    text-align: center;
+}
 .documentDecorations-minimizeButton {
     background:$alt-accent;
     opacity: 0.8;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index a46087c9a..8bf1a42d1 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -142,6 +142,30 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         e.preventDefault();
     }
 
+    onCloseDown = (e: React.PointerEvent): void => {
+        e.stopPropagation();
+        if (e.button === 0) {
+            document.removeEventListener("pointermove", this.onCloseMove);
+            document.addEventListener("pointermove", this.onCloseMove);
+            document.removeEventListener("pointerup", this.onCloseUp);
+            document.addEventListener("pointerup", this.onCloseUp);
+        }
+    }
+    onCloseMove = (e: PointerEvent): void => {
+        e.stopPropagation();
+        if (e.button === 0) {
+        }
+    }
+    @action
+    onCloseUp = (e: PointerEvent): void => {
+        e.stopPropagation();
+        if (e.button === 0) {
+            SelectionManager.SelectedDocuments().map(dv => dv.props.RemoveDocument && dv.props.RemoveDocument(dv.props.Document));
+            SelectionManager.DeselectAll();
+            document.removeEventListener("pointermove", this.onCloseMove);
+            document.removeEventListener("pointerup", this.onCloseUp);
+        }
+    }
     onMinimizeDown = (e: React.PointerEvent): void => {
         e.stopPropagation();
         if (e.button === 0) {
@@ -219,7 +243,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         e.stopPropagation();
     }
 
-    onLinkButtonMoved = (e: PointerEvent): void => {
+    onLinkButtonMoved = async (e: PointerEvent) => {
         if (this._linkButton.current != null) {
             document.removeEventListener("pointermove", this.onLinkButtonMoved)
             document.removeEventListener("pointerup", this.onLinkButtonUp);
@@ -234,10 +258,13 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                     (linkDoc.GetT(KeyStore.LinkedFromDocs, Document)) as Document) : [];
             draggedDocs.push(...draggedFromDocs);
             if (draggedDocs.length) {
-                let dragData = new DragManager.DocumentDragData(draggedDocs.map(d => {
-                    let annot = d.GetT(KeyStore.AnnotationOn, Document); // bcz: ... needs to change ... the annotationOn document may not have been retrieved yet...
-                    return annot && annot != FieldWaiting ? annot : d;
-                }));
+                let moddrag = [] as Document[];
+                for (let i = 0; i < draggedDocs.length; i++) {
+                    let doc = await draggedDocs[i].GetTAsync(KeyStore.AnnotationOn, Document);
+                    if (doc)
+                        moddrag.push(doc);
+                }
+                let dragData = new DragManager.DocumentDragData(moddrag);
                 DragManager.StartDocumentDrag([this._linkButton.current], dragData, {
                     handlers: {
                         dragComplete: action(() => { }),
@@ -400,6 +427,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             }}>
                 <div className="documentDecorations-minimizeButton" onPointerDown={this.onMinimizeDown}>...</div>
                 <input ref={this.keyinput} className="title" type="text" name="dynbox" value={this.getValue()} onChange={this.handleChange} onPointerDown={this.onPointerDown} onKeyPress={this.enterPressed} />
+                <div className="documentDecorations-closeButton" onPointerDown={this.onCloseDown}>X</div>
                 <div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
                 <div id="documentDecorations-topResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
                 <div id="documentDecorations-topRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index daeca69e2..9eee23a1d 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -128,6 +128,7 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
         }
         if (type.indexOf("pdf") !== -1) {
             ctor = Documents.PdfDocument;
+            options.nativeWidth = 600;
         }
         if (type.indexOf("html") !== -1) {
             if (path.includes('localhost')) {
@@ -177,7 +178,6 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
             let item = e.dataTransfer.items[i];
             if (item.kind === "string" && item.type.indexOf("uri") != -1) {
                 e.dataTransfer.items[i].getAsString(action((s: string) => {
-                    let document: Document;
                     request.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + s), (err, res, body) => {
                         let type = res.headers["content-type"];
                         if (type) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 3484e963e..1ddb84a99 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -20,6 +20,7 @@ import React = require("react");
 import v5 = require("uuid/v5");
 import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
 import { PreviewCursor } from "./PreviewCursor";
+import { NumberField } from "../../../../fields/NumberField";
 
 @observer
 export class CollectionFreeFormView extends CollectionViewBase {
@@ -83,13 +84,17 @@ export class CollectionFreeFormView extends CollectionViewBase {
                 let dragDoc = de.data.droppedDocuments[0];
                 let dragX = dragDoc.GetNumber(KeyStore.X, 0);
                 let dragY = dragDoc.GetNumber(KeyStore.Y, 0);
-                droppedDocs.map(d => {
+                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));
-                    if (!d.GetNumber(KeyStore.Width, 0)) {
+                    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);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index e2239c8be..df150a045 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -135,6 +135,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             this.marqueeInkDelete(inkData);
             // }, 100);
             this.cleanupInteractions();
+            SelectionManager.DeselectAll();
         }
     }
     @action
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 3cb3cf782..c9fed7f66 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,5 +1,5 @@
 import * as htmlToImage from "html-to-image";
-import { action, computed, observable, reaction, IReactionDisposer, trace, keys } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from "mobx-react";
 import 'react-image-lightbox/style.css';
 import Measure from "react-measure";
@@ -10,6 +10,7 @@ import { FieldWaiting, Opt } from '../../../fields/Field';
 import { ImageField } from '../../../fields/ImageField';
 import { KeyStore } from '../../../fields/KeyStore';
 import { PDFField } from '../../../fields/PDFField';
+import { RouteStore } from "../../../server/RouteStore";
 import { Utils } from '../../../Utils';
 import { Annotation } from './Annotation';
 import { FieldView, FieldViewProps } from './FieldView';
@@ -17,8 +18,6 @@ import "./ImageBox.scss";
 import "./PDFBox.scss";
 import { Sticky } from './Sticky'; //you should look at sticky and annotation, because they are used here
 import React = require("react")
-import { RouteStore } from "../../../server/RouteStore";
-import { NumberField } from "../../../fields/NumberField";
 
 /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx
  * This method renders PDF and puts all kinds of functionalities such as annotation, highlighting, 
@@ -378,8 +377,9 @@ export class PDFBox extends React.Component<FieldViewProps> {
     saveThumbnail = () => {
         setTimeout(() => {
             var me = this;
-            htmlToImage.toPng(this._mainDiv.current!,
-                { width: me.props.doc.GetNumber(KeyStore.NativeWidth, 0), height: me.props.doc.GetNumber(KeyStore.NativeHeight, 0), quality: 0.5 })
+            let nwidth = me.props.doc.GetNumber(KeyStore.NativeWidth, 0);
+            let nheight = me.props.doc.GetNumber(KeyStore.NativeHeight, 0);
+            htmlToImage.toPng(this._mainDiv.current!, { width: nwidth, height: nheight, quality: 1 })
                 .then(function (dataUrl: string) {
                     me.props.doc.SetData(KeyStore.Thumbnail, new URL(dataUrl), ImageField);
                     me.props.doc.SetNumber(KeyStore.ThumbnailPage, me.props.doc.GetNumber(KeyStore.CurPage, -1));
-- 
cgit v1.2.3-70-g09d2


From 93bed89512baa506c1b5ed2223ffc83fa6b5390e Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Thu, 4 Apr 2019 18:26:33 -0400
Subject: Changed linter stuff

---
 src/client/Server.ts                               | 12 ++---
 src/client/util/DocumentManager.ts                 |  4 +-
 src/client/util/Transform.ts                       | 30 ++++--------
 src/client/util/TypedEvent.ts                      |  3 +-
 src/client/views/DocumentDecorations.tsx           |  2 +-
 src/client/views/InkingStroke.tsx                  |  5 +-
 src/client/views/Main.tsx                          |  9 ++--
 .../views/collections/CollectionViewBase.tsx       | 13 +++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 10 ++--
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  8 ++--
 src/client/views/nodes/DocumentView.tsx            |  6 ++-
 src/client/views/nodes/FieldView.tsx               |  5 +-
 src/debug/Viewer.tsx                               |  3 +-
 src/fields/Document.ts                             |  2 +-
 src/mobile/ImageUpload.tsx                         | 54 +++++++++++-----------
 src/server/index.ts                                |  8 ++--
 16 files changed, 82 insertions(+), 92 deletions(-)

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

diff --git a/src/client/Server.ts b/src/client/Server.ts
index e3f4448cb..45c7144ca 100644
--- a/src/client/Server.ts
+++ b/src/client/Server.ts
@@ -39,8 +39,8 @@ export class Server {
             } else if (cached !== FieldWaiting) {
                 setTimeout(() => cb(cached as Field), 0);
             } else {
-                reaction(() =>
-                    this.ClientFieldsCached.get(fieldid), (field, reaction) => {
+                reaction(() => this.ClientFieldsCached.get(fieldid),
+                    (field, reaction) => {
                         if (field !== FieldWaiting) {
                             reaction.dispose()
                             cb(field)
@@ -91,8 +91,8 @@ export class Server {
                         }
                     }
                 }
-                reaction(() =>
-                    waitingFieldIds.map(id => this.ClientFieldsCached.get(id)), (cachedFields, reaction) => {
+                reaction(() => waitingFieldIds.map(id => this.ClientFieldsCached.get(id)),
+                    (cachedFields, reaction) => {
                         if (!cachedFields.some(field => !field)) {
                             reaction.dispose();
                             for (let field of cachedFields) {
@@ -151,7 +151,7 @@ export class Server {
     @action
     private static cacheField(clientField: Field) {
         var cached = this.ClientFieldsCached.get(clientField.Id);
-        if (!cached || cached === FieldWaiting) {
+        if (!cached) {
             this.ClientFieldsCached.set(clientField.Id, clientField);
         } else {
             // probably should overwrite the values within any field that was already here...
@@ -163,7 +163,7 @@ export class Server {
     static updateField(field: { _id: string, data: any, type: Types }) {
         if (Server.ClientFieldsCached.has(field._id)) {
             var f = Server.ClientFieldsCached.get(field._id);
-            if (f && f !== FieldWaiting) {
+            if (f) {
                 // console.log("Applying        : " + field._id);
                 f.UpdateFromServer(field.data);
                 f.init(() => { });
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 7cb368f47..fb489edb6 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -42,7 +42,7 @@ export class DocumentManager {
             let doc = view.props.Document;
             // if (view.props.ContainingCollectionView instanceof CollectionFreeFormView) {
 
-            if (Object.is(doc, toFind)) {
+            if (doc === toFind) {
                 toReturn = view;
                 return;
             }
@@ -63,7 +63,7 @@ export class DocumentManager {
             let doc = view.props.Document;
             // if (view.props.ContainingCollectionView instanceof CollectionFreeFormView) {
 
-            if (Object.is(doc, toFind)) {
+            if (doc === toFind) {
                 toReturn.push(view);
             } else {
                 let docSrc = doc.GetT(KeyStore.Prototype, Document);
diff --git a/src/client/util/Transform.ts b/src/client/util/Transform.ts
index 8608264a1..54effd512 100644
--- a/src/client/util/Transform.ts
+++ b/src/client/util/Transform.ts
@@ -62,26 +62,19 @@ export class Transform {
         return this;
     }
 
-    translated = (x: number, y: number): Transform =>
-        this.copy().translate(x, y)
+    translated = (x: number, y: number): Transform => this.copy().translate(x, y)
 
-    preTranslated = (x: number, y: number): Transform =>
-        this.copy().preTranslate(x, y)
+    preTranslated = (x: number, y: number): Transform => this.copy().preTranslate(x, y)
 
-    scaled = (scale: number): Transform =>
-        this.copy().scale(scale)
+    scaled = (scale: number): Transform => this.copy().scale(scale)
 
-    scaledAbout = (scale: number, x: number, y: number): Transform =>
-        this.copy().scaleAbout(scale, x, y)
+    scaledAbout = (scale: number, x: number, y: number): Transform => this.copy().scaleAbout(scale, x, y)
 
-    preScaled = (scale: number): Transform =>
-        this.copy().preScale(scale)
+    preScaled = (scale: number): Transform => this.copy().preScale(scale)
 
-    transformed = (transform: Transform): Transform =>
-        this.copy().transform(transform)
+    transformed = (transform: Transform): Transform => this.copy().transform(transform)
 
-    preTransformed = (transform: Transform): Transform =>
-        this.copy().preTransform(transform)
+    preTransformed = (transform: Transform): Transform => this.copy().preTransform(transform)
 
     transformPoint = (x: number, y: number): [number, number] => {
         x *= this._scale;
@@ -91,8 +84,7 @@ export class Transform {
         return [x, y];
     }
 
-    transformDirection = (x: number, y: number): [number, number] =>
-        [x * this._scale, y * this._scale]
+    transformDirection = (x: number, y: number): [number, number] => [x * this._scale, y * this._scale]
 
     transformBounds(x: number, y: number, width: number, height: number): { x: number, y: number, width: number, height: number } {
         [x, y] = this.transformPoint(x, y);
@@ -100,10 +92,8 @@ export class Transform {
         return { x, y, width, height };
     }
 
-    inverse = () =>
-        new Transform(-this._translateX / this._scale, -this._translateY / this._scale, 1 / this._scale)
+    inverse = () => new Transform(-this._translateX / this._scale, -this._translateY / this._scale, 1 / this._scale)
 
-    copy = () =>
-        new Transform(this._translateX, this._translateY, this._scale)
+    copy = () => new Transform(this._translateX, this._translateY, this._scale)
 
 }
\ No newline at end of file
diff --git a/src/client/util/TypedEvent.ts b/src/client/util/TypedEvent.ts
index c590d3734..1b251da25 100644
--- a/src/client/util/TypedEvent.ts
+++ b/src/client/util/TypedEvent.ts
@@ -36,6 +36,5 @@ export class TypedEvent<T> {
     this.listenersOncer = [];
   }
 
-  pipe = (te: TypedEvent<T>): Disposable =>
-    this.on((e) => te.emit(e))
+  pipe = (te: TypedEvent<T>): Disposable => this.on((e) => te.emit(e))
 }
\ No newline at end of file
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index b0aa190e5..a6117dfa0 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -75,7 +75,7 @@ export class DocumentDecorations extends React.Component {
         this._dragging = true;
         document.removeEventListener("pointermove", this.onBackgroundMove);
         document.removeEventListener("pointerup", this.onBackgroundUp);
-        DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(docView => (docView as any)._mainCont.current), dragData, {
+        DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(docView => docView.ContentRef.current!), dragData, {
             handlers: {
                 dragComplete: action(() => this._dragging = false),
             },
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index dbb79c0c6..12b15a3f0 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -29,8 +29,9 @@ export class InkingStroke extends React.Component<StrokeProps> {
         }
     }
 
-    parseData = (line: Array<{ x: number, y: number }>): string =>
-        !line.length ? "" : "M " + line.map(p => (p.x + this.props.offsetX) + " " + (p.y + this.props.offsetY)).join(" L ")
+    parseData = (line: Array<{ x: number, y: number }>): string => {
+        return !line.length ? "" : "M " + line.map(p => (p.x + this.props.offsetX) + " " + (p.y + this.props.offsetY)).join(" L ")
+    }
 
     createStyle() {
         switch (this._strokeTool) {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 446c3d55f..33759d38a 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -371,7 +371,8 @@ export class Main extends React.Component {
     }
 }
 
-Documents.initProtos().then(() =>
-    CurrentUserUtils.loadCurrentUser()).then(() => {
-        ReactDOM.render(<Main />, document.getElementById('root'));
-    });
+(async () => {
+    await Documents.initProtos()
+    await CurrentUserUtils.loadCurrentUser()
+    ReactDOM.render(<Main />, document.getElementById('root'));
+})()
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index 51280275c..2b858789f 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -134,7 +134,7 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
         e.stopPropagation()
         e.preventDefault()
 
-        if (html && html.indexOf("<img") != 0 && !html.startsWith("<a")) {
+        if (html && html.indexOf("<img") !== 0 && !html.startsWith("<a")) {
             console.log("not good");
             let htmlDoc = Documents.HtmlDocument(html, { ...options, width: 300, height: 300 });
             htmlDoc.SetText(KeyStore.DocumentText, text);
@@ -142,10 +142,11 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
             return;
         }
 
+        // tslint:disable-next-line:prefer-for-of
         for (let i = 0; i < e.dataTransfer.items.length; i++) {
             const upload = window.location.origin + RouteStore.upload;
             let item = e.dataTransfer.items[i];
-            if (item.kind === "string" && item.type.indexOf("uri") != -1) {
+            if (item.kind === "string" && item.type.indexOf("uri") !== -1) {
                 e.dataTransfer.items[i].getAsString(action((s: string) => {
                     request.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + s), (err, res, body) => {
                         let type = res.headers["content-type"];
@@ -160,7 +161,7 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
                 }))
             }
             let type = item.type
-            if (item.kind == "file") {
+            if (item.kind === "file") {
                 let file = item.getAsFile();
                 let formData = new FormData()
 
@@ -171,15 +172,15 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
                 fetch(upload, {
                     method: 'POST',
                     body: formData
-                }).then((res: Response) =>
-                    res.json()).then(json => {
+                }).then(async (res: Response) => {
+                    const json = await res.json();
                     json.map((file: any) => {
                         let path = window.location.origin + file
                         runInAction(() => {
                             let doc = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300 })
 
                             let docs = this.props.Document.GetT(KeyStore.Data, ListField);
-                            if (docs != FieldWaiting) {
+                            if (docs !== FieldWaiting) {
                                 if (!docs) {
                                     docs = new ListField<Document>();
                                     this.props.Document.Set(KeyStore.Data, docs)
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8c5d3f536..f03205739 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -54,7 +54,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
         var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
         return this.props.Document.GetList(this.props.fieldKey, [] as Document[]).reduce((active, doc) => {
             var page = doc.GetNumber(KeyStore.Page, -1);
-            if (page == curPage || page == -1) {
+            if (page === curPage || page === -1) {
                 active.push(doc);
             }
             return active;
@@ -115,7 +115,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
 
     @action
     onPointerDown = (e: React.PointerEvent): void => {
-        if (((e.button === 2 && (!this.isAnnotationOverlay || this.zoomScaling != 1)) || e.button == 0) && this.props.active()) {
+        if (((e.button === 2 && (!this.isAnnotationOverlay || this.zoomScaling !== 1)) || e.button === 0) && this.props.active()) {
             document.removeEventListener("pointermove", this.onPointerMove);
             document.addEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
@@ -137,7 +137,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
     @action
     onPointerMove = (e: PointerEvent): void => {
         if (!e.cancelBubble && this.props.active()) {
-            if ((!this.isAnnotationOverlay || this.zoomScaling != 1) && !e.shiftKey) {
+            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 [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
@@ -247,7 +247,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
             removeDocument: this.props.removeDocument,
             ScreenToLocalTransform: this.getTransform,
             isTopMost: false,
-            selectOnLoad: document.Id == this._selectOnLoaded,
+            selectOnLoad: document.Id === this._selectOnLoaded,
             PanelWidth: document.Width,
             PanelHeight: document.Height,
             ContentScaling: this.noScaling,
@@ -263,7 +263,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
         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) => {
             var page = doc.GetNumber(KeyStore.Page, -1);
-            if (page == curPage || page == -1)
+            if (page === curPage || page === -1)
                 prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
             return prev;
         }, [] as JSX.Element[])
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index e6475ee2a..ce453b4af 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -52,12 +52,12 @@ export class CollectionFreeFormDocumentView extends React.Component<DocumentView
         this.props.Document.SetData(KeyStore.ZIndex, h, NumberField)
     }
 
-    contentScaling = () =>
-        this.nativeWidth > 0 ? this.width / this.nativeWidth : 1
+    contentScaling = () => this.nativeWidth > 0 ? this.width / this.nativeWidth : 1
 
     getTransform = (): Transform =>
-        this.props.ScreenToLocalTransform().
-            translate(-this.props.Document.GetNumber(KeyStore.X, 0), -this.props.Document.GetNumber(KeyStore.Y, 0)).scale(1 / this.contentScaling())
+        this.props.ScreenToLocalTransform()
+            .translate(-this.props.Document.GetNumber(KeyStore.X, 0), -this.props.Document.GetNumber(KeyStore.Y, 0))
+            .scale(1 / this.contentScaling())
 
     @computed
     get docView() {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 34eb8919f..3b146654c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -85,6 +85,9 @@ export function FakeJsxArgs(keys: string[], fields: string[] = []): JsxArgs {
 @observer
 export class DocumentView extends React.Component<DocumentViewProps> {
     private _mainCont = React.createRef<HTMLDivElement>();
+    public get ContentRef() {
+        return this._mainCont;
+    }
     private _downX: number = 0;
     private _downY: number = 0;
     @computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
@@ -287,8 +290,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 
 
-    isSelected = () =>
-        SelectionManager.IsSelected(this)
+    isSelected = () => SelectionManager.IsSelected(this)
 
     select = (ctrlPressed: boolean) => {
         SelectionManager.SelectDoc(this, ctrlPressed)
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index d6035a076..d9422ae9b 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -96,8 +96,7 @@ export class FieldView extends React.Component<FieldViewProps> {
         }
         else if (field instanceof ListField) {
             return (<div>
-                {(field as ListField<Field>).Data.map(f =>
-                    f instanceof Document ? f.Title : f.GetValue().toString()).join(", ")}
+                {(field as ListField<Field>).Data.map(f => f instanceof Document ? f.Title : f.GetValue().toString()).join(", ")}
             </div>)
         }
         // bcz: this belongs here, but it doesn't render well so taking it out for now
@@ -107,7 +106,7 @@ export class FieldView extends React.Component<FieldViewProps> {
         else if (field instanceof NumberField) {
             return <p>{field.Data}</p>
         }
-        else if (field != FieldWaiting) {
+        else if (field !== FieldWaiting) {
             return <p>{JSON.stringify(field.GetValue())}</p>
         }
         else
diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx
index 9f52d0ea6..f13cccd24 100644
--- a/src/debug/Viewer.tsx
+++ b/src/debug/Viewer.tsx
@@ -177,8 +177,7 @@ class Viewer extends React.Component {
                     onChange={this.inputOnChange}
                     onKeyDown={this.onKeyPress} />
                 <div>
-                    {this.ids.map(id =>
-                        <DebugViewer fieldId={id} key={id}></DebugViewer>)}
+                    {this.ids.map(id => <DebugViewer fieldId={id} key={id}></DebugViewer>)}
                 </div>
             </>
         )
diff --git a/src/fields/Document.ts b/src/fields/Document.ts
index 45e4f93f6..6d13c96b8 100644
--- a/src/fields/Document.ts
+++ b/src/fields/Document.ts
@@ -90,7 +90,7 @@ export class Document extends Field {
             }
         } else {
             let doc: FieldValue<Document> = this;
-            while (doc && doc !== FieldWaiting && field !== FieldWaiting) {
+            while (doc && field !== FieldWaiting) {
                 let curField = doc.fields.get(key.Id);
                 let curProxy = doc._proxies.get(key.Id);
                 if (!curField || (curProxy && curField.field.Id !== curProxy)) {
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
index c4d346876..c3684a0eb 100644
--- a/src/mobile/ImageUpload.tsx
+++ b/src/mobile/ImageUpload.tsx
@@ -9,6 +9,7 @@ import { RouteStore } from '../server/RouteStore';
 import { ServerUtils } from '../server/ServerUtil';
 import "./ImageUpload.scss";
 import React = require('react');
+import { Opt } from '../fields/Field';
 
 
 
@@ -20,47 +21,44 @@ import React = require('react');
 //     }
 // }
 
-const onFileLoad = (file: any) => {
+const onFileLoad = async (file: any) => {
     let imgPrev = document.getElementById("img_preview")
     if (imgPrev) {
         let files: File[] = file.target.files;
-        if (files.length != 0) {
+        if (files.length !== 0) {
             console.log(files[0]);
             let formData = new FormData();
             formData.append("file", files[0]);
 
             const upload = window.location.origin + "/upload"
-            fetch(upload, {
+            const res = await fetch(upload, {
                 method: 'POST',
                 body: formData
-            }).then((res: Response) =>
-                res.json()).then(json => {
-                json.map((file: any) => {
-                    let path = window.location.origin + file
-                    var doc: Document = Documents.ImageDocument(path, { nativeWidth: 200, width: 200 })
+            });
+            const json = await res.json();
+            json.map(async (file: any) => {
+                let path = window.location.origin + file
+                var doc: Document = Documents.ImageDocument(path, { nativeWidth: 200, width: 200 })
 
-                    rp.get(ServerUtils.prepend(RouteStore.getUserDocumentId)).then(res => {
-                        if (res) {
-                            return Server.GetField(res);
-                        }
-                        throw new Error("No user id returned");
-                    }).then(field => {
-                        if (field instanceof Document) {
-                            return field.GetTAsync(KeyStore.OptionalRightCollection, Document)
-                        }
-                    }).then(pending => {
-                        if (pending) {
-                            pending.GetOrCreateAsync(KeyStore.Data, ListField, list => {
-                                list.Data.push(doc);
-                            })
-                        }
-                    });
+                const res = await rp.get(ServerUtils.prepend(RouteStore.getUserDocumentId));
+                if (!res) {
+                    throw new Error("No user id returned");
+                }
+                const field = await Server.GetField(res);
+                let pending: Opt<Document>;
+                if (field instanceof Document) {
+                    pending = await field.GetTAsync(KeyStore.OptionalRightCollection, Document)
+                }
+                if (pending) {
+                    pending.GetOrCreateAsync(KeyStore.Data, ListField, list => {
+                        list.Data.push(doc);
+                    })
+                }
+            });
 
-                    // console.log(window.location.origin + file[0])
+            // console.log(window.location.origin + file[0])
 
-                    //imgPrev.setAttribute("src", window.location.origin + files[0].name)
-                })
-            })
+            //imgPrev.setAttribute("src", window.location.origin + files[0].name)
         }
     }
 }
diff --git a/src/server/index.ts b/src/server/index.ts
index 0c61f6885..ce8bbefe8 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -263,10 +263,10 @@ function deleteFields() {
     return Database.Instance.deleteAll();
 }
 
-function deleteAll() {
-    return Database.Instance.deleteAll().then(() =>
-        Database.Instance.deleteAll('sessions')).then(() =>
-            Database.Instance.deleteAll('users'));
+async function deleteAll() {
+    await Database.Instance.deleteAll();
+    await Database.Instance.deleteAll('sessions');
+    await Database.Instance.deleteAll('users');
 }
 
 function barReceived(guid: String) {
-- 
cgit v1.2.3-70-g09d2


From 318dacd846ab06358e26ca49e711e06434287d16 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Thu, 4 Apr 2019 19:37:10 -0400
Subject: Most stuff seems to be working

---
 src/client/util/DragManager.ts                        |  9 +++++----
 src/client/views/collections/CollectionBaseView.tsx   | 16 +++++++++-------
 src/client/views/collections/CollectionSchemaView.tsx | 18 +++++++++---------
 src/client/views/collections/CollectionTreeView.tsx   | 19 +++++++++++++++----
 src/client/views/collections/CollectionView.tsx       |  7 ++++++-
 src/client/views/collections/CollectionViewBase.tsx   |  2 ++
 .../collectionFreeForm/CollectionFreeFormView.tsx     |  1 +
 src/client/views/nodes/DocumentView.tsx               |  2 +-
 8 files changed, 48 insertions(+), 26 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index ff778302b..d8e70a8b4 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -6,9 +6,9 @@ import { ImageField } from "../../fields/ImageField";
 import { KeyStore } from "../../fields/KeyStore";
 import { CollectionView } from "../views/collections/CollectionView";
 import { DocumentView } from "../views/nodes/DocumentView";
-import { emptyFunction } from "../../Utils";
+import { returnFalse } from "../../Utils";
 
-export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, removeFunc: (containingCollection: CollectionView) => void = () => { }) {
+export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, moveFunc?: DragManager.MoveFunction) {
     let onRowMove = action((e: PointerEvent): void => {
         e.stopPropagation();
         e.preventDefault();
@@ -16,7 +16,7 @@ export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc:
         document.removeEventListener("pointermove", onRowMove);
         document.removeEventListener('pointerup', onRowUp);
         var dragData = new DragManager.DocumentDragData([docFunc()]);
-        dragData.removeDocument = removeFunc;
+        dragData.moveDocument = moveFunc;
         DragManager.StartDocumentDrag([_reference.current!], dragData);
     });
     let onRowUp = action((e: PointerEvent): void => {
@@ -101,6 +101,7 @@ export namespace DragManager {
         };
     }
 
+    export type MoveFunction = (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
     export class DocumentDragData {
         constructor(dragDoc: Document[]) {
             this.draggedDocuments = dragDoc;
@@ -111,7 +112,7 @@ export namespace DragManager {
         xOffset?: number;
         yOffset?: number;
         aliasOnDrop?: boolean;
-        moveDocument?: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
+        moveDocument?: MoveFunction;
         [id: string]: any;
     }
 
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 9b68ee06c..ee752a2ca 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -9,6 +9,7 @@ import { Document } from '../../../fields/Document';
 import { ListField } from '../../../fields/ListField';
 import { action } from 'mobx';
 import { Transform } from '../../util/Transform';
+import { observer } from 'mobx-react';
 
 export enum CollectionViewType {
     Invalid,
@@ -35,6 +36,7 @@ export interface CollectionViewProps extends FieldViewProps {
 
 export const COLLECTION_BORDER_WIDTH = 1;
 
+@observer
 export class CollectionBaseView extends React.Component<CollectionViewProps> {
     get collectionViewType(): CollectionViewType {
         let Document = this.props.Document;
@@ -72,8 +74,8 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
             if (this.createsCycle(annots[i], containerDocument))
                 return true;
         }
-        for (let containerProto: FieldValue<Document> = containerDocument; containerProto && containerProto != FieldWaiting; containerProto = containerProto.GetPrototype()) {
-            if (containerProto.Id == documentToAdd.Id)
+        for (let containerProto: FieldValue<Document> = containerDocument; containerProto && containerProto !== FieldWaiting; containerProto = containerProto.GetPrototype()) {
+            if (containerProto.Id === documentToAdd.Id)
                 return true;
         }
         return false;
@@ -91,14 +93,14 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
             //TODO This won't create the field if it doesn't already exist
             const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>())
             if (!this.createsCycle(doc, props.Document)) {
-                if (!value.some(v => v.Id == doc.Id) || allowDuplicates)
+                if (!value.some(v => v.Id === doc.Id) || allowDuplicates)
                     value.push(doc);
             }
             else
                 return false;
         } else {
             let proto = props.Document.GetPrototype();
-            if (!proto || proto == FieldWaiting || !this.createsCycle(proto, doc)) {
+            if (!proto || proto === FieldWaiting || !this.createsCycle(proto, doc)) {
                 props.Document.SetOnPrototype(props.fieldKey, new ListField([doc]));
             }
             else
@@ -114,13 +116,13 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>())
         let index = -1;
         for (let i = 0; i < value.length; i++) {
-            if (value[i].Id == doc.Id) {
+            if (value[i].Id === doc.Id) {
                 index = i;
                 break;
             }
         }
         doc.GetTAsync(KeyStore.AnnotationOn, Document).then((annotationOn) => {
-            if (annotationOn == props.Document) {
+            if (annotationOn === props.Document) {
                 doc.Set(KeyStore.AnnotationOn, undefined, true);
             }
         })
@@ -138,7 +140,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
     @action.bound
     moveDocument(doc: Document, targetCollection: Document, addDocument: (doc: Document) => boolean): boolean {
         if (this.props.Document === targetCollection) {
-            return false;
+            return true;
         }
         if (this.removeDocument(doc)) {
             return addDocument(doc);
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index ced46cc25..9dade94f1 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -90,7 +90,7 @@ export class CollectionSchemaView extends CollectionViewBase {
             <FieldView {...props} />
         )
         let reference = React.createRef<HTMLDivElement>();
-        let onItemDown = setupDrag(reference, () => props.Document, (containingCollection: CollectionView) => this.props.removeDocument(props.Document));
+        let onItemDown = setupDrag(reference, () => props.Document, this.props.moveDocument);
         let applyToDoc = (doc: Document, value: string) => {
             let script = CompileScript(value, { this: doc }, true);
             if (!script.compiled) {
@@ -150,7 +150,7 @@ export class CollectionSchemaView extends CollectionViewBase {
                 }
             }),
             style: {
-                background: rowInfo.index == this._selectedIndex ? "lightGray" : "white",
+                background: rowInfo.index === this._selectedIndex ? "lightGray" : "white",
                 //color: rowInfo.index == this._selectedIndex ? "white" : "black"
             }
         };
@@ -179,8 +179,8 @@ export class CollectionSchemaView extends CollectionViewBase {
     @action
     toggleExpander = (event: React.ChangeEvent<HTMLInputElement>) => {
         this._startSplitPercent = this.splitPercentage;
-        if (this._startSplitPercent == this.splitPercentage) {
-            this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage == 0 ? 33 : 0);
+        if (this._startSplitPercent === this.splitPercentage) {
+            this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage === 0 ? 33 : 0);
         }
     }
 
@@ -209,8 +209,8 @@ export class CollectionSchemaView extends CollectionViewBase {
     onDividerUp = (e: PointerEvent): void => {
         document.removeEventListener("pointermove", this.onDividerMove);
         document.removeEventListener('pointerup', this.onDividerUp);
-        if (this._startSplitPercent == this.splitPercentage) {
-            this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage == 0 ? 33 : 0);
+        if (this._startSplitPercent === this.splitPercentage) {
+            this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage === 0 ? 33 : 0);
         }
     }
     onDividerDown = (e: React.PointerEvent) => {
@@ -290,7 +290,7 @@ export class CollectionSchemaView extends CollectionViewBase {
         let doc: any = selected ? selected.Get(new Key(this.previewScript)) : undefined;
 
         // let doc = CompileScript(this.previewScript, { this: selected }, true)();
-        let content = this._selectedIndex == -1 || !selected ? (null) : (
+        let content = this._selectedIndex === -1 || !selected ? (null) : (
             <Measure onResize={this.setScaling}>
                 {({ measureRef }) =>
                     <div className="collectionSchemaView-content" ref={measureRef}>
@@ -313,7 +313,7 @@ export class CollectionSchemaView extends CollectionViewBase {
                 }
             </Measure>
         )
-        let dividerDragger = this.splitPercentage == 0 ? (null) :
+        let dividerDragger = this.splitPercentage === 0 ? (null) :
             <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />
 
         //options button and menu
@@ -323,7 +323,7 @@ export class CollectionSchemaView extends CollectionViewBase {
                 <div id="schema-options-header"><h5><b>Options</b></h5></div>
                 <div id="options-flyout-div">
                     <h6 className="schema-options-subHeader">Preview Window</h6>
-                    <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={this.splitPercentage != 0} onChange={this.toggleExpander} />  Show Preview </div>
+                    <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={this.splitPercentage !== 0} onChange={this.toggleExpander} />  Show Preview </div>
                     <h6 className="schema-options-subHeader" >Displayed Columns</h6>
                     <ul id="schema-col-checklist" >
                         {Array.from(Object.keys(allKeys)).map(item =>
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 6f9ba40d2..32d74164d 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -7,7 +7,7 @@ import { Document } from "../../../fields/Document";
 import { FieldWaiting } from "../../../fields/Field";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ListField } from "../../../fields/ListField";
-import { setupDrag } from "../../util/DragManager";
+import { setupDrag, DragManager } from "../../util/DragManager";
 import { EditableView } from "../EditableView";
 import "./CollectionTreeView.scss";
 import { CollectionView } from "./CollectionView";
@@ -19,6 +19,7 @@ import { COLLECTION_BORDER_WIDTH } from './CollectionBaseView';
 export interface TreeViewProps {
     document: Document;
     deleteDoc: (doc: Document) => void;
+    moveDocument: DragManager.MoveFunction;
 }
 
 export enum BulletType {
@@ -49,6 +50,16 @@ class TreeView extends React.Component<TreeViewProps> {
         }
     }
 
+    @action
+    move: DragManager.MoveFunction = (document, target, addDoc) => {
+        if (this.props.document === target) {
+            return true;
+        }
+        //TODO This should check if it was removed
+        this.remove(document)
+        return addDoc(document);
+    }
+
     renderBullet(type: BulletType) {
         let onClicked = action(() => this._collapsed = !this._collapsed);
         let bullet: IconProp | undefined = undefined;
@@ -64,7 +75,7 @@ class TreeView extends React.Component<TreeViewProps> {
      */
     renderTitle() {
         let reference = React.createRef<HTMLDivElement>();
-        let onItemDown = setupDrag(reference, () => this.props.document, (containingCollection: CollectionView) => this.props.deleteDoc(this.props.document));
+        let onItemDown = setupDrag(reference, () => this.props.document, this.props.moveDocument);
         let editableView = (titleString: string) =>
             (<EditableView
                 display={"inline"}
@@ -92,7 +103,7 @@ class TreeView extends React.Component<TreeViewProps> {
             if (!this._collapsed) {
                 bulletType = BulletType.Collapsible;
                 childElements = <ul>
-                    {children.Data.map(value => <TreeView key={value.Id} document={value} deleteDoc={this.remove} />)}
+                    {children.Data.map(value => <TreeView key={value.Id} document={value} deleteDoc={this.remove} moveDocument={this.move} />)}
                 </ul>
             }
             else bulletType = BulletType.Collapsed;
@@ -122,7 +133,7 @@ export class CollectionTreeView extends CollectionViewBase {
         var children = this.props.Document.GetT<ListField<Document>>(KeyStore.Data, ListField);
         let childrenElement = !children || children === FieldWaiting ? (null) :
             (children.Data.map(value =>
-                <TreeView document={value} key={value.Id} deleteDoc={this.remove} />)
+                <TreeView document={value} key={value.Id} deleteDoc={this.remove} moveDocument={this.props.moveDocument} />)
             )
 
         return (
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index d440dcff9..5b4caf58d 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -8,16 +8,21 @@ import { CollectionTreeView } from './CollectionTreeView';
 import { ContextMenu } from '../ContextMenu';
 import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
 import { KeyStore } from '../../../fields/KeyStore';
+import { observer } from 'mobx-react';
+
+@observer
 export class CollectionView extends React.Component<FieldViewProps> {
     public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(CollectionView, fieldStr) }
 
     private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
         let props = { ...this.props, ...renderProps };
         switch (type) {
-            case CollectionViewType.Freeform: return (<CollectionFreeFormView {...props} />)
             case CollectionViewType.Schema: return (<CollectionSchemaView {...props} />)
             case CollectionViewType.Docking: return (<CollectionDockingView {...props} />)
             case CollectionViewType.Tree: return (<CollectionTreeView {...props} />)
+            case CollectionViewType.Freeform:
+            default:
+                return (<CollectionFreeFormView {...props} />)
         }
         return (null);
     }
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index 2b858789f..147e5fd6b 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -80,6 +80,8 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
             } else if (de.data.moveDocument) {
                 const move = de.data.moveDocument;
                 added = de.data.droppedDocuments.reduce((added, d) => added || move(d, this.props.Document, this.props.addDocument), false)
+            } else {
+                added = de.data.droppedDocuments.reduce((added, d) => added || this.props.addDocument(d), false)
             }
             e.stopPropagation();
             return added;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f03205739..4aad06607 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -245,6 +245,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
             Document: document,
             addDocument: this.props.addDocument,
             removeDocument: this.props.removeDocument,
+            moveDocument: this.props.moveDocument,
             ScreenToLocalTransform: this.getTransform,
             isTopMost: false,
             selectOnLoad: document.Id === this._selectOnLoaded,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3b146654c..2a9b3b9ac 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -27,7 +27,7 @@ export interface DocumentViewProps {
     Document: Document;
     addDocument?: (doc: Document, allowDuplicates?: boolean) => boolean;
     removeDocument?: (doc: Document) => boolean;
-    moveDocument?: (doc: Document, targetCollection: Document, addDocument: (document: Document) => void) => boolean;
+    moveDocument?: (doc: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
     ScreenToLocalTransform: () => Transform;
     isTopMost: boolean;
     ContentScaling: () => number;
-- 
cgit v1.2.3-70-g09d2


From 0403ee1f31d3f127a44aaabdfd21785d7efa6430 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Fri, 5 Apr 2019 01:17:41 -0400
Subject: Fixed remaining linter warnings

---
 src/client/northstar/core/BaseObject.ts            |   2 +-
 .../northstar/core/attribute/AttributeModel.ts     |   4 +-
 .../core/attribute/AttributeTransformationModel.ts |   2 +-
 .../core/attribute/CalculatedAttributeModel.ts     |   4 +-
 src/client/northstar/core/filter/FilterModel.ts    |   6 +-
 .../northstar/core/filter/ValueComparision.ts      |   8 +-
 src/client/northstar/dash-fields/HistogramField.ts |   8 +-
 .../dash-nodes/HistogramBinPrimitiveCollection.ts  |  46 ++++-----
 src/client/northstar/dash-nodes/HistogramBox.tsx   |  12 +--
 .../dash-nodes/HistogramBoxPrimitives.tsx          |  18 ++--
 .../dash-nodes/HistogramLabelPrimitives.tsx        |  10 +-
 src/client/northstar/manager/Gateway.ts            |   8 +-
 src/client/northstar/model/ModelExtensions.ts      |  14 +--
 src/client/northstar/model/ModelHelpers.ts         |  10 +-
 .../model/binRanges/DateTimeVisualBinRange.ts      |  24 ++---
 .../northstar/model/binRanges/VisualBinRange.ts    |   4 -
 .../model/binRanges/VisualBinRangeHelper.ts        |   7 +-
 .../northstar/model/idea/MetricTypeMapping.ts      |  28 +++---
 src/client/northstar/operations/BaseOperation.ts   |   8 +-
 .../northstar/operations/HistogramOperation.ts     |   2 +-
 src/client/northstar/utils/ArrayUtil.ts            |  14 +--
 src/client/northstar/utils/GeometryUtil.ts         |   2 +-
 src/client/northstar/utils/MathUtil.ts             |  12 +--
 src/client/northstar/utils/SizeConverter.ts        |   4 +-
 src/client/northstar/utils/Utils.ts                |  12 +--
 src/client/views/DocumentDecorations.tsx           |   4 +-
 src/client/views/Main.tsx                          |   4 +-
 .../views/collections/CollectionBaseView.tsx       |   8 +-
 .../views/collections/CollectionDockingView.tsx    |  30 +++---
 src/client/views/collections/CollectionPDFView.tsx |   2 +-
 .../views/collections/CollectionSchemaView.tsx     |   2 +-
 .../views/collections/CollectionVideoView.tsx      |   4 +-
 src/client/views/collections/CollectionView.tsx    |   2 +-
 .../views/collections/CollectionViewBase.tsx       |   4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   4 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |  12 +--
 .../collectionFreeForm/PreviewCursor.tsx           |   2 +-
 src/client/views/nodes/Annotation.tsx              | 108 ++++++++++-----------
 src/client/views/nodes/AudioBox.tsx                |   2 +-
 src/client/views/nodes/DocumentContentsView.tsx    |   2 +-
 src/client/views/nodes/KeyValueBox.tsx             |   6 +-
 src/client/views/nodes/LinkBox.tsx                 |   2 +-
 src/client/views/nodes/LinkMenu.tsx                |   2 +-
 src/client/views/nodes/VideoBox.tsx                |   2 +-
 src/client/views/nodes/WebBox.tsx                  |   2 +-
 src/fields/AudioField.ts                           |   2 +-
 src/fields/BasicField.ts                           |   2 +-
 src/fields/ImageField.ts                           |   2 +-
 src/fields/ListField.ts                            |  10 +-
 src/fields/PDFField.ts                             |   2 +-
 src/fields/VideoField.ts                           |   2 +-
 src/server/index.ts                                |   2 +-
 52 files changed, 245 insertions(+), 250 deletions(-)

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

diff --git a/src/client/northstar/core/BaseObject.ts b/src/client/northstar/core/BaseObject.ts
index e9e766e31..f1761e643 100644
--- a/src/client/northstar/core/BaseObject.ts
+++ b/src/client/northstar/core/BaseObject.ts
@@ -4,7 +4,7 @@ import { IDisposable } from '../utils/IDisposable'
 export class BaseObject implements IEquatable, IDisposable {
 
     public Equals(other: Object): boolean {
-        return this == other;
+        return this === other;
     }
 
     public Dispose(): void {
diff --git a/src/client/northstar/core/attribute/AttributeModel.ts b/src/client/northstar/core/attribute/AttributeModel.ts
index 124a5b45a..230bfecc8 100644
--- a/src/client/northstar/core/attribute/AttributeModel.ts
+++ b/src/client/northstar/core/attribute/AttributeModel.ts
@@ -34,7 +34,7 @@ export class ColumnAttributeModel extends AttributeModel {
     }
 
     public Equals(other: ColumnAttributeModel): boolean {
-        return this.Attribute.rawName == other.Attribute.rawName;
+        return this.Attribute.rawName === other.Attribute.rawName;
     }
 }
 
@@ -105,7 +105,7 @@ export class BackendAttributeModel extends AttributeModel {
     }
 
     public Equals(other: BackendAttributeModel): boolean {
-        return this.Id == other.Id;
+        return this.Id === other.Id;
     }
 
 }
\ No newline at end of file
diff --git a/src/client/northstar/core/attribute/AttributeTransformationModel.ts b/src/client/northstar/core/attribute/AttributeTransformationModel.ts
index cc5aa7154..f50b78d51 100644
--- a/src/client/northstar/core/attribute/AttributeTransformationModel.ts
+++ b/src/client/northstar/core/attribute/AttributeTransformationModel.ts
@@ -41,7 +41,7 @@ export class AttributeTransformationModel implements IEquatable {
     }
 
     public Equals(other: AttributeTransformationModel): boolean {
-        return this.AggregateFunction == other.AggregateFunction &&
+        return this.AggregateFunction === other.AggregateFunction &&
             this.AttributeModel.Equals(other.AttributeModel);
     }
 }
\ No newline at end of file
diff --git a/src/client/northstar/core/attribute/CalculatedAttributeModel.ts b/src/client/northstar/core/attribute/CalculatedAttributeModel.ts
index ab96c794d..0b8e0d12c 100644
--- a/src/client/northstar/core/attribute/CalculatedAttributeModel.ts
+++ b/src/client/northstar/core/attribute/CalculatedAttributeModel.ts
@@ -11,7 +11,7 @@ export class CalculatedAttributeManager {
     public static CreateBackendAttributeModel(id: string, dataType: DataType, displayName: string, codeName: string, visualizationHints: VisualizationHint[]): BackendAttributeModel {
         var filtered = this.AllCalculatedAttributes.filter(am => {
             if (am instanceof BackendAttributeModel &&
-                am.Id == id) {
+                am.Id === id) {
                 return true;
             }
             return false;
@@ -27,7 +27,7 @@ export class CalculatedAttributeManager {
     public static CreateCodeAttributeModel(code: string, codeName: string, visualizationHints: VisualizationHint[]): CodeAttributeModel {
         var filtered = this.AllCalculatedAttributes.filter(am => {
             if (am instanceof CodeAttributeModel &&
-                am.CodeName == codeName) {
+                am.CodeName === codeName) {
                 return true;
             }
             return false;
diff --git a/src/client/northstar/core/filter/FilterModel.ts b/src/client/northstar/core/filter/FilterModel.ts
index aee99d2b6..20fca77f5 100644
--- a/src/client/northstar/core/filter/FilterModel.ts
+++ b/src/client/northstar/core/filter/FilterModel.ts
@@ -46,16 +46,16 @@ export class FilterModel {
         let filtered = baseOperation.FilterModels.filter(fm => fm && fm.ValueComparisons.length > 0);
         if (!isFirst && filtered.length > 0) {
             filterModels.push(...filtered);
-            ret = "(" + baseOperation.FilterModels.filter(fm => fm != null).map(fm => fm.ToPythonString()).join(" || ") + ")";
+            ret = "(" + baseOperation.FilterModels.filter(fm => fm !== null).map(fm => fm.ToPythonString()).join(" || ") + ")";
         }
         if (Utils.isBaseFilterConsumer(baseOperation) && baseOperation.Links) {
             let children = new Array<string>();
             let linkedGraphNodes = baseOperation.Links;
             linkedGraphNodes.map(linkVm => {
                 let filterDoc = linkVm.Get(KeyStore.LinkedFromDocs);
-                if (filterDoc && filterDoc != FieldWaiting && filterDoc instanceof Document) {
+                if (filterDoc && filterDoc !== FieldWaiting && filterDoc instanceof Document) {
                     let filterHistogram = filterDoc.GetT(KeyStore.Data, HistogramField);
-                    if (filterHistogram && filterHistogram != FieldWaiting) {
+                    if (filterHistogram && filterHistogram !== FieldWaiting) {
                         if (!visitedFilterProviders.has(filterHistogram.Data)) {
                             let child = FilterModel.GetFilterModelsRecursive(filterHistogram.Data, visitedFilterProviders, filterModels, false);
                             if (child !== "") {
diff --git a/src/client/northstar/core/filter/ValueComparision.ts b/src/client/northstar/core/filter/ValueComparision.ts
index 1e729d06e..1a3e461f5 100644
--- a/src/client/northstar/core/filter/ValueComparision.ts
+++ b/src/client/northstar/core/filter/ValueComparision.ts
@@ -20,7 +20,7 @@ export class ValueComparison {
         if (this.Predicate !== (other as ValueComparison).Predicate)
             return false;
         let isComplex = (typeof this.Value === "object");
-        if (!isComplex && this.Value != (other as ValueComparison).Value)
+        if (!isComplex && this.Value !== (other as ValueComparison).Value)
             return false;
         if (isComplex && !this.Value.Equals((other as ValueComparison).Value))
             return false;
@@ -58,13 +58,13 @@ export class ValueComparison {
         var rawName = this.attributeModel.CodeName;
         switch (this.Predicate) {
             case Predicate.STARTS_WITH:
-                ret += rawName + " != null && " + rawName + ".StartsWith(" + val + ") ";
+                ret += rawName + " !== null && " + rawName + ".StartsWith(" + val + ") ";
                 return ret;
             case Predicate.ENDS_WITH:
-                ret += rawName + " != null && " + rawName + ".EndsWith(" + val + ") ";
+                ret += rawName + " !== null && " + rawName + ".EndsWith(" + val + ") ";
                 return ret;
             case Predicate.CONTAINS:
-                ret += rawName + " != null && " + rawName + ".Contains(" + val + ") ";
+                ret += rawName + " !== null && " + rawName + ".Contains(" + val + ") ";
                 return ret;
             default:
                 ret += rawName + " " + op + " " + val + " ";
diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts
index 90be70b80..f939ccd83 100644
--- a/src/client/northstar/dash-fields/HistogramField.ts
+++ b/src/client/northstar/dash-fields/HistogramField.ts
@@ -16,7 +16,7 @@ export class HistogramField extends BasicField<HistogramOperation> {
     omitKeys(obj: any, keys: any) {
         var dup: any = {};
         for (var key in obj) {
-            if (keys.indexOf(key) == -1) {
+            if (keys.indexOf(key) === -1) {
                 dup[key] = obj[key];
             }
         }
@@ -54,13 +54,13 @@ export class HistogramField extends BasicField<HistogramOperation> {
         let schema = CurrentUserUtils.GetNorthstarSchema(jp.SchemaName);
         if (schema) {
             CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => {
-                if (attr.displayName == jp.X.AttributeModel.Attribute.DisplayName) {
+                if (attr.displayName === jp.X.AttributeModel.Attribute.DisplayName) {
                     X = new AttributeTransformationModel(new ColumnAttributeModel(attr), jp.X.AggregateFunction);
                 }
-                if (attr.displayName == jp.Y.AttributeModel.Attribute.DisplayName) {
+                if (attr.displayName === jp.Y.AttributeModel.Attribute.DisplayName) {
                     Y = new AttributeTransformationModel(new ColumnAttributeModel(attr), jp.Y.AggregateFunction);
                 }
-                if (attr.displayName == jp.V.AttributeModel.Attribute.DisplayName) {
+                if (attr.displayName === jp.V.AttributeModel.Attribute.DisplayName) {
                     V = new AttributeTransformationModel(new ColumnAttributeModel(attr), jp.V.AggregateFunction);
                 }
             });
diff --git a/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts b/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts
index 43e768c62..cdae90c8b 100644
--- a/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts
+++ b/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts
@@ -48,28 +48,28 @@ export class HistogramBinPrimitiveCollection {
 
         // adjust brush rects (stacking or not)
         var allBrushIndex = ModelHelpers.AllBrushIndex(this.histoResult);
-        var filteredBinPrims = this.BinPrimitives.filter(b => b.BrushIndex != allBrushIndex && b.DataValue != 0.0);
+        var filteredBinPrims = this.BinPrimitives.filter(b => b.BrushIndex !== allBrushIndex && b.DataValue !== 0.0);
         filteredBinPrims.reduce((sum, fbp) => {
-            if (histoBox.ChartType == ChartType.VerticalBar) {
-                if (this.histoOp.Y.AggregateFunction == AggregateFunction.Count) {
+            if (histoBox.ChartType === ChartType.VerticalBar) {
+                if (this.histoOp.Y.AggregateFunction === AggregateFunction.Count) {
                     fbp.Rect = new PIXIRectangle(fbp.Rect.x, fbp.Rect.y - sum, fbp.Rect.width, fbp.Rect.height);
                     fbp.MarginRect = new PIXIRectangle(fbp.MarginRect.x, fbp.MarginRect.y - sum, fbp.MarginRect.width, fbp.MarginRect.height);
                     return sum + fbp.Rect.height;
                 }
-                if (this.histoOp.Y.AggregateFunction == AggregateFunction.Avg) {
+                if (this.histoOp.Y.AggregateFunction === AggregateFunction.Avg) {
                     var w = fbp.Rect.width / 2.0;
                     fbp.Rect = new PIXIRectangle(fbp.Rect.x + sum, fbp.Rect.y, fbp.Rect.width / filteredBinPrims.length, fbp.Rect.height);
                     fbp.MarginRect = new PIXIRectangle(fbp.MarginRect.x - w + sum + (fbp.Rect.width / 2.0), fbp.MarginRect.y, fbp.MarginRect.width, fbp.MarginRect.height);
                     return sum + fbp.Rect.width;
                 }
             }
-            else if (histoBox.ChartType == ChartType.HorizontalBar) {
-                if (this.histoOp.X.AggregateFunction == AggregateFunction.Count) {
+            else if (histoBox.ChartType === ChartType.HorizontalBar) {
+                if (this.histoOp.X.AggregateFunction === AggregateFunction.Count) {
                     fbp.Rect = new PIXIRectangle(fbp.Rect.x + sum, fbp.Rect.y, fbp.Rect.width, fbp.Rect.height);
                     fbp.MarginRect = new PIXIRectangle(fbp.MarginRect.x + sum, fbp.MarginRect.y, fbp.MarginRect.width, fbp.MarginRect.height);
                     return sum + fbp.Rect.width;
                 }
-                if (this.histoOp.X.AggregateFunction == AggregateFunction.Avg) {
+                if (this.histoOp.X.AggregateFunction === AggregateFunction.Avg) {
                     var h = fbp.Rect.height / 2.0;
                     fbp.Rect = new PIXIRectangle(fbp.Rect.x, fbp.Rect.y + sum, fbp.Rect.width, fbp.Rect.height / filteredBinPrims.length);
                     fbp.MarginRect = new PIXIRectangle(fbp.MarginRect.x, fbp.MarginRect.y - h + sum + (fbp.Rect.height / 2.0), fbp.MarginRect.width, fbp.MarginRect.height);
@@ -79,19 +79,19 @@ export class HistogramBinPrimitiveCollection {
             return 0;
         }, 0);
         this.BinPrimitives = this.BinPrimitives.reverse();
-        var f = this.BinPrimitives.filter(b => b.BrushIndex == allBrushIndex);
+        var f = this.BinPrimitives.filter(b => b.BrushIndex === allBrushIndex);
         this.HitGeom = f.length > 0 ? f[0].Rect : PIXIRectangle.EMPTY;
     }
 
     private setupBrushing(bin: Bin, normalization: number) {
         var overlapBrushIndex = ModelHelpers.OverlapBrushIndex(this.histoResult);
         var orderedBrushes = [this.histoResult.brushes![0], this.histoResult.brushes![overlapBrushIndex]];
-        this.histoResult.brushes!.map(brush => brush.brushIndex != 0 && brush.brushIndex != overlapBrushIndex && orderedBrushes.push(brush));
+        this.histoResult.brushes!.map(brush => brush.brushIndex !== 0 && brush.brushIndex !== overlapBrushIndex && orderedBrushes.push(brush));
         return {
             orderedBrushes,
             maxAxis: orderedBrushes.reduce((prev, Brush) => {
                 let aggResult = this.getBinValue(normalization, bin, Brush.brushIndex!);
-                return aggResult != undefined && aggResult > prev ? aggResult : prev;
+                return aggResult !== undefined && aggResult > prev ? aggResult : prev;
             }, Number.MIN_VALUE)
         };
     }
@@ -99,7 +99,7 @@ export class HistogramBinPrimitiveCollection {
     private createHeatmapBinPrimitives(bin: Bin, brush: Brush, brushFactorSum: number): number {
 
         let unNormalizedValue = this.getBinValue(2, bin, brush.brushIndex!);
-        if (unNormalizedValue == undefined)
+        if (unNormalizedValue === undefined)
             return brushFactorSum;
 
         var normalizedValue = (unNormalizedValue - this._histoBox.ValueRange[0]) / (Math.abs((this._histoBox.ValueRange[1] - this._histoBox.ValueRange[0])) < HistogramBinPrimitiveCollection.TOLERANCE ?
@@ -112,7 +112,7 @@ export class HistogramBinPrimitiveCollection {
         let [yFrom, yTo] = this.sizeConverter.DataToScreenYAxisRange(this._histoBox.VisualBinRanges, 1, bin);
 
         var returnBrushFactorSum = brushFactorSum;
-        if (allUnNormalizedValue != undefined) {
+        if (allUnNormalizedValue !== undefined) {
             var brushFactor = (unNormalizedValue / allUnNormalizedValue);
             returnBrushFactorSum += brushFactor;
             returnBrushFactorSum = Math.min(returnBrushFactorSum, 1.0);
@@ -141,11 +141,11 @@ export class HistogramBinPrimitiveCollection {
 
     private createSinglePointChartBinPrimitives(bin: Bin, brush: Brush): number {
         let unNormalizedValue = this.getBinValue(2, bin, brush.brushIndex!);
-        if (unNormalizedValue != undefined) {
+        if (unNormalizedValue !== undefined) {
             let [xFrom, xTo] = this.sizeConverter.DataToScreenPointRange(0, bin, ModelHelpers.CreateAggregateKey(this.histoOp.Schema!.distinctAttributeParameters, this.histoOp.X, this.histoResult, brush.brushIndex!));
             let [yFrom, yTo] = this.sizeConverter.DataToScreenPointRange(1, bin, ModelHelpers.CreateAggregateKey(this.histoOp.Schema!.distinctAttributeParameters, this.histoOp.Y, this.histoResult, brush.brushIndex!));
 
-            if (xFrom != undefined && yFrom != undefined && xTo != undefined && yTo != undefined)
+            if (xFrom !== undefined && yFrom !== undefined && xTo !== undefined && yTo !== undefined)
                 this.createBinPrimitive(-1, brush, PIXIRectangle.EMPTY, 0, xFrom, xTo, yFrom, yTo, this.baseColorFromBrush(brush), 1, unNormalizedValue);
         }
         return 0;
@@ -153,7 +153,7 @@ export class HistogramBinPrimitiveCollection {
 
     private createVerticalBarChartBinPrimitives(bin: Bin, brush: Brush, binBrushMaxAxis: number, normalization: number): number {
         let dataValue = this.getBinValue(1, bin, brush.brushIndex!);
-        if (dataValue != undefined) {
+        if (dataValue !== undefined) {
             let [yFrom, yValue, yTo] = this.sizeConverter.DataToScreenNormalizedRange(dataValue, normalization, 1, binBrushMaxAxis);
             let [xFrom, xTo] = this.sizeConverter.DataToScreenXAxisRange(this._histoBox.VisualBinRanges, 0, bin);
 
@@ -163,14 +163,14 @@ export class HistogramBinPrimitiveCollection {
                 this.sizeConverter.DataToScreenY(yValue - yMarginAbsolute) - this.sizeConverter.DataToScreenY(yValue + yMarginAbsolute));
 
             this.createBinPrimitive(1, brush, marginRect, 0, xFrom, xTo, yFrom, yTo,
-                this.baseColorFromBrush(brush), normalization != 0 ? 1 : 0.6 * binBrushMaxAxis / this.sizeConverter.DataRanges[1] + 0.4, dataValue);
+                this.baseColorFromBrush(brush), normalization !== 0 ? 1 : 0.6 * binBrushMaxAxis / this.sizeConverter.DataRanges[1] + 0.4, dataValue);
         }
         return 0;
     }
 
     private createHorizontalBarChartBinPrimitives(bin: Bin, brush: Brush, binBrushMaxAxis: number, normalization: number): number {
         let dataValue = this.getBinValue(0, bin, brush.brushIndex!);
-        if (dataValue != undefined) {
+        if (dataValue !== undefined) {
             let [xFrom, xValue, xTo] = this.sizeConverter.DataToScreenNormalizedRange(dataValue, normalization, 0, binBrushMaxAxis);
             let [yFrom, yTo] = this.sizeConverter.DataToScreenYAxisRange(this._histoBox.VisualBinRanges, 1, bin);
 
@@ -181,15 +181,15 @@ export class HistogramBinPrimitiveCollection {
                 2.0);
 
             this.createBinPrimitive(0, brush, marginRect, 0, xFrom, xTo, yFrom, yTo,
-                this.baseColorFromBrush(brush), normalization != 1 ? 1 : 0.6 * binBrushMaxAxis / this.sizeConverter.DataRanges[0] + 0.4, dataValue);
+                this.baseColorFromBrush(brush), normalization !== 1 ? 1 : 0.6 * binBrushMaxAxis / this.sizeConverter.DataRanges[0] + 0.4, dataValue);
         }
         return 0;
     }
 
     public getBinValue(axis: number, bin: Bin, brushIndex: number) {
-        var aggregateKey = ModelHelpers.CreateAggregateKey(this.histoOp.Schema!.distinctAttributeParameters, axis == 0 ? this.histoOp.X : axis == 1 ? this.histoOp.Y : this.histoOp.V, this.histoResult, brushIndex);
+        var aggregateKey = ModelHelpers.CreateAggregateKey(this.histoOp.Schema!.distinctAttributeParameters, axis === 0 ? this.histoOp.X : axis === 1 ? this.histoOp.Y : this.histoOp.V, this.histoResult, brushIndex);
         let dataValue = ModelHelpers.GetAggregateResult(bin, aggregateKey) as DoubleValueAggregateResult;
-        return dataValue != null && dataValue.hasResult ? dataValue.result : undefined;
+        return dataValue !== null && dataValue.hasResult ? dataValue.result : undefined;
     }
 
     private getMargin(bin: Bin, brush: Brush, axis: AttributeTransformationModel) {
@@ -218,13 +218,13 @@ export class HistogramBinPrimitiveCollection {
 
     private baseColorFromBrush(brush: Brush): number {
         let bc = StyleConstants.BRUSH_COLORS;
-        if (brush.brushIndex == ModelHelpers.RestBrushIndex(this.histoResult)) {
+        if (brush.brushIndex === ModelHelpers.RestBrushIndex(this.histoResult)) {
             return StyleConstants.HIGHLIGHT_COLOR;
         }
-        else if (brush.brushIndex == ModelHelpers.OverlapBrushIndex(this.histoResult)) {
+        else if (brush.brushIndex === ModelHelpers.OverlapBrushIndex(this.histoResult)) {
             return StyleConstants.OVERLAP_COLOR;
         }
-        else if (brush.brushIndex == ModelHelpers.AllBrushIndex(this.histoResult)) {
+        else if (brush.brushIndex === ModelHelpers.AllBrushIndex(this.histoResult)) {
             return 0x00ff00;
         }
         else if (bc.length > 0) {
diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index 5938bf9b1..c9658d741 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -55,7 +55,7 @@ export class HistogramBox extends React.Component<FieldViewProps> {
     dropX = (e: Event, de: DragManager.DropEvent) => {
         if (de.data instanceof DragManager.DocumentDragData) {
             let h = de.data.draggedDocuments[0].GetT(KeyStore.Data, HistogramField);
-            if (h && h != FieldWaiting) {
+            if (h && h !== FieldWaiting) {
                 this.HistoOp.X = h.Data.X;
             }
             e.stopPropagation();
@@ -66,7 +66,7 @@ export class HistogramBox extends React.Component<FieldViewProps> {
     dropY = (e: Event, de: DragManager.DropEvent) => {
         if (de.data instanceof DragManager.DocumentDragData) {
             let h = de.data.draggedDocuments[0].GetT(KeyStore.Data, HistogramField);
-            if (h && h != FieldWaiting) {
+            if (h && h !== FieldWaiting) {
                 this.HistoOp.Y = h.Data.X;
             }
             e.stopPropagation();
@@ -76,11 +76,11 @@ export class HistogramBox extends React.Component<FieldViewProps> {
 
     @action
     xLabelPointerDown = (e: React.PointerEvent) => {
-        this.HistoOp.X = new AttributeTransformationModel(this.HistoOp.X.AttributeModel, this.HistoOp.X.AggregateFunction == AggregateFunction.None ? AggregateFunction.Count : AggregateFunction.None);
+        this.HistoOp.X = new AttributeTransformationModel(this.HistoOp.X.AttributeModel, this.HistoOp.X.AggregateFunction === AggregateFunction.None ? AggregateFunction.Count : AggregateFunction.None);
     }
     @action
     yLabelPointerDown = (e: React.PointerEvent) => {
-        this.HistoOp.Y = new AttributeTransformationModel(this.HistoOp.Y.AttributeModel, this.HistoOp.Y.AggregateFunction == AggregateFunction.None ? AggregateFunction.Count : AggregateFunction.None);
+        this.HistoOp.Y = new AttributeTransformationModel(this.HistoOp.Y.AttributeModel, this.HistoOp.Y.AggregateFunction === AggregateFunction.None ? AggregateFunction.Count : AggregateFunction.None);
     }
 
     componentDidMount() {
@@ -119,7 +119,7 @@ export class HistogramBox extends React.Component<FieldViewProps> {
         if (catalog) {
             this.props.Document.GetTAsync(this.props.fieldKey, HistogramField).then((histoOp: Opt<HistogramField>) => runInAction(() => {
                 this.HistoOp = histoOp ? histoOp.Data : HistogramOperation.Empty;
-                if (this.HistoOp != HistogramOperation.Empty) {
+                if (this.HistoOp !== HistogramOperation.Empty) {
                     reaction(() => this.props.Document.GetList(KeyStore.LinkedFromDocs, []), (docs: Document[]) => this.HistoOp.Links.splice(0, this.HistoOp.Links.length, ...docs), { fireImmediately: true });
                     reaction(() => this.props.Document.GetList(KeyStore.BrushingDocs, []).length,
                         () => {
@@ -128,7 +128,7 @@ export class HistogramBox extends React.Component<FieldViewProps> {
                             this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...brushingDocs.map((brush, i) => {
                                 brush.SetNumber(KeyStore.BackgroundColor, StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length]);
                                 let brushed = brush.GetList(KeyStore.BrushingDocs, [] as Document[]);
-                                return { l: brush, b: brushed[0].Id == proto.Id ? brushed[1] : brushed[0] }
+                                return { l: brush, b: brushed[0].Id === proto.Id ? brushed[1] : brushed[0] }
                             }));
                         }, { fireImmediately: true });
                     reaction(() => this.createOperationParamsCache, () => this.HistoOp.Update(), { fireImmediately: true });
diff --git a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
index e9adb3ce5..66d31846c 100644
--- a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
@@ -44,7 +44,7 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
     }
 
     private getSelectionToggle(binPrimitives: HistogramBinPrimitive[], allBrushIndex: number, filterModel: FilterModel) {
-        let allBrushPrim = ArrayUtil.FirstOrDefault(binPrimitives, bp => bp.BrushIndex == allBrushIndex);
+        let allBrushPrim = ArrayUtil.FirstOrDefault(binPrimitives, bp => bp.BrushIndex === allBrushIndex);
         return !allBrushPrim ? () => { } : () => runInAction(() => {
             if (ArrayUtil.Contains(this.histoOp.FilterModels, filterModel)) {
                 this._selectedPrims.splice(this._selectedPrims.indexOf(allBrushPrim!), 1);
@@ -63,9 +63,9 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
         let labels = this.props.HistoBox.VisualBinRanges[axis].GetLabels();
         return labels.reduce((prims, binLabel, i) => {
             let r = this.props.HistoBox.SizeConverter.DataToScreenRange(binLabel.minValue!, binLabel.maxValue!, axis);
-            prims.push(this.drawLine(r.xFrom, r.yFrom, axis == 0 ? 0 : r.xTo - r.xFrom, axis == 0 ? r.yTo - r.yFrom : 0));
-            if (i == labels.length - 1)
-                prims.push(this.drawLine(axis == 0 ? r.xTo : r.xFrom, axis == 0 ? r.yFrom : r.yTo, axis == 0 ? 0 : r.xTo - r.xFrom, axis == 0 ? r.yTo - r.yFrom : 0));
+            prims.push(this.drawLine(r.xFrom, r.yFrom, axis === 0 ? 0 : r.xTo - r.xFrom, axis === 0 ? r.yTo - r.yFrom : 0));
+            if (i === labels.length - 1)
+                prims.push(this.drawLine(axis === 0 ? r.xTo : r.xFrom, axis === 0 ? r.yFrom : r.yTo, axis === 0 ? 0 : r.xTo - r.xFrom, axis === 0 ? r.yTo - r.yFrom : 0));
             return prims;
         }, [] as JSX.Element[]);
     }
@@ -86,8 +86,8 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
             xFrom += width;
             width = -width;
         }
-        let trans2Xpercent = width == 0 ? `1px` : `${(xFrom + width) / this.renderDimension * 100}%`;
-        let trans2Ypercent = height == 0 ? `1px` : `${(yFrom + height) / this.renderDimension * 100}%`;
+        let trans2Xpercent = width === 0 ? `1px` : `${(xFrom + width) / this.renderDimension * 100}%`;
+        let trans2Ypercent = height === 0 ? `1px` : `${(yFrom + height) / this.renderDimension * 100}%`;
         let line = (<div className="histogramboxprimitives-line" style={{ width: trans2Xpercent, height: trans2Ypercent, }} />);
         return this.drawEntity(xFrom, yFrom, line);
     }
@@ -102,10 +102,10 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
         }
         let widthPercent = r.width / this.renderDimension * 100;
         let heightPercent = r.height / this.renderDimension * 100;
-        let rect = (<div className={`histogramboxprimitives-${classExt}`} onPointerDown={(e: React.PointerEvent) => { if (e.button == 0) tapHandler() }}
+        let rect = (<div className={`histogramboxprimitives-${classExt}`} onPointerDown={(e: React.PointerEvent) => { if (e.button === 0) tapHandler() }}
             style={{
-                borderBottomStyle: barAxis == 1 ? "none" : "solid",
-                borderLeftStyle: barAxis == 0 ? "none" : "solid",
+                borderBottomStyle: barAxis === 1 ? "none" : "solid",
+                borderLeftStyle: barAxis === 0 ? "none" : "solid",
                 width: `${widthPercent}%`,
                 height: `${heightPercent}%`,
                 background: color ? `${LABColor.RGBtoHexString(color)}` : ""
diff --git a/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx b/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx
index 93b237deb..dd62e9146 100644
--- a/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx
+++ b/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx
@@ -34,7 +34,7 @@ export class HistogramLabelPrimitives extends React.Component<HistogramPrimitive
         let vb = this.props.HistoBox.VisualBinRanges;
         if (!vb.length || !sc.Initialized)
             return (null);
-        let dim = (axis == 0 ? this.props.HistoBox.PanelWidth : this.props.HistoBox.PanelHeight) / ((axis == 0 && vb[axis] instanceof NominalVisualBinRange) ?
+        let dim = (axis === 0 ? this.props.HistoBox.PanelWidth : this.props.HistoBox.PanelHeight) / ((axis === 0 && vb[axis] instanceof NominalVisualBinRange) ?
             (12 + 5) : //  (<number>FontStyles.AxisLabel.fontSize + 5)));
             sc.MaxLabelSizes[axis].coords[axis] + 5);
 
@@ -47,17 +47,17 @@ export class HistogramLabelPrimitives extends React.Component<HistogramPrimitive
                 let xStart = (axis === 0 ? r.xFrom + (r.xTo - r.xFrom) / 2.0 : r.xFrom - 10 - textWidth);
                 let yStart = (axis === 1 ? r.yFrom - textHeight / 2 : r.yFrom);
 
-                if (axis == 0 && vb[axis] instanceof NominalVisualBinRange) {
+                if (axis === 0 && vb[axis] instanceof NominalVisualBinRange) {
                     let space = (r.xTo - r.xFrom) / sc.RenderDimension * this.props.HistoBox.PanelWidth;
                     xStart += Math.max(textWidth / 2, (1 - textWidth / space) * textWidth / 2) - textHeight / 2;
                 }
 
-                let xPercent = axis == 1 ? `${xStart}px` : `${xStart / sc.RenderDimension * 100}%`
-                let yPercent = axis == 0 ? `${this.props.HistoBox.PanelHeight - sc.BottomOffset - textHeight}px` : `${yStart / sc.RenderDimension * 100}%`
+                let xPercent = axis === 1 ? `${xStart}px` : `${xStart / sc.RenderDimension * 100}%`
+                let yPercent = axis === 0 ? `${this.props.HistoBox.PanelHeight - sc.BottomOffset - textHeight}px` : `${yStart / sc.RenderDimension * 100}%`
 
                 prims.push(
                     <div className="histogramLabelPrimitives-placer" key={DashUtils.GenerateGuid()} style={{ transform: `translate(${xPercent}, ${yPercent})` }}>
-                        <div className="histogramLabelPrimitives-gridlabel" style={{ transform: `rotate(${axis == 0 ? sc.LabelAngle : 0}rad)` }}>
+                        <div className="histogramLabelPrimitives-gridlabel" style={{ transform: `rotate(${axis === 0 ? sc.LabelAngle : 0}rad)` }}>
                             {label}
                         </div>
                     </div>
diff --git a/src/client/northstar/manager/Gateway.ts b/src/client/northstar/manager/Gateway.ts
index 3e72a50ae..ba6fe2ad5 100644
--- a/src/client/northstar/manager/Gateway.ts
+++ b/src/client/northstar/manager/Gateway.ts
@@ -49,7 +49,7 @@ export class Gateway {
 
     public async Compile(data: any): Promise<CompileResults | undefined> {
         const json = await this.MakePostJsonRequest("compile", data);
-        if (json != null) {
+        if (json !== null) {
             const cr = CompileResults.fromJS(json);
             return cr;
         }
@@ -108,7 +108,7 @@ export class Gateway {
 
     public async StartOperation(data: any): Promise<OperationReference | undefined> {
         const json = await this.MakePostJsonRequest("operation", data);
-        if (json != null) {
+        if (json !== null) {
             const or = OperationReference.fromJS(json);
             return or;
         }
@@ -116,7 +116,7 @@ export class Gateway {
 
     public async GetResult(data: any): Promise<Result | undefined> {
         const json = await this.MakePostJsonRequest("result", data);
-        if (json != null) {
+        if (json !== null) {
             const res = Result.fromJS(json);
             return res;
         }
@@ -163,7 +163,7 @@ export class Gateway {
 
     public static ConstructUrl(appendix: string): string {
         let base = Settings.Instance.ServerUrl;
-        if (base.slice(-1) == "/") {
+        if (base.slice(-1) === "/") {
             base = base.slice(0, -1);
         }
         let url = base + "/" + Settings.Instance.ServerApiPath + "/" + appendix;
diff --git a/src/client/northstar/model/ModelExtensions.ts b/src/client/northstar/model/ModelExtensions.ts
index 9fcba7f1c..e4bf77ed8 100644
--- a/src/client/northstar/model/ModelExtensions.ts
+++ b/src/client/northstar/model/ModelExtensions.ts
@@ -3,23 +3,23 @@ import { Utils } from '../utils/Utils'
 
 import { FilterModel } from '../core/filter/FilterModel'
 
-(SingleDimensionAggregateParameters as any).prototype["Equals"] = function (other: Object) {
+(SingleDimensionAggregateParameters as any).prototype.Equals = function (other: Object) {
     if (!Utils.EqualityHelper(this, other)) return false;
     if (!Utils.EqualityHelper((this as SingleDimensionAggregateParameters).attributeParameters!,
         (other as SingleDimensionAggregateParameters).attributeParameters!)) return false;
-    if (!((this as SingleDimensionAggregateParameters).attributeParameters! as any)["Equals"]((other as SingleDimensionAggregateParameters).attributeParameters)) return false;
+    if (!((this as SingleDimensionAggregateParameters).attributeParameters! as any).Equals((other as SingleDimensionAggregateParameters).attributeParameters)) return false;
     return true;
 }
 
 {
-    (AttributeParameters as any).prototype["Equals"] = function (other: AttributeParameters) {
+    (AttributeParameters as any).prototype.Equals = function (other: AttributeParameters) {
         return (<any>this).constructor.name === (<any>other).constructor.name &&
             this.rawName === other.rawName;
     }
 }
 
 {
-    (Solution as any).prototype["Equals"] = function (other: Object) {
+    (Solution as any).prototype.Equals = function (other: Object) {
         if (!Utils.EqualityHelper(this, other)) return false;
         if ((this as Solution).solutionId !== (other as Solution).solutionId) return false;
         return true;
@@ -27,11 +27,11 @@ import { FilterModel } from '../core/filter/FilterModel'
 }
 
 {
-    (MarginAggregateParameters as any).prototype["Equals"] = function (other: Object) {
+    (MarginAggregateParameters as any).prototype.Equals = function (other: Object) {
         if (!Utils.EqualityHelper(this, other)) return false;
         if (!Utils.EqualityHelper((this as SingleDimensionAggregateParameters).attributeParameters!,
             (other as SingleDimensionAggregateParameters).attributeParameters!)) return false;
-        if (!((this as SingleDimensionAggregateParameters).attributeParameters! as any)["Equals"]((other as SingleDimensionAggregateParameters).attributeParameters!)) return false;
+        if (!((this as SingleDimensionAggregateParameters).attributeParameters! as any).Equals((other as SingleDimensionAggregateParameters).attributeParameters!)) return false;
 
         if ((this as MarginAggregateParameters).aggregateFunction !== (other as MarginAggregateParameters).aggregateFunction) return false;
         return true;
@@ -39,7 +39,7 @@ import { FilterModel } from '../core/filter/FilterModel'
 }
 
 {
-    (Brush as any).prototype["Equals"] = function (other: Object) {
+    (Brush as any).prototype.Equals = function (other: Object) {
         if (!Utils.EqualityHelper(this, other)) return false;
         if ((this as Brush).brushEnum !== (other as Brush).brushEnum) return false;
         if ((this as Brush).brushIndex !== (other as Brush).brushIndex) return false;
diff --git a/src/client/northstar/model/ModelHelpers.ts b/src/client/northstar/model/ModelHelpers.ts
index d0711fb69..1a58e6180 100644
--- a/src/client/northstar/model/ModelHelpers.ts
+++ b/src/client/northstar/model/ModelHelpers.ts
@@ -16,7 +16,7 @@ export class ModelHelpers {
     public static CreateAggregateKey(distinctAttributeParameters: AttributeParameters | undefined, atm: AttributeTransformationModel, histogramResult: HistogramResult,
         brushIndex: number, aggParameters?: SingleDimensionAggregateParameters): AggregateKey {
         {
-            if (aggParameters == undefined) {
+            if (aggParameters === undefined) {
                 aggParameters = ModelHelpers.GetAggregateParameter(distinctAttributeParameters, atm);
             }
             else {
@@ -146,7 +146,7 @@ export class ModelHelpers {
     }
 
     public static GetAggregateResult(bin: Bin, aggregateKey: AggregateKey) {
-        if (aggregateKey.aggregateParameterIndex == -1 || aggregateKey.brushIndex == -1) {
+        if (aggregateKey.aggregateParameterIndex === -1 || aggregateKey.brushIndex === -1) {
             return null;
         }
         return bin.aggregateResults![aggregateKey.aggregateParameterIndex! * bin.ySize! + aggregateKey.brushIndex!];
@@ -157,9 +157,9 @@ export class ModelHelpers {
         var ret = new Array<AggregateFunction>();
         ret.push(AggregateFunction.None);
         ret.push(AggregateFunction.Count);
-        if (atm.AttributeModel.DataType == DataType.Float ||
-            atm.AttributeModel.DataType == DataType.Double ||
-            atm.AttributeModel.DataType == DataType.Int) {
+        if (atm.AttributeModel.DataType === DataType.Float ||
+            atm.AttributeModel.DataType === DataType.Double ||
+            atm.AttributeModel.DataType === DataType.Int) {
             ret.push(AggregateFunction.Avg);
             ret.push(AggregateFunction.Sum);
         }
diff --git a/src/client/northstar/model/binRanges/DateTimeVisualBinRange.ts b/src/client/northstar/model/binRanges/DateTimeVisualBinRange.ts
index 9313fb1a7..f5872aa4c 100644
--- a/src/client/northstar/model/binRanges/DateTimeVisualBinRange.ts
+++ b/src/client/northstar/model/binRanges/DateTimeVisualBinRange.ts
@@ -39,24 +39,24 @@ export class DateTimeVisualBinRange extends VisualBinRange {
 
     public GetLabel(value: number): string {
         var dt = DateTimeVisualBinRange.TicksToDate(value);
-        if (this.DataBinRange.step!.dateTimeStepGranularity == DateTimeStepGranularity.Second ||
-            this.DataBinRange.step!.dateTimeStepGranularity == DateTimeStepGranularity.Minute) {
+        if (this.DataBinRange.step!.dateTimeStepGranularity === DateTimeStepGranularity.Second ||
+            this.DataBinRange.step!.dateTimeStepGranularity === DateTimeStepGranularity.Minute) {
             return ("" + this.pad(dt.getMinutes(), 2) + ":" + this.pad(dt.getSeconds(), 2));
             //return dt.ToString("mm:ss");
         }
-        else if (this.DataBinRange.step!.dateTimeStepGranularity == DateTimeStepGranularity.Hour) {
+        else if (this.DataBinRange.step!.dateTimeStepGranularity === DateTimeStepGranularity.Hour) {
             return (this.pad(dt.getHours(), 2) + ":" + this.pad(dt.getMinutes(), 2));
             //return dt.ToString("HH:mm");
         }
-        else if (this.DataBinRange.step!.dateTimeStepGranularity == DateTimeStepGranularity.Day) {
+        else if (this.DataBinRange.step!.dateTimeStepGranularity === DateTimeStepGranularity.Day) {
             return ((dt.getMonth() + 1) + "/" + dt.getDate() + "/" + dt.getFullYear());
             //return dt.ToString("MM/dd/yyyy");
         }
-        else if (this.DataBinRange.step!.dateTimeStepGranularity == DateTimeStepGranularity.Month) {
+        else if (this.DataBinRange.step!.dateTimeStepGranularity === DateTimeStepGranularity.Month) {
             //return dt.ToString("MM/yyyy");
             return ((dt.getMonth() + 1) + "/" + dt.getFullYear());
         }
-        else if (this.DataBinRange.step!.dateTimeStepGranularity == DateTimeStepGranularity.Year) {
+        else if (this.DataBinRange.step!.dateTimeStepGranularity === DateTimeStepGranularity.Year) {
             return "" + dt.getFullYear();
         }
         return "n/a";
@@ -82,22 +82,22 @@ export class DateTimeVisualBinRange extends VisualBinRange {
     public static AddToDateTimeTicks(ticks: number, dateTimeStep: DateTimeStep): number {
         var copiedDate = DateTimeVisualBinRange.TicksToDate(ticks);
         var returnDate: Date = new Date(Date.now());
-        if (dateTimeStep.dateTimeStepGranularity == DateTimeStepGranularity.Second) {
+        if (dateTimeStep.dateTimeStepGranularity === DateTimeStepGranularity.Second) {
             returnDate = new Date(copiedDate.setSeconds(copiedDate.getSeconds() + dateTimeStep.dateTimeStepValue!));
         }
-        else if (dateTimeStep.dateTimeStepGranularity == DateTimeStepGranularity.Minute) {
+        else if (dateTimeStep.dateTimeStepGranularity === DateTimeStepGranularity.Minute) {
             returnDate = new Date(copiedDate.setMinutes(copiedDate.getMinutes() + dateTimeStep.dateTimeStepValue!));
         }
-        else if (dateTimeStep.dateTimeStepGranularity == DateTimeStepGranularity.Hour) {
+        else if (dateTimeStep.dateTimeStepGranularity === DateTimeStepGranularity.Hour) {
             returnDate = new Date(copiedDate.setHours(copiedDate.getHours() + dateTimeStep.dateTimeStepValue!));
         }
-        else if (dateTimeStep.dateTimeStepGranularity == DateTimeStepGranularity.Day) {
+        else if (dateTimeStep.dateTimeStepGranularity === DateTimeStepGranularity.Day) {
             returnDate = new Date(copiedDate.setDate(copiedDate.getDate() + dateTimeStep.dateTimeStepValue!));
         }
-        else if (dateTimeStep.dateTimeStepGranularity == DateTimeStepGranularity.Month) {
+        else if (dateTimeStep.dateTimeStepGranularity === DateTimeStepGranularity.Month) {
             returnDate = new Date(copiedDate.setMonth(copiedDate.getMonth() + dateTimeStep.dateTimeStepValue!));
         }
-        else if (dateTimeStep.dateTimeStepGranularity == DateTimeStepGranularity.Year) {
+        else if (dateTimeStep.dateTimeStepGranularity === DateTimeStepGranularity.Year) {
             returnDate = new Date(copiedDate.setFullYear(copiedDate.getFullYear() + dateTimeStep.dateTimeStepValue!));
         }
         return DateTimeVisualBinRange.DateToTicks(returnDate);
diff --git a/src/client/northstar/model/binRanges/VisualBinRange.ts b/src/client/northstar/model/binRanges/VisualBinRange.ts
index f53008f9a..a0766e494 100644
--- a/src/client/northstar/model/binRanges/VisualBinRange.ts
+++ b/src/client/northstar/model/binRanges/VisualBinRange.ts
@@ -2,10 +2,6 @@ import { BinLabel } from '../../model/idea/idea'
 
 export abstract class VisualBinRange {
 
-    constructor() {
-
-    }
-
     public abstract AddStep(value: number): number;
 
     public abstract GetValueFromIndex(index: number): number;
diff --git a/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts b/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts
index 53d585bb4..63ee61909 100644
--- a/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts
+++ b/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts
@@ -39,8 +39,7 @@ export class VisualBinRangeHelper {
             var aggregateKey = ModelHelpers.CreateAggregateKey(distinctAttributeParameters, attr, histoResult, ModelHelpers.AllBrushIndex(histoResult));
             var minValue = Number.MAX_VALUE;
             var maxValue = Number.MIN_VALUE;
-            for (var b = 0; b < histoResult.brushes!.length; b++) {
-                var brush = histoResult.brushes![b];
+            for (const brush of histoResult.brushes!) {
                 aggregateKey.brushIndex = brush.brushIndex;
                 for (var key in histoResult.bins) {
                     if (histoResult.bins.hasOwnProperty(key)) {
@@ -56,12 +55,12 @@ export class VisualBinRangeHelper {
 
             let visualBinRange = QuantitativeVisualBinRange.Initialize(minValue, maxValue, 10, false);
 
-            if (chartType == ChartType.HorizontalBar || chartType == ChartType.VerticalBar) {
+            if (chartType === ChartType.HorizontalBar || chartType === ChartType.VerticalBar) {
                 visualBinRange = QuantitativeVisualBinRange.Initialize(Math.min(0, minValue),
                     Math.max(0, (visualBinRange as QuantitativeVisualBinRange).DataBinRange.maxValue!),
                     SETTINGS_X_BINS, false);
             }
-            else if (chartType == ChartType.SinglePoint) {
+            else if (chartType === ChartType.SinglePoint) {
                 visualBinRange = QuantitativeVisualBinRange.Initialize(Math.min(0, minValue), Math.max(0, maxValue),
                     SETTINGS_X_BINS, false);
             }
diff --git a/src/client/northstar/model/idea/MetricTypeMapping.ts b/src/client/northstar/model/idea/MetricTypeMapping.ts
index 11e0c871a..e9759cf16 100644
--- a/src/client/northstar/model/idea/MetricTypeMapping.ts
+++ b/src/client/northstar/model/idea/MetricTypeMapping.ts
@@ -5,20 +5,20 @@ import { Dictionary } from 'typescript-collections';
 export class MetricTypeMapping {
 
     public static GetMetricInterpretation(metricType: MetricType): MetricInterpretation {
-        if (metricType == MetricType.Accuracy ||
-            metricType == MetricType.F1 ||
-            metricType == MetricType.F1Macro ||
-            metricType == MetricType.F1Micro ||
-            metricType == MetricType.JaccardSimilarityScore ||
-            metricType == MetricType.ObjectDetectionAveragePrecision ||
-            metricType == MetricType.Precision ||
-            metricType == MetricType.PrecisionAtTopK ||
-            metricType == MetricType.NormalizedMutualInformation ||
-            metricType == MetricType.Recall ||
-            metricType == MetricType.RocAucMacro ||
-            metricType == MetricType.RocAuc ||
-            metricType == MetricType.RocAucMicro ||
-            metricType == MetricType.RSquared) {
+        if (metricType === MetricType.Accuracy ||
+            metricType === MetricType.F1 ||
+            metricType === MetricType.F1Macro ||
+            metricType === MetricType.F1Micro ||
+            metricType === MetricType.JaccardSimilarityScore ||
+            metricType === MetricType.ObjectDetectionAveragePrecision ||
+            metricType === MetricType.Precision ||
+            metricType === MetricType.PrecisionAtTopK ||
+            metricType === MetricType.NormalizedMutualInformation ||
+            metricType === MetricType.Recall ||
+            metricType === MetricType.RocAucMacro ||
+            metricType === MetricType.RocAuc ||
+            metricType === MetricType.RocAucMicro ||
+            metricType === MetricType.RSquared) {
             return MetricInterpretation.HigherIsBetter;
         }
         return MetricInterpretation.LowerIsBetter;
diff --git a/src/client/northstar/operations/BaseOperation.ts b/src/client/northstar/operations/BaseOperation.ts
index f545b2c58..a14337763 100644
--- a/src/client/northstar/operations/BaseOperation.ts
+++ b/src/client/northstar/operations/BaseOperation.ts
@@ -29,11 +29,11 @@ export abstract class BaseOperation {
         // let filterModels: FilterModel[] = [];
         // return FilterModel.GetFilterModelsRecursive(this, new Set<GraphNode<BaseOperationViewModel, FilterLinkViewModel>>(), filterModels, true)
         // if (this.OverridingFilters.length > 0) {
-        //     return "(" + this.OverridingFilters.filter(fm => fm != null).map(fm => fm.ToPythonString()).join(" || ") + ")";
+        //     return "(" + this.OverridingFilters.filter(fm => fm !== null).map(fm => fm.ToPythonString()).join(" || ") + ")";
         // }
         // let rdg = MainManager.Instance.MainViewModel.FilterReverseDependencyGraph;
         // let sliceModel = this.TypedViewModel.IncomingSliceModel;
-        // if (sliceModel != null && sliceModel.Source != null && instanceOfIBaseFilterProvider(sliceModel.Source) && rdg.has(sliceModel.Source)) {
+        // if (sliceModel !== null && sliceModel.Source !== null && instanceOfIBaseFilterProvider(sliceModel.Source) && rdg.has(sliceModel.Source)) {
         //     let filterModels = sliceModel.Source.FilterModels.map(f => f);
         //     return FilterModel.GetFilterModelsRecursive(rdg.get(sliceModel.Source), new Set<GraphNode<BaseOperationViewModel, FilterLinkViewModel>>(), filterModels, false);
         // }
@@ -99,8 +99,8 @@ export abstract class BaseOperation {
                     if (result instanceof ErrorResult) {
                         throw new Error((result as ErrorResult).message);
                     }
-                    if (this.RequestSalt == pollPromise.RequestSalt) {
-                        if (result && (!this.Result || this.Result.progress != result.progress)) {
+                    if (this.RequestSalt === pollPromise.RequestSalt) {
+                        if (result && (!this.Result || this.Result.progress !== result.progress)) {
                             /*if (operationViewModel.Result !== null && operationViewModel.Result !== undefined) {
                                 let t1 = performance.now();
                                 console.log((t1 - start) + " milliseconds.");
diff --git a/src/client/northstar/operations/HistogramOperation.ts b/src/client/northstar/operations/HistogramOperation.ts
index e63de1632..7f48d1629 100644
--- a/src/client/northstar/operations/HistogramOperation.ts
+++ b/src/client/northstar/operations/HistogramOperation.ts
@@ -67,7 +67,7 @@ export class HistogramOperation extends BaseOperation implements IBaseFilterCons
         let brushes: string[] = [];
         this.BrushLinks.map(brushLink => {
             let brushHistogram = brushLink.b.GetT(KeyStore.Data, HistogramField);
-            if (brushHistogram && brushHistogram != FieldWaiting) {
+            if (brushHistogram && brushHistogram !== FieldWaiting) {
                 let filterModels: FilterModel[] = [];
                 brushes.push(FilterModel.GetFilterModelsRecursive(brushHistogram.Data, new Set<IBaseFilterProvider>(), filterModels, false));
             }
diff --git a/src/client/northstar/utils/ArrayUtil.ts b/src/client/northstar/utils/ArrayUtil.ts
index f35c98317..a52ca6d96 100644
--- a/src/client/northstar/utils/ArrayUtil.ts
+++ b/src/client/northstar/utils/ArrayUtil.ts
@@ -7,14 +7,14 @@ export class ArrayUtil {
             return false;
         }
         let isComplex = typeof arr1[0] === "object";
-        for (let i = 0; i < arr1.length; i++) {
-            if (isComplex && "Equals" in arr1[i]) {
-                if (arr1[i].Equals(arr2)) {
+        for (const ele of arr1) {
+            if (isComplex && "Equals" in ele) {
+                if (ele.Equals(arr2)) {
                     return true;
                 }
             }
             else {
-                if (arr1[i] === arr2) {
+                if (ele === arr2) {
                     return true;
                 }
             }
@@ -63,9 +63,9 @@ export class ArrayUtil {
 
     public static Distinct(arr: any[]): any[] {
         let ret = [];
-        for (let i = 0; i < arr.length; i++) {
-            if (!ArrayUtil.Contains(ret, arr[i])) {
-                ret.push(arr[i]);
+        for (const ele of arr) {
+            if (!ArrayUtil.Contains(ret, ele)) {
+                ret.push(ele);
             }
         }
         return ret;
diff --git a/src/client/northstar/utils/GeometryUtil.ts b/src/client/northstar/utils/GeometryUtil.ts
index d5f3ba631..6d8acea20 100644
--- a/src/client/northstar/utils/GeometryUtil.ts
+++ b/src/client/northstar/utils/GeometryUtil.ts
@@ -85,7 +85,7 @@ export class GeometryUtil {
     //         var xi = vs[i].x, yi = vs[i].y;
     //         var xj = vs[j].x, yj = vs[j].y;
 
-    //         var intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
+    //         var intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
     //         if (intersect) 
     //            inside = !inside;
     //     }
diff --git a/src/client/northstar/utils/MathUtil.ts b/src/client/northstar/utils/MathUtil.ts
index bb7e73871..7aa255096 100644
--- a/src/client/northstar/utils/MathUtil.ts
+++ b/src/client/northstar/utils/MathUtil.ts
@@ -93,7 +93,7 @@ export class MathUtil {
     public static DistToLineSegment(v: PIXIPoint, w: PIXIPoint, p: PIXIPoint) {
         // Return minimum distance between line segment vw and point p
         var l2 = MathUtil.DistSquared(v, w);  // i.e. |w-v|^2 -  avoid a sqrt
-        if (l2 == 0.0) return MathUtil.Dist(p, v);   // v == w case
+        if (l2 === 0.0) return MathUtil.Dist(p, v);   // v === w case
         // Consider the line extending the segment, parameterized as v + t (w - v).
         // We find projection of point p onto the line. 
         // It falls where t = [(p-v) . (w-v)] / |w-v|^2
@@ -117,7 +117,7 @@ export class MathUtil {
         var b2 = ps2.x - pe2.x;
 
         var delta = a1 * b2 - a2 * b1;
-        if (delta == 0) {
+        if (delta === 0) {
             return undefined;
         }
         var c2 = a2 * ps2.x + b2 * ps2.y;
@@ -147,19 +147,19 @@ export class MathUtil {
         var ret = new Array<PIXIPoint>();
         var dist = this.Dist(lineFrom, lineTo)
         var inter = this.LineSegmentIntersection(lineFrom, lineTo, r1, r2);
-        if (inter != null && this.PointInPIXIRectangle(inter, rect) &&
+        if (inter && this.PointInPIXIRectangle(inter, rect) &&
             this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist)
             ret.push(inter);
         inter = this.LineSegmentIntersection(lineFrom, lineTo, r2, r3);
-        if (inter != null && this.PointInPIXIRectangle(inter, rect) &&
+        if (inter && this.PointInPIXIRectangle(inter, rect) &&
             this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist)
             ret.push(inter);
         inter = this.LineSegmentIntersection(lineFrom, lineTo, r3, r4);
-        if (inter != null && this.PointInPIXIRectangle(inter, rect) &&
+        if (inter && this.PointInPIXIRectangle(inter, rect) &&
             this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist)
             ret.push(inter);
         inter = this.LineSegmentIntersection(lineFrom, lineTo, r4, r1);
-        if (inter != null && this.PointInPIXIRectangle(inter, rect) &&
+        if (inter && this.PointInPIXIRectangle(inter, rect) &&
             this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist)
             ret.push(inter);
         return ret;
diff --git a/src/client/northstar/utils/SizeConverter.ts b/src/client/northstar/utils/SizeConverter.ts
index bb91ed4a7..b5c4a16ab 100644
--- a/src/client/northstar/utils/SizeConverter.ts
+++ b/src/client/northstar/utils/SizeConverter.ts
@@ -54,7 +54,7 @@ export class SizeConverter {
     }
 
     public DataToScreenNormalizedRange(dataValue: number, normalization: number, axis: number, binBrushMaxAxis: number) {
-        var value = normalization != 1 - axis || binBrushMaxAxis == 0 ? dataValue : (dataValue - 0) / (binBrushMaxAxis - 0) * this.DataRanges[axis];
+        var value = normalization !== 1 - axis || binBrushMaxAxis === 0 ? dataValue : (dataValue - 0) / (binBrushMaxAxis - 0) * this.DataRanges[axis];
         var from = this.DataToScreenCoord(Math.min(0, value), axis);
         var to = this.DataToScreenCoord(Math.max(0, value), axis);
         return [from, value, to];
@@ -85,7 +85,7 @@ export class SizeConverter {
         return flip ? (this.RenderDimension) - retY : retY;
     }
     public DataToScreenCoord(v: number, axis: number) {
-        if (axis == 0)
+        if (axis === 0)
             return this.DataToScreenX(v);
         return this.DataToScreenY(v);
     }
diff --git a/src/client/northstar/utils/Utils.ts b/src/client/northstar/utils/Utils.ts
index b35dce820..c96b4cbd9 100644
--- a/src/client/northstar/utils/Utils.ts
+++ b/src/client/northstar/utils/Utils.ts
@@ -27,17 +27,17 @@ export class Utils {
 
     public static isBaseBrushable<T>(obj: Object): obj is IBaseBrushable<T> {
         let typed = <IBaseBrushable<T>>obj;
-        return typed != null && typed.BrusherModels !== undefined;
+        return typed !== null && typed.BrusherModels !== undefined;
     }
 
     public static isBaseFilterProvider(obj: Object): obj is IBaseFilterProvider {
         let typed = <IBaseFilterProvider>obj;
-        return typed != null && typed.FilterModels !== undefined;
+        return typed !== null && typed.FilterModels !== undefined;
     }
 
     public static isBaseFilterConsumer(obj: Object): obj is IBaseFilterConsumer {
         let typed = <IBaseFilterConsumer>obj;
-        return typed != null && typed.FilterOperand !== undefined;
+        return typed !== null && typed.FilterOperand !== undefined;
     }
 
     public static EncodeQueryData(data: any): string {
@@ -63,9 +63,9 @@ export class Utils {
     public static GetQueryVariable(variable: string) {
         let query = window.location.search.substring(1);
         let vars = query.split("&");
-        for (let i = 0; i < vars.length; i++) {
-            let pair = vars[i].split("=");
-            if (decodeURIComponent(pair[0]) == variable) {
+        for (const variable of vars) {
+            let pair = variable.split("=");
+            if (decodeURIComponent(pair[0]) === variable) {
                 return decodeURIComponent(pair[1]);
             }
         }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 11bf24505..1bb00a3fc 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -60,10 +60,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             if (text[0] === '#') {
                 let command = text.slice(1, text.length);
                 this._fieldKey = new Key(command)
-                // if (command == "Title" || command == "title") {
+                // if (command === "Title" || command === "title") {
                 //     this._fieldKey = KeyStore.Title;
                 // }
-                // else if (command == "Width" || command == "width") {
+                // else if (command === "Width" || command === "width") {
                 //     this._fieldKey = KeyStore.Width;
                 // }
                 this._title = "changed"
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 33759d38a..74f868c11 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -62,7 +62,7 @@ export class Main extends React.Component {
 
     @computed private get mainContainer(): Document | undefined {
         let doc = this.userDocument.GetT(KeyStore.ActiveWorkspace, Document);
-        return doc == FieldWaiting ? undefined : doc;
+        return doc === FieldWaiting ? undefined : doc;
     }
 
     private set mainContainer(doc: Document | undefined) {
@@ -84,7 +84,7 @@ export class Main extends React.Component {
         configure({ enforceActions: "observed" });
         if (window.location.pathname !== RouteStore.home) {
             let pathname = window.location.pathname.split("/");
-            if (pathname.length > 1 && pathname[pathname.length - 2] == 'doc') {
+            if (pathname.length > 1 && pathname[pathname.length - 2] === 'doc') {
                 CurrentUserUtils.MainDocId = pathname[pathname.length - 1];
             }
         };
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index ee752a2ca..7a5ab6e3c 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -65,13 +65,13 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
 
     createsCycle(documentToAdd: Document, containerDocument: Document): boolean {
         let data = documentToAdd.GetList<Document>(KeyStore.Data, []);
-        for (let i = 0; i < data.length; i++) {
-            if (this.createsCycle(data[i], containerDocument))
+        for (const doc of data) {
+            if (this.createsCycle(doc, containerDocument))
                 return true;
         }
         let annots = documentToAdd.GetList<Document>(KeyStore.Annotations, []);
-        for (let i = 0; i < annots.length; i++) {
-            if (this.createsCycle(annots[i], containerDocument))
+        for (const annot of annots) {
+            if (this.createsCycle(annot, containerDocument))
                 return true;
         }
         for (let containerProto: FieldValue<Document> = containerDocument; containerProto && containerProto !== FieldWaiting; containerProto = containerProto.GetPrototype()) {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 1b0ad0bee..2380709d2 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -101,12 +101,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             newRow.addChild(newContentItem, undefined, true);
             newRow.addChild(collayout, 0, true);
 
-            collayout.config["width"] = 50;
-            newContentItem.config["width"] = 50;
+            collayout.config.width = 50;
+            newContentItem.config.width = 50;
         }
         if (minimize) {
-            newContentItem.config["width"] = 10;
-            newContentItem.config["height"] = 10;
+            newContentItem.config.width = 10;
+            newContentItem.config.height = 10;
         }
         newContentItem.callDownwards('_$init');
         this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]);
@@ -124,7 +124,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                 this._goldenLayout = new GoldenLayout(JSON.parse(config));
             }
             else {
-                if (config == JSON.stringify(this._goldenLayout.toConfig()))
+                if (config === JSON.stringify(this._goldenLayout.toConfig()))
                     return;
                 try {
                     this._goldenLayout.unbind('itemDropped', this.itemDropped);
@@ -154,7 +154,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             reaction(
                 () => this.props.Document.GetText(KeyStore.Data, ""),
                 () => {
-                    if (!this._goldenLayout || this._ignoreStateChange != JSON.stringify(this._goldenLayout.toConfig())) {
+                    if (!this._goldenLayout || this._ignoreStateChange !== JSON.stringify(this._goldenLayout.toConfig())) {
                         setTimeout(() => this.setupGoldenLayout(), 1);
                     }
                     this._ignoreStateChange = "";
@@ -193,7 +193,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     @action
     onPointerDown = (e: React.PointerEvent): void => {
         var className = (e.target as any).className;
-        if ((className == "lm_title" || className == "lm_tab lm_active") && (e.ctrlKey || e.altKey)) {
+        if ((className === "lm_title" || className === "lm_tab lm_active") && (e.ctrlKey || e.altKey)) {
             e.stopPropagation();
             e.preventDefault();
             let docid = (e.target as any).DashDocId;
@@ -209,7 +209,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                         })
             }));
         }
-        if (className == "lm_drag_handle" || className == "lm_close" || className == "lm_maximise" || className == "lm_minimise" || className == "lm_close_tab") {
+        if (className === "lm_drag_handle" || className === "lm_close" || className === "lm_maximise" || className === "lm_minimise" || className === "lm_close_tab") {
             this._flush = true;
         }
         if (this.props.active()) {
@@ -227,12 +227,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         this.stateChanged();
     }
     tabCreated = (tab: any) => {
-        if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type != "stack") {
-            if (tab.titleElement[0].textContent.indexOf("-waiting") != -1) {
+        if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") {
+            if (tab.titleElement[0].textContent.indexOf("-waiting") !== -1) {
                 Server.GetField(tab.contentItem.config.props.documentId, action((f: Opt<Field>) => {
-                    if (f != undefined && f instanceof Document) {
+                    if (f !== undefined && f instanceof Document) {
                         f.GetTAsync(KeyStore.Title, TextField, (tfield) => {
-                            if (tfield != undefined) {
+                            if (tfield !== undefined) {
                                 tab.titleElement[0].textContent = f.Title;
                             }
                         })
@@ -296,9 +296,9 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         Server.GetField(this.props.documentId, action((f: Opt<Field>) => this._document = f as Document));
     }
 
-    private _nativeWidth = () => { return this._document!.GetNumber(KeyStore.NativeWidth, this._panelWidth); }
-    private _nativeHeight = () => { return this._document!.GetNumber(KeyStore.NativeHeight, this._panelHeight); }
-    private _contentScaling = () => { return this._panelWidth / (this._nativeWidth() ? this._nativeWidth() : this._panelWidth); }
+    private _nativeWidth = () => this._document!.GetNumber(KeyStore.NativeWidth, this._panelWidth)
+    private _nativeHeight = () => this._document!.GetNumber(KeyStore.NativeHeight, this._panelHeight)
+    private _contentScaling = () => this._panelWidth / (this._nativeWidth() ? this._nativeWidth() : this._panelWidth)
 
     ScreenToLocalTransform = () => {
         let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current!);
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 14ed70b8c..1f2e71f6e 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -33,7 +33,7 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
     }
 
     onContextMenu = (e: React.MouseEvent): void => {
-        if (!e.isPropagationStopped() && this.props.Document.Id != "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+        if (!e.isPropagationStopped() && this.props.Document.Id !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
             ContextMenu.Instance.addItem({ description: "PDFOptions", event: () => { } });
         }
     }
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 9dade94f1..3817d5333 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -151,7 +151,7 @@ export class CollectionSchemaView extends CollectionViewBase {
             }),
             style: {
                 background: rowInfo.index === this._selectedIndex ? "lightGray" : "white",
-                //color: rowInfo.index == this._selectedIndex ? "white" : "black"
+                //color: rowInfo.index === this._selectedIndex ? "white" : "black"
             }
         };
     }
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 3ab6db5ef..afadfeefb 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -61,7 +61,7 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
     @action
     updateTimecode = () => {
         if (this._player) {
-            if ((this._player as any).AHackBecauseSomethingResetsTheVideoToZero != -1) {
+            if ((this._player as any).AHackBecauseSomethingResetsTheVideoToZero !== -1) {
                 this._player.currentTime = (this._player as any).AHackBecauseSomethingResetsTheVideoToZero;
                 (this._player as any).AHackBecauseSomethingResetsTheVideoToZero = -1;
             } else {
@@ -103,7 +103,7 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
     }
 
     onContextMenu = (e: React.MouseEvent): void => {
-        if (!e.isPropagationStopped() && this.props.Document.Id != "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+        if (!e.isPropagationStopped() && this.props.Document.Id !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
             ContextMenu.Instance.addItem({ description: "VideoOptions", event: () => { } });
         }
     }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 5b4caf58d..1b0cfd57b 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -28,7 +28,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
     }
 
     onContextMenu = (e: React.MouseEvent): void => {
-        if (!e.isPropagationStopped() && this.props.Document.Id != CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+        if (!e.isPropagationStopped() && this.props.Document.Id !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
             ContextMenu.Instance.addItem({ description: "Freeform", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform) })
             ContextMenu.Instance.addItem({ description: "Schema", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema) })
             ContextMenu.Instance.addItem({ description: "Treeview", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Tree) })
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index b4784ae9c..3af3c5d63 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -91,11 +91,11 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
             let sourceDoc: Document = de.data.linkSourceDocumentView.props.Document;
             if (sourceDoc) runInAction(() => {
                 let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document)
-                if (srcTarg && srcTarg != FieldWaiting) {
+                if (srcTarg && srcTarg !== FieldWaiting) {
                     let linkDocs = srcTarg.GetList(KeyStore.LinkedToDocs, [] as Document[]);
                     linkDocs.map(linkDoc => {
                         let targDoc = linkDoc.GetT(KeyStore.LinkedToDocs, Document);
-                        if (targDoc && targDoc != FieldWaiting) {
+                        if (targDoc && targDoc !== FieldWaiting) {
                             let dropdoc = targDoc.MakeDelegate();
                             de.data.droppedDocuments.push(dropdoc);
                             this.props.addDocument(dropdoc, false);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 19227f5e0..52e40d64a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -176,8 +176,8 @@ export class CollectionFreeFormView extends CollectionViewBase {
             e.stopPropagation();
             e.preventDefault();
         } else {
-            // if (modes[e.deltaMode] == 'pixels') coefficient = 50;
-            // else if (modes[e.deltaMode] == 'lines') coefficient = 1000; // This should correspond to line-height??
+            // if (modes[e.deltaMode] === 'pixels') coefficient = 50;
+            // else if (modes[e.deltaMode] === 'lines') coefficient = 1000; // This should correspond to line-height??
             let transform = this.getTransform();
 
             let deltaScale = (1 - (e.deltaY / coefficient));
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 7022fd972..704db1d4a 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -48,7 +48,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
 
     @action
     onPointerDown = (e: React.PointerEvent): void => {
-        if (e.buttons == 1 && !e.altKey && !e.metaKey && this.props.container.props.active()) {
+        if (e.buttons === 1 && !e.altKey && !e.metaKey && this.props.container.props.active()) {
             this._downX = this._lastX = e.pageX;
             this._downY = this._lastY = e.pageY;
             this._used = false;
@@ -63,7 +63,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         this._lastX = e.pageX;
         this._lastY = e.pageY;
         if (!e.cancelBubble) {
-            if (!this._used && e.buttons == 1 && !e.altKey && !e.metaKey &&
+            if (!this._used && e.buttons === 1 && !e.altKey && !e.metaKey &&
                 (Math.abs(this._lastX - this._downX) > MarqueeView.DRAG_THRESHOLD || Math.abs(this._lastY - this._downY) > MarqueeView.DRAG_THRESHOLD)) {
                 this._visible = true;
             }
@@ -99,15 +99,15 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
 
     @action
     marqueeCommand = (e: KeyboardEvent) => {
-        if (e.key == "Backspace" || e.key == "Delete") {
+        if (e.key === "Backspace" || e.key === "Delete") {
             this.marqueeSelect().map(d => this.props.removeDocument(d));
             let ink = this.props.container.props.Document.GetT(KeyStore.Ink, InkField);
-            if (ink && ink != FieldWaiting) {
+            if (ink && ink !== FieldWaiting) {
                 this.marqueeInkDelete(ink.Data);
             }
             this.cleanupInteractions();
         }
-        if (e.key == "c") {
+        if (e.key === "c") {
             let bounds = this.Bounds;
             let selected = this.marqueeSelect().map(d => {
                 this.props.removeDocument(d);
@@ -118,7 +118,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 return d;
             });
             let ink = this.props.container.props.Document.GetT(KeyStore.Ink, InkField);
-            let inkData = ink && ink != FieldWaiting ? ink.Data : undefined;
+            let inkData = ink && ink !== FieldWaiting ? ink.Data : undefined;
             //setTimeout(() => {
             let newCollection = Documents.FreeformDocument(selected, {
                 x: bounds.left,
diff --git a/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx b/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx
index 93c98f7b0..599461f85 100644
--- a/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx
+++ b/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx
@@ -33,7 +33,7 @@ export class PreviewCursor extends React.Component<PreviewCursorProps>  {
 
     @action
     onPointerDown = (e: React.PointerEvent) => {
-        if (e.button == 0 && this.props.container.props.active()) {
+        if (e.button === 0 && this.props.container.props.active()) {
             document.removeEventListener("keypress", this.onKeyPress, false);
             this._showOnUp = true;
             this.DownX = e.pageX;
diff --git a/src/client/views/nodes/Annotation.tsx b/src/client/views/nodes/Annotation.tsx
index a2c7be1a8..e4f17940c 100644
--- a/src/client/views/nodes/Annotation.tsx
+++ b/src/client/views/nodes/Annotation.tsx
@@ -4,13 +4,13 @@ import { observer } from "mobx-react"
 import { observable, action } from 'mobx';
 import 'react-pdf/dist/Page/AnnotationLayer.css'
 
-interface IProps{
+interface IProps {
     Span: HTMLSpanElement;
-    X: number; 
-    Y: number; 
-    Highlights: any[]; 
-    Annotations: any[]; 
-    CurrAnno: any[]; 
+    X: number;
+    Y: number;
+    Highlights: any[];
+    Annotations: any[];
+    CurrAnno: any[];
 
 }
 
@@ -23,95 +23,95 @@ interface IProps{
  */
 @observer
 export class Annotation extends React.Component<IProps> {
-    
+
     /**
      * changes color of the span (highlighted section)
      */
-    onColorChange = (e:React.PointerEvent) => {
-        if (e.currentTarget.innerHTML == "r"){
+    onColorChange = (e: React.PointerEvent) => {
+        if (e.currentTarget.innerHTML === "r") {
             this.props.Span.style.backgroundColor = "rgba(255,0,0, 0.3)"
-        } else if (e.currentTarget.innerHTML == "b"){
+        } else if (e.currentTarget.innerHTML === "b") {
             this.props.Span.style.backgroundColor = "rgba(0,255, 255, 0.3)"
-        } else if (e.currentTarget.innerHTML == "y"){
+        } else if (e.currentTarget.innerHTML === "y") {
             this.props.Span.style.backgroundColor = "rgba(255,255,0, 0.3)"
-        } else if (e.currentTarget.innerHTML == "g"){
+        } else if (e.currentTarget.innerHTML === "g") {
             this.props.Span.style.backgroundColor = "rgba(76, 175, 80, 0.3)"
         }
-      
+
     }
 
     /**
      * removes the highlighted span. Supposed to remove Annotation too, but I don't know how to unmount this
      */
     @action
-    onRemove = (e:any) => {
-        let index:number = -1; 
+    onRemove = (e: any) => {
+        let index: number = -1;
         //finding the highlight in the highlight array 
         this.props.Highlights.forEach((e) => {
-            for (let i = 0; i < e.spans.length; i++){
-                if (e.spans[i] == this.props.Span){
-                    index = this.props.Highlights.indexOf(e); 
-                    this.props.Highlights.splice(index, 1); 
+            for (const span of e.spans) {
+                if (span === this.props.Span) {
+                    index = this.props.Highlights.indexOf(e);
+                    this.props.Highlights.splice(index, 1);
                 }
             }
         })
 
         //removing from CurrAnno and Annotation array 
-        this.props.Annotations.splice(index, 1); 
+        this.props.Annotations.splice(index, 1);
         this.props.CurrAnno.pop()
-        
+
         //removing span from div
-        if(this.props.Span.parentElement){
-            let nodesArray = this.props.Span.parentElement.childNodes; 
+        if (this.props.Span.parentElement) {
+            let nodesArray = this.props.Span.parentElement.childNodes;
             nodesArray.forEach((e) => {
-                if (e == this.props.Span){
-                    if (this.props.Span.parentElement){   
+                if (e === this.props.Span) {
+                    if (this.props.Span.parentElement) {
                         this.props.Highlights.forEach((item) => {
-                            if (item == e){
-                                item.remove(); 
+                            if (item === e) {
+                                item.remove();
                             }
                         })
-                        e.remove();                   
+                        e.remove();
                     }
                 }
-            }) 
+            })
         }
-    
-        
+
+
     }
 
     render() {
         return (
-            <div 
-            style = {{
-            position: "absolute",
-            top: "20px", 
-            left: "0px",  
-            zIndex: 1, 
-            transform: `translate(${this.props.X}px, ${this.props.Y}px)`,
-            
-            }}>
-                <div style = {{width:"200px", height:"50px", backgroundColor: "orange"}}>
+            <div
+                style={{
+                    position: "absolute",
+                    top: "20px",
+                    left: "0px",
+                    zIndex: 1,
+                    transform: `translate(${this.props.X}px, ${this.props.Y}px)`,
+
+                }}>
+                <div style={{ width: "200px", height: "50px", backgroundColor: "orange" }}>
                     <button
-                    style = {{borderRadius: "25px", width:"25%", height:"100%"}}
-                    onClick = {this.onRemove}
+                        style={{ borderRadius: "25px", width: "25%", height: "100%" }}
+                        onClick={this.onRemove}
                     >x</button>
-                    <div style = {{width:"75%", height: "100%" , display:"inline-block"}}>
-                        <button onPointerDown = {this.onColorChange} style = {{backgroundColor:"red", borderRadius:"50%", color: "transparent"}}>r</button>
-                        <button onPointerDown = {this.onColorChange} style = {{backgroundColor:"blue", borderRadius:"50%", color: "transparent"}}>b</button>
-                        <button onPointerDown = {this.onColorChange} style = {{backgroundColor:"yellow", borderRadius:"50%", color:"transparent"}}>y</button>
-                        <button onPointerDown = {this.onColorChange} style = {{backgroundColor:"green", borderRadius:"50%", color:"transparent"}}>g</button>
+                    <div style={{ width: "75%", height: "100%", display: "inline-block" }}>
+                        <button onPointerDown={this.onColorChange} style={{ backgroundColor: "red", borderRadius: "50%", color: "transparent" }}>r</button>
+                        <button onPointerDown={this.onColorChange} style={{ backgroundColor: "blue", borderRadius: "50%", color: "transparent" }}>b</button>
+                        <button onPointerDown={this.onColorChange} style={{ backgroundColor: "yellow", borderRadius: "50%", color: "transparent" }}>y</button>
+                        <button onPointerDown={this.onColorChange} style={{ backgroundColor: "green", borderRadius: "50%", color: "transparent" }}>g</button>
                     </div>
-                    
+
                 </div>
-                <div  style = {{width:"200px", height:"200"}}>
-                    <textarea style = {{width: "100%", height: "100%"}}
-                    defaultValue = "Enter Text Here..."
-                   
+                <div style={{ width: "200px", height: "200" }}>
+                    <textarea style={{ width: "100%", height: "100%" }}
+                        defaultValue="Enter Text Here..."
+
                     ></textarea>
                 </div>
             </div>
-          
+
         );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 0ce991485..1bd934c25 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -29,7 +29,7 @@ export class AudioBox extends React.Component<FieldViewProps> {
 
     render() {
         let field = this.props.Document.Get(this.props.fieldKey)
-        let path = field == FieldWaiting ? "http://techslides.com/demos/samples/sample.mp3" :
+        let path = field === FieldWaiting ? "http://techslides.com/demos/samples/sample.mp3" :
             field instanceof AudioField ? field.Data.href : "http://techslides.com/demos/samples/sample.mp3";
 
         return (
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 1c9155dce..34cc326aa 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -77,7 +77,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         }
         for (const key of this.layoutFields) {
             let field = this.props.Document.Get(key);
-            bindings[key.Name] = field && field != FieldWaiting ? field.GetValue() : field;
+            bindings[key.Name] = field && field !== FieldWaiting ? field.GetValue() : field;
         }
         return bindings;
     }
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 9b067aeeb..9958a7c5a 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -32,10 +32,10 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
 
     @action
     onEnterKey = (e: React.KeyboardEvent): void => {
-        if (e.key == 'Enter') {
+        if (e.key === 'Enter') {
             if (this._keyInput && this._valueInput) {
                 let doc = this.props.Document.GetT(KeyStore.Data, Document);
-                if (!doc || doc == FieldWaiting) {
+                if (!doc || doc === FieldWaiting) {
                     return
                 }
                 let realDoc = doc;
@@ -70,7 +70,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
 
     createTable = () => {
         let doc = this.props.Document.GetT(KeyStore.Data, Document);
-        if (!doc || doc == FieldWaiting) {
+        if (!doc || doc === FieldWaiting) {
             return <tr><td>Loading...</td></tr>
         }
         let realDoc = doc;
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index e81f8fec7..4791d6029 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -49,7 +49,7 @@ export class LinkBox extends React.Component<Props> {
                 } else if (contextDoc instanceof Document) {
                     this.props.pairedDoc.GetTAsync(KeyStore.Page, NumberField).then((pfield: any) => {
                         contextDoc.GetTAsync(KeyStore.CurPage, NumberField).then((cfield: any) => {
-                            if (pfield != cfield)
+                            if (pfield !== cfield)
                                 contextDoc.SetNumber(KeyStore.CurPage, pfield.Data);
                             let contextView = DocumentManager.Instance.getDocumentView(contextDoc);
                             if (contextView) {
diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx
index 5eeb40772..6c2d24630 100644
--- a/src/client/views/nodes/LinkMenu.tsx
+++ b/src/client/views/nodes/LinkMenu.tsx
@@ -24,7 +24,7 @@ export class LinkMenu extends React.Component<Props> {
     renderLinkItems(links: Document[], key: Key, type: string) {
         return links.map(link => {
             let doc = link.GetT(key, Document);
-            if (doc && doc != FieldWaiting) {
+            if (doc && doc !== FieldWaiting) {
                 return <LinkBox key={doc.Id} linkDoc={link} linkName={link.Title} pairedDoc={doc} showEditor={action(() => this._editingLink = link)} type={type} />
             }
         })
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index b4590df34..a08b320e8 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -34,7 +34,7 @@ export class VideoBox extends React.Component<FieldViewProps> {
             var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
             var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
             var newNativeHeight = nativeWidth * r.entry.height / r.entry.width;
-            if (!nativeHeight && newNativeHeight != nativeHeight && !isNaN(newNativeHeight)) {
+            if (!nativeHeight && newNativeHeight !== nativeHeight && !isNaN(newNativeHeight)) {
                 this.props.Document.SetNumber(KeyStore.Height, newNativeHeight / nativeWidth * this.props.Document.GetNumber(KeyStore.Width, 0));
                 this.props.Document.SetNumber(KeyStore.NativeHeight, newNativeHeight);
             }
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 92e2fabd4..c1d389001 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -20,7 +20,7 @@ export class WebBox extends React.Component<FieldViewProps> {
 
     render() {
         let field = this.props.Document.Get(this.props.fieldKey);
-        let path = field == FieldWaiting ? "https://image.flaticon.com/icons/svg/66/66163.svg" :
+        let path = field === FieldWaiting ? "https://image.flaticon.com/icons/svg/66/66163.svg" :
             field instanceof WebField ? field.Data.href : "https://crossorigin.me/" + "https://cs.brown.edu";
 
         let content = this.html ?
diff --git a/src/fields/AudioField.ts b/src/fields/AudioField.ts
index 8864471ae..252a5b74e 100644
--- a/src/fields/AudioField.ts
+++ b/src/fields/AudioField.ts
@@ -4,7 +4,7 @@ import { Types } from "../server/Message";
 
 export class AudioField extends BasicField<URL> {
     constructor(data: URL | undefined = undefined, id?: FieldId, save: boolean = true) {
-        super(data == undefined ? new URL("http://techslides.com/demos/samples/sample.mp3") : data, save, id);
+        super(data === undefined ? new URL("http://techslides.com/demos/samples/sample.mp3") : data, save, id);
     }
 
     toString(): string {
diff --git a/src/fields/BasicField.ts b/src/fields/BasicField.ts
index a92c4a236..3083a1937 100644
--- a/src/fields/BasicField.ts
+++ b/src/fields/BasicField.ts
@@ -46,7 +46,7 @@ export abstract class BasicField<T> extends Field {
 
     @action
     TrySetValue(value: any): boolean {
-        if (typeof value == typeof this.data) {
+        if (typeof value === typeof this.data) {
             this.Data = value;
             return true;
         }
diff --git a/src/fields/ImageField.ts b/src/fields/ImageField.ts
index a9ece7d7b..ef616b2ad 100644
--- a/src/fields/ImageField.ts
+++ b/src/fields/ImageField.ts
@@ -4,7 +4,7 @@ import { Types } from "../server/Message";
 
 export class ImageField extends BasicField<URL> {
     constructor(data: URL | undefined = undefined, id?: FieldId, save: boolean = true) {
-        super(data == undefined ? new URL("http://cs.brown.edu/~bcz/bob_fettucine.jpg") : data, save, id);
+        super(data === undefined ? new URL("http://cs.brown.edu/~bcz/bob_fettucine.jpg") : data, save, id);
     }
 
     toString(): string {
diff --git a/src/fields/ListField.ts b/src/fields/ListField.ts
index c4008bd12..815a3df73 100644
--- a/src/fields/ListField.ts
+++ b/src/fields/ListField.ts
@@ -26,7 +26,7 @@ export class ListField<T extends Field> extends BasicField<T[]> {
         }
         this.observeDisposer = observe(this.Data as IObservableArray<T>, (change: IArrayChange<T> | IArraySplice<T>) => {
             this.updateProxies()
-            if (change.type == "splice") {
+            if (change.type === "splice") {
                 UndoManager.AddEvent({
                     undo: () => this.Data.splice(change.index, change.addedCount, ...change.removed),
                     redo: () => this.Data.splice(change.index, change.removedCount, ...change.added)
@@ -57,8 +57,8 @@ export class ListField<T extends Field> extends BasicField<T[]> {
     }
     private arraysEqual(a: any[], b: any[]) {
         if (a === b) return true;
-        if (a == null || b == null) return false;
-        if (a.length != b.length) return false;
+        if (a === null || b === null) return false;
+        if (a.length !== b.length) return false;
 
         // If you don't care about the order of the elements inside
         // the array, you should sort both arrays here.
@@ -79,9 +79,9 @@ export class ListField<T extends Field> extends BasicField<T[]> {
                 var added = this.data.length < this._proxies.length;
                 var deleted = this.data.length > this._proxies.length;
                 for (let i = 0; i < dataids.length && added; i++)
-                    added = proxies.indexOf(dataids[i]) != -1;
+                    added = proxies.indexOf(dataids[i]) !== -1;
                 for (let i = 0; i < this._proxies.length && deleted; i++)
-                    deleted = dataids.indexOf(proxies[i]) != -1;
+                    deleted = dataids.indexOf(proxies[i]) !== -1;
 
                 this._processingServerUpdate = true;
                 for (let i = 0; i < proxies.length && added; i++) {
diff --git a/src/fields/PDFField.ts b/src/fields/PDFField.ts
index b6625387e..436c1cf2b 100644
--- a/src/fields/PDFField.ts
+++ b/src/fields/PDFField.ts
@@ -7,7 +7,7 @@ import { Types } from "../server/Message";
 
 export class PDFField extends BasicField<URL> {
     constructor(data: URL | undefined = undefined, id?: FieldId, save: boolean = true) {
-        super(data == undefined ? new URL("http://cs.brown.edu/~bcz/bob_fettucine.jpg") : data, save, id);
+        super(data === undefined ? new URL("http://cs.brown.edu/~bcz/bob_fettucine.jpg") : data, save, id);
     }
 
     toString(): string {
diff --git a/src/fields/VideoField.ts b/src/fields/VideoField.ts
index 626e4ec83..992cc1641 100644
--- a/src/fields/VideoField.ts
+++ b/src/fields/VideoField.ts
@@ -4,7 +4,7 @@ import { Types } from "../server/Message";
 
 export class VideoField extends BasicField<URL> {
     constructor(data: URL | undefined = undefined, id?: FieldId, save: boolean = true) {
-        super(data == undefined ? new URL("http://techslides.com/demos/sample-videos/small.mp4") : data, save, id);
+        super(data === undefined ? new URL("http://techslides.com/demos/sample-videos/small.mp4") : data, save, id);
     }
 
     toString(): string {
diff --git a/src/server/index.ts b/src/server/index.ts
index ce8bbefe8..f60e6e293 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -143,7 +143,7 @@ addSecureRoute(
     Method.GET,
     (user, res, req) => {
         let detector = new mobileDetect(req.headers['user-agent'] || "");
-        if (detector.mobile() != null) {
+        if (detector.mobile() !== null) {
             res.sendFile(path.join(__dirname, '../../deploy/mobile/image.html'));
         } else {
             res.sendFile(path.join(__dirname, '../../deploy/index.html'));
-- 
cgit v1.2.3-70-g09d2


From 49310acbdfd7ca239c939208b3766c54e980f6f1 Mon Sep 17 00:00:00 2001
From: ab <abdullah_ahmed@brown.edu>
Date: Sat, 6 Apr 2019 18:43:15 -0400
Subject: drop fraught but technically undoable

---
 src/client/util/TooltipTextMenu.tsx                |   2 +-
 src/client/util/UndoManager.ts                     |  23 ++++++--
 src/client/views/InkingCanvas.tsx                  |  14 +++--
 src/client/views/InkingControl.tsx                 |   3 +-
 .../views/collections/CollectionDockingView.tsx    |   1 +
 src/client/views/collections/CollectionView.tsx    |   7 +--
 .../views/collections/CollectionViewBase.tsx       |  60 ++++++++++++---------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   2 +-
 src/client/views/nodes/PDFBox.tsx                  |   4 +-
 src/server/database.ts                             |   6 +--
 .../upload_011289d8c22f5422cf6f7e27a8c780ef.jpg    | Bin 0 -> 218352 bytes
 .../upload_01b38428ccc02261e24689b9d887b206.PNG    | Bin 0 -> 14176 bytes
 .../upload_01e81815633ee270c8b4ffca88a7773e.jpg    | Bin 0 -> 218352 bytes
 .../upload_031371580a23b87aaafb587ece875819.png    | Bin 0 -> 4183 bytes
 .../upload_03c44479fe728bbcc2f76dab407544d5.png    | Bin 0 -> 10718 bytes
 .../upload_05ceaf4112a0e3754a53ab59f4b1ff07.png    | Bin 0 -> 6199 bytes
 .../upload_061bdf72416cbef898d4cd1a0152ce1f.jpg    | Bin 0 -> 218352 bytes
 .../upload_07488f31d6eacb28458c11eb982fa654.jpg    | Bin 0 -> 218352 bytes
 .../upload_08afadab5ded674ec3f244b2bd124b8b.PNG    | Bin 0 -> 14176 bytes
 .../upload_09df67e3e4c3bcd2a571d8be5f0acf48.PNG    | Bin 0 -> 7337 bytes
 .../upload_10b64e99df3aac032564573c3699bda4.png    | Bin 0 -> 10718 bytes
 .../upload_11162dd1b00ed2a26923aa9d2fa9889e.png    | Bin 0 -> 4183 bytes
 .../upload_115d44864ee866f2986188501a59342b.png    | Bin 0 -> 10718 bytes
 .../upload_1465783317d01ed75e87e169b56928fd.PNG    | Bin 0 -> 14176 bytes
 .../upload_156cb9406f94654dea0e227e377f0a75.PNG    | Bin 0 -> 7337 bytes
 .../upload_166eb5d1d46cf268a59e01b4b32528cd.PNG    | Bin 0 -> 7337 bytes
 .../upload_17d2b2f3ec801e6fdcc6d29e523e946b.PNG    | Bin 0 -> 7337 bytes
 .../upload_1863f67c2c193452cc5192f0ebac2a7b.png    | Bin 0 -> 6199 bytes
 .../upload_18d64bf4a483e78a36153abcd26af503.png    | Bin 0 -> 20776 bytes
 .../upload_1a7925dd1140a2b5a7cc5c62ad20a8e8.png    | Bin 0 -> 20776 bytes
 .../upload_1c47eb68831fff82bc6982e5df4d02d5.PNG    | Bin 0 -> 14176 bytes
 .../upload_1c8f6233068ed1535384500768557e68.png    | Bin 0 -> 20776 bytes
 .../upload_1ea7ee56753d0327192da9666de9e305.PNG    | Bin 0 -> 14160 bytes
 .../upload_2098441ee0ad89e51e825e2fb5f641d2.png    | Bin 0 -> 4183 bytes
 .../upload_23a30791fc2afceb92310a665d364dbd.jpg    | Bin 0 -> 218352 bytes
 .../upload_2436e82c2c54dd8f33f74e4440d11104.png    | Bin 0 -> 10718 bytes
 .../upload_2443e73bbbd67712bcedc04f0ead5617.PNG    | Bin 0 -> 14160 bytes
 .../upload_2515fc22ce67c256bf3039bc84e54432.jpg    | Bin 0 -> 136356 bytes
 .../upload_280bb85121601366f65632649edd9425.jpg    | Bin 0 -> 136356 bytes
 .../upload_284a94d4ba485d6799ee772521846a59.jpg    | Bin 0 -> 218352 bytes
 .../upload_28550bbb245dfbe9b24b2354ed349f0c.jpg    | Bin 0 -> 218352 bytes
 .../upload_2abbef5268ab7ca77dad96775cb52345.png    | Bin 0 -> 6199 bytes
 .../upload_2b5ec0fba66b5b3b5e47a2c53a1005be.jpg    | Bin 0 -> 218352 bytes
 .../upload_2b6f66b8972b2f4991239720bb1df7d4.png    | Bin 0 -> 6199 bytes
 .../upload_2b75a95a64abfe997aa8e12a69d2bc43.png    | Bin 0 -> 498481 bytes
 .../upload_2c689fe57038ad6a324297e92badd705.png    | Bin 0 -> 20776 bytes
 .../upload_2d86441185e38ed16bbf4234d8857e13.PNG    | Bin 0 -> 14176 bytes
 .../upload_2e68bb961e82faddbaa501c5c4e51795.png    | Bin 0 -> 6199 bytes
 .../upload_303438040b77efc92ad5a6617edf7000.png    | Bin 0 -> 4183 bytes
 .../upload_303b28fc64dd65c58382fe2b0a513190.jpg    | Bin 0 -> 136356 bytes
 .../upload_319f73a65644974e6502a79dfb60b777.jpg    | Bin 0 -> 218352 bytes
 .../upload_33290055bf62326211eb846bc32d7ab9.PNG    | Bin 0 -> 14176 bytes
 .../upload_352949a7d58209759d0ab420726acce3.png    | Bin 0 -> 20776 bytes
 .../upload_365e7ad24d7b896dc24525068e8f0fba.png    | Bin 0 -> 4183 bytes
 .../upload_38001ea16c21a82601586e9d7c823e5f.png    | Bin 0 -> 20776 bytes
 .../upload_3bb64e7eb87805eb7c2c67352ab3e3f6.PNG    | Bin 0 -> 14176 bytes
 .../upload_3cb5a8a9454a1b42a69a3d274c0a00e7.png    | Bin 0 -> 6199 bytes
 .../upload_3db8e110c40cb0770e79a61171eba542.jpg    | Bin 0 -> 218352 bytes
 .../upload_41fc781a82b1f33b631e5aa68c6a1a76.PNG    | Bin 0 -> 14160 bytes
 .../upload_42ecb2422b80f6da3afb62543391f58c.PNG    | Bin 0 -> 7337 bytes
 .../upload_431be602a66059169d1f4c4cd4646476.PNG    | Bin 0 -> 14176 bytes
 .../upload_4543a61bc7c5f79c788b749a9ce7535a.PNG    | Bin 0 -> 7337 bytes
 .../upload_45e88f05b821eca6b85c1fc7b048af63.jpg    | Bin 0 -> 136356 bytes
 .../upload_493879305f109a4412223137f7031944.png    | Bin 0 -> 6199 bytes
 .../upload_49ac63665b883ea4702733fc8d70f6b1.PNG    | Bin 0 -> 14176 bytes
 .../upload_4bc2bdf9a392b8e567b964f59cfd32a8.PNG    | Bin 0 -> 7337 bytes
 .../upload_4e4560c1f0081b87af4c010792d799fc.jpg    | Bin 0 -> 136356 bytes
 .../upload_4e56e150f5c92f8980c4df49603747cc.png    | Bin 0 -> 6199 bytes
 .../upload_4e897855f35d0aa419d27bfaedae4435.png    | Bin 0 -> 10718 bytes
 .../upload_5495509165b60d3c04b8e509a7d187ba.PNG    | Bin 0 -> 14176 bytes
 .../upload_54d3dcb522f9fe3beb060103248a5ee9.png    | Bin 0 -> 10718 bytes
 .../upload_568996cd49829901200e1bec2b49a176.PNG    | Bin 0 -> 7337 bytes
 .../upload_5757898e2b8507438e1bdaf7b7fee4bb.png    | Bin 0 -> 10718 bytes
 .../upload_57c4353c9d3250bac49753be6b8973c6.png    | Bin 0 -> 20776 bytes
 .../upload_57f2901b84275f3e40ca9d41cb7c057d.PNG    | Bin 0 -> 14176 bytes
 .../upload_580d262f8a55c2fe3acbc6f0e690ad56.png    | Bin 0 -> 6199 bytes
 .../upload_58782b7ba1ac3963c4fdd5a987bf0aca.PNG    | Bin 0 -> 7337 bytes
 .../upload_588f580df306a338f7be0483dc30d576.jpg    | Bin 0 -> 136356 bytes
 .../upload_590c965f849c296ae3841689ce80b548.png    | Bin 0 -> 20776 bytes
 .../upload_5915c6516d7a7db8164f7e395b81973e.PNG    | Bin 0 -> 7337 bytes
 .../upload_594db1ea036d584bc71de979ce57832b.PNG    | Bin 0 -> 14176 bytes
 .../upload_5bf829a0b264b321b9b17267ab06a11b.png    | Bin 0 -> 20776 bytes
 .../upload_5cba158af25fb2058c1b2e130cd3fefc.png    | Bin 0 -> 6199 bytes
 .../upload_5d5f91ac61a5e55192c1de4402483126.png    | Bin 0 -> 10718 bytes
 .../upload_5ea64a359728395c1608b8783f6fb192.PNG    | Bin 0 -> 7337 bytes
 .../upload_5f0bace6da44bc7084c6ad01fcfe1012.png    | Bin 0 -> 4183 bytes
 .../upload_5fe9f9fe6eb4a78dcde144c9d4e5d6bb.png    | Bin 0 -> 10718 bytes
 .../upload_604cd121c8ecb30eb4d13cded9ce8746.png    | Bin 0 -> 20776 bytes
 .../upload_60f89c7647c6ed08d977e485fea9e033.jpg    | Bin 0 -> 218352 bytes
 .../upload_62900e4354015fc2bc78f00afbc1bc21.png    | Bin 0 -> 10718 bytes
 .../upload_62f1a7fa74224de7248c62d6d4242fd0.jpg    | Bin 0 -> 218352 bytes
 .../upload_63a7127553e83f381212c80f607c6db5.png    | Bin 0 -> 10718 bytes
 .../upload_63c37ab66ad16d68ef96670b1d04ee1c.png    | Bin 0 -> 6199 bytes
 .../upload_665fb37c1505fdf48a665990caf09d26.png    | Bin 0 -> 4183 bytes
 .../upload_67c7a2ac39caabaac7b90861a1314b8f.png    | Bin 0 -> 10718 bytes
 .../upload_680394edbfb281065997525cc114f996.PNG    | Bin 0 -> 7337 bytes
 .../upload_684090a0c50a035aaa0df23c18043df8.png    | Bin 0 -> 10718 bytes
 .../upload_6bd24b1193058458d6ea268b06963931.PNG    | Bin 0 -> 7337 bytes
 .../upload_6bde8211989cf1b540b3c6dcf16cd51a.PNG    | Bin 0 -> 7337 bytes
 .../upload_6d5051266bd739f260105072299b5c79.png    | Bin 0 -> 4183 bytes
 .../upload_6d9a3ec37d85659265f2b622a6d24dcb.jpg    | Bin 0 -> 136356 bytes
 .../upload_6f222fae83f0185e361bf6a757ba9800.png    | Bin 0 -> 10718 bytes
 .../upload_6f5b0c8a474e44d76cbbea5f629e8fba.png    | Bin 0 -> 498481 bytes
 .../upload_7068f1e2ec782980b4eded2ff974da7f.jpg    | Bin 0 -> 136356 bytes
 .../upload_72c05840ea8bddc44575309923d4c63b.jpg    | Bin 0 -> 218352 bytes
 .../upload_73b42f2f26e037078c1f1c63913fc9aa.jpg    | Bin 0 -> 136356 bytes
 .../upload_7412310b57204cb3c36d5aba670589fc.PNG    | Bin 0 -> 14176 bytes
 .../upload_75d331ba20a80ca0f6fd18bc0b37b8b3.PNG    | Bin 0 -> 14176 bytes
 .../upload_77eab9365e5c11d5e1ac08fca6c862ab.png    | Bin 0 -> 4183 bytes
 .../upload_786b4b94fda60ba60da3dfe2be091b6b.png    | Bin 0 -> 6199 bytes
 .../upload_793745d2b6c8a5c3dbeea343b9332846.PNG    | Bin 0 -> 14160 bytes
 .../upload_7c369564861f079a17bd77081ace597b.PNG    | Bin 0 -> 14160 bytes
 .../upload_7c7f3f317ea27a2ddad9e8a34f636b78.png    | Bin 0 -> 20776 bytes
 .../upload_7f357bbbe7756dabad0b9cb49c9ceb0c.PNG    | Bin 0 -> 14176 bytes
 .../upload_814802d9faf217a7b09c1c95c4ecfd05.png    | Bin 0 -> 48172 bytes
 .../upload_83180cda4fa509f6ff0654376c1da7f2.jpg    | Bin 0 -> 218352 bytes
 .../upload_8321ece56de47e0bf5345fe30d23414e.PNG    | Bin 0 -> 7337 bytes
 .../upload_871f1af44ec7a2140f32433f3015d603.jpg    | Bin 0 -> 218352 bytes
 .../upload_892d1d9d981f679fedaa2c5bf193fd20.jpg    | Bin 0 -> 136356 bytes
 .../upload_89721bf0d244b1fcc52f70a7d9e3593b.jpg    | Bin 0 -> 136356 bytes
 .../upload_89e66f8d531a44a63fa3355602813ae7.png    | Bin 0 -> 6199 bytes
 .../upload_8a6090d2b978221d0810b954f7b14065.PNG    | Bin 0 -> 14176 bytes
 .../upload_8b92f6ffc9a8c8353fd141c6dc71895f.jpg    | Bin 0 -> 136356 bytes
 .../upload_8bfca62429aada8f6b5dae1dcde24cab.png    | Bin 0 -> 20776 bytes
 .../upload_8e9e27d1918f3a0a5f242397079e66fa.png    | Bin 0 -> 20776 bytes
 .../upload_8ef0953ff77d42848b6e7f37e9bd506d.PNG    | Bin 0 -> 14176 bytes
 .../upload_902c6b9c79da47ec00238f4831795077.jpg    | Bin 0 -> 218352 bytes
 .../upload_90d380b38492369c1470868b019f0d0c.jpg    | Bin 0 -> 218352 bytes
 .../upload_95af42b1b5db521aa1539980fe85e71a.jpg    | Bin 0 -> 136356 bytes
 .../upload_97031060c857a0e8f15af3fcc038a2b8.jpg    | Bin 0 -> 218352 bytes
 .../upload_97903efc6c08245acaea37eefacf289f.png    | Bin 0 -> 20776 bytes
 .../upload_9974b3459e182fef33b8e467058b7525.png    | Bin 0 -> 4183 bytes
 .../upload_9a8429dac8feba05bbda2b53b5da4efa.jpg    | Bin 0 -> 218352 bytes
 .../upload_9bbc86def05076b89080adcc37481324.jpg    | Bin 0 -> 136356 bytes
 .../upload_9cd52fc451f3d459021127a7d51d108b.png    | Bin 0 -> 20776 bytes
 .../upload_9de9f004f258cee8c55bf872362d6453.jpg    | Bin 0 -> 218352 bytes
 .../upload_a0286ae2a1eec8c31c8276ec067d91aa.png    | Bin 0 -> 20776 bytes
 .../upload_a04473da584e5a602d0f8984a670a91c.jpg    | Bin 0 -> 136356 bytes
 .../upload_a2cf3f6ab4ab5b249ebf8100e461aa27.png    | Bin 0 -> 4183 bytes
 .../upload_a3095034ec19353c9ee55221088f01d8.PNG    | Bin 0 -> 14176 bytes
 .../upload_a520a6856021104b8130f8d3b42d85c0.png    | Bin 0 -> 6199 bytes
 .../upload_a5c26f52ddcbd705ffbd3dfd7cd2ce40.jpg    | Bin 0 -> 136356 bytes
 .../upload_a60864ff7892974e941e42a2441e530d.png    | Bin 0 -> 6199 bytes
 .../upload_a77f86dbeaa0c3cecc2516f9d9cecf1d.png    | Bin 0 -> 4183 bytes
 .../upload_aa2bbeb0871a2d6b4b54524ac2758270.jpg    | Bin 0 -> 218352 bytes
 .../upload_aa9fec4828bfdfef3ae20d43997fb274.png    | Bin 0 -> 10718 bytes
 .../upload_ab2bba2cee88991561f20d7cd69ec6e1.png    | Bin 0 -> 10718 bytes
 .../upload_ae1c01e481fc6b753c78605bfbe680b9.png    | Bin 0 -> 20776 bytes
 .../upload_b0d25a0dccc640f78dcbc291f930b5d6.png    | Bin 0 -> 6199 bytes
 .../upload_b0e527b27b7cabf651b29c9d834d24f3.PNG    | Bin 0 -> 7337 bytes
 .../upload_b0ea7e49e84cf91075962a75c76d3233.png    | Bin 0 -> 6199 bytes
 .../upload_b1b334829e1d99d7cd32fae5044cc103.PNG    | Bin 0 -> 7337 bytes
 .../upload_b52adf6e1ecdbbe76aa3c038ddc3b02b.PNG    | Bin 0 -> 14160 bytes
 .../upload_b6c992aa6c434520887521e11bb5f492.png    | Bin 0 -> 4183 bytes
 .../upload_ba04eb816ef3bec770aa7eebf3b21cb1.PNG    | Bin 0 -> 14176 bytes
 .../upload_ba58066314a79d1c845241fb4400e44d.png    | Bin 0 -> 20776 bytes
 .../upload_bb6b521b4df6bd783d6a5b83a66caa0a.jpg    | Bin 0 -> 136356 bytes
 .../upload_bcb173a15068ded66b39623ab18bfdb7.jpg    | Bin 0 -> 218352 bytes
 .../upload_be4034faba6a5313dfb8a53e18ffc188.png    | Bin 0 -> 10718 bytes
 .../upload_be5f90995b34cd30d9a269b5a9f10fcc.PNG    | Bin 0 -> 7337 bytes
 .../upload_c1a964ca145f45e7e666cfaf7c8398e3.png    | Bin 0 -> 4183 bytes
 .../upload_c6764790a8182d796acddb76d3df595e.PNG    | Bin 0 -> 14176 bytes
 .../upload_c81bff72d05e0bb7f13376a46506ec2a.jpg    | Bin 0 -> 39678 bytes
 .../upload_c98c0184a19b621b2bc0575b49996a69.png    | Bin 0 -> 20776 bytes
 .../upload_cb4d875a9f73d1f735da5ae82dce6eb1.jpg    | Bin 0 -> 136356 bytes
 .../upload_cd08524995e5f124505fe3d7ab38a41f.png    | Bin 0 -> 4183 bytes
 .../upload_cd73b55488fcc7e5e5e5f2a4c43bc1fd.png    | Bin 0 -> 20776 bytes
 .../upload_ce357d5a6d727ac44110c60dd379428f.png    | Bin 0 -> 10718 bytes
 .../upload_d00c34a1e1c319a9373b5f136c38a866.jpg    | Bin 0 -> 136356 bytes
 .../upload_d242527805ab4c52f2385ae57b85e6fb.png    | Bin 0 -> 20776 bytes
 .../upload_d28007fa68c809cd3c0a78d4a8727275.png    | Bin 0 -> 4183 bytes
 .../upload_d35ea9c62bf8da0352a16193b880469e.jpg    | Bin 0 -> 218352 bytes
 .../upload_d4e0e9d2aee31b5931129dc3bc83e813.png    | Bin 0 -> 6199 bytes
 .../upload_d5801c03d9562a612d1907085ba39373.jpg    | Bin 0 -> 218352 bytes
 .../upload_d6d0e3a302e0edafa70785c39041e522.PNG    | Bin 0 -> 14176 bytes
 .../upload_d6e725cc697751599b177e0cf330b9f2.jpg    | Bin 0 -> 218352 bytes
 .../upload_d794d56fcab3b93b6c79aa201cba34f8.png    | Bin 0 -> 48172 bytes
 .../upload_d8abeaef04a42678432e5e1d3377a2d0.png    | Bin 0 -> 20776 bytes
 .../upload_dac5245c657d29c1c4dfba98472fb50d.pdf    | Bin 0 -> 331305 bytes
 .../upload_db3c5a0b560c9da35607359d86cf2b97.png    | Bin 0 -> 4183 bytes
 .../upload_dc8821dc596b1938c855e0affbe614dd.png    | Bin 0 -> 20776 bytes
 .../upload_df7697c4832990aad0e8d9f0daa1711b.png    | Bin 0 -> 10718 bytes
 .../upload_e009e45db30e47383fad5d71908dc69d.png    | Bin 0 -> 20776 bytes
 .../upload_e15738d092ff38197dc1fb41a4747d16.jpg    | Bin 0 -> 218352 bytes
 .../upload_e51079d9af97a412353d63c60e1ea96d.png    | Bin 0 -> 4183 bytes
 .../upload_e5422cdcfa80628842ff3aacf14ad25e.png    | Bin 0 -> 20776 bytes
 .../upload_e6256196c38216ac539ef7e8ee28ab02.png    | Bin 0 -> 6199 bytes
 .../upload_e9668199bd12c905f27be76b11366d68.png    | Bin 0 -> 6199 bytes
 .../upload_e98ebeeebd5f0ddc98ea9b3a2d9f40d2.png    | Bin 0 -> 4183 bytes
 .../upload_eb2f002129e0cfb5c940f6b4ecc5a8f2.jpg    | Bin 0 -> 136356 bytes
 .../upload_ec02ff06b3154d269086e23ebf6ad942.png    | Bin 0 -> 10718 bytes
 .../upload_ef3873102a70850a4da2040eab002c55.jpg    | Bin 0 -> 136356 bytes
 .../upload_f259c9aa37b0919d6d20ad8d75e8329e.png    | Bin 0 -> 10718 bytes
 .../upload_f48143c75b8c2412785e3ed31ebbb150.PNG    | Bin 0 -> 7337 bytes
 .../upload_f5347da10a373480807fc3469533b9fe.png    | Bin 0 -> 4183 bytes
 .../upload_f6e36f6d3de7a2036eeff617f186b78f.png    | Bin 0 -> 4183 bytes
 .../upload_f6f1ed7a268523278457de08ee61bcfa.PNG    | Bin 0 -> 7337 bytes
 .../upload_f73607b7cf818631165aca9d32ef19c5.jpg    | Bin 0 -> 136356 bytes
 .../upload_f804a6c7ec796718d66108af0b1e6d0e.png    | Bin 0 -> 10718 bytes
 .../upload_f9ed03ab046d7f579d6f531df9c4050a.PNG    | Bin 0 -> 14176 bytes
 .../upload_fac4f686367ad63f2fcc1d8b4f74f123.png    | Bin 0 -> 4183 bytes
 .../upload_faebd8bdc2b00efe50b232c9cf4e63b6.PNG    | Bin 0 -> 7337 bytes
 .../upload_fb3cc3a1d715a6ffa1c72bde91faf3af.png    | Bin 0 -> 6199 bytes
 .../upload_fe2dbe44900645eba61207748f63ade3.jpg    | Bin 0 -> 136356 bytes
 .../upload_ffd58755419ab98f03d56fedd16736e9.jpg    | Bin 0 -> 218352 bytes
 205 files changed, 79 insertions(+), 43 deletions(-)
 create mode 100644 src/server/public/files/upload_011289d8c22f5422cf6f7e27a8c780ef.jpg
 create mode 100644 src/server/public/files/upload_01b38428ccc02261e24689b9d887b206.PNG
 create mode 100644 src/server/public/files/upload_01e81815633ee270c8b4ffca88a7773e.jpg
 create mode 100644 src/server/public/files/upload_031371580a23b87aaafb587ece875819.png
 create mode 100644 src/server/public/files/upload_03c44479fe728bbcc2f76dab407544d5.png
 create mode 100644 src/server/public/files/upload_05ceaf4112a0e3754a53ab59f4b1ff07.png
 create mode 100644 src/server/public/files/upload_061bdf72416cbef898d4cd1a0152ce1f.jpg
 create mode 100644 src/server/public/files/upload_07488f31d6eacb28458c11eb982fa654.jpg
 create mode 100644 src/server/public/files/upload_08afadab5ded674ec3f244b2bd124b8b.PNG
 create mode 100644 src/server/public/files/upload_09df67e3e4c3bcd2a571d8be5f0acf48.PNG
 create mode 100644 src/server/public/files/upload_10b64e99df3aac032564573c3699bda4.png
 create mode 100644 src/server/public/files/upload_11162dd1b00ed2a26923aa9d2fa9889e.png
 create mode 100644 src/server/public/files/upload_115d44864ee866f2986188501a59342b.png
 create mode 100644 src/server/public/files/upload_1465783317d01ed75e87e169b56928fd.PNG
 create mode 100644 src/server/public/files/upload_156cb9406f94654dea0e227e377f0a75.PNG
 create mode 100644 src/server/public/files/upload_166eb5d1d46cf268a59e01b4b32528cd.PNG
 create mode 100644 src/server/public/files/upload_17d2b2f3ec801e6fdcc6d29e523e946b.PNG
 create mode 100644 src/server/public/files/upload_1863f67c2c193452cc5192f0ebac2a7b.png
 create mode 100644 src/server/public/files/upload_18d64bf4a483e78a36153abcd26af503.png
 create mode 100644 src/server/public/files/upload_1a7925dd1140a2b5a7cc5c62ad20a8e8.png
 create mode 100644 src/server/public/files/upload_1c47eb68831fff82bc6982e5df4d02d5.PNG
 create mode 100644 src/server/public/files/upload_1c8f6233068ed1535384500768557e68.png
 create mode 100644 src/server/public/files/upload_1ea7ee56753d0327192da9666de9e305.PNG
 create mode 100644 src/server/public/files/upload_2098441ee0ad89e51e825e2fb5f641d2.png
 create mode 100644 src/server/public/files/upload_23a30791fc2afceb92310a665d364dbd.jpg
 create mode 100644 src/server/public/files/upload_2436e82c2c54dd8f33f74e4440d11104.png
 create mode 100644 src/server/public/files/upload_2443e73bbbd67712bcedc04f0ead5617.PNG
 create mode 100644 src/server/public/files/upload_2515fc22ce67c256bf3039bc84e54432.jpg
 create mode 100644 src/server/public/files/upload_280bb85121601366f65632649edd9425.jpg
 create mode 100644 src/server/public/files/upload_284a94d4ba485d6799ee772521846a59.jpg
 create mode 100644 src/server/public/files/upload_28550bbb245dfbe9b24b2354ed349f0c.jpg
 create mode 100644 src/server/public/files/upload_2abbef5268ab7ca77dad96775cb52345.png
 create mode 100644 src/server/public/files/upload_2b5ec0fba66b5b3b5e47a2c53a1005be.jpg
 create mode 100644 src/server/public/files/upload_2b6f66b8972b2f4991239720bb1df7d4.png
 create mode 100644 src/server/public/files/upload_2b75a95a64abfe997aa8e12a69d2bc43.png
 create mode 100644 src/server/public/files/upload_2c689fe57038ad6a324297e92badd705.png
 create mode 100644 src/server/public/files/upload_2d86441185e38ed16bbf4234d8857e13.PNG
 create mode 100644 src/server/public/files/upload_2e68bb961e82faddbaa501c5c4e51795.png
 create mode 100644 src/server/public/files/upload_303438040b77efc92ad5a6617edf7000.png
 create mode 100644 src/server/public/files/upload_303b28fc64dd65c58382fe2b0a513190.jpg
 create mode 100644 src/server/public/files/upload_319f73a65644974e6502a79dfb60b777.jpg
 create mode 100644 src/server/public/files/upload_33290055bf62326211eb846bc32d7ab9.PNG
 create mode 100644 src/server/public/files/upload_352949a7d58209759d0ab420726acce3.png
 create mode 100644 src/server/public/files/upload_365e7ad24d7b896dc24525068e8f0fba.png
 create mode 100644 src/server/public/files/upload_38001ea16c21a82601586e9d7c823e5f.png
 create mode 100644 src/server/public/files/upload_3bb64e7eb87805eb7c2c67352ab3e3f6.PNG
 create mode 100644 src/server/public/files/upload_3cb5a8a9454a1b42a69a3d274c0a00e7.png
 create mode 100644 src/server/public/files/upload_3db8e110c40cb0770e79a61171eba542.jpg
 create mode 100644 src/server/public/files/upload_41fc781a82b1f33b631e5aa68c6a1a76.PNG
 create mode 100644 src/server/public/files/upload_42ecb2422b80f6da3afb62543391f58c.PNG
 create mode 100644 src/server/public/files/upload_431be602a66059169d1f4c4cd4646476.PNG
 create mode 100644 src/server/public/files/upload_4543a61bc7c5f79c788b749a9ce7535a.PNG
 create mode 100644 src/server/public/files/upload_45e88f05b821eca6b85c1fc7b048af63.jpg
 create mode 100644 src/server/public/files/upload_493879305f109a4412223137f7031944.png
 create mode 100644 src/server/public/files/upload_49ac63665b883ea4702733fc8d70f6b1.PNG
 create mode 100644 src/server/public/files/upload_4bc2bdf9a392b8e567b964f59cfd32a8.PNG
 create mode 100644 src/server/public/files/upload_4e4560c1f0081b87af4c010792d799fc.jpg
 create mode 100644 src/server/public/files/upload_4e56e150f5c92f8980c4df49603747cc.png
 create mode 100644 src/server/public/files/upload_4e897855f35d0aa419d27bfaedae4435.png
 create mode 100644 src/server/public/files/upload_5495509165b60d3c04b8e509a7d187ba.PNG
 create mode 100644 src/server/public/files/upload_54d3dcb522f9fe3beb060103248a5ee9.png
 create mode 100644 src/server/public/files/upload_568996cd49829901200e1bec2b49a176.PNG
 create mode 100644 src/server/public/files/upload_5757898e2b8507438e1bdaf7b7fee4bb.png
 create mode 100644 src/server/public/files/upload_57c4353c9d3250bac49753be6b8973c6.png
 create mode 100644 src/server/public/files/upload_57f2901b84275f3e40ca9d41cb7c057d.PNG
 create mode 100644 src/server/public/files/upload_580d262f8a55c2fe3acbc6f0e690ad56.png
 create mode 100644 src/server/public/files/upload_58782b7ba1ac3963c4fdd5a987bf0aca.PNG
 create mode 100644 src/server/public/files/upload_588f580df306a338f7be0483dc30d576.jpg
 create mode 100644 src/server/public/files/upload_590c965f849c296ae3841689ce80b548.png
 create mode 100644 src/server/public/files/upload_5915c6516d7a7db8164f7e395b81973e.PNG
 create mode 100644 src/server/public/files/upload_594db1ea036d584bc71de979ce57832b.PNG
 create mode 100644 src/server/public/files/upload_5bf829a0b264b321b9b17267ab06a11b.png
 create mode 100644 src/server/public/files/upload_5cba158af25fb2058c1b2e130cd3fefc.png
 create mode 100644 src/server/public/files/upload_5d5f91ac61a5e55192c1de4402483126.png
 create mode 100644 src/server/public/files/upload_5ea64a359728395c1608b8783f6fb192.PNG
 create mode 100644 src/server/public/files/upload_5f0bace6da44bc7084c6ad01fcfe1012.png
 create mode 100644 src/server/public/files/upload_5fe9f9fe6eb4a78dcde144c9d4e5d6bb.png
 create mode 100644 src/server/public/files/upload_604cd121c8ecb30eb4d13cded9ce8746.png
 create mode 100644 src/server/public/files/upload_60f89c7647c6ed08d977e485fea9e033.jpg
 create mode 100644 src/server/public/files/upload_62900e4354015fc2bc78f00afbc1bc21.png
 create mode 100644 src/server/public/files/upload_62f1a7fa74224de7248c62d6d4242fd0.jpg
 create mode 100644 src/server/public/files/upload_63a7127553e83f381212c80f607c6db5.png
 create mode 100644 src/server/public/files/upload_63c37ab66ad16d68ef96670b1d04ee1c.png
 create mode 100644 src/server/public/files/upload_665fb37c1505fdf48a665990caf09d26.png
 create mode 100644 src/server/public/files/upload_67c7a2ac39caabaac7b90861a1314b8f.png
 create mode 100644 src/server/public/files/upload_680394edbfb281065997525cc114f996.PNG
 create mode 100644 src/server/public/files/upload_684090a0c50a035aaa0df23c18043df8.png
 create mode 100644 src/server/public/files/upload_6bd24b1193058458d6ea268b06963931.PNG
 create mode 100644 src/server/public/files/upload_6bde8211989cf1b540b3c6dcf16cd51a.PNG
 create mode 100644 src/server/public/files/upload_6d5051266bd739f260105072299b5c79.png
 create mode 100644 src/server/public/files/upload_6d9a3ec37d85659265f2b622a6d24dcb.jpg
 create mode 100644 src/server/public/files/upload_6f222fae83f0185e361bf6a757ba9800.png
 create mode 100644 src/server/public/files/upload_6f5b0c8a474e44d76cbbea5f629e8fba.png
 create mode 100644 src/server/public/files/upload_7068f1e2ec782980b4eded2ff974da7f.jpg
 create mode 100644 src/server/public/files/upload_72c05840ea8bddc44575309923d4c63b.jpg
 create mode 100644 src/server/public/files/upload_73b42f2f26e037078c1f1c63913fc9aa.jpg
 create mode 100644 src/server/public/files/upload_7412310b57204cb3c36d5aba670589fc.PNG
 create mode 100644 src/server/public/files/upload_75d331ba20a80ca0f6fd18bc0b37b8b3.PNG
 create mode 100644 src/server/public/files/upload_77eab9365e5c11d5e1ac08fca6c862ab.png
 create mode 100644 src/server/public/files/upload_786b4b94fda60ba60da3dfe2be091b6b.png
 create mode 100644 src/server/public/files/upload_793745d2b6c8a5c3dbeea343b9332846.PNG
 create mode 100644 src/server/public/files/upload_7c369564861f079a17bd77081ace597b.PNG
 create mode 100644 src/server/public/files/upload_7c7f3f317ea27a2ddad9e8a34f636b78.png
 create mode 100644 src/server/public/files/upload_7f357bbbe7756dabad0b9cb49c9ceb0c.PNG
 create mode 100644 src/server/public/files/upload_814802d9faf217a7b09c1c95c4ecfd05.png
 create mode 100644 src/server/public/files/upload_83180cda4fa509f6ff0654376c1da7f2.jpg
 create mode 100644 src/server/public/files/upload_8321ece56de47e0bf5345fe30d23414e.PNG
 create mode 100644 src/server/public/files/upload_871f1af44ec7a2140f32433f3015d603.jpg
 create mode 100644 src/server/public/files/upload_892d1d9d981f679fedaa2c5bf193fd20.jpg
 create mode 100644 src/server/public/files/upload_89721bf0d244b1fcc52f70a7d9e3593b.jpg
 create mode 100644 src/server/public/files/upload_89e66f8d531a44a63fa3355602813ae7.png
 create mode 100644 src/server/public/files/upload_8a6090d2b978221d0810b954f7b14065.PNG
 create mode 100644 src/server/public/files/upload_8b92f6ffc9a8c8353fd141c6dc71895f.jpg
 create mode 100644 src/server/public/files/upload_8bfca62429aada8f6b5dae1dcde24cab.png
 create mode 100644 src/server/public/files/upload_8e9e27d1918f3a0a5f242397079e66fa.png
 create mode 100644 src/server/public/files/upload_8ef0953ff77d42848b6e7f37e9bd506d.PNG
 create mode 100644 src/server/public/files/upload_902c6b9c79da47ec00238f4831795077.jpg
 create mode 100644 src/server/public/files/upload_90d380b38492369c1470868b019f0d0c.jpg
 create mode 100644 src/server/public/files/upload_95af42b1b5db521aa1539980fe85e71a.jpg
 create mode 100644 src/server/public/files/upload_97031060c857a0e8f15af3fcc038a2b8.jpg
 create mode 100644 src/server/public/files/upload_97903efc6c08245acaea37eefacf289f.png
 create mode 100644 src/server/public/files/upload_9974b3459e182fef33b8e467058b7525.png
 create mode 100644 src/server/public/files/upload_9a8429dac8feba05bbda2b53b5da4efa.jpg
 create mode 100644 src/server/public/files/upload_9bbc86def05076b89080adcc37481324.jpg
 create mode 100644 src/server/public/files/upload_9cd52fc451f3d459021127a7d51d108b.png
 create mode 100644 src/server/public/files/upload_9de9f004f258cee8c55bf872362d6453.jpg
 create mode 100644 src/server/public/files/upload_a0286ae2a1eec8c31c8276ec067d91aa.png
 create mode 100644 src/server/public/files/upload_a04473da584e5a602d0f8984a670a91c.jpg
 create mode 100644 src/server/public/files/upload_a2cf3f6ab4ab5b249ebf8100e461aa27.png
 create mode 100644 src/server/public/files/upload_a3095034ec19353c9ee55221088f01d8.PNG
 create mode 100644 src/server/public/files/upload_a520a6856021104b8130f8d3b42d85c0.png
 create mode 100644 src/server/public/files/upload_a5c26f52ddcbd705ffbd3dfd7cd2ce40.jpg
 create mode 100644 src/server/public/files/upload_a60864ff7892974e941e42a2441e530d.png
 create mode 100644 src/server/public/files/upload_a77f86dbeaa0c3cecc2516f9d9cecf1d.png
 create mode 100644 src/server/public/files/upload_aa2bbeb0871a2d6b4b54524ac2758270.jpg
 create mode 100644 src/server/public/files/upload_aa9fec4828bfdfef3ae20d43997fb274.png
 create mode 100644 src/server/public/files/upload_ab2bba2cee88991561f20d7cd69ec6e1.png
 create mode 100644 src/server/public/files/upload_ae1c01e481fc6b753c78605bfbe680b9.png
 create mode 100644 src/server/public/files/upload_b0d25a0dccc640f78dcbc291f930b5d6.png
 create mode 100644 src/server/public/files/upload_b0e527b27b7cabf651b29c9d834d24f3.PNG
 create mode 100644 src/server/public/files/upload_b0ea7e49e84cf91075962a75c76d3233.png
 create mode 100644 src/server/public/files/upload_b1b334829e1d99d7cd32fae5044cc103.PNG
 create mode 100644 src/server/public/files/upload_b52adf6e1ecdbbe76aa3c038ddc3b02b.PNG
 create mode 100644 src/server/public/files/upload_b6c992aa6c434520887521e11bb5f492.png
 create mode 100644 src/server/public/files/upload_ba04eb816ef3bec770aa7eebf3b21cb1.PNG
 create mode 100644 src/server/public/files/upload_ba58066314a79d1c845241fb4400e44d.png
 create mode 100644 src/server/public/files/upload_bb6b521b4df6bd783d6a5b83a66caa0a.jpg
 create mode 100644 src/server/public/files/upload_bcb173a15068ded66b39623ab18bfdb7.jpg
 create mode 100644 src/server/public/files/upload_be4034faba6a5313dfb8a53e18ffc188.png
 create mode 100644 src/server/public/files/upload_be5f90995b34cd30d9a269b5a9f10fcc.PNG
 create mode 100644 src/server/public/files/upload_c1a964ca145f45e7e666cfaf7c8398e3.png
 create mode 100644 src/server/public/files/upload_c6764790a8182d796acddb76d3df595e.PNG
 create mode 100644 src/server/public/files/upload_c81bff72d05e0bb7f13376a46506ec2a.jpg
 create mode 100644 src/server/public/files/upload_c98c0184a19b621b2bc0575b49996a69.png
 create mode 100644 src/server/public/files/upload_cb4d875a9f73d1f735da5ae82dce6eb1.jpg
 create mode 100644 src/server/public/files/upload_cd08524995e5f124505fe3d7ab38a41f.png
 create mode 100644 src/server/public/files/upload_cd73b55488fcc7e5e5e5f2a4c43bc1fd.png
 create mode 100644 src/server/public/files/upload_ce357d5a6d727ac44110c60dd379428f.png
 create mode 100644 src/server/public/files/upload_d00c34a1e1c319a9373b5f136c38a866.jpg
 create mode 100644 src/server/public/files/upload_d242527805ab4c52f2385ae57b85e6fb.png
 create mode 100644 src/server/public/files/upload_d28007fa68c809cd3c0a78d4a8727275.png
 create mode 100644 src/server/public/files/upload_d35ea9c62bf8da0352a16193b880469e.jpg
 create mode 100644 src/server/public/files/upload_d4e0e9d2aee31b5931129dc3bc83e813.png
 create mode 100644 src/server/public/files/upload_d5801c03d9562a612d1907085ba39373.jpg
 create mode 100644 src/server/public/files/upload_d6d0e3a302e0edafa70785c39041e522.PNG
 create mode 100644 src/server/public/files/upload_d6e725cc697751599b177e0cf330b9f2.jpg
 create mode 100644 src/server/public/files/upload_d794d56fcab3b93b6c79aa201cba34f8.png
 create mode 100644 src/server/public/files/upload_d8abeaef04a42678432e5e1d3377a2d0.png
 create mode 100644 src/server/public/files/upload_dac5245c657d29c1c4dfba98472fb50d.pdf
 create mode 100644 src/server/public/files/upload_db3c5a0b560c9da35607359d86cf2b97.png
 create mode 100644 src/server/public/files/upload_dc8821dc596b1938c855e0affbe614dd.png
 create mode 100644 src/server/public/files/upload_df7697c4832990aad0e8d9f0daa1711b.png
 create mode 100644 src/server/public/files/upload_e009e45db30e47383fad5d71908dc69d.png
 create mode 100644 src/server/public/files/upload_e15738d092ff38197dc1fb41a4747d16.jpg
 create mode 100644 src/server/public/files/upload_e51079d9af97a412353d63c60e1ea96d.png
 create mode 100644 src/server/public/files/upload_e5422cdcfa80628842ff3aacf14ad25e.png
 create mode 100644 src/server/public/files/upload_e6256196c38216ac539ef7e8ee28ab02.png
 create mode 100644 src/server/public/files/upload_e9668199bd12c905f27be76b11366d68.png
 create mode 100644 src/server/public/files/upload_e98ebeeebd5f0ddc98ea9b3a2d9f40d2.png
 create mode 100644 src/server/public/files/upload_eb2f002129e0cfb5c940f6b4ecc5a8f2.jpg
 create mode 100644 src/server/public/files/upload_ec02ff06b3154d269086e23ebf6ad942.png
 create mode 100644 src/server/public/files/upload_ef3873102a70850a4da2040eab002c55.jpg
 create mode 100644 src/server/public/files/upload_f259c9aa37b0919d6d20ad8d75e8329e.png
 create mode 100644 src/server/public/files/upload_f48143c75b8c2412785e3ed31ebbb150.PNG
 create mode 100644 src/server/public/files/upload_f5347da10a373480807fc3469533b9fe.png
 create mode 100644 src/server/public/files/upload_f6e36f6d3de7a2036eeff617f186b78f.png
 create mode 100644 src/server/public/files/upload_f6f1ed7a268523278457de08ee61bcfa.PNG
 create mode 100644 src/server/public/files/upload_f73607b7cf818631165aca9d32ef19c5.jpg
 create mode 100644 src/server/public/files/upload_f804a6c7ec796718d66108af0b1e6d0e.png
 create mode 100644 src/server/public/files/upload_f9ed03ab046d7f579d6f531df9c4050a.PNG
 create mode 100644 src/server/public/files/upload_fac4f686367ad63f2fcc1d8b4f74f123.png
 create mode 100644 src/server/public/files/upload_faebd8bdc2b00efe50b232c9cf4e63b6.PNG
 create mode 100644 src/server/public/files/upload_fb3cc3a1d715a6ffa1c72bde91faf3af.png
 create mode 100644 src/server/public/files/upload_fe2dbe44900645eba61207748f63ade3.jpg
 create mode 100644 src/server/public/files/upload_ffd58755419ab98f03d56fedd16736e9.jpg

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

diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 2a613ba8b..3a6eadac0 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -49,7 +49,7 @@ export class TooltipTextMenu {
       e.preventDefault();
       view.focus();
       items.forEach(({ command, dom }) => {
-        if (dom.contains(e.srcElement)) {
+        if (e.srcElement && dom.contains(e.srcElement as Node)) {
           let active = command(view.state, view.dispatch, view);
           //uncomment this if we want the bullet button to disappear if current selection is bulleted
           // dom.style.display = active ? "" : "none"
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index 6d1b2f1b8..92a6c14e2 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -34,7 +34,20 @@ function propertyDecorator(target: any, key: string | symbol) {
         }
     })
 }
-export function undoBatch(target: any, key: string | symbol, descriptor?: TypedPropertyDescriptor<any>): any {
+
+export function undoBatch(target: any, key: string | symbol, descriptor?: TypedPropertyDescriptor<any>): any;
+export function undoBatch(fn: (...args: any[]) => any): (...args: any[]) => any;
+export function undoBatch(target: any, key?: string | symbol, descriptor?: TypedPropertyDescriptor<any>): any {
+    if (!key) {
+        return function () {
+            let batch = UndoManager.StartBatch("");
+            try {
+                return target.apply(undefined, arguments)
+            } finally {
+                batch.end();
+            }
+        }
+    }
     if (!descriptor) {
         propertyDecorator(target, key);
         return;
@@ -84,6 +97,7 @@ export namespace UndoManager {
     export function GetOpenBatches(): Without<Batch, 'end'>[] {
         return openBatches;
     }
+
     export class Batch {
         private disposed: boolean = false;
 
@@ -125,8 +139,11 @@ export namespace UndoManager {
 
     export function RunInBatch(fn: () => void, batchName: string) {
         let batch = StartBatch(batchName);
-        fn();
-        batch.end();
+        try {
+            fn();
+        } finally {
+            batch.end();
+        }
     }
 
     export const Undo = action(() => {
diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx
index 123ff679b..f7a3601bc 100644
--- a/src/client/views/InkingCanvas.tsx
+++ b/src/client/views/InkingCanvas.tsx
@@ -10,6 +10,7 @@ import "./InkingCanvas.scss";
 import { InkingControl } from "./InkingControl";
 import { InkingStroke } from "./InkingStroke";
 import React = require("react");
+import { undoBatch } from "../util/UndoManager";
 
 interface InkCanvasProps {
     getScreenTransform: () => Transform;
@@ -21,10 +22,13 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
     static InkOffset: number = 50000;
     private _currentStrokeId: string = "";
     public static IntersectStrokeRect(stroke: StrokeData, selRect: { left: number, top: number, width: number, height: number }): boolean {
-        return stroke.pathData.reduce((inside, val) => inside ||
-            (selRect.left < val.x - InkingCanvas.InkOffset && selRect.left + selRect.width > val.x - InkingCanvas.InkOffset &&
-                selRect.top < val.y - InkingCanvas.InkOffset && selRect.top + selRect.height > val.y - InkingCanvas.InkOffset)
-            , false);
+        return stroke.pathData.reduce((inside: boolean, val) => inside ||
+            (
+                selRect.left < val.x - InkingCanvas.InkOffset &&
+                selRect.left + selRect.width > val.x - InkingCanvas.InkOffset &&
+                selRect.top < val.y - InkingCanvas.InkOffset &&
+                selRect.top + selRect.height > val.y - InkingCanvas.InkOffset
+            ), false);
     }
 
     @computed
@@ -83,7 +87,7 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
             }
             this.inkData = data;
         }
-    }
+    };
 
     relativeCoordinatesForEvent = (ex: number, ey: number): { x: number, y: number } => {
         let [x, y] = this.props.getScreenTransform().transformPoint(ex, ey);
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index c1519dff8..dcaf472ca 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -21,7 +21,7 @@ export class InkingControl extends React.Component {
     @observable private _selectedColor: string = "rgb(244, 67, 54)";
     @observable private _selectedWidth: string = "25";
     @observable private _open: boolean = false;
-    @observable private _colorPickerDisplay: boolean = false;
+    @observable private _colorPickerDisplay = false;
 
     constructor(props: Readonly<{}>) {
         super(props);
@@ -76,6 +76,7 @@ export class InkingControl extends React.Component {
         this._open = !this._open;
     }
 
+
     @action
     toggleColorPicker = () => {
         this._colorPickerDisplay = !this._colorPickerDisplay;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 39e0dd989..4d9985db3 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -226,6 +226,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     itemDropped = () => {
         this.stateChanged();
     }
+
     tabCreated = (tab: any) => {
         if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type != "stack") {
             if (tab.titleElement[0].textContent.indexOf("-waiting") != -1) {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 014aa1d8f..42ed3c86b 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -14,6 +14,7 @@ import { CollectionViewProps } from "./CollectionViewBase";
 import { CollectionTreeView } from "./CollectionTreeView";
 import { Field, FieldId, FieldWaiting } from "../../../fields/Field";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
+import { undoBatch } from "../../util/UndoManager";
 
 export enum CollectionViewType {
     Invalid,
@@ -134,9 +135,9 @@ export class CollectionView extends React.Component<CollectionViewProps> {
 
     specificContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped() && this.props.Document.Id != CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
-            ContextMenu.Instance.addItem({ description: "Freeform", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform) })
-            ContextMenu.Instance.addItem({ description: "Schema", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema) })
-            ContextMenu.Instance.addItem({ description: "Treeview", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Tree) })
+            ContextMenu.Instance.addItem({ description: "Freeform", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform)) })
+            ContextMenu.Instance.addItem({ description: "Schema", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema)) })
+            ContextMenu.Instance.addItem({ description: "Treeview", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Tree)) })
         }
     }
 
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index 316d20c9d..2042b2712 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -4,7 +4,7 @@ import { ListField } from "../../../fields/ListField";
 import React = require("react");
 import { KeyStore } from "../../../fields/KeyStore";
 import { FieldWaiting, Opt } from "../../../fields/Field";
-import { undoBatch } from "../../util/UndoManager";
+import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { DragManager } from "../../util/DragManager";
 import { Documents, DocumentOptions } from "../../documents/Documents";
 import { Key } from "../../../fields/Key";
@@ -14,9 +14,10 @@ import { RouteStore } from "../../../server/RouteStore";
 import { TupleField } from "../../../fields/TupleField";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { NumberField } from "../../../fields/NumberField";
-import request = require("request");
+import * as rp from "request-promise";
 import { ServerUtils } from "../../../server/ServerUtil";
 import { Server } from "../../Server";
+import { emptyStatement } from "babel-types";
 
 export interface CollectionViewProps {
     fieldKey: Key;
@@ -130,6 +131,7 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
         return ctor ? ctor(path, options) : undefined;
     }
 
+    @undoBatch
     @action
     protected onDrop(e: React.DragEvent, options: DocumentOptions): void {
         let that = this;
@@ -151,23 +153,28 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
             return;
         }
 
+        let batch = UndoManager.StartBatch("collection view drop");
+        let promises: Promise<void>[] = [];
         for (let i = 0; i < e.dataTransfer.items.length; i++) {
             const upload = window.location.origin + RouteStore.upload;
             let item = e.dataTransfer.items[i];
             if (item.kind === "string" && item.type.indexOf("uri") != -1) {
-                e.dataTransfer.items[i].getAsString(action((s: string) => {
-                    let document: Document;
-                    request.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + s), (err, res, body) => {
+                let str: string;
+                let prom = new Promise<string>(res =>
+                    e.dataTransfer.items[i].getAsString(res)).then(action((s: string) => {
+                        str = s;
+                        return rp.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + s))
+                    })).then(res => {
                         let type = res.headers["content-type"];
                         if (type) {
-                            let doc = this.getDocumentFromType(type, s, { ...options, width: 300, nativeWidth: 300 })
+                            let doc = this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 })
                             if (doc) {
                                 this.props.addDocument(doc, false);
                             }
                         }
                     });
-                    // this.props.addDocument(Documents.WebDocument(s, { ...options, width: 300, height: 300 }), false)
-                }))
+                promises.push(prom);
+                // this.props.addDocument(Documents.WebDocument(s, { ...options, width: 300, height: 300 }), false)
             }
             let type = item.type
             if (item.kind == "file") {
@@ -178,31 +185,36 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
                     formData.append('file', file)
                 }
 
-                fetch(upload, {
+                let prom = fetch(upload, {
                     method: 'POST',
                     body: formData
                 }).then((res: Response) => {
                     return res.json()
                 }).then(json => {
-                    json.map((file: any) => {
+                    json.map(action((file: any) => {
                         let path = window.location.origin + file
-                        runInAction(() => {
-                            let doc = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300 })
-
-                            let docs = that.props.Document.GetT(KeyStore.Data, ListField);
-                            if (docs != FieldWaiting) {
-                                if (!docs) {
-                                    docs = new ListField<Document>();
-                                    that.props.Document.Set(KeyStore.Data, docs)
-                                }
-                                if (doc) {
-                                    docs.Data.push(doc);
-                                }
+                        let doc = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300 })
+
+                        let docs = that.props.Document.GetT(KeyStore.Data, ListField);
+                        if (docs != FieldWaiting) {
+                            if (!docs) {
+                                docs = new ListField<Document>();
+                                that.props.Document.Set(KeyStore.Data, docs)
+                            }
+                            if (doc) {
+                                docs.Data.push(doc);
                             }
-                        })
-                    })
+                        }
+                    }))
                 })
+                promises.push(prom);
             }
         }
+
+        if (promises.length) {
+            Promise.all(promises).catch(() => { }).then(() => batch.end());
+        } else {
+            batch.end();
+        }
     }
 }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c5178f69d..2dcf645d0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -188,7 +188,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
     onDrop = (e: React.DragEvent): void => {
         var pt = this.getTransform().transformPoint(e.pageX, e.pageY);
         super.onDrop(e, { x: pt[0], y: pt[1] });
-    }
+    };
 
     onDragOver = (): void => {
     }
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 28a1f9757..ba022b007 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -151,7 +151,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
      */
     makeEditableAndHighlight = (colour: string) => {
         var range, sel = window.getSelection();
-        if (sel.rangeCount && sel.getRangeAt) {
+        if (sel && sel.rangeCount && sel.getRangeAt) {
             range = sel.getRangeAt(0);
         }
         document.designMode = "on";
@@ -159,7 +159,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
             document.execCommand("HiliteColor", false, colour);
         }
 
-        if (range) {
+        if (sel && range) {
             sel.removeAllRanges();
             sel.addRange(range);
 
diff --git a/src/server/database.ts b/src/server/database.ts
index a42d29aac..374cedcbb 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -26,9 +26,9 @@ export class Database {
                     console.log(err.message);
                     console.log(err.errmsg);
                 }
-                if (res) {
-                    console.log(JSON.stringify(res.result));
-                }
+                // if (res) {
+                //     console.log(JSON.stringify(res.result));
+                // }
                 callback()
             });
         }
diff --git a/src/server/public/files/upload_011289d8c22f5422cf6f7e27a8c780ef.jpg b/src/server/public/files/upload_011289d8c22f5422cf6f7e27a8c780ef.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_011289d8c22f5422cf6f7e27a8c780ef.jpg differ
diff --git a/src/server/public/files/upload_01b38428ccc02261e24689b9d887b206.PNG b/src/server/public/files/upload_01b38428ccc02261e24689b9d887b206.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_01b38428ccc02261e24689b9d887b206.PNG differ
diff --git a/src/server/public/files/upload_01e81815633ee270c8b4ffca88a7773e.jpg b/src/server/public/files/upload_01e81815633ee270c8b4ffca88a7773e.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_01e81815633ee270c8b4ffca88a7773e.jpg differ
diff --git a/src/server/public/files/upload_031371580a23b87aaafb587ece875819.png b/src/server/public/files/upload_031371580a23b87aaafb587ece875819.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_031371580a23b87aaafb587ece875819.png differ
diff --git a/src/server/public/files/upload_03c44479fe728bbcc2f76dab407544d5.png b/src/server/public/files/upload_03c44479fe728bbcc2f76dab407544d5.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_03c44479fe728bbcc2f76dab407544d5.png differ
diff --git a/src/server/public/files/upload_05ceaf4112a0e3754a53ab59f4b1ff07.png b/src/server/public/files/upload_05ceaf4112a0e3754a53ab59f4b1ff07.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_05ceaf4112a0e3754a53ab59f4b1ff07.png differ
diff --git a/src/server/public/files/upload_061bdf72416cbef898d4cd1a0152ce1f.jpg b/src/server/public/files/upload_061bdf72416cbef898d4cd1a0152ce1f.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_061bdf72416cbef898d4cd1a0152ce1f.jpg differ
diff --git a/src/server/public/files/upload_07488f31d6eacb28458c11eb982fa654.jpg b/src/server/public/files/upload_07488f31d6eacb28458c11eb982fa654.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_07488f31d6eacb28458c11eb982fa654.jpg differ
diff --git a/src/server/public/files/upload_08afadab5ded674ec3f244b2bd124b8b.PNG b/src/server/public/files/upload_08afadab5ded674ec3f244b2bd124b8b.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_08afadab5ded674ec3f244b2bd124b8b.PNG differ
diff --git a/src/server/public/files/upload_09df67e3e4c3bcd2a571d8be5f0acf48.PNG b/src/server/public/files/upload_09df67e3e4c3bcd2a571d8be5f0acf48.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_09df67e3e4c3bcd2a571d8be5f0acf48.PNG differ
diff --git a/src/server/public/files/upload_10b64e99df3aac032564573c3699bda4.png b/src/server/public/files/upload_10b64e99df3aac032564573c3699bda4.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_10b64e99df3aac032564573c3699bda4.png differ
diff --git a/src/server/public/files/upload_11162dd1b00ed2a26923aa9d2fa9889e.png b/src/server/public/files/upload_11162dd1b00ed2a26923aa9d2fa9889e.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_11162dd1b00ed2a26923aa9d2fa9889e.png differ
diff --git a/src/server/public/files/upload_115d44864ee866f2986188501a59342b.png b/src/server/public/files/upload_115d44864ee866f2986188501a59342b.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_115d44864ee866f2986188501a59342b.png differ
diff --git a/src/server/public/files/upload_1465783317d01ed75e87e169b56928fd.PNG b/src/server/public/files/upload_1465783317d01ed75e87e169b56928fd.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_1465783317d01ed75e87e169b56928fd.PNG differ
diff --git a/src/server/public/files/upload_156cb9406f94654dea0e227e377f0a75.PNG b/src/server/public/files/upload_156cb9406f94654dea0e227e377f0a75.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_156cb9406f94654dea0e227e377f0a75.PNG differ
diff --git a/src/server/public/files/upload_166eb5d1d46cf268a59e01b4b32528cd.PNG b/src/server/public/files/upload_166eb5d1d46cf268a59e01b4b32528cd.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_166eb5d1d46cf268a59e01b4b32528cd.PNG differ
diff --git a/src/server/public/files/upload_17d2b2f3ec801e6fdcc6d29e523e946b.PNG b/src/server/public/files/upload_17d2b2f3ec801e6fdcc6d29e523e946b.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_17d2b2f3ec801e6fdcc6d29e523e946b.PNG differ
diff --git a/src/server/public/files/upload_1863f67c2c193452cc5192f0ebac2a7b.png b/src/server/public/files/upload_1863f67c2c193452cc5192f0ebac2a7b.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_1863f67c2c193452cc5192f0ebac2a7b.png differ
diff --git a/src/server/public/files/upload_18d64bf4a483e78a36153abcd26af503.png b/src/server/public/files/upload_18d64bf4a483e78a36153abcd26af503.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_18d64bf4a483e78a36153abcd26af503.png differ
diff --git a/src/server/public/files/upload_1a7925dd1140a2b5a7cc5c62ad20a8e8.png b/src/server/public/files/upload_1a7925dd1140a2b5a7cc5c62ad20a8e8.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_1a7925dd1140a2b5a7cc5c62ad20a8e8.png differ
diff --git a/src/server/public/files/upload_1c47eb68831fff82bc6982e5df4d02d5.PNG b/src/server/public/files/upload_1c47eb68831fff82bc6982e5df4d02d5.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_1c47eb68831fff82bc6982e5df4d02d5.PNG differ
diff --git a/src/server/public/files/upload_1c8f6233068ed1535384500768557e68.png b/src/server/public/files/upload_1c8f6233068ed1535384500768557e68.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_1c8f6233068ed1535384500768557e68.png differ
diff --git a/src/server/public/files/upload_1ea7ee56753d0327192da9666de9e305.PNG b/src/server/public/files/upload_1ea7ee56753d0327192da9666de9e305.PNG
new file mode 100644
index 000000000..acd759e09
Binary files /dev/null and b/src/server/public/files/upload_1ea7ee56753d0327192da9666de9e305.PNG differ
diff --git a/src/server/public/files/upload_2098441ee0ad89e51e825e2fb5f641d2.png b/src/server/public/files/upload_2098441ee0ad89e51e825e2fb5f641d2.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_2098441ee0ad89e51e825e2fb5f641d2.png differ
diff --git a/src/server/public/files/upload_23a30791fc2afceb92310a665d364dbd.jpg b/src/server/public/files/upload_23a30791fc2afceb92310a665d364dbd.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_23a30791fc2afceb92310a665d364dbd.jpg differ
diff --git a/src/server/public/files/upload_2436e82c2c54dd8f33f74e4440d11104.png b/src/server/public/files/upload_2436e82c2c54dd8f33f74e4440d11104.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_2436e82c2c54dd8f33f74e4440d11104.png differ
diff --git a/src/server/public/files/upload_2443e73bbbd67712bcedc04f0ead5617.PNG b/src/server/public/files/upload_2443e73bbbd67712bcedc04f0ead5617.PNG
new file mode 100644
index 000000000..acd759e09
Binary files /dev/null and b/src/server/public/files/upload_2443e73bbbd67712bcedc04f0ead5617.PNG differ
diff --git a/src/server/public/files/upload_2515fc22ce67c256bf3039bc84e54432.jpg b/src/server/public/files/upload_2515fc22ce67c256bf3039bc84e54432.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_2515fc22ce67c256bf3039bc84e54432.jpg differ
diff --git a/src/server/public/files/upload_280bb85121601366f65632649edd9425.jpg b/src/server/public/files/upload_280bb85121601366f65632649edd9425.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_280bb85121601366f65632649edd9425.jpg differ
diff --git a/src/server/public/files/upload_284a94d4ba485d6799ee772521846a59.jpg b/src/server/public/files/upload_284a94d4ba485d6799ee772521846a59.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_284a94d4ba485d6799ee772521846a59.jpg differ
diff --git a/src/server/public/files/upload_28550bbb245dfbe9b24b2354ed349f0c.jpg b/src/server/public/files/upload_28550bbb245dfbe9b24b2354ed349f0c.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_28550bbb245dfbe9b24b2354ed349f0c.jpg differ
diff --git a/src/server/public/files/upload_2abbef5268ab7ca77dad96775cb52345.png b/src/server/public/files/upload_2abbef5268ab7ca77dad96775cb52345.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_2abbef5268ab7ca77dad96775cb52345.png differ
diff --git a/src/server/public/files/upload_2b5ec0fba66b5b3b5e47a2c53a1005be.jpg b/src/server/public/files/upload_2b5ec0fba66b5b3b5e47a2c53a1005be.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_2b5ec0fba66b5b3b5e47a2c53a1005be.jpg differ
diff --git a/src/server/public/files/upload_2b6f66b8972b2f4991239720bb1df7d4.png b/src/server/public/files/upload_2b6f66b8972b2f4991239720bb1df7d4.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_2b6f66b8972b2f4991239720bb1df7d4.png differ
diff --git a/src/server/public/files/upload_2b75a95a64abfe997aa8e12a69d2bc43.png b/src/server/public/files/upload_2b75a95a64abfe997aa8e12a69d2bc43.png
new file mode 100644
index 000000000..5bac81362
Binary files /dev/null and b/src/server/public/files/upload_2b75a95a64abfe997aa8e12a69d2bc43.png differ
diff --git a/src/server/public/files/upload_2c689fe57038ad6a324297e92badd705.png b/src/server/public/files/upload_2c689fe57038ad6a324297e92badd705.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_2c689fe57038ad6a324297e92badd705.png differ
diff --git a/src/server/public/files/upload_2d86441185e38ed16bbf4234d8857e13.PNG b/src/server/public/files/upload_2d86441185e38ed16bbf4234d8857e13.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_2d86441185e38ed16bbf4234d8857e13.PNG differ
diff --git a/src/server/public/files/upload_2e68bb961e82faddbaa501c5c4e51795.png b/src/server/public/files/upload_2e68bb961e82faddbaa501c5c4e51795.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_2e68bb961e82faddbaa501c5c4e51795.png differ
diff --git a/src/server/public/files/upload_303438040b77efc92ad5a6617edf7000.png b/src/server/public/files/upload_303438040b77efc92ad5a6617edf7000.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_303438040b77efc92ad5a6617edf7000.png differ
diff --git a/src/server/public/files/upload_303b28fc64dd65c58382fe2b0a513190.jpg b/src/server/public/files/upload_303b28fc64dd65c58382fe2b0a513190.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_303b28fc64dd65c58382fe2b0a513190.jpg differ
diff --git a/src/server/public/files/upload_319f73a65644974e6502a79dfb60b777.jpg b/src/server/public/files/upload_319f73a65644974e6502a79dfb60b777.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_319f73a65644974e6502a79dfb60b777.jpg differ
diff --git a/src/server/public/files/upload_33290055bf62326211eb846bc32d7ab9.PNG b/src/server/public/files/upload_33290055bf62326211eb846bc32d7ab9.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_33290055bf62326211eb846bc32d7ab9.PNG differ
diff --git a/src/server/public/files/upload_352949a7d58209759d0ab420726acce3.png b/src/server/public/files/upload_352949a7d58209759d0ab420726acce3.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_352949a7d58209759d0ab420726acce3.png differ
diff --git a/src/server/public/files/upload_365e7ad24d7b896dc24525068e8f0fba.png b/src/server/public/files/upload_365e7ad24d7b896dc24525068e8f0fba.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_365e7ad24d7b896dc24525068e8f0fba.png differ
diff --git a/src/server/public/files/upload_38001ea16c21a82601586e9d7c823e5f.png b/src/server/public/files/upload_38001ea16c21a82601586e9d7c823e5f.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_38001ea16c21a82601586e9d7c823e5f.png differ
diff --git a/src/server/public/files/upload_3bb64e7eb87805eb7c2c67352ab3e3f6.PNG b/src/server/public/files/upload_3bb64e7eb87805eb7c2c67352ab3e3f6.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_3bb64e7eb87805eb7c2c67352ab3e3f6.PNG differ
diff --git a/src/server/public/files/upload_3cb5a8a9454a1b42a69a3d274c0a00e7.png b/src/server/public/files/upload_3cb5a8a9454a1b42a69a3d274c0a00e7.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_3cb5a8a9454a1b42a69a3d274c0a00e7.png differ
diff --git a/src/server/public/files/upload_3db8e110c40cb0770e79a61171eba542.jpg b/src/server/public/files/upload_3db8e110c40cb0770e79a61171eba542.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_3db8e110c40cb0770e79a61171eba542.jpg differ
diff --git a/src/server/public/files/upload_41fc781a82b1f33b631e5aa68c6a1a76.PNG b/src/server/public/files/upload_41fc781a82b1f33b631e5aa68c6a1a76.PNG
new file mode 100644
index 000000000..acd759e09
Binary files /dev/null and b/src/server/public/files/upload_41fc781a82b1f33b631e5aa68c6a1a76.PNG differ
diff --git a/src/server/public/files/upload_42ecb2422b80f6da3afb62543391f58c.PNG b/src/server/public/files/upload_42ecb2422b80f6da3afb62543391f58c.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_42ecb2422b80f6da3afb62543391f58c.PNG differ
diff --git a/src/server/public/files/upload_431be602a66059169d1f4c4cd4646476.PNG b/src/server/public/files/upload_431be602a66059169d1f4c4cd4646476.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_431be602a66059169d1f4c4cd4646476.PNG differ
diff --git a/src/server/public/files/upload_4543a61bc7c5f79c788b749a9ce7535a.PNG b/src/server/public/files/upload_4543a61bc7c5f79c788b749a9ce7535a.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_4543a61bc7c5f79c788b749a9ce7535a.PNG differ
diff --git a/src/server/public/files/upload_45e88f05b821eca6b85c1fc7b048af63.jpg b/src/server/public/files/upload_45e88f05b821eca6b85c1fc7b048af63.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_45e88f05b821eca6b85c1fc7b048af63.jpg differ
diff --git a/src/server/public/files/upload_493879305f109a4412223137f7031944.png b/src/server/public/files/upload_493879305f109a4412223137f7031944.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_493879305f109a4412223137f7031944.png differ
diff --git a/src/server/public/files/upload_49ac63665b883ea4702733fc8d70f6b1.PNG b/src/server/public/files/upload_49ac63665b883ea4702733fc8d70f6b1.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_49ac63665b883ea4702733fc8d70f6b1.PNG differ
diff --git a/src/server/public/files/upload_4bc2bdf9a392b8e567b964f59cfd32a8.PNG b/src/server/public/files/upload_4bc2bdf9a392b8e567b964f59cfd32a8.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_4bc2bdf9a392b8e567b964f59cfd32a8.PNG differ
diff --git a/src/server/public/files/upload_4e4560c1f0081b87af4c010792d799fc.jpg b/src/server/public/files/upload_4e4560c1f0081b87af4c010792d799fc.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_4e4560c1f0081b87af4c010792d799fc.jpg differ
diff --git a/src/server/public/files/upload_4e56e150f5c92f8980c4df49603747cc.png b/src/server/public/files/upload_4e56e150f5c92f8980c4df49603747cc.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_4e56e150f5c92f8980c4df49603747cc.png differ
diff --git a/src/server/public/files/upload_4e897855f35d0aa419d27bfaedae4435.png b/src/server/public/files/upload_4e897855f35d0aa419d27bfaedae4435.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_4e897855f35d0aa419d27bfaedae4435.png differ
diff --git a/src/server/public/files/upload_5495509165b60d3c04b8e509a7d187ba.PNG b/src/server/public/files/upload_5495509165b60d3c04b8e509a7d187ba.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_5495509165b60d3c04b8e509a7d187ba.PNG differ
diff --git a/src/server/public/files/upload_54d3dcb522f9fe3beb060103248a5ee9.png b/src/server/public/files/upload_54d3dcb522f9fe3beb060103248a5ee9.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_54d3dcb522f9fe3beb060103248a5ee9.png differ
diff --git a/src/server/public/files/upload_568996cd49829901200e1bec2b49a176.PNG b/src/server/public/files/upload_568996cd49829901200e1bec2b49a176.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_568996cd49829901200e1bec2b49a176.PNG differ
diff --git a/src/server/public/files/upload_5757898e2b8507438e1bdaf7b7fee4bb.png b/src/server/public/files/upload_5757898e2b8507438e1bdaf7b7fee4bb.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_5757898e2b8507438e1bdaf7b7fee4bb.png differ
diff --git a/src/server/public/files/upload_57c4353c9d3250bac49753be6b8973c6.png b/src/server/public/files/upload_57c4353c9d3250bac49753be6b8973c6.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_57c4353c9d3250bac49753be6b8973c6.png differ
diff --git a/src/server/public/files/upload_57f2901b84275f3e40ca9d41cb7c057d.PNG b/src/server/public/files/upload_57f2901b84275f3e40ca9d41cb7c057d.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_57f2901b84275f3e40ca9d41cb7c057d.PNG differ
diff --git a/src/server/public/files/upload_580d262f8a55c2fe3acbc6f0e690ad56.png b/src/server/public/files/upload_580d262f8a55c2fe3acbc6f0e690ad56.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_580d262f8a55c2fe3acbc6f0e690ad56.png differ
diff --git a/src/server/public/files/upload_58782b7ba1ac3963c4fdd5a987bf0aca.PNG b/src/server/public/files/upload_58782b7ba1ac3963c4fdd5a987bf0aca.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_58782b7ba1ac3963c4fdd5a987bf0aca.PNG differ
diff --git a/src/server/public/files/upload_588f580df306a338f7be0483dc30d576.jpg b/src/server/public/files/upload_588f580df306a338f7be0483dc30d576.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_588f580df306a338f7be0483dc30d576.jpg differ
diff --git a/src/server/public/files/upload_590c965f849c296ae3841689ce80b548.png b/src/server/public/files/upload_590c965f849c296ae3841689ce80b548.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_590c965f849c296ae3841689ce80b548.png differ
diff --git a/src/server/public/files/upload_5915c6516d7a7db8164f7e395b81973e.PNG b/src/server/public/files/upload_5915c6516d7a7db8164f7e395b81973e.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_5915c6516d7a7db8164f7e395b81973e.PNG differ
diff --git a/src/server/public/files/upload_594db1ea036d584bc71de979ce57832b.PNG b/src/server/public/files/upload_594db1ea036d584bc71de979ce57832b.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_594db1ea036d584bc71de979ce57832b.PNG differ
diff --git a/src/server/public/files/upload_5bf829a0b264b321b9b17267ab06a11b.png b/src/server/public/files/upload_5bf829a0b264b321b9b17267ab06a11b.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_5bf829a0b264b321b9b17267ab06a11b.png differ
diff --git a/src/server/public/files/upload_5cba158af25fb2058c1b2e130cd3fefc.png b/src/server/public/files/upload_5cba158af25fb2058c1b2e130cd3fefc.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_5cba158af25fb2058c1b2e130cd3fefc.png differ
diff --git a/src/server/public/files/upload_5d5f91ac61a5e55192c1de4402483126.png b/src/server/public/files/upload_5d5f91ac61a5e55192c1de4402483126.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_5d5f91ac61a5e55192c1de4402483126.png differ
diff --git a/src/server/public/files/upload_5ea64a359728395c1608b8783f6fb192.PNG b/src/server/public/files/upload_5ea64a359728395c1608b8783f6fb192.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_5ea64a359728395c1608b8783f6fb192.PNG differ
diff --git a/src/server/public/files/upload_5f0bace6da44bc7084c6ad01fcfe1012.png b/src/server/public/files/upload_5f0bace6da44bc7084c6ad01fcfe1012.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_5f0bace6da44bc7084c6ad01fcfe1012.png differ
diff --git a/src/server/public/files/upload_5fe9f9fe6eb4a78dcde144c9d4e5d6bb.png b/src/server/public/files/upload_5fe9f9fe6eb4a78dcde144c9d4e5d6bb.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_5fe9f9fe6eb4a78dcde144c9d4e5d6bb.png differ
diff --git a/src/server/public/files/upload_604cd121c8ecb30eb4d13cded9ce8746.png b/src/server/public/files/upload_604cd121c8ecb30eb4d13cded9ce8746.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_604cd121c8ecb30eb4d13cded9ce8746.png differ
diff --git a/src/server/public/files/upload_60f89c7647c6ed08d977e485fea9e033.jpg b/src/server/public/files/upload_60f89c7647c6ed08d977e485fea9e033.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_60f89c7647c6ed08d977e485fea9e033.jpg differ
diff --git a/src/server/public/files/upload_62900e4354015fc2bc78f00afbc1bc21.png b/src/server/public/files/upload_62900e4354015fc2bc78f00afbc1bc21.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_62900e4354015fc2bc78f00afbc1bc21.png differ
diff --git a/src/server/public/files/upload_62f1a7fa74224de7248c62d6d4242fd0.jpg b/src/server/public/files/upload_62f1a7fa74224de7248c62d6d4242fd0.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_62f1a7fa74224de7248c62d6d4242fd0.jpg differ
diff --git a/src/server/public/files/upload_63a7127553e83f381212c80f607c6db5.png b/src/server/public/files/upload_63a7127553e83f381212c80f607c6db5.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_63a7127553e83f381212c80f607c6db5.png differ
diff --git a/src/server/public/files/upload_63c37ab66ad16d68ef96670b1d04ee1c.png b/src/server/public/files/upload_63c37ab66ad16d68ef96670b1d04ee1c.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_63c37ab66ad16d68ef96670b1d04ee1c.png differ
diff --git a/src/server/public/files/upload_665fb37c1505fdf48a665990caf09d26.png b/src/server/public/files/upload_665fb37c1505fdf48a665990caf09d26.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_665fb37c1505fdf48a665990caf09d26.png differ
diff --git a/src/server/public/files/upload_67c7a2ac39caabaac7b90861a1314b8f.png b/src/server/public/files/upload_67c7a2ac39caabaac7b90861a1314b8f.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_67c7a2ac39caabaac7b90861a1314b8f.png differ
diff --git a/src/server/public/files/upload_680394edbfb281065997525cc114f996.PNG b/src/server/public/files/upload_680394edbfb281065997525cc114f996.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_680394edbfb281065997525cc114f996.PNG differ
diff --git a/src/server/public/files/upload_684090a0c50a035aaa0df23c18043df8.png b/src/server/public/files/upload_684090a0c50a035aaa0df23c18043df8.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_684090a0c50a035aaa0df23c18043df8.png differ
diff --git a/src/server/public/files/upload_6bd24b1193058458d6ea268b06963931.PNG b/src/server/public/files/upload_6bd24b1193058458d6ea268b06963931.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_6bd24b1193058458d6ea268b06963931.PNG differ
diff --git a/src/server/public/files/upload_6bde8211989cf1b540b3c6dcf16cd51a.PNG b/src/server/public/files/upload_6bde8211989cf1b540b3c6dcf16cd51a.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_6bde8211989cf1b540b3c6dcf16cd51a.PNG differ
diff --git a/src/server/public/files/upload_6d5051266bd739f260105072299b5c79.png b/src/server/public/files/upload_6d5051266bd739f260105072299b5c79.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_6d5051266bd739f260105072299b5c79.png differ
diff --git a/src/server/public/files/upload_6d9a3ec37d85659265f2b622a6d24dcb.jpg b/src/server/public/files/upload_6d9a3ec37d85659265f2b622a6d24dcb.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_6d9a3ec37d85659265f2b622a6d24dcb.jpg differ
diff --git a/src/server/public/files/upload_6f222fae83f0185e361bf6a757ba9800.png b/src/server/public/files/upload_6f222fae83f0185e361bf6a757ba9800.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_6f222fae83f0185e361bf6a757ba9800.png differ
diff --git a/src/server/public/files/upload_6f5b0c8a474e44d76cbbea5f629e8fba.png b/src/server/public/files/upload_6f5b0c8a474e44d76cbbea5f629e8fba.png
new file mode 100644
index 000000000..5bac81362
Binary files /dev/null and b/src/server/public/files/upload_6f5b0c8a474e44d76cbbea5f629e8fba.png differ
diff --git a/src/server/public/files/upload_7068f1e2ec782980b4eded2ff974da7f.jpg b/src/server/public/files/upload_7068f1e2ec782980b4eded2ff974da7f.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_7068f1e2ec782980b4eded2ff974da7f.jpg differ
diff --git a/src/server/public/files/upload_72c05840ea8bddc44575309923d4c63b.jpg b/src/server/public/files/upload_72c05840ea8bddc44575309923d4c63b.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_72c05840ea8bddc44575309923d4c63b.jpg differ
diff --git a/src/server/public/files/upload_73b42f2f26e037078c1f1c63913fc9aa.jpg b/src/server/public/files/upload_73b42f2f26e037078c1f1c63913fc9aa.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_73b42f2f26e037078c1f1c63913fc9aa.jpg differ
diff --git a/src/server/public/files/upload_7412310b57204cb3c36d5aba670589fc.PNG b/src/server/public/files/upload_7412310b57204cb3c36d5aba670589fc.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_7412310b57204cb3c36d5aba670589fc.PNG differ
diff --git a/src/server/public/files/upload_75d331ba20a80ca0f6fd18bc0b37b8b3.PNG b/src/server/public/files/upload_75d331ba20a80ca0f6fd18bc0b37b8b3.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_75d331ba20a80ca0f6fd18bc0b37b8b3.PNG differ
diff --git a/src/server/public/files/upload_77eab9365e5c11d5e1ac08fca6c862ab.png b/src/server/public/files/upload_77eab9365e5c11d5e1ac08fca6c862ab.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_77eab9365e5c11d5e1ac08fca6c862ab.png differ
diff --git a/src/server/public/files/upload_786b4b94fda60ba60da3dfe2be091b6b.png b/src/server/public/files/upload_786b4b94fda60ba60da3dfe2be091b6b.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_786b4b94fda60ba60da3dfe2be091b6b.png differ
diff --git a/src/server/public/files/upload_793745d2b6c8a5c3dbeea343b9332846.PNG b/src/server/public/files/upload_793745d2b6c8a5c3dbeea343b9332846.PNG
new file mode 100644
index 000000000..acd759e09
Binary files /dev/null and b/src/server/public/files/upload_793745d2b6c8a5c3dbeea343b9332846.PNG differ
diff --git a/src/server/public/files/upload_7c369564861f079a17bd77081ace597b.PNG b/src/server/public/files/upload_7c369564861f079a17bd77081ace597b.PNG
new file mode 100644
index 000000000..acd759e09
Binary files /dev/null and b/src/server/public/files/upload_7c369564861f079a17bd77081ace597b.PNG differ
diff --git a/src/server/public/files/upload_7c7f3f317ea27a2ddad9e8a34f636b78.png b/src/server/public/files/upload_7c7f3f317ea27a2ddad9e8a34f636b78.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_7c7f3f317ea27a2ddad9e8a34f636b78.png differ
diff --git a/src/server/public/files/upload_7f357bbbe7756dabad0b9cb49c9ceb0c.PNG b/src/server/public/files/upload_7f357bbbe7756dabad0b9cb49c9ceb0c.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_7f357bbbe7756dabad0b9cb49c9ceb0c.PNG differ
diff --git a/src/server/public/files/upload_814802d9faf217a7b09c1c95c4ecfd05.png b/src/server/public/files/upload_814802d9faf217a7b09c1c95c4ecfd05.png
new file mode 100644
index 000000000..4bd1e688a
Binary files /dev/null and b/src/server/public/files/upload_814802d9faf217a7b09c1c95c4ecfd05.png differ
diff --git a/src/server/public/files/upload_83180cda4fa509f6ff0654376c1da7f2.jpg b/src/server/public/files/upload_83180cda4fa509f6ff0654376c1da7f2.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_83180cda4fa509f6ff0654376c1da7f2.jpg differ
diff --git a/src/server/public/files/upload_8321ece56de47e0bf5345fe30d23414e.PNG b/src/server/public/files/upload_8321ece56de47e0bf5345fe30d23414e.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_8321ece56de47e0bf5345fe30d23414e.PNG differ
diff --git a/src/server/public/files/upload_871f1af44ec7a2140f32433f3015d603.jpg b/src/server/public/files/upload_871f1af44ec7a2140f32433f3015d603.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_871f1af44ec7a2140f32433f3015d603.jpg differ
diff --git a/src/server/public/files/upload_892d1d9d981f679fedaa2c5bf193fd20.jpg b/src/server/public/files/upload_892d1d9d981f679fedaa2c5bf193fd20.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_892d1d9d981f679fedaa2c5bf193fd20.jpg differ
diff --git a/src/server/public/files/upload_89721bf0d244b1fcc52f70a7d9e3593b.jpg b/src/server/public/files/upload_89721bf0d244b1fcc52f70a7d9e3593b.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_89721bf0d244b1fcc52f70a7d9e3593b.jpg differ
diff --git a/src/server/public/files/upload_89e66f8d531a44a63fa3355602813ae7.png b/src/server/public/files/upload_89e66f8d531a44a63fa3355602813ae7.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_89e66f8d531a44a63fa3355602813ae7.png differ
diff --git a/src/server/public/files/upload_8a6090d2b978221d0810b954f7b14065.PNG b/src/server/public/files/upload_8a6090d2b978221d0810b954f7b14065.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_8a6090d2b978221d0810b954f7b14065.PNG differ
diff --git a/src/server/public/files/upload_8b92f6ffc9a8c8353fd141c6dc71895f.jpg b/src/server/public/files/upload_8b92f6ffc9a8c8353fd141c6dc71895f.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_8b92f6ffc9a8c8353fd141c6dc71895f.jpg differ
diff --git a/src/server/public/files/upload_8bfca62429aada8f6b5dae1dcde24cab.png b/src/server/public/files/upload_8bfca62429aada8f6b5dae1dcde24cab.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_8bfca62429aada8f6b5dae1dcde24cab.png differ
diff --git a/src/server/public/files/upload_8e9e27d1918f3a0a5f242397079e66fa.png b/src/server/public/files/upload_8e9e27d1918f3a0a5f242397079e66fa.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_8e9e27d1918f3a0a5f242397079e66fa.png differ
diff --git a/src/server/public/files/upload_8ef0953ff77d42848b6e7f37e9bd506d.PNG b/src/server/public/files/upload_8ef0953ff77d42848b6e7f37e9bd506d.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_8ef0953ff77d42848b6e7f37e9bd506d.PNG differ
diff --git a/src/server/public/files/upload_902c6b9c79da47ec00238f4831795077.jpg b/src/server/public/files/upload_902c6b9c79da47ec00238f4831795077.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_902c6b9c79da47ec00238f4831795077.jpg differ
diff --git a/src/server/public/files/upload_90d380b38492369c1470868b019f0d0c.jpg b/src/server/public/files/upload_90d380b38492369c1470868b019f0d0c.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_90d380b38492369c1470868b019f0d0c.jpg differ
diff --git a/src/server/public/files/upload_95af42b1b5db521aa1539980fe85e71a.jpg b/src/server/public/files/upload_95af42b1b5db521aa1539980fe85e71a.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_95af42b1b5db521aa1539980fe85e71a.jpg differ
diff --git a/src/server/public/files/upload_97031060c857a0e8f15af3fcc038a2b8.jpg b/src/server/public/files/upload_97031060c857a0e8f15af3fcc038a2b8.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_97031060c857a0e8f15af3fcc038a2b8.jpg differ
diff --git a/src/server/public/files/upload_97903efc6c08245acaea37eefacf289f.png b/src/server/public/files/upload_97903efc6c08245acaea37eefacf289f.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_97903efc6c08245acaea37eefacf289f.png differ
diff --git a/src/server/public/files/upload_9974b3459e182fef33b8e467058b7525.png b/src/server/public/files/upload_9974b3459e182fef33b8e467058b7525.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_9974b3459e182fef33b8e467058b7525.png differ
diff --git a/src/server/public/files/upload_9a8429dac8feba05bbda2b53b5da4efa.jpg b/src/server/public/files/upload_9a8429dac8feba05bbda2b53b5da4efa.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_9a8429dac8feba05bbda2b53b5da4efa.jpg differ
diff --git a/src/server/public/files/upload_9bbc86def05076b89080adcc37481324.jpg b/src/server/public/files/upload_9bbc86def05076b89080adcc37481324.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_9bbc86def05076b89080adcc37481324.jpg differ
diff --git a/src/server/public/files/upload_9cd52fc451f3d459021127a7d51d108b.png b/src/server/public/files/upload_9cd52fc451f3d459021127a7d51d108b.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_9cd52fc451f3d459021127a7d51d108b.png differ
diff --git a/src/server/public/files/upload_9de9f004f258cee8c55bf872362d6453.jpg b/src/server/public/files/upload_9de9f004f258cee8c55bf872362d6453.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_9de9f004f258cee8c55bf872362d6453.jpg differ
diff --git a/src/server/public/files/upload_a0286ae2a1eec8c31c8276ec067d91aa.png b/src/server/public/files/upload_a0286ae2a1eec8c31c8276ec067d91aa.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_a0286ae2a1eec8c31c8276ec067d91aa.png differ
diff --git a/src/server/public/files/upload_a04473da584e5a602d0f8984a670a91c.jpg b/src/server/public/files/upload_a04473da584e5a602d0f8984a670a91c.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_a04473da584e5a602d0f8984a670a91c.jpg differ
diff --git a/src/server/public/files/upload_a2cf3f6ab4ab5b249ebf8100e461aa27.png b/src/server/public/files/upload_a2cf3f6ab4ab5b249ebf8100e461aa27.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_a2cf3f6ab4ab5b249ebf8100e461aa27.png differ
diff --git a/src/server/public/files/upload_a3095034ec19353c9ee55221088f01d8.PNG b/src/server/public/files/upload_a3095034ec19353c9ee55221088f01d8.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_a3095034ec19353c9ee55221088f01d8.PNG differ
diff --git a/src/server/public/files/upload_a520a6856021104b8130f8d3b42d85c0.png b/src/server/public/files/upload_a520a6856021104b8130f8d3b42d85c0.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_a520a6856021104b8130f8d3b42d85c0.png differ
diff --git a/src/server/public/files/upload_a5c26f52ddcbd705ffbd3dfd7cd2ce40.jpg b/src/server/public/files/upload_a5c26f52ddcbd705ffbd3dfd7cd2ce40.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_a5c26f52ddcbd705ffbd3dfd7cd2ce40.jpg differ
diff --git a/src/server/public/files/upload_a60864ff7892974e941e42a2441e530d.png b/src/server/public/files/upload_a60864ff7892974e941e42a2441e530d.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_a60864ff7892974e941e42a2441e530d.png differ
diff --git a/src/server/public/files/upload_a77f86dbeaa0c3cecc2516f9d9cecf1d.png b/src/server/public/files/upload_a77f86dbeaa0c3cecc2516f9d9cecf1d.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_a77f86dbeaa0c3cecc2516f9d9cecf1d.png differ
diff --git a/src/server/public/files/upload_aa2bbeb0871a2d6b4b54524ac2758270.jpg b/src/server/public/files/upload_aa2bbeb0871a2d6b4b54524ac2758270.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_aa2bbeb0871a2d6b4b54524ac2758270.jpg differ
diff --git a/src/server/public/files/upload_aa9fec4828bfdfef3ae20d43997fb274.png b/src/server/public/files/upload_aa9fec4828bfdfef3ae20d43997fb274.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_aa9fec4828bfdfef3ae20d43997fb274.png differ
diff --git a/src/server/public/files/upload_ab2bba2cee88991561f20d7cd69ec6e1.png b/src/server/public/files/upload_ab2bba2cee88991561f20d7cd69ec6e1.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_ab2bba2cee88991561f20d7cd69ec6e1.png differ
diff --git a/src/server/public/files/upload_ae1c01e481fc6b753c78605bfbe680b9.png b/src/server/public/files/upload_ae1c01e481fc6b753c78605bfbe680b9.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_ae1c01e481fc6b753c78605bfbe680b9.png differ
diff --git a/src/server/public/files/upload_b0d25a0dccc640f78dcbc291f930b5d6.png b/src/server/public/files/upload_b0d25a0dccc640f78dcbc291f930b5d6.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_b0d25a0dccc640f78dcbc291f930b5d6.png differ
diff --git a/src/server/public/files/upload_b0e527b27b7cabf651b29c9d834d24f3.PNG b/src/server/public/files/upload_b0e527b27b7cabf651b29c9d834d24f3.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_b0e527b27b7cabf651b29c9d834d24f3.PNG differ
diff --git a/src/server/public/files/upload_b0ea7e49e84cf91075962a75c76d3233.png b/src/server/public/files/upload_b0ea7e49e84cf91075962a75c76d3233.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_b0ea7e49e84cf91075962a75c76d3233.png differ
diff --git a/src/server/public/files/upload_b1b334829e1d99d7cd32fae5044cc103.PNG b/src/server/public/files/upload_b1b334829e1d99d7cd32fae5044cc103.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_b1b334829e1d99d7cd32fae5044cc103.PNG differ
diff --git a/src/server/public/files/upload_b52adf6e1ecdbbe76aa3c038ddc3b02b.PNG b/src/server/public/files/upload_b52adf6e1ecdbbe76aa3c038ddc3b02b.PNG
new file mode 100644
index 000000000..acd759e09
Binary files /dev/null and b/src/server/public/files/upload_b52adf6e1ecdbbe76aa3c038ddc3b02b.PNG differ
diff --git a/src/server/public/files/upload_b6c992aa6c434520887521e11bb5f492.png b/src/server/public/files/upload_b6c992aa6c434520887521e11bb5f492.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_b6c992aa6c434520887521e11bb5f492.png differ
diff --git a/src/server/public/files/upload_ba04eb816ef3bec770aa7eebf3b21cb1.PNG b/src/server/public/files/upload_ba04eb816ef3bec770aa7eebf3b21cb1.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_ba04eb816ef3bec770aa7eebf3b21cb1.PNG differ
diff --git a/src/server/public/files/upload_ba58066314a79d1c845241fb4400e44d.png b/src/server/public/files/upload_ba58066314a79d1c845241fb4400e44d.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_ba58066314a79d1c845241fb4400e44d.png differ
diff --git a/src/server/public/files/upload_bb6b521b4df6bd783d6a5b83a66caa0a.jpg b/src/server/public/files/upload_bb6b521b4df6bd783d6a5b83a66caa0a.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_bb6b521b4df6bd783d6a5b83a66caa0a.jpg differ
diff --git a/src/server/public/files/upload_bcb173a15068ded66b39623ab18bfdb7.jpg b/src/server/public/files/upload_bcb173a15068ded66b39623ab18bfdb7.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_bcb173a15068ded66b39623ab18bfdb7.jpg differ
diff --git a/src/server/public/files/upload_be4034faba6a5313dfb8a53e18ffc188.png b/src/server/public/files/upload_be4034faba6a5313dfb8a53e18ffc188.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_be4034faba6a5313dfb8a53e18ffc188.png differ
diff --git a/src/server/public/files/upload_be5f90995b34cd30d9a269b5a9f10fcc.PNG b/src/server/public/files/upload_be5f90995b34cd30d9a269b5a9f10fcc.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_be5f90995b34cd30d9a269b5a9f10fcc.PNG differ
diff --git a/src/server/public/files/upload_c1a964ca145f45e7e666cfaf7c8398e3.png b/src/server/public/files/upload_c1a964ca145f45e7e666cfaf7c8398e3.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_c1a964ca145f45e7e666cfaf7c8398e3.png differ
diff --git a/src/server/public/files/upload_c6764790a8182d796acddb76d3df595e.PNG b/src/server/public/files/upload_c6764790a8182d796acddb76d3df595e.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_c6764790a8182d796acddb76d3df595e.PNG differ
diff --git a/src/server/public/files/upload_c81bff72d05e0bb7f13376a46506ec2a.jpg b/src/server/public/files/upload_c81bff72d05e0bb7f13376a46506ec2a.jpg
new file mode 100644
index 000000000..9bb7f57d2
Binary files /dev/null and b/src/server/public/files/upload_c81bff72d05e0bb7f13376a46506ec2a.jpg differ
diff --git a/src/server/public/files/upload_c98c0184a19b621b2bc0575b49996a69.png b/src/server/public/files/upload_c98c0184a19b621b2bc0575b49996a69.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_c98c0184a19b621b2bc0575b49996a69.png differ
diff --git a/src/server/public/files/upload_cb4d875a9f73d1f735da5ae82dce6eb1.jpg b/src/server/public/files/upload_cb4d875a9f73d1f735da5ae82dce6eb1.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_cb4d875a9f73d1f735da5ae82dce6eb1.jpg differ
diff --git a/src/server/public/files/upload_cd08524995e5f124505fe3d7ab38a41f.png b/src/server/public/files/upload_cd08524995e5f124505fe3d7ab38a41f.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_cd08524995e5f124505fe3d7ab38a41f.png differ
diff --git a/src/server/public/files/upload_cd73b55488fcc7e5e5e5f2a4c43bc1fd.png b/src/server/public/files/upload_cd73b55488fcc7e5e5e5f2a4c43bc1fd.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_cd73b55488fcc7e5e5e5f2a4c43bc1fd.png differ
diff --git a/src/server/public/files/upload_ce357d5a6d727ac44110c60dd379428f.png b/src/server/public/files/upload_ce357d5a6d727ac44110c60dd379428f.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_ce357d5a6d727ac44110c60dd379428f.png differ
diff --git a/src/server/public/files/upload_d00c34a1e1c319a9373b5f136c38a866.jpg b/src/server/public/files/upload_d00c34a1e1c319a9373b5f136c38a866.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_d00c34a1e1c319a9373b5f136c38a866.jpg differ
diff --git a/src/server/public/files/upload_d242527805ab4c52f2385ae57b85e6fb.png b/src/server/public/files/upload_d242527805ab4c52f2385ae57b85e6fb.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_d242527805ab4c52f2385ae57b85e6fb.png differ
diff --git a/src/server/public/files/upload_d28007fa68c809cd3c0a78d4a8727275.png b/src/server/public/files/upload_d28007fa68c809cd3c0a78d4a8727275.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_d28007fa68c809cd3c0a78d4a8727275.png differ
diff --git a/src/server/public/files/upload_d35ea9c62bf8da0352a16193b880469e.jpg b/src/server/public/files/upload_d35ea9c62bf8da0352a16193b880469e.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_d35ea9c62bf8da0352a16193b880469e.jpg differ
diff --git a/src/server/public/files/upload_d4e0e9d2aee31b5931129dc3bc83e813.png b/src/server/public/files/upload_d4e0e9d2aee31b5931129dc3bc83e813.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_d4e0e9d2aee31b5931129dc3bc83e813.png differ
diff --git a/src/server/public/files/upload_d5801c03d9562a612d1907085ba39373.jpg b/src/server/public/files/upload_d5801c03d9562a612d1907085ba39373.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_d5801c03d9562a612d1907085ba39373.jpg differ
diff --git a/src/server/public/files/upload_d6d0e3a302e0edafa70785c39041e522.PNG b/src/server/public/files/upload_d6d0e3a302e0edafa70785c39041e522.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_d6d0e3a302e0edafa70785c39041e522.PNG differ
diff --git a/src/server/public/files/upload_d6e725cc697751599b177e0cf330b9f2.jpg b/src/server/public/files/upload_d6e725cc697751599b177e0cf330b9f2.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_d6e725cc697751599b177e0cf330b9f2.jpg differ
diff --git a/src/server/public/files/upload_d794d56fcab3b93b6c79aa201cba34f8.png b/src/server/public/files/upload_d794d56fcab3b93b6c79aa201cba34f8.png
new file mode 100644
index 000000000..4bd1e688a
Binary files /dev/null and b/src/server/public/files/upload_d794d56fcab3b93b6c79aa201cba34f8.png differ
diff --git a/src/server/public/files/upload_d8abeaef04a42678432e5e1d3377a2d0.png b/src/server/public/files/upload_d8abeaef04a42678432e5e1d3377a2d0.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_d8abeaef04a42678432e5e1d3377a2d0.png differ
diff --git a/src/server/public/files/upload_dac5245c657d29c1c4dfba98472fb50d.pdf b/src/server/public/files/upload_dac5245c657d29c1c4dfba98472fb50d.pdf
new file mode 100644
index 000000000..7c9fa9a6f
Binary files /dev/null and b/src/server/public/files/upload_dac5245c657d29c1c4dfba98472fb50d.pdf differ
diff --git a/src/server/public/files/upload_db3c5a0b560c9da35607359d86cf2b97.png b/src/server/public/files/upload_db3c5a0b560c9da35607359d86cf2b97.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_db3c5a0b560c9da35607359d86cf2b97.png differ
diff --git a/src/server/public/files/upload_dc8821dc596b1938c855e0affbe614dd.png b/src/server/public/files/upload_dc8821dc596b1938c855e0affbe614dd.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_dc8821dc596b1938c855e0affbe614dd.png differ
diff --git a/src/server/public/files/upload_df7697c4832990aad0e8d9f0daa1711b.png b/src/server/public/files/upload_df7697c4832990aad0e8d9f0daa1711b.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_df7697c4832990aad0e8d9f0daa1711b.png differ
diff --git a/src/server/public/files/upload_e009e45db30e47383fad5d71908dc69d.png b/src/server/public/files/upload_e009e45db30e47383fad5d71908dc69d.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_e009e45db30e47383fad5d71908dc69d.png differ
diff --git a/src/server/public/files/upload_e15738d092ff38197dc1fb41a4747d16.jpg b/src/server/public/files/upload_e15738d092ff38197dc1fb41a4747d16.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_e15738d092ff38197dc1fb41a4747d16.jpg differ
diff --git a/src/server/public/files/upload_e51079d9af97a412353d63c60e1ea96d.png b/src/server/public/files/upload_e51079d9af97a412353d63c60e1ea96d.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_e51079d9af97a412353d63c60e1ea96d.png differ
diff --git a/src/server/public/files/upload_e5422cdcfa80628842ff3aacf14ad25e.png b/src/server/public/files/upload_e5422cdcfa80628842ff3aacf14ad25e.png
new file mode 100644
index 000000000..c178a9106
Binary files /dev/null and b/src/server/public/files/upload_e5422cdcfa80628842ff3aacf14ad25e.png differ
diff --git a/src/server/public/files/upload_e6256196c38216ac539ef7e8ee28ab02.png b/src/server/public/files/upload_e6256196c38216ac539ef7e8ee28ab02.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_e6256196c38216ac539ef7e8ee28ab02.png differ
diff --git a/src/server/public/files/upload_e9668199bd12c905f27be76b11366d68.png b/src/server/public/files/upload_e9668199bd12c905f27be76b11366d68.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_e9668199bd12c905f27be76b11366d68.png differ
diff --git a/src/server/public/files/upload_e98ebeeebd5f0ddc98ea9b3a2d9f40d2.png b/src/server/public/files/upload_e98ebeeebd5f0ddc98ea9b3a2d9f40d2.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_e98ebeeebd5f0ddc98ea9b3a2d9f40d2.png differ
diff --git a/src/server/public/files/upload_eb2f002129e0cfb5c940f6b4ecc5a8f2.jpg b/src/server/public/files/upload_eb2f002129e0cfb5c940f6b4ecc5a8f2.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_eb2f002129e0cfb5c940f6b4ecc5a8f2.jpg differ
diff --git a/src/server/public/files/upload_ec02ff06b3154d269086e23ebf6ad942.png b/src/server/public/files/upload_ec02ff06b3154d269086e23ebf6ad942.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_ec02ff06b3154d269086e23ebf6ad942.png differ
diff --git a/src/server/public/files/upload_ef3873102a70850a4da2040eab002c55.jpg b/src/server/public/files/upload_ef3873102a70850a4da2040eab002c55.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_ef3873102a70850a4da2040eab002c55.jpg differ
diff --git a/src/server/public/files/upload_f259c9aa37b0919d6d20ad8d75e8329e.png b/src/server/public/files/upload_f259c9aa37b0919d6d20ad8d75e8329e.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_f259c9aa37b0919d6d20ad8d75e8329e.png differ
diff --git a/src/server/public/files/upload_f48143c75b8c2412785e3ed31ebbb150.PNG b/src/server/public/files/upload_f48143c75b8c2412785e3ed31ebbb150.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_f48143c75b8c2412785e3ed31ebbb150.PNG differ
diff --git a/src/server/public/files/upload_f5347da10a373480807fc3469533b9fe.png b/src/server/public/files/upload_f5347da10a373480807fc3469533b9fe.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_f5347da10a373480807fc3469533b9fe.png differ
diff --git a/src/server/public/files/upload_f6e36f6d3de7a2036eeff617f186b78f.png b/src/server/public/files/upload_f6e36f6d3de7a2036eeff617f186b78f.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_f6e36f6d3de7a2036eeff617f186b78f.png differ
diff --git a/src/server/public/files/upload_f6f1ed7a268523278457de08ee61bcfa.PNG b/src/server/public/files/upload_f6f1ed7a268523278457de08ee61bcfa.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_f6f1ed7a268523278457de08ee61bcfa.PNG differ
diff --git a/src/server/public/files/upload_f73607b7cf818631165aca9d32ef19c5.jpg b/src/server/public/files/upload_f73607b7cf818631165aca9d32ef19c5.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_f73607b7cf818631165aca9d32ef19c5.jpg differ
diff --git a/src/server/public/files/upload_f804a6c7ec796718d66108af0b1e6d0e.png b/src/server/public/files/upload_f804a6c7ec796718d66108af0b1e6d0e.png
new file mode 100644
index 000000000..2ff127e97
Binary files /dev/null and b/src/server/public/files/upload_f804a6c7ec796718d66108af0b1e6d0e.png differ
diff --git a/src/server/public/files/upload_f9ed03ab046d7f579d6f531df9c4050a.PNG b/src/server/public/files/upload_f9ed03ab046d7f579d6f531df9c4050a.PNG
new file mode 100644
index 000000000..2b64654dd
Binary files /dev/null and b/src/server/public/files/upload_f9ed03ab046d7f579d6f531df9c4050a.PNG differ
diff --git a/src/server/public/files/upload_fac4f686367ad63f2fcc1d8b4f74f123.png b/src/server/public/files/upload_fac4f686367ad63f2fcc1d8b4f74f123.png
new file mode 100644
index 000000000..0bacb6189
Binary files /dev/null and b/src/server/public/files/upload_fac4f686367ad63f2fcc1d8b4f74f123.png differ
diff --git a/src/server/public/files/upload_faebd8bdc2b00efe50b232c9cf4e63b6.PNG b/src/server/public/files/upload_faebd8bdc2b00efe50b232c9cf4e63b6.PNG
new file mode 100644
index 000000000..9105f02e3
Binary files /dev/null and b/src/server/public/files/upload_faebd8bdc2b00efe50b232c9cf4e63b6.PNG differ
diff --git a/src/server/public/files/upload_fb3cc3a1d715a6ffa1c72bde91faf3af.png b/src/server/public/files/upload_fb3cc3a1d715a6ffa1c72bde91faf3af.png
new file mode 100644
index 000000000..23e8efe47
Binary files /dev/null and b/src/server/public/files/upload_fb3cc3a1d715a6ffa1c72bde91faf3af.png differ
diff --git a/src/server/public/files/upload_fe2dbe44900645eba61207748f63ade3.jpg b/src/server/public/files/upload_fe2dbe44900645eba61207748f63ade3.jpg
new file mode 100644
index 000000000..756f4572f
Binary files /dev/null and b/src/server/public/files/upload_fe2dbe44900645eba61207748f63ade3.jpg differ
diff --git a/src/server/public/files/upload_ffd58755419ab98f03d56fedd16736e9.jpg b/src/server/public/files/upload_ffd58755419ab98f03d56fedd16736e9.jpg
new file mode 100644
index 000000000..bb78e4c23
Binary files /dev/null and b/src/server/public/files/upload_ffd58755419ab98f03d56fedd16736e9.jpg differ
-- 
cgit v1.2.3-70-g09d2


From a72fcdd0ebc06a3c851007c6ed89ab13a9a0d835 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 8 Apr 2019 18:07:13 -0400
Subject: fixed up a bunch of small things + started on text wackiness.

---
 src/client/util/DocumentManager.ts                 |   2 +-
 src/client/util/DragManager.ts                     |   3 +-
 src/client/util/SelectionManager.ts                |   2 +
 src/client/views/InkingCanvas.scss                 |   3 +
 src/client/views/Main.scss                         |  23 +-
 src/client/views/Main.tsx                          |  66 ++--
 .../CollectionFreeFormLinksView.tsx                |  91 +++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   2 +
 src/client/views/nodes/FormattedTextBox.tsx        | 338 ++++++++++++---------
 9 files changed, 313 insertions(+), 217 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index bf59fbb43..341959936 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -29,7 +29,7 @@ export class DocumentManager {
 
     public getAllDocumentViews(collection: Document) {
         return this.DocumentViews.filter(dv =>
-            dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document == collection);
+            dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document.Id === collection.Id);
     }
 
     public getDocumentView(toFind: Document): DocumentView | null {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index c1919093e..0ee7ed2b3 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,7 +1,5 @@
 import { action } from "mobx";
 import { Document } from "../../fields/Document";
-import { ImageField } from "../../fields/ImageField";
-import { KeyStore } from "../../fields/KeyStore";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import { CollectionView } from "../views/collections/CollectionView";
 import { DocumentDecorations } from "../views/DocumentDecorations";
@@ -18,6 +16,7 @@ export function setupDrag(
             e.stopPropagation();
             e.preventDefault();
 
+            // TODO: bcz -- this needs to have a drag threshold so that it doesn't trigger when just selecting.
             document.removeEventListener("pointermove", onRowMove);
             document.removeEventListener("pointerup", onRowUp);
             var dragData = new DragManager.DocumentDragData([docFunc()]);
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 958c14491..79d4ceb25 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,6 +1,7 @@
 import { observable, action } from "mobx";
 import { DocumentView } from "../views/nodes/DocumentView";
 import { Document } from "../../fields/Document";
+import { Main } from "../views/Main";
 
 export namespace SelectionManager {
     class Manager {
@@ -40,6 +41,7 @@ export namespace SelectionManager {
         }
         manager.SelectedDocuments.length = 0;
         if (found) manager.SelectedDocuments.push(found);
+        Main.Instance.SetTextDoc(undefined, undefined);
     }
 
     export function SelectedDocuments(): Array<DocumentView> {
diff --git a/src/client/views/InkingCanvas.scss b/src/client/views/InkingCanvas.scss
index c5a2a50cb..42ae38c73 100644
--- a/src/client/views/InkingCanvas.scss
+++ b/src/client/views/InkingCanvas.scss
@@ -1,5 +1,8 @@
 @import "global_variables";
 
+.inkingCanvas {
+    opacity:0.99;
+}
 .inkingCanvas-paths-ink, .inkingCanvas-paths-markers, .inkingCanvas-noSelect, .inkingCanvas-canSelect {
     position: absolute;
     top: 0;
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 594803aab..fe7f007b0 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -4,7 +4,7 @@ html,
 body {
     width: 100%;
     height: 100%;
-    overflow: hidden;
+    overflow: auto;
     font-family: $sans-serif;
     margin: 0;
     position:absolute;
@@ -157,12 +157,33 @@ button:hover {
         cursor: pointer;
     }
 }
+#root {
+    overflow: visible;
+}
 #main-div {
     width:100%;
     height:100%;
     position:absolute;
     top: 0;
     left:0;
+    overflow: scroll;
+}
+.mainDiv-textInput {
+    background:pink;
+        width: 200px;
+        height: 200px;
+        position:absolute;
+        overflow: visible;
+        top: 0;
+        left: 0;
+        .formattedTextBox-cont {
+            background:pink;
+            width: 100%;
+            height: 100%;
+            position:absolute;
+            top: 0;
+            left: 0;
+    }
 }
 #mainContent-div {
     width:100%;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index b4ab9e21d..c9467e130 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -1,7 +1,7 @@
 import { IconName, library } from '@fortawesome/fontawesome-svg-core';
 import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, configure, observable, runInAction } from 'mobx';
+import { action, computed, configure, observable, runInAction, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import "normalize.css";
 import * as React from 'react';
@@ -36,6 +36,7 @@ import { DocumentDecorations } from './DocumentDecorations';
 import { InkingControl } from './InkingControl';
 import "./Main.scss";
 import { DocumentView } from './nodes/DocumentView';
+import { FormattedTextBox } from './nodes/FormattedTextBox';
 
 @observer
 export class Main extends React.Component {
@@ -203,6 +204,30 @@ export class Main extends React.Component {
     focusDocument = (doc: Document) => { }
     noScaling = () => 1;
 
+    @observable _textDoc?: Document = undefined;
+    _textRect: any;
+    @action
+    SetTextDoc(textDoc?: Document, div?: HTMLDivElement) {
+        this._textDoc = undefined;
+        this._textDoc = textDoc;
+        if (div)
+            this._textRect = div.getBoundingClientRect();
+    }
+
+    @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;
+            return <div className="mainDiv-textInput" style={{ transform: `translate(${x}px, ${y}px)`, width: `${w}px`, height: `${h}px` }} >
+                <FormattedTextBox fieldKey={KeyStore.Archives} doc={this._textDoc} isSelected={() => true} select={() => { }} isTopMost={true} selectOnLoad={true} bindings={undefined} />
+            </ div>
+        }
+        else return (null);
+    }
+
     @computed
     get mainContent() {
         return !this.mainContainer ? (null) :
@@ -296,24 +321,27 @@ export class Main extends React.Component {
                 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}
-                        </div>
-                    }
-                </Measure>
-                <ContextMenu />
-                {this.nodesMenu}
-                {this.miscButtons}
-                {workspaceMenu}
-                <InkingControl />
-            </div>
+            [
+                <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}
+                            </div>
+                        }
+                    </Measure>
+                    <ContextMenu />
+                    {this.nodesMenu}
+                    {this.miscButtons}
+                    {workspaceMenu}
+                    <InkingControl />
+                </div>,
+                this.activeTextBox
+            ]
         );
     }
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index d4809ac1c..1189dd4e8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,4 +1,4 @@
-import { computed, reaction, runInAction, trace } from "mobx";
+import { computed, reaction } from "mobx";
 import { observer } from "mobx-react";
 import { Document } from "../../../../fields/Document";
 import { FieldWaiting } from "../../../../fields/Field";
@@ -11,59 +11,60 @@ import { CollectionViewProps } from "../CollectionViewBase";
 import "./CollectionFreeFormLinksView.scss";
 import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
 import React = require("react");
-import v5 = require("uuid/v5");
-import { find } from "async";
 
 @observer
 export class CollectionFreeFormLinksView extends React.Component<CollectionViewProps> {
 
+    HackToAvoidReactionFiringUnnecessarily?: Document = undefined;
     componentDidMount() {
-        reaction(() => {
-            return DocumentManager.Instance.getAllDocumentViews(this.props.Document).map(dv => dv.props.Document.GetNumber(KeyStore.X, 0))
-        }, () => {
-            let views = DocumentManager.Instance.getAllDocumentViews(this.props.Document);
-            for (let i = 0; i < views.length; i++) {
-                for (let j = 0; j < views.length; j++) {
-                    let srcDoc = views[j].props.Document;
-                    let dstDoc = views[i].props.Document;
-                    let x1 = srcDoc.GetNumber(KeyStore.X, 0);
-                    let x1w = srcDoc.GetNumber(KeyStore.Width, -1);
-                    let x2 = dstDoc.GetNumber(KeyStore.X, 0);
-                    let x2w = dstDoc.GetNumber(KeyStore.Width, -1);
-                    if (x1w < 0 || x2w < 0 || i == j)
-                        continue;
-                    let dstTarg = dstDoc;
-                    let srcTarg = srcDoc;
-                    let findBrush = (field: ListField<Document>) => field.Data.findIndex(brush => {
-                        let bdocs = brush ? brush.GetList(KeyStore.BrushingDocs, [] as Document[]) : [];
-                        return (bdocs.length && ((bdocs[0] == dstTarg && bdocs[1] == srcTarg)) ? true : false)
-                    });
-                    let brushAction = (field: ListField<Document>) => {
-                        let found = findBrush(field);
-                        if (found != -1) {
-                            console.log("REMOVE BRUSH " + srcTarg.Title + " " + dstTarg.Title);
-                            field.Data.splice(found, 1);
-                        }
-                    };
-                    if (Math.abs(x1 + x1w - x2) < 20) {
-                        let linkDoc: Document = new Document();
-                        linkDoc.SetText(KeyStore.Title, "Histogram Brush");
-                        linkDoc.SetText(KeyStore.LinkDescription, "Brush between " + srcTarg.Title + " and " + dstTarg.Title);
-                        linkDoc.SetData(KeyStore.BrushingDocs, [dstTarg, srcTarg], ListField);
-
-                        brushAction = brushAction = (field: ListField<Document>) => {
-                            if (findBrush(field) == -1) {
-                                console.log("ADD BRUSH " + srcTarg.Title + " " + dstTarg.Title);
-                                (findBrush(field) == -1) && field.Data.push(linkDoc);
+        this.HackToAvoidReactionFiringUnnecessarily = this.props.Document
+        reaction(() =>
+            DocumentManager.Instance.getAllDocumentViews(this.HackToAvoidReactionFiringUnnecessarily!).
+                map(dv => dv.props.Document.GetNumber(KeyStore.X, 0)),
+            () => {
+                let views = DocumentManager.Instance.getAllDocumentViews(this.props.Document);
+                for (let i = 0; i < views.length; i++) {
+                    for (let j = 0; j < views.length; j++) {
+                        let srcDoc = views[j].props.Document;
+                        let dstDoc = views[i].props.Document;
+                        let x1 = srcDoc.GetNumber(KeyStore.X, 0);
+                        let x1w = srcDoc.GetNumber(KeyStore.Width, -1);
+                        let x2 = dstDoc.GetNumber(KeyStore.X, 0);
+                        let x2w = dstDoc.GetNumber(KeyStore.Width, -1);
+                        if (x1w < 0 || x2w < 0 || i == j)
+                            continue;
+                        let dstTarg = dstDoc;
+                        let srcTarg = srcDoc;
+                        let findBrush = (field: ListField<Document>) => field.Data.findIndex(brush => {
+                            let bdocs = brush ? brush.GetList(KeyStore.BrushingDocs, [] as Document[]) : [];
+                            return (bdocs.length && ((bdocs[0] == dstTarg && bdocs[1] == srcTarg)) ? true : false)
+                        });
+                        let brushAction = (field: ListField<Document>) => {
+                            let found = findBrush(field);
+                            if (found != -1) {
+                                console.log("REMOVE BRUSH " + srcTarg.Title + " " + dstTarg.Title);
+                                field.Data.splice(found, 1);
                             }
                         };
-                    }
-                    dstTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction);
-                    srcTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction);
+                        if (Math.abs(x1 + x1w - x2) < 20) {
+                            let linkDoc: Document = new Document();
+                            linkDoc.SetText(KeyStore.Title, "Histogram Brush");
+                            linkDoc.SetText(KeyStore.LinkDescription, "Brush between " + srcTarg.Title + " and " + dstTarg.Title);
+                            linkDoc.SetData(KeyStore.BrushingDocs, [dstTarg, srcTarg], ListField);
 
+                            brushAction = brushAction = (field: ListField<Document>) => {
+                                if (findBrush(field) == -1) {
+                                    console.log("ADD BRUSH " + srcTarg.Title + " " + dstTarg.Title);
+                                    (findBrush(field) == -1) && field.Data.push(linkDoc);
+                                }
+                            };
+                        }
+                        dstTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction);
+                        srcTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction);
+
+                    }
                 }
-            }
-        })
+            })
     }
     documentAnchors(view: DocumentView) {
         let equalViews = [view];
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 1ddb84a99..2a86d0ee1 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -21,6 +21,7 @@ import v5 = require("uuid/v5");
 import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
 import { PreviewCursor } from "./PreviewCursor";
 import { NumberField } from "../../../../fields/NumberField";
+import { Main } from "../../Main";
 
 @observer
 export class CollectionFreeFormView extends CollectionViewBase {
@@ -185,6 +186,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
 
     @action
     private SetPan(panX: number, panY: number) {
+        Main.Instance.SetTextDoc(undefined, undefined);
         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 0c0efc437..e59179874 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -2,19 +2,19 @@ import { action, IReactionDisposer, reaction } from "mobx";
 import { baseKeymap } from "prosemirror-commands";
 import { history, redo, undo } from "prosemirror-history";
 import { keymap } from "prosemirror-keymap";
-import { schema } from "../../util/RichTextSchema";
-import { EditorState, Transaction } from "prosemirror-state";
+import { EditorState, Plugin, Transaction } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
-import { Opt, FieldWaiting } from "../../../fields/Field";
-import "./FormattedTextBox.scss";
-import React = require("react");
+import { FieldWaiting, Opt } from "../../../fields/Field";
+import { KeyStore } from "../../../fields/KeyStore";
 import { RichTextField } from "../../../fields/RichTextField";
-import { FieldViewProps, FieldView } from "./FieldView";
-import { Plugin } from "prosemirror-state";
-import { Decoration, DecorationSet } from "prosemirror-view";
+import { inpRules } from "../../util/RichTextRules";
+import { schema } from "../../util/RichTextSchema";
 import { TooltipTextMenu } from "../../util/TooltipTextMenu";
 import { ContextMenu } from "../../views/ContextMenu";
-import { inpRules } from "../../util/RichTextRules";
+import { Main } from "../Main";
+import { FieldView, FieldViewProps } from "./FieldView";
+import "./FormattedTextBox.scss";
+import React = require("react");
 const { buildMenuItems } = require("prosemirror-example-setup");
 const { menuBar } = require("prosemirror-menu");
 
@@ -35,156 +35,196 @@ const { menuBar } = require("prosemirror-menu");
 //  this will edit the document and assign the new value to that field.
 //]
 export class FormattedTextBox extends React.Component<FieldViewProps> {
-  public static LayoutString(fieldStr: string = "DataKey") {
-    return FieldView.LayoutString(FormattedTextBox, fieldStr);
-  }
-  private _ref: React.RefObject<HTMLDivElement>;
-  private _editorView: Opt<EditorView>;
-  private _reactionDisposer: Opt<IReactionDisposer>;
-
-  constructor(props: FieldViewProps) {
-    super(props);
-
-    this._ref = React.createRef();
-    this.onChange = this.onChange.bind(this);
-  }
-
-  dispatchTransaction = (tx: Transaction) => {
-    if (this._editorView) {
-      const state = this._editorView.state.apply(tx);
-      this._editorView.updateState(state);
-      const { doc, fieldKey } = this.props;
-      doc.SetDataOnPrototype(
-        fieldKey,
-        JSON.stringify(state.toJSON()),
-        RichTextField
-      );
-      // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
+    public static LayoutString(fieldStr: string = "DataKey") {
+        return FieldView.LayoutString(FormattedTextBox, fieldStr);
+    }
+    private _ref: React.RefObject<HTMLDivElement>;
+    private _editorView: Opt<EditorView>;
+    private _reactionDisposer: Opt<IReactionDisposer>;
+    private _inputReactionDisposer: Opt<IReactionDisposer>;
+
+    constructor(props: FieldViewProps) {
+        super(props);
+
+        this._ref = React.createRef();
+        this.onChange = this.onChange.bind(this);
     }
-  };
-
-  componentDidMount() {
-    let state: EditorState;
-    const config = {
-      schema,
-      inpRules, //these currently don't do anything, but could eventually be helpful
-      plugins: [
-        history(),
-        keymap({ "Mod-z": undo, "Mod-y": redo }),
-        keymap(baseKeymap),
-        this.tooltipMenuPlugin()
-      ]
+
+    dispatchTransaction = (tx: Transaction) => {
+        if (this._editorView) {
+            const state = this._editorView.state.apply(tx);
+            this._editorView.updateState(state);
+            this.FieldDoc.SetDataOnPrototype(
+                this.FieldKey,
+                JSON.stringify(state.toJSON()),
+                RichTextField
+            );
+            // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
+        }
     };
 
-    let field = this.props.doc.GetT(this.props.fieldKey, RichTextField);
-    if (field && field != FieldWaiting && field.Data) {
-      state = EditorState.fromJSON(config, JSON.parse(field.Data));
-    } else {
-      state = EditorState.create(config);
+    get FieldDoc() { return this.props.fieldKey === KeyStore.Archives ? Main.Instance._textDoc! : this.props.doc; }
+    get FieldKey() { return this.props.fieldKey === KeyStore.Archives ? KeyStore.Data : this.props.fieldKey; }
+
+    componentDidMount() {
+        const config = {
+            schema,
+            inpRules, //these currently don't do anything, but could eventually be helpful
+            plugins: [
+                history(),
+                keymap({ "Mod-z": undo, "Mod-y": redo }),
+                keymap(baseKeymap),
+                this.tooltipMenuPlugin()
+            ]
+        };
+
+        if (this.props.fieldKey === KeyStore.Archives) {
+            this._inputReactionDisposer = reaction(() => Main.Instance._textDoc && Main.Instance._textDoc.Id,
+                () => {
+                    if (this._editorView)
+                        this._editorView!.destroy();
+
+                    this.setupEditor(config);
+                }
+            )
+        }
+
+        this._reactionDisposer = reaction(
+            () => {
+                const field = this.FieldDoc.GetT(this.FieldKey, RichTextField);
+                return field && field != FieldWaiting ? field.Data : undefined;
+            },
+            field => {
+                if (field && this._editorView) {
+                    this._editorView.updateState(
+                        EditorState.fromJSON(config, JSON.parse(field))
+                    );
+                }
+            }
+        );
+        this.setupEditor(config);
+    }
+
+    private setupEditor(config: any) {
+
+        let state: EditorState;
+        let field = this.FieldDoc.GetT(this.FieldKey, RichTextField);
+        if (field && field != FieldWaiting && field.Data) {
+            state = EditorState.fromJSON(config, JSON.parse(field.Data));
+        } else {
+            state = EditorState.create(config);
+        }
+        if (this._ref.current) {
+            this._editorView = new EditorView(this._ref.current, {
+                state,
+                dispatchTransaction: this.dispatchTransaction
+            });
+        }
+
+        if (this.props.selectOnLoad) {
+            this.props.select();
+            this._editorView!.focus();
+        }
     }
-    if (this._ref.current) {
-      this._editorView = new EditorView(this._ref.current, {
-        state,
-        dispatchTransaction: this.dispatchTransaction
-      });
+
+    componentWillUnmount() {
+        if (this._editorView) {
+            this._editorView.destroy();
+        }
+        if (this._reactionDisposer) {
+            this._reactionDisposer();
+        }
+        if (this._inputReactionDisposer) {
+            this._inputReactionDisposer();
+        }
+    }
+
+    shouldComponentUpdate() {
+        return false;
+    }
+
+    @action
+    onChange(e: React.ChangeEvent<HTMLInputElement>) {
+        const { fieldKey, doc } = this.props;
+        doc.SetOnPrototype(fieldKey, new RichTextField(e.target.value));
+        // doc.SetData(fieldKey, e.target.value, RichTextField);
     }
+    onPointerDown = (e: React.PointerEvent): void => {
+        if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
+            e.stopPropagation();
+        }
+        if (e.buttons === 1 && this.props.fieldKey !== KeyStore.Archives)
+            e.preventDefault();
+    };
+    onPointerUp = (e: React.PointerEvent): void => {
+        if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
+            e.stopPropagation();
+        }
+        if (this.props.fieldKey !== KeyStore.Archives) {
+            e.preventDefault();
+            Main.Instance.SetTextDoc(this.props.doc, this._ref.current!);
+        }
+    };
 
-    this._reactionDisposer = reaction(
-      () => {
-        const field = this.props.doc.GetT(this.props.fieldKey, RichTextField);
-        return field && field != FieldWaiting ? field.Data : undefined;
-      },
-      field => {
-        if (field && this._editorView) {
-          this._editorView.updateState(
-            EditorState.fromJSON(config, JSON.parse(field))
-          );
+    onFocused = (e: React.FocusEvent): void => {
+        if (this.props.fieldKey !== KeyStore.Archives) {
+            Main.Instance.SetTextDoc(this.props.doc, this._ref.current!);
         }
-      }
-    );
-    if (this.props.selectOnLoad) {
-      this.props.select();
-      this._editorView!.focus();
     }
-  }
 
-  componentWillUnmount() {
-    if (this._editorView) {
-      this._editorView.destroy();
+    //REPLACE THIS WITH CAPABILITIES SPECIFIC TO THIS TYPE OF NODE
+    textCapability = (e: React.MouseEvent): void => { };
+
+    specificContextMenu = (e: React.MouseEvent): void => {
+        ContextMenu.Instance.addItem({
+            description: "Text Capability",
+            event: this.textCapability
+        });
+
+        // ContextMenu.Instance.addItem({
+        //     description: "Submenu",
+        //     items: [
+        //         {
+        //             description: "item 1", event:
+        //     },
+        //         {
+        //             description: "item 2", event:
+        //     }
+        //     ]
+        // })
+        // e.stopPropagation()
+    };
+
+    onPointerWheel = (e: React.WheelEvent): void => {
+        e.stopPropagation();
+    };
+
+    tooltipMenuPlugin() {
+        return new Plugin({
+            view(_editorView) {
+                return new TooltipTextMenu(_editorView);
+            }
+        });
     }
-    if (this._reactionDisposer) {
-      this._reactionDisposer();
+    onKeyPress(e: React.KeyboardEvent) {
+        e.stopPropagation();
+        // stop propagation doesn't seem to stop propagation of native keyboard events.
+        // so we set a flag on the native event that marks that the event's been handled.
+        // (e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
     }
-  }
-
-  shouldComponentUpdate() {
-    return false;
-  }
-
-  @action
-  onChange(e: React.ChangeEvent<HTMLInputElement>) {
-    const { fieldKey, doc } = this.props;
-    doc.SetOnPrototype(fieldKey, new RichTextField(e.target.value));
-    // doc.SetData(fieldKey, e.target.value, RichTextField);
-  }
-  onPointerDown = (e: React.PointerEvent): void => {
-    if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
-      e.stopPropagation();
+    render() {
+        return (
+            <div
+                className="formattedTextBox-cont"
+                onKeyDown={this.onKeyPress}
+                onKeyPress={this.onKeyPress}
+                onPointerUp={this.onPointerUp}
+                onPointerDown={this.onPointerDown}
+                onContextMenu={this.specificContextMenu}
+                // tfs: do we need this event handler
+                onWheel={this.onPointerWheel}
+                ref={this._ref}
+            />
+        );
     }
-  };
-
-  //REPLACE THIS WITH CAPABILITIES SPECIFIC TO THIS TYPE OF NODE
-  textCapability = (e: React.MouseEvent): void => {};
-
-  specificContextMenu = (e: React.MouseEvent): void => {
-    ContextMenu.Instance.addItem({
-      description: "Text Capability",
-      event: this.textCapability
-    });
-
-    // ContextMenu.Instance.addItem({
-    //     description: "Submenu",
-    //     items: [
-    //         {
-    //             description: "item 1", event:
-    //     },
-    //         {
-    //             description: "item 2", event:
-    //     }
-    //     ]
-    // })
-    // e.stopPropagation()
-  };
-
-  onPointerWheel = (e: React.WheelEvent): void => {
-    e.stopPropagation();
-  };
-
-  tooltipMenuPlugin() {
-    return new Plugin({
-      view(_editorView) {
-        return new TooltipTextMenu(_editorView);
-      }
-    });
-  }
-  onKeyPress(e: React.KeyboardEvent) {
-    e.stopPropagation();
-    // stop propagation doesn't seem to stop propagation of native keyboard events.
-    // so we set a flag on the native event that marks that the event's been handled.
-    // (e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
-  }
-  render() {
-    return (
-      <div
-        className="formattedTextBox-cont"
-        onKeyDown={this.onKeyPress}
-        onKeyPress={this.onKeyPress}
-        onPointerDown={this.onPointerDown}
-        onContextMenu={this.specificContextMenu}
-        // tfs: do we need this event handler
-        onWheel={this.onPointerWheel}
-        ref={this._ref}
-      />
-    );
-  }
 }
-- 
cgit v1.2.3-70-g09d2


From cecbafdd8e42b1fa021a54fe624c3cf68e8645bd Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Mon, 8 Apr 2019 22:22:50 -0400
Subject: Renamed CollectionViewBase to CollectionSubView Fixed bug with adding
 documents to PDF and videos

---
 .../views/collections/CollectionDockingView.tsx    |   2 +-
 src/client/views/collections/CollectionPDFView.tsx |   9 +-
 .../views/collections/CollectionSchemaView.tsx     |   4 +-
 src/client/views/collections/CollectionSubView.tsx | 231 +++++++++++++++++++++
 .../views/collections/CollectionTreeView.tsx       |   4 +-
 .../views/collections/CollectionVideoView.tsx      |  14 +-
 .../views/collections/CollectionViewBase.tsx       | 231 ---------------------
 .../CollectionFreeFormLinksView.tsx                |   2 +-
 .../CollectionFreeFormRemoteCursors.tsx            |   2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   4 +-
 10 files changed, 249 insertions(+), 254 deletions(-)
 create mode 100644 src/client/views/collections/CollectionSubView.tsx
 delete mode 100644 src/client/views/collections/CollectionViewBase.tsx

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

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index c5e1e7f4b..c3757a377 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -15,7 +15,7 @@ import { DocumentView } from "../nodes/DocumentView";
 import "./CollectionDockingView.scss";
 import { COLLECTION_BORDER_WIDTH } from "./CollectionBaseView";
 import React = require("react");
-import { SubCollectionViewProps } from "./CollectionViewBase";
+import { SubCollectionViewProps } from "./CollectionSubView";
 import { ServerUtils } from "../../../server/ServerUtil";
 import { DragManager } from "../../util/DragManager";
 import { TextField } from "../../../fields/TextField";
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 1f2e71f6e..32f50afc0 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -2,16 +2,15 @@ import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ContextMenu } from "../ContextMenu";
-import { CollectionViewProps } from "./CollectionViewBase";
 import "./CollectionPDFView.scss"
 import React = require("react");
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
-import { FieldView } from "../nodes/FieldView";
+import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from "./CollectionBaseView";
 
 
 @observer
-export class CollectionPDFView extends React.Component<CollectionViewProps> {
+export class CollectionPDFView extends React.Component<FieldViewProps> {
 
     public static LayoutString(fieldKey: string = "DataKey") {
         return FieldView.LayoutString(CollectionPDFView, fieldKey);
@@ -23,7 +22,7 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
     @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : -1;
 
     private get uIButtons() {
-        let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]);
+        let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale);
         return (
             <div className="collectionPdfView-buttonTray" key="tray" style={{ transform: `scale(${scaling}, ${scaling})` }}>
                 <button className="collectionPdfView-backward" onClick={this.onPageBack}>{"<"}</button>
@@ -39,7 +38,7 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
     }
 
     private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => {
-        let props = { ...renderProps, ...this.props };
+        let props = { ...this.props, ...renderProps };
         return (
             <>
                 <CollectionFreeFormView {...props} />
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 4604945d1..9b780f29b 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -23,7 +23,7 @@ import { DocumentView } from "../nodes/DocumentView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
 import { CollectionView } from "./CollectionView";
-import { CollectionViewBase } from "./CollectionViewBase";
+import { CollectionSubView } from "./CollectionSubView";
 import { TextField } from "../../../fields/TextField";
 import { COLLECTION_BORDER_WIDTH } from "./CollectionBaseView";
 import { emptyFunction, returnFalse } from "../../../Utils";
@@ -56,7 +56,7 @@ class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggl
 }
 
 @observer
-export class CollectionSchemaView extends CollectionViewBase {
+export class CollectionSchemaView extends CollectionSubView {
     private _mainCont = React.createRef<HTMLDivElement>();
     private _startSplitPercent = 0;
     private DIVIDER_WIDTH = 4;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
new file mode 100644
index 000000000..59b89e119
--- /dev/null
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -0,0 +1,231 @@
+import { action, runInAction } from "mobx";
+import { Document } from "../../../fields/Document";
+import { ListField } from "../../../fields/ListField";
+import React = require("react");
+import { KeyStore } from "../../../fields/KeyStore";
+import { FieldWaiting, Opt } from "../../../fields/Field";
+import { undoBatch, UndoManager } from "../../util/UndoManager";
+import { DragManager } from "../../util/DragManager";
+import { Documents, DocumentOptions } from "../../documents/Documents";
+import { RouteStore } from "../../../server/RouteStore";
+import { TupleField } from "../../../fields/TupleField";
+import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
+import { NumberField } from "../../../fields/NumberField";
+import { ServerUtils } from "../../../server/ServerUtil";
+import { Server } from "../../Server";
+import { FieldViewProps } from "../nodes/FieldView";
+import * as rp from 'request-promise'
+
+export interface CollectionViewProps extends FieldViewProps {
+    addDocument: (document: Document, allowDuplicates?: boolean) => boolean;
+    removeDocument: (document: Document) => boolean;
+    moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
+}
+
+export interface SubCollectionViewProps extends CollectionViewProps {
+}
+
+export type CursorEntry = TupleField<[string, string], [number, number]>;
+
+export class CollectionSubView extends React.Component<SubCollectionViewProps> {
+    private dropDisposer?: DragManager.DragDropDisposer;
+    protected createDropTarget = (ele: HTMLDivElement) => {
+        if (this.dropDisposer) {
+            this.dropDisposer();
+        }
+        if (ele) {
+            this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+        }
+    }
+
+    @action
+    protected setCursorPosition(position: [number, number]) {
+        let ind;
+        let doc = this.props.Document;
+        let id = CurrentUserUtils.id;
+        let email = CurrentUserUtils.email;
+        if (id && email) {
+            let textInfo: [string, string] = [id, email];
+            doc.GetTAsync(KeyStore.Prototype, Document).then(proto => {
+                if (!proto) {
+                    return;
+                }
+                proto.GetOrCreateAsync<ListField<CursorEntry>>(KeyStore.Cursors, ListField, action((field: ListField<CursorEntry>) => {
+                    let cursors = field.Data;
+                    if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.Data[0][0] === id)) > -1) {
+                        cursors[ind].Data[1] = position;
+                    } else {
+                        let entry = new TupleField<[string, string], [number, number]>([textInfo, position]);
+                        cursors.push(entry);
+                    }
+                }))
+            })
+        }
+    }
+
+    @undoBatch
+    @action
+    protected drop(e: Event, de: DragManager.DropEvent): boolean {
+        if (de.data instanceof DragManager.DocumentDragData) {
+            if (de.data.aliasOnDrop || de.data.copyOnDrop) {
+                [KeyStore.Width, KeyStore.Height, KeyStore.CurPage].map(key =>
+                    de.data.draggedDocuments.map((draggedDocument: Document, i: number) =>
+                        draggedDocument.GetTAsync(key, NumberField, (f: Opt<NumberField>) => f ? de.data.droppedDocuments[i].SetNumber(key, f.Data) : null)));
+            }
+            let added = false;
+            if (de.data.aliasOnDrop) {
+                added = de.data.droppedDocuments.reduce((added: boolean, d) => added || this.props.addDocument(d), false);
+            } else if (de.data.moveDocument) {
+                const move = de.data.moveDocument;
+                added = de.data.droppedDocuments.reduce((added: boolean, d) => added || move(d, this.props.Document, this.props.addDocument), false)
+            } else {
+                added = de.data.droppedDocuments.reduce((added: boolean, d) => added || this.props.addDocument(d), false)
+            }
+            e.stopPropagation();
+            return added;
+        }
+        if (de.data instanceof DragManager.LinkDragData) {
+            let sourceDoc: Document = de.data.linkSourceDocumentView.props.Document;
+            if (sourceDoc) runInAction(() => {
+                let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document)
+                if (srcTarg && srcTarg !== FieldWaiting) {
+                    let linkDocs = srcTarg.GetList(KeyStore.LinkedToDocs, [] as Document[]);
+                    linkDocs.map(linkDoc => {
+                        let targDoc = linkDoc.GetT(KeyStore.LinkedToDocs, Document);
+                        if (targDoc && targDoc !== FieldWaiting) {
+                            let dropdoc = targDoc.MakeDelegate();
+                            de.data.droppedDocuments.push(dropdoc);
+                            this.props.addDocument(dropdoc, false);
+                        }
+                    })
+                }
+            })
+            return true;
+        }
+        return false;
+    }
+
+    protected getDocumentFromType(type: string, path: string, options: DocumentOptions): Opt<Document> {
+        let ctor: ((path: string, options: DocumentOptions) => Document) | undefined;
+        if (type.indexOf("image") !== -1) {
+            ctor = Documents.ImageDocument;
+        }
+        if (type.indexOf("video") !== -1) {
+            ctor = Documents.VideoDocument;
+        }
+        if (type.indexOf("audio") !== -1) {
+            ctor = Documents.AudioDocument;
+        }
+        if (type.indexOf("pdf") !== -1) {
+            ctor = Documents.PdfDocument;
+            options.nativeWidth = 1200;
+        }
+        if (type.indexOf("html") !== -1) {
+            if (path.includes('localhost')) {
+                let s = path.split('/');
+                let id = s[s.length - 1];
+                Server.GetField(id).then(field => {
+                    if (field instanceof Document) {
+                        let alias = field.CreateAlias();
+                        alias.SetNumber(KeyStore.X, options.x || 0);
+                        alias.SetNumber(KeyStore.Y, options.y || 0);
+                        alias.SetNumber(KeyStore.Width, options.width || 300);
+                        alias.SetNumber(KeyStore.Height, options.height || options.width || 300);
+                        this.props.addDocument(alias, false);
+                    }
+                })
+                return undefined;
+            }
+            ctor = Documents.WebDocument;
+            options = { height: options.width, ...options, title: path };
+        }
+        return ctor ? ctor(path, options) : undefined;
+    }
+
+    @undoBatch
+    @action
+    protected onDrop(e: React.DragEvent, options: DocumentOptions): void {
+        let html = e.dataTransfer.getData("text/html");
+        let text = e.dataTransfer.getData("text/plain");
+
+        if (text && text.startsWith("<div")) {
+            return;
+        }
+        e.stopPropagation()
+        e.preventDefault()
+
+        if (html && html.indexOf("<img") !== 0 && !html.startsWith("<a")) {
+            console.log("not good");
+            let htmlDoc = Documents.HtmlDocument(html, { ...options, width: 300, height: 300 });
+            htmlDoc.SetText(KeyStore.DocumentText, text);
+            this.props.addDocument(htmlDoc, false);
+            return;
+        }
+
+        let batch = UndoManager.StartBatch("collection view drop");
+        let promises: Promise<void>[] = [];
+        // tslint:disable-next-line:prefer-for-of
+        for (let i = 0; i < e.dataTransfer.items.length; i++) {
+            const upload = window.location.origin + RouteStore.upload;
+            let item = e.dataTransfer.items[i];
+            if (item.kind === "string" && item.type.indexOf("uri") !== -1) {
+                let str: string;
+                let prom = new Promise<string>(res =>
+                    e.dataTransfer.items[i].getAsString(res)).then(action((s: string) => {
+                        str = s;
+                        return rp.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + s))
+                    })).then(res => {
+                        let type = res.headers["content-type"];
+                        if (type) {
+                            let doc = this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 })
+                            if (doc) {
+                                this.props.addDocument(doc, false);
+                            }
+                        }
+                    });
+                promises.push(prom);
+                // this.props.addDocument(Documents.WebDocument(s, { ...options, width: 300, height: 300 }), false)
+            }
+            let type = item.type
+            if (item.kind === "file") {
+                let file = item.getAsFile();
+                let formData = new FormData()
+
+                if (file) {
+                    formData.append('file', file)
+                }
+
+                let prom = fetch(upload, {
+                    method: 'POST',
+                    body: formData
+                }).then(async (res: Response) => {
+                    const json = await res.json();
+                    json.map((file: any) => {
+                        let path = window.location.origin + file
+                        runInAction(() => {
+                            let doc = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300 })
+
+                            let docs = this.props.Document.GetT(KeyStore.Data, ListField);
+                            if (docs !== FieldWaiting) {
+                                if (!docs) {
+                                    docs = new ListField<Document>();
+                                    this.props.Document.Set(KeyStore.Data, docs)
+                                }
+                                if (doc) {
+                                    docs.Data.push(doc);
+                                }
+                            }
+                        });
+                    });
+                });
+                promises.push(prom);
+            }
+        }
+
+        if (promises.length) {
+            Promise.all(promises).catch(() => { }).then(() => batch.end());
+        } else {
+            batch.end();
+        }
+    }
+}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index dce35868e..2bdf0baa6 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -11,7 +11,7 @@ import { setupDrag, DragManager } from "../../util/DragManager";
 import { EditableView } from "../EditableView";
 import "./CollectionTreeView.scss";
 import { CollectionView } from "./CollectionView";
-import { CollectionViewBase } from "./CollectionViewBase";
+import { CollectionSubView } from "./CollectionSubView";
 import React = require("react")
 import { COLLECTION_BORDER_WIDTH } from './CollectionBaseView';
 import { props } from 'bluebird';
@@ -120,7 +120,7 @@ class TreeView extends React.Component<TreeViewProps> {
 }
 
 @observer
-export class CollectionTreeView extends CollectionViewBase {
+export class CollectionTreeView extends CollectionSubView {
 
     @action
     remove = (document: Document) => {
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index afadfeefb..1acc76c85 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -1,20 +1,16 @@
-import { action, computed, observable, trace } from "mobx";
+import { action, observable, trace } from "mobx";
 import { observer } from "mobx-react";
-import { Document } from "../../../fields/Document";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ContextMenu } from "../ContextMenu";
-import { CollectionView } from "./CollectionView";
 import { CollectionViewType, CollectionBaseView, CollectionRenderProps } from "./CollectionBaseView";
-import { CollectionViewProps } from "./CollectionViewBase";
 import React = require("react");
-import { FieldId } from "../../../fields/Field";
 import "./CollectionVideoView.scss"
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
-import { FieldView } from "../nodes/FieldView";
+import { FieldView, FieldViewProps } from "../nodes/FieldView";
 
 
 @observer
-export class CollectionVideoView extends React.Component<CollectionViewProps> {
+export class CollectionVideoView extends React.Component<FieldViewProps> {
     private _intervalTimer: any = undefined;
     private _player: HTMLVideoElement | undefined = undefined;
 
@@ -25,7 +21,7 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
         return FieldView.LayoutString(CollectionVideoView, fieldKey);
     }
     private get uIButtons() {
-        let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]);
+        let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale);
         return ([
             <div className="collectionVideoView-time" key="time" onPointerDown={this.onResetDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
                 <span>{"" + Math.round(this._currentTimecode)}</span>
@@ -109,7 +105,7 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
     }
 
     private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => {
-        let props = { ...renderProps, ...this.props };
+        let props = { ...this.props, ...renderProps };
         return (
             <>
                 <CollectionFreeFormView {...props} />
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
deleted file mode 100644
index ad1294f76..000000000
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ /dev/null
@@ -1,231 +0,0 @@
-import { action, runInAction } from "mobx";
-import { Document } from "../../../fields/Document";
-import { ListField } from "../../../fields/ListField";
-import React = require("react");
-import { KeyStore } from "../../../fields/KeyStore";
-import { FieldWaiting, Opt } from "../../../fields/Field";
-import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { DragManager } from "../../util/DragManager";
-import { Documents, DocumentOptions } from "../../documents/Documents";
-import { RouteStore } from "../../../server/RouteStore";
-import { TupleField } from "../../../fields/TupleField";
-import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
-import { NumberField } from "../../../fields/NumberField";
-import { ServerUtils } from "../../../server/ServerUtil";
-import { Server } from "../../Server";
-import { FieldViewProps } from "../nodes/FieldView";
-import * as rp from 'request-promise'
-
-export interface CollectionViewProps extends FieldViewProps {
-    addDocument: (document: Document, allowDuplicates?: boolean) => boolean;
-    removeDocument: (document: Document) => boolean;
-    moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
-}
-
-export interface SubCollectionViewProps extends CollectionViewProps {
-}
-
-export type CursorEntry = TupleField<[string, string], [number, number]>;
-
-export class CollectionViewBase extends React.Component<SubCollectionViewProps> {
-    private dropDisposer?: DragManager.DragDropDisposer;
-    protected createDropTarget = (ele: HTMLDivElement) => {
-        if (this.dropDisposer) {
-            this.dropDisposer();
-        }
-        if (ele) {
-            this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
-        }
-    }
-
-    @action
-    protected setCursorPosition(position: [number, number]) {
-        let ind;
-        let doc = this.props.Document;
-        let id = CurrentUserUtils.id;
-        let email = CurrentUserUtils.email;
-        if (id && email) {
-            let textInfo: [string, string] = [id, email];
-            doc.GetTAsync(KeyStore.Prototype, Document).then(proto => {
-                if (!proto) {
-                    return;
-                }
-                proto.GetOrCreateAsync<ListField<CursorEntry>>(KeyStore.Cursors, ListField, action((field: ListField<CursorEntry>) => {
-                    let cursors = field.Data;
-                    if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.Data[0][0] === id)) > -1) {
-                        cursors[ind].Data[1] = position;
-                    } else {
-                        let entry = new TupleField<[string, string], [number, number]>([textInfo, position]);
-                        cursors.push(entry);
-                    }
-                }))
-            })
-        }
-    }
-
-    @undoBatch
-    @action
-    protected drop(e: Event, de: DragManager.DropEvent): boolean {
-        if (de.data instanceof DragManager.DocumentDragData) {
-            if (de.data.aliasOnDrop || de.data.copyOnDrop) {
-                [KeyStore.Width, KeyStore.Height, KeyStore.CurPage].map(key =>
-                    de.data.draggedDocuments.map((draggedDocument: Document, i: number) =>
-                        draggedDocument.GetTAsync(key, NumberField, (f: Opt<NumberField>) => f ? de.data.droppedDocuments[i].SetNumber(key, f.Data) : null)));
-            }
-            let added = false;
-            if (de.data.aliasOnDrop) {
-                added = de.data.droppedDocuments.reduce((added: boolean, d) => added || this.props.addDocument(d), false);
-            } else if (de.data.moveDocument) {
-                const move = de.data.moveDocument;
-                added = de.data.droppedDocuments.reduce((added: boolean, d) => added || move(d, this.props.Document, this.props.addDocument), false)
-            } else {
-                added = de.data.droppedDocuments.reduce((added: boolean, d) => added || this.props.addDocument(d), false)
-            }
-            e.stopPropagation();
-            return added;
-        }
-        if (de.data instanceof DragManager.LinkDragData) {
-            let sourceDoc: Document = de.data.linkSourceDocumentView.props.Document;
-            if (sourceDoc) runInAction(() => {
-                let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document)
-                if (srcTarg && srcTarg !== FieldWaiting) {
-                    let linkDocs = srcTarg.GetList(KeyStore.LinkedToDocs, [] as Document[]);
-                    linkDocs.map(linkDoc => {
-                        let targDoc = linkDoc.GetT(KeyStore.LinkedToDocs, Document);
-                        if (targDoc && targDoc !== FieldWaiting) {
-                            let dropdoc = targDoc.MakeDelegate();
-                            de.data.droppedDocuments.push(dropdoc);
-                            this.props.addDocument(dropdoc, false);
-                        }
-                    })
-                }
-            })
-            return true;
-        }
-        return false;
-    }
-
-    protected getDocumentFromType(type: string, path: string, options: DocumentOptions): Opt<Document> {
-        let ctor: ((path: string, options: DocumentOptions) => Document) | undefined;
-        if (type.indexOf("image") !== -1) {
-            ctor = Documents.ImageDocument;
-        }
-        if (type.indexOf("video") !== -1) {
-            ctor = Documents.VideoDocument;
-        }
-        if (type.indexOf("audio") !== -1) {
-            ctor = Documents.AudioDocument;
-        }
-        if (type.indexOf("pdf") !== -1) {
-            ctor = Documents.PdfDocument;
-            options.nativeWidth = 1200;
-        }
-        if (type.indexOf("html") !== -1) {
-            if (path.includes('localhost')) {
-                let s = path.split('/');
-                let id = s[s.length - 1];
-                Server.GetField(id).then(field => {
-                    if (field instanceof Document) {
-                        let alias = field.CreateAlias();
-                        alias.SetNumber(KeyStore.X, options.x || 0);
-                        alias.SetNumber(KeyStore.Y, options.y || 0);
-                        alias.SetNumber(KeyStore.Width, options.width || 300);
-                        alias.SetNumber(KeyStore.Height, options.height || options.width || 300);
-                        this.props.addDocument(alias, false);
-                    }
-                })
-                return undefined;
-            }
-            ctor = Documents.WebDocument;
-            options = { height: options.width, ...options, title: path };
-        }
-        return ctor ? ctor(path, options) : undefined;
-    }
-
-    @undoBatch
-    @action
-    protected onDrop(e: React.DragEvent, options: DocumentOptions): void {
-        let html = e.dataTransfer.getData("text/html");
-        let text = e.dataTransfer.getData("text/plain");
-
-        if (text && text.startsWith("<div")) {
-            return;
-        }
-        e.stopPropagation()
-        e.preventDefault()
-
-        if (html && html.indexOf("<img") !== 0 && !html.startsWith("<a")) {
-            console.log("not good");
-            let htmlDoc = Documents.HtmlDocument(html, { ...options, width: 300, height: 300 });
-            htmlDoc.SetText(KeyStore.DocumentText, text);
-            this.props.addDocument(htmlDoc, false);
-            return;
-        }
-
-        let batch = UndoManager.StartBatch("collection view drop");
-        let promises: Promise<void>[] = [];
-        // tslint:disable-next-line:prefer-for-of
-        for (let i = 0; i < e.dataTransfer.items.length; i++) {
-            const upload = window.location.origin + RouteStore.upload;
-            let item = e.dataTransfer.items[i];
-            if (item.kind === "string" && item.type.indexOf("uri") !== -1) {
-                let str: string;
-                let prom = new Promise<string>(res =>
-                    e.dataTransfer.items[i].getAsString(res)).then(action((s: string) => {
-                        str = s;
-                        return rp.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + s))
-                    })).then(res => {
-                        let type = res.headers["content-type"];
-                        if (type) {
-                            let doc = this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 })
-                            if (doc) {
-                                this.props.addDocument(doc, false);
-                            }
-                        }
-                    });
-                promises.push(prom);
-                // this.props.addDocument(Documents.WebDocument(s, { ...options, width: 300, height: 300 }), false)
-            }
-            let type = item.type
-            if (item.kind === "file") {
-                let file = item.getAsFile();
-                let formData = new FormData()
-
-                if (file) {
-                    formData.append('file', file)
-                }
-
-                let prom = fetch(upload, {
-                    method: 'POST',
-                    body: formData
-                }).then(async (res: Response) => {
-                    const json = await res.json();
-                    json.map((file: any) => {
-                        let path = window.location.origin + file
-                        runInAction(() => {
-                            let doc = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300 })
-
-                            let docs = this.props.Document.GetT(KeyStore.Data, ListField);
-                            if (docs !== FieldWaiting) {
-                                if (!docs) {
-                                    docs = new ListField<Document>();
-                                    this.props.Document.Set(KeyStore.Data, docs)
-                                }
-                                if (doc) {
-                                    docs.Data.push(doc);
-                                }
-                            }
-                        });
-                    });
-                });
-                promises.push(prom);
-            }
-        }
-
-        if (promises.length) {
-            Promise.all(promises).catch(() => { }).then(() => batch.end());
-        } else {
-            batch.end();
-        }
-    }
-}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index abc6cecff..c288e7abf 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -7,7 +7,7 @@ import { ListField } from "../../../../fields/ListField";
 import { Utils } from "../../../../Utils";
 import { DocumentManager } from "../../../util/DocumentManager";
 import { DocumentView } from "../../nodes/DocumentView";
-import { CollectionViewProps } from "../CollectionViewBase";
+import { CollectionViewProps } from "../CollectionSubView";
 import "./CollectionFreeFormLinksView.scss";
 import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
 import React = require("react");
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index f664f4e65..17656e4ae 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -1,7 +1,7 @@
 import { computed } from "mobx";
 import { observer } from "mobx-react";
 import { KeyStore } from "../../../../fields/KeyStore";
-import { CollectionViewProps, CursorEntry } from "../CollectionViewBase";
+import { CollectionViewProps, CursorEntry } from "../CollectionSubView";
 import "./CollectionFreeFormView.scss";
 import React = require("react");
 import v5 = require("uuid/v5");
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 731ba3332..1c3899b24 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -12,7 +12,7 @@ import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDo
 import { DocumentContentsView } from "../../nodes/DocumentContentsView";
 import { DocumentViewProps } from "../../nodes/DocumentView";
 import { COLLECTION_BORDER_WIDTH } from "../CollectionBaseView";
-import { CollectionViewBase } from "../CollectionViewBase";
+import { CollectionSubView } from "../CollectionSubView";
 import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView";
 import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
@@ -26,7 +26,7 @@ import { NumberField } from "../../../../fields/NumberField";
 import { Main } from "../../Main";
 
 @observer
-export class CollectionFreeFormView extends CollectionViewBase {
+export class CollectionFreeFormView extends CollectionSubView {
     public _canvasRef = React.createRef<HTMLDivElement>();
     private _selectOnLoaded: string = ""; // id of document that should be selected once it's loaded (used for click-to-type)
 
-- 
cgit v1.2.3-70-g09d2


From 5e086920bf97297a02bcd38faea56454c2220279 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Mon, 8 Apr 2019 22:45:22 -0400
Subject: Enabled semi-colon and braces linter rule

---
 src/Utils.ts                                       | 12 +--
 src/client/Server.ts                               | 35 ++++----
 src/client/SocketStub.ts                           | 24 +++---
 src/client/documents/Documents.ts                  | 38 +++++----
 src/client/northstar/core/BaseObject.ts            |  4 +-
 .../northstar/core/attribute/AttributeModel.ts     |  6 +-
 .../core/attribute/AttributeTransformationModel.ts | 17 ++--
 .../core/attribute/CalculatedAttributeModel.ts     |  2 +-
 .../northstar/core/brusher/IBaseBrushable.ts       |  4 +-
 src/client/northstar/core/brusher/IBaseBrusher.ts  |  2 +-
 src/client/northstar/core/filter/FilterModel.ts    |  2 +-
 .../northstar/core/filter/IBaseFilterConsumer.ts   |  4 +-
 .../northstar/core/filter/IBaseFilterProvider.ts   |  2 +-
 .../northstar/core/filter/ValueComparision.ts      | 16 ++--
 src/client/northstar/dash-fields/HistogramField.ts |  2 +-
 .../dash-nodes/HistogramBinPrimitiveCollection.ts  | 12 +--
 src/client/northstar/dash-nodes/HistogramBox.tsx   | 24 +++---
 .../dash-nodes/HistogramBoxPrimitives.tsx          | 40 +++++----
 .../dash-nodes/HistogramLabelPrimitives.tsx        | 15 ++--
 src/client/northstar/manager/Gateway.ts            |  2 +-
 src/client/northstar/model/ModelExtensions.ts      | 18 ++--
 src/client/northstar/model/ModelHelpers.ts         |  8 +-
 .../model/binRanges/AlphabeticVisualBinRange.ts    |  4 +-
 .../model/binRanges/DateTimeVisualBinRange.ts      |  4 +-
 .../model/binRanges/NominalVisualBinRange.ts       |  4 +-
 .../model/binRanges/QuantitativeVisualBinRange.ts  |  2 +-
 .../northstar/model/binRanges/VisualBinRange.ts    |  2 +-
 .../model/binRanges/VisualBinRangeHelper.ts        | 14 +--
 src/client/northstar/operations/BaseOperation.ts   |  9 +-
 .../northstar/operations/HistogramOperation.ts     |  2 +-
 src/client/northstar/utils/ArrayUtil.ts            |  2 +-
 src/client/northstar/utils/Extensions.ts           |  6 +-
 src/client/northstar/utils/GeometryUtil.ts         | 34 ++++----
 src/client/northstar/utils/MathUtil.ts             | 38 +++++----
 src/client/northstar/utils/SizeConverter.ts        | 16 ++--
 src/client/northstar/utils/StyleContants.ts        |  2 +-
 src/client/northstar/utils/Utils.ts                |  8 +-
 src/client/util/DocumentManager.ts                 | 12 +--
 src/client/util/DragManager.ts                     |  5 +-
 src/client/util/RichTextSchema.tsx                 | 42 ++++-----
 src/client/util/Scripting.ts                       | 10 +--
 src/client/util/ScrollBox.tsx                      |  4 +-
 src/client/util/SelectionManager.ts                |  4 +-
 src/client/util/TooltipTextMenu.tsx                | 38 ++++-----
 src/client/util/Transform.ts                       | 20 ++---
 src/client/util/TypedEvent.ts                      |  2 +-
 src/client/util/UndoManager.ts                     | 24 +++---
 src/client/views/ContextMenu.tsx                   | 14 +--
 src/client/views/ContextMenuItem.tsx               |  4 +-
 src/client/views/DocumentDecorations.tsx           | 99 +++++++++++-----------
 src/client/views/EditableView.tsx                  | 10 +--
 src/client/views/InkingCanvas.tsx                  | 15 ++--
 src/client/views/InkingControl.tsx                 | 12 +--
 src/client/views/InkingStroke.tsx                  |  6 +-
 src/client/views/Main.tsx                          | 55 ++++++------
 .../views/collections/CollectionBaseView.tsx       | 34 +++++---
 .../views/collections/CollectionDockingView.tsx    | 37 ++++----
 src/client/views/collections/CollectionPDFView.tsx |  6 +-
 .../views/collections/CollectionSchemaView.tsx     | 39 ++++-----
 src/client/views/collections/CollectionSubView.tsx | 41 ++++-----
 .../views/collections/CollectionTreeView.tsx       | 14 +--
 .../views/collections/CollectionVideoView.tsx      |  6 +-
 src/client/views/collections/CollectionView.tsx    | 20 ++---
 .../CollectionFreeFormLinkView.tsx                 |  2 +-
 .../CollectionFreeFormLinksView.tsx                | 18 ++--
 .../CollectionFreeFormRemoteCursors.tsx            |  6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 33 ++++----
 .../collections/collectionFreeForm/MarqueeView.tsx | 15 ++--
 .../collectionFreeForm/PreviewCursor.tsx           |  5 +-
 src/client/views/nodes/Annotation.tsx              | 22 ++---
 src/client/views/nodes/AudioBox.tsx                | 12 +--
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 12 +--
 src/client/views/nodes/DocumentContentsView.tsx    |  6 +-
 src/client/views/nodes/DocumentView.tsx            | 35 ++++----
 src/client/views/nodes/FieldView.tsx               | 29 ++++---
 src/client/views/nodes/FormattedTextBox.tsx        | 17 ++--
 src/client/views/nodes/ImageBox.tsx                | 12 +--
 src/client/views/nodes/KeyValueBox.tsx             | 18 ++--
 src/client/views/nodes/KeyValuePair.tsx            | 14 +--
 src/client/views/nodes/LinkBox.tsx                 | 17 ++--
 src/client/views/nodes/LinkEditor.tsx              |  6 +-
 src/client/views/nodes/LinkMenu.tsx                | 10 +--
 src/client/views/nodes/PDFBox.tsx                  | 56 ++++++------
 src/client/views/nodes/Sticky.tsx                  |  6 +-
 src/client/views/nodes/VideoBox.tsx                | 10 +--
 src/client/views/nodes/WebBox.tsx                  |  6 +-
 src/debug/Test.tsx                                 |  8 +-
 src/debug/Viewer.tsx                               | 48 +++++------
 src/fields/AudioField.ts                           |  2 +-
 src/fields/BasicField.ts                           |  6 +-
 src/fields/Document.ts                             | 25 +++---
 src/fields/DocumentReference.ts                    |  2 +-
 src/fields/Field.ts                                |  2 +-
 src/fields/HtmlField.ts                            |  2 +-
 src/fields/ImageField.ts                           |  2 +-
 src/fields/InkField.ts                             |  2 +-
 src/fields/Key.ts                                  |  6 +-
 src/fields/KeyStore.ts                             |  3 +-
 src/fields/ListField.ts                            | 31 ++++---
 src/fields/NumberField.ts                          |  4 +-
 src/fields/PDFField.ts                             |  4 +-
 src/fields/RichTextField.ts                        |  2 +-
 src/fields/TextField.ts                            |  4 +-
 src/fields/TupleField.ts                           |  6 +-
 src/fields/VideoField.ts                           |  2 +-
 src/fields/WebField.ts                             |  2 +-
 src/mobile/ImageUpload.tsx                         | 14 +--
 src/server/Client.ts                               |  4 +-
 src/server/authentication/config/passport.ts       |  6 +-
 .../authentication/controllers/WorkspacesMenu.tsx  |  2 +-
 .../authentication/controllers/user_controller.ts  | 20 ++---
 .../authentication/models/current_user_utils.ts    |  8 +-
 src/server/authentication/models/user_model.ts     |  2 +-
 src/server/database.ts                             | 34 ++++----
 src/server/index.ts                                | 76 ++++++++---------
 test/test.ts                                       |  6 +-
 tslint.json                                        | 16 ++--
 117 files changed, 901 insertions(+), 826 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 8bd7f2f5c..b0e66787e 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -6,16 +6,16 @@ import { Message, Types } from './server/Message';
 export class Utils {
 
     public static GenerateGuid(): string {
-        return v4()
+        return v4();
     }
 
     public static GenerateDeterministicGuid(seed: string): string {
-        return v5(seed, v5.URL)
+        return v5(seed, v5.URL);
     }
 
     public static GetScreenTransform(ele: HTMLElement): { scale: number, translateX: number, translateY: number } {
         if (!ele) {
-            return { scale: 1, translateX: 1, translateY: 1 }
+            return { scale: 1, translateX: 1, translateY: 1 };
         }
         const rect = ele.getBoundingClientRect();
         const scale = ele.offsetWidth === 0 && rect.width === 0 ? 1 : rect.width / ele.offsetWidth;
@@ -55,8 +55,8 @@ export class Utils {
         return (args: any) => {
             this.log(prefix, messageName, args, true);
             func(args);
-        }
-    };
+        };
+    }
 
     public static Emit<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T) {
         this.log("Emit", message.Name, args, false);
@@ -81,7 +81,7 @@ export class Utils {
     public static AddServerHandlerCallback<T>(socket: Socket, message: Message<T>, handler: (args: [T, (res: any) => any]) => any) {
         socket.on(message.Message, (arg: T, fn: (res: any) => any) => {
             this.log('S receiving', message.Name, arg, true);
-            handler([arg, this.loggingCallback('S sending', fn, message.Name)])
+            handler([arg, this.loggingCallback('S sending', fn, message.Name)]);
         });
     }
 }
diff --git a/src/client/Server.ts b/src/client/Server.ts
index 45c7144ca..857101a33 100644
--- a/src/client/Server.ts
+++ b/src/client/Server.ts
@@ -1,7 +1,7 @@
-import { Key } from "../fields/Key"
+import { Key } from "../fields/Key";
 import { ObservableMap, action, reaction } from "mobx";
-import { Field, FieldWaiting, FIELD_WAITING, Opt, FieldId } from "../fields/Field"
-import { Document } from "../fields/Document"
+import { Field, FieldWaiting, FIELD_WAITING, Opt, FieldId } from "../fields/Field";
+import { Document } from "../fields/Document";
 import { SocketStub, FieldMap } from "./SocketStub";
 import * as OpenSocket from 'socket.io-client';
 import { Utils } from "./../Utils";
@@ -10,7 +10,7 @@ import { MessageStore, Types } from "./../server/Message";
 export class Server {
     public static ClientFieldsCached: ObservableMap<FieldId, Field | FIELD_WAITING> = new ObservableMap();
     static Socket: SocketIOClient.Socket = OpenSocket(`${window.location.protocol}//${window.location.hostname}:4321`);
-    static GUID: string = Utils.GenerateGuid()
+    static GUID: string = Utils.GenerateGuid();
 
 
     // Retrieves the cached value of the field and sends a request to the server for the real value (if it's not cached).
@@ -25,15 +25,16 @@ export class Server {
                 this.ClientFieldsCached.set(fieldid, FieldWaiting);
                 SocketStub.SEND_FIELD_REQUEST(fieldid, action((field: Field | undefined) => {
                     let cached = this.ClientFieldsCached.get(fieldid);
-                    if (cached !== FieldWaiting)
+                    if (cached !== FieldWaiting) {
                         cb(cached);
+                    }
                     else {
                         if (field) {
                             this.ClientFieldsCached.set(fieldid, field);
                         } else {
-                            this.ClientFieldsCached.delete(fieldid)
+                            this.ClientFieldsCached.delete(fieldid);
                         }
-                        cb(field)
+                        cb(field);
                     }
                 }));
             } else if (cached !== FieldWaiting) {
@@ -42,12 +43,12 @@ export class Server {
                 reaction(() => this.ClientFieldsCached.get(fieldid),
                     (field, reaction) => {
                         if (field !== FieldWaiting) {
-                            reaction.dispose()
-                            cb(field)
+                            reaction.dispose();
+                            cb(field);
                         }
-                    })
+                    });
             }
-        }
+        };
         if (callback) {
             fn(callback);
         } else {
@@ -79,15 +80,15 @@ export class Server {
                     let field = fields[id];
                     if (field) {
                         if (!(this.ClientFieldsCached.get(field.Id) instanceof Field)) {
-                            this.ClientFieldsCached.set(field.Id, field)
+                            this.ClientFieldsCached.set(field.Id, field);
                         } else {
-                            throw new Error("we shouldn't be trying to replace things that are already in the cache")
+                            throw new Error("we shouldn't be trying to replace things that are already in the cache");
                         }
                     } else {
                         if (this.ClientFieldsCached.get(id) === FieldWaiting) {
                             this.ClientFieldsCached.delete(id);
                         } else {
-                            throw new Error("we shouldn't be trying to replace things that are already in the cache")
+                            throw new Error("we shouldn't be trying to replace things that are already in the cache");
                         }
                     }
                 }
@@ -99,9 +100,9 @@ export class Server {
                                 let realField = field as Field;
                                 existingFields[realField.Id] = realField;
                             }
-                            cb({ ...fields, ...existingFields })
+                            cb({ ...fields, ...existingFields });
                         }
-                    }, { fireImmediately: true })
+                    }, { fireImmediately: true });
             }));
         };
         if (callback) {
@@ -139,7 +140,7 @@ export class Server {
 
     public static UpdateField(field: Field) {
         if (!this.ClientFieldsCached.has(field.Id)) {
-            this.ClientFieldsCached.set(field.Id, field)
+            this.ClientFieldsCached.set(field.Id, field);
         }
         SocketStub.SEND_SET_FIELD(field);
     }
diff --git a/src/client/SocketStub.ts b/src/client/SocketStub.ts
index c3cd8bee6..257973e3d 100644
--- a/src/client/SocketStub.ts
+++ b/src/client/SocketStub.ts
@@ -1,7 +1,7 @@
-import { Key } from "../fields/Key"
-import { Field, FieldId, Opt } from "../fields/Field"
+import { Key } from "../fields/Key";
+import { Field, FieldId, Opt } from "../fields/Field";
 import { ObservableMap } from "mobx";
-import { Document } from "../fields/Document"
+import { Document } from "../fields/Document";
 import { MessageStore, DocumentTransfer } from "../server/Message";
 import { Utils } from "../Utils";
 import { Server } from "./Server";
@@ -37,7 +37,7 @@ export class SocketStub {
         // document.fields.forEach((f, key) => (this.FieldStore.get(document.Id) as Document)._proxies.set(key.Id, (f as Field).Id));
 
         console.log("sending " + document.Title);
-        Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(document.ToJson()))
+        Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(document.ToJson()));
     }
 
     public static SEND_FIELD_REQUEST(fieldid: FieldId): Promise<Opt<Field>>;
@@ -50,12 +50,12 @@ export class SocketStub {
                 } else {
                     cb(undefined);
                 }
-            })
-        }
+            });
+        };
         if (callback) {
             fn(callback);
         } else {
-            return new Promise(fn)
+            return new Promise(fn);
         }
     }
 
@@ -65,7 +65,7 @@ export class SocketStub {
             for (let field of fields) {
                 fieldMap[field._id] = ServerUtils.FromJson(field);
             }
-            callback(fieldMap)
+            callback(fieldMap);
         });
     }
 
@@ -78,8 +78,9 @@ export class SocketStub {
 
         // server updates its document to hold a proxy mapping from key => fieldId
         var document = this.FieldStore.get(doc.Id) as Document;
-        if (document)
+        if (document) {
             document._proxies.set(key.Id, value.Id);
+        }
 
         // server adds the field to its repository of fields
         this.FieldStore.set(value.Id, value);
@@ -93,8 +94,9 @@ export class SocketStub {
 
         // Server removes the field id from the document's list of field proxies
         var document = this.FieldStore.get(doc.Id) as Document;
-        if (document)
+        if (document) {
             document._proxies.delete(key.Id);
+        }
     }
 
     public static SEND_SET_FIELD(field: Field) {
@@ -103,6 +105,6 @@ export class SocketStub {
         // ...SOCKET(SET_FIELD, field id, serialized field value)
 
         // Server updates the value of the field in its fieldstore
-        Utils.Emit(Server.Socket, MessageStore.SetField, field.ToJson())
+        Utils.Emit(Server.Socket, MessageStore.SetField, field.ToJson());
     }
 }
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 613b94abd..72e6e57ab 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -64,7 +64,7 @@ export namespace Documents {
     const webProtoId = "webProto";
     const collProtoId = "collectionProto";
     const kvpProtoId = "kvpProto";
-    const videoProtoId = "videoProto"
+    const videoProtoId = "videoProto";
     const audioProtoId = "audioProto";
 
     export function initProtos(): Promise<void> {
@@ -102,7 +102,7 @@ export namespace Documents {
         if (options.height !== undefined) { doc.SetNumber(KeyStore.Height, options.height); }
         if (options.panx !== undefined) { doc.SetNumber(KeyStore.PanX, options.panx); }
         if (options.pany !== undefined) { doc.SetNumber(KeyStore.PanY, options.pany); }
-        return doc
+        return doc;
     }
 
     function setupPrototypeOptions(protoId: string, title: string, layout: string, options: DocumentOptions): Document {
@@ -110,10 +110,12 @@ export namespace Documents {
     }
     function SetInstanceOptions<T, U extends Field & { Data: T }>(doc: Document, options: DocumentOptions, value: [T, { new(): U }] | Document, id?: string) {
         var deleg = doc.MakeDelegate(id);
-        if (value instanceof Document)
-            deleg.Set(KeyStore.Data, value)
-        else
+        if (value instanceof Document) {
+            deleg.Set(KeyStore.Data, value);
+        }
+        else {
             deleg.SetData(KeyStore.Data, value[0], value[1]);
+        }
         return assignOptions(deleg, options);
     }
 
@@ -134,7 +136,7 @@ export namespace Documents {
     function CreateTextPrototype(): Document {
         let textProto = setupPrototypeOptions(textProtoId, "TEXT_PROTO", FormattedTextBox.LayoutString(),
             { x: 0, y: 0, width: 300, height: 150, layoutKeys: [KeyStore.Data] });
-        return textProto
+        return textProto;
     }
     function CreatePdfPrototype(): Document {
         let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("AnnotationsKey"),
@@ -168,7 +170,7 @@ export namespace Documents {
     }
     function CreateAudioPrototype(): Document {
         let audioProto = setupPrototypeOptions(audioProtoId, "AUDIO_PROTO", AudioBox.LayoutString(),
-            { x: 0, y: 0, width: 300, height: 150, layoutKeys: [KeyStore.Data] })
+            { x: 0, y: 0, width: 300, height: 150, layoutKeys: [KeyStore.Data] });
         return audioProto;
     }
 
@@ -205,22 +207,22 @@ export namespace Documents {
         return assignToDelegate(SetInstanceOptions(webProto, options, [html, HtmlField]).MakeDelegate(), options);
     }
     export function KVPDocument(document: Document, options: DocumentOptions = {}, id?: string) {
-        return assignToDelegate(SetInstanceOptions(kvpProto, options, document, id), options)
+        return assignToDelegate(SetInstanceOptions(kvpProto, options, document, id), options);
     }
     export function FreeformDocument(documents: Array<Document>, options: DocumentOptions, id?: string, makePrototype: boolean = true) {
         if (!makePrototype) {
-            return SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Freeform }, [documents, ListField], id)
+            return SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Freeform }, [documents, ListField], id);
         }
-        return assignToDelegate(SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Freeform }, [documents, ListField], id).MakeDelegate(), options)
+        return assignToDelegate(SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Freeform }, [documents, ListField], id).MakeDelegate(), options);
     }
     export function SchemaDocument(documents: Array<Document>, options: DocumentOptions, id?: string) {
-        return assignToDelegate(SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Schema }, [documents, ListField], id), options)
+        return assignToDelegate(SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Schema }, [documents, ListField], id), options);
     }
     export function TreeDocument(documents: Array<Document>, options: DocumentOptions, id?: string) {
-        return assignToDelegate(SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Tree }, [documents, ListField], id), options)
+        return assignToDelegate(SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Tree }, [documents, ListField], id), options);
     }
     export function DockDocument(config: string, options: DocumentOptions, id?: string) {
-        return assignToDelegate(SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Docking }, [config, TextField], id), options)
+        return assignToDelegate(SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Docking }, [config, TextField], id), options);
     }
 
     export function CaptionDocument(doc: Document) {
@@ -240,13 +242,13 @@ export namespace Documents {
             <div style="position:relative; height:15%; text-align:center; ">`
             + FormattedTextBox.LayoutString("CaptionKey") +
             `</div> 
-        </div>` };
+        </div>`; }
     export function FixedCaption(fieldName: string = "Caption") {
         return `<div style="position:absolute; height:30px; bottom:0; width:100%">
             <div style="position:absolute; width:100%; height:100%; text-align:center;bottom:0;">`
             + FormattedTextBox.LayoutString(fieldName + "Key") +
             `</div> 
-        </div>` };
+        </div>`; }
 
     function OuterCaption() {
         return (`
@@ -258,7 +260,7 @@ export namespace Documents {
         <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"CaptionKey"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
     </div>
 </div>       
-        `)
+        `);
     }
     function InnerCaption() {
         return (`
@@ -270,7 +272,7 @@ export namespace Documents {
             <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"CaptionKey"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
         </div>
     </div>       
-            `)
+            `);
     }
 
     /*
@@ -293,6 +295,6 @@ export namespace Documents {
             <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"CaptionKey"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
         </div>
     </div>       
-            `)
+            `);
     }
 }
\ No newline at end of file
diff --git a/src/client/northstar/core/BaseObject.ts b/src/client/northstar/core/BaseObject.ts
index f1761e643..ed3818071 100644
--- a/src/client/northstar/core/BaseObject.ts
+++ b/src/client/northstar/core/BaseObject.ts
@@ -1,5 +1,5 @@
-import { IEquatable } from '../utils/IEquatable'
-import { IDisposable } from '../utils/IDisposable'
+import { IEquatable } from '../utils/IEquatable';
+import { IDisposable } from '../utils/IDisposable';
 
 export class BaseObject implements IEquatable, IDisposable {
 
diff --git a/src/client/northstar/core/attribute/AttributeModel.ts b/src/client/northstar/core/attribute/AttributeModel.ts
index 230bfecc8..c89b1617c 100644
--- a/src/client/northstar/core/attribute/AttributeModel.ts
+++ b/src/client/northstar/core/attribute/AttributeModel.ts
@@ -1,5 +1,5 @@
-import { Attribute, DataType, VisualizationHint } from '../../model/idea/idea'
-import { BaseObject } from '../BaseObject'
+import { Attribute, DataType, VisualizationHint } from '../../model/idea/idea';
+import { BaseObject } from '../BaseObject';
 import { observable } from "mobx";
 
 export abstract class AttributeModel extends BaseObject {
@@ -93,7 +93,7 @@ export class BackendAttributeModel extends AttributeModel {
     }
 
     public get DisplayName(): string {
-        return this._displayName.ReplaceAll("_", " ");;
+        return this._displayName.ReplaceAll("_", " ");
     }
 
     public get CodeName(): string {
diff --git a/src/client/northstar/core/attribute/AttributeTransformationModel.ts b/src/client/northstar/core/attribute/AttributeTransformationModel.ts
index f50b78d51..66485183b 100644
--- a/src/client/northstar/core/attribute/AttributeTransformationModel.ts
+++ b/src/client/northstar/core/attribute/AttributeTransformationModel.ts
@@ -1,4 +1,4 @@
-;
+
 import { computed, observable } from "mobx";
 import { AggregateFunction } from "../../model/idea/idea";
 import { AttributeModel } from "./AttributeModel";
@@ -20,16 +20,21 @@ export class AttributeTransformationModel implements IEquatable {
         if (this.AggregateFunction === AggregateFunction.Count) {
             return "count";
         }
-        if (this.AggregateFunction === AggregateFunction.Avg)
+        if (this.AggregateFunction === AggregateFunction.Avg) {
             displayName = "avg(" + displayName + ")";
-        else if (this.AggregateFunction === AggregateFunction.Max)
+        }
+        else if (this.AggregateFunction === AggregateFunction.Max) {
             displayName = "max(" + displayName + ")";
-        else if (this.AggregateFunction === AggregateFunction.Min)
+ }
+        else if (this.AggregateFunction === AggregateFunction.Min) {
             displayName = "min(" + displayName + ")";
-        else if (this.AggregateFunction === AggregateFunction.Sum)
+ }
+        else if (this.AggregateFunction === AggregateFunction.Sum) {
             displayName = "sum(" + displayName + ")";
-        else if (this.AggregateFunction === AggregateFunction.SumE)
+ }
+        else if (this.AggregateFunction === AggregateFunction.SumE) {
             displayName = "sumE(" + displayName + ")";
+ }
 
         return displayName;
     }
diff --git a/src/client/northstar/core/attribute/CalculatedAttributeModel.ts b/src/client/northstar/core/attribute/CalculatedAttributeModel.ts
index 0b8e0d12c..a197c1305 100644
--- a/src/client/northstar/core/attribute/CalculatedAttributeModel.ts
+++ b/src/client/northstar/core/attribute/CalculatedAttributeModel.ts
@@ -1,5 +1,5 @@
 import { BackendAttributeModel, AttributeModel, CodeAttributeModel } from "./AttributeModel";
-import { DataType, VisualizationHint } from '../../model/idea/idea'
+import { DataType, VisualizationHint } from '../../model/idea/idea';
 
 export class CalculatedAttributeManager {
     public static AllCalculatedAttributes: Array<AttributeModel> = new Array<AttributeModel>();
diff --git a/src/client/northstar/core/brusher/IBaseBrushable.ts b/src/client/northstar/core/brusher/IBaseBrushable.ts
index 99a36636f..c46db4d22 100644
--- a/src/client/northstar/core/brusher/IBaseBrushable.ts
+++ b/src/client/northstar/core/brusher/IBaseBrushable.ts
@@ -1,6 +1,6 @@
-import { PIXIPoint } from '../../utils/MathUtil'
+import { PIXIPoint } from '../../utils/MathUtil';
 import { IEquatable } from '../../utils/IEquatable';
-import { Document } from '../../../../fields/Document'
+import { Document } from '../../../../fields/Document';
 
 export interface IBaseBrushable<T> extends IEquatable {
     BrusherModels: Array<Document>;
diff --git a/src/client/northstar/core/brusher/IBaseBrusher.ts b/src/client/northstar/core/brusher/IBaseBrusher.ts
index d7ae65464..d2de6ed62 100644
--- a/src/client/northstar/core/brusher/IBaseBrusher.ts
+++ b/src/client/northstar/core/brusher/IBaseBrusher.ts
@@ -1,4 +1,4 @@
-import { PIXIPoint } from '../../utils/MathUtil'
+import { PIXIPoint } from '../../utils/MathUtil';
 import { IEquatable } from '../../utils/IEquatable';
 
 
diff --git a/src/client/northstar/core/filter/FilterModel.ts b/src/client/northstar/core/filter/FilterModel.ts
index 20fca77f5..e2ba3f652 100644
--- a/src/client/northstar/core/filter/FilterModel.ts
+++ b/src/client/northstar/core/filter/FilterModel.ts
@@ -15,7 +15,7 @@ export class FilterModel {
 
     public Equals(other: FilterModel): boolean {
         if (!Utils.EqualityHelper(this, other)) return false;
-        if (!this.isSame(this.ValueComparisons, (other as FilterModel).ValueComparisons)) return false;
+        if (!this.isSame(this.ValueComparisons, (other).ValueComparisons)) return false;
         return true;
     }
 
diff --git a/src/client/northstar/core/filter/IBaseFilterConsumer.ts b/src/client/northstar/core/filter/IBaseFilterConsumer.ts
index 93f66a154..59d7adf4c 100644
--- a/src/client/northstar/core/filter/IBaseFilterConsumer.ts
+++ b/src/client/northstar/core/filter/IBaseFilterConsumer.ts
@@ -1,5 +1,5 @@
-import { FilterOperand } from '../filter/FilterOperand'
-import { IEquatable } from '../../utils/IEquatable'
+import { FilterOperand } from '../filter/FilterOperand';
+import { IEquatable } from '../../utils/IEquatable';
 import { Document } from "../../../../fields/Document";
 
 export interface IBaseFilterConsumer extends IEquatable {
diff --git a/src/client/northstar/core/filter/IBaseFilterProvider.ts b/src/client/northstar/core/filter/IBaseFilterProvider.ts
index d082bfe12..fc3301b11 100644
--- a/src/client/northstar/core/filter/IBaseFilterProvider.ts
+++ b/src/client/northstar/core/filter/IBaseFilterProvider.ts
@@ -1,4 +1,4 @@
-import { FilterModel } from '../filter/FilterModel'
+import { FilterModel } from '../filter/FilterModel';
 
 export interface IBaseFilterProvider {
     FilterModels: Array<FilterModel>;
diff --git a/src/client/northstar/core/filter/ValueComparision.ts b/src/client/northstar/core/filter/ValueComparision.ts
index 1a3e461f5..80b1242a9 100644
--- a/src/client/northstar/core/filter/ValueComparision.ts
+++ b/src/client/northstar/core/filter/ValueComparision.ts
@@ -1,5 +1,5 @@
-import { Predicate } from '../../model/idea/idea'
-import { Utils } from '../../utils/Utils'
+import { Predicate } from '../../model/idea/idea';
+import { Utils } from '../../utils/Utils';
 import { AttributeModel } from '../attribute/AttributeModel';
 
 export class ValueComparison {
@@ -15,15 +15,19 @@ export class ValueComparison {
     }
 
     public Equals(other: Object): boolean {
-        if (!Utils.EqualityHelper(this, other))
+        if (!Utils.EqualityHelper(this, other)) {
             return false;
-        if (this.Predicate !== (other as ValueComparison).Predicate)
+        }
+        if (this.Predicate !== (other as ValueComparison).Predicate) {
             return false;
+        }
         let isComplex = (typeof this.Value === "object");
-        if (!isComplex && this.Value !== (other as ValueComparison).Value)
+        if (!isComplex && this.Value !== (other as ValueComparison).Value) {
             return false;
-        if (isComplex && !this.Value.Equals((other as ValueComparison).Value))
+        }
+        if (isComplex && !this.Value.Equals((other as ValueComparison).Value)) {
             return false;
+        }
         return true;
     }
 
diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts
index fa2a9c008..6abde4677 100644
--- a/src/client/northstar/dash-fields/HistogramField.ts
+++ b/src/client/northstar/dash-fields/HistogramField.ts
@@ -41,7 +41,7 @@ export class HistogramField extends BasicField<HistogramOperation> {
 
             data: this.toString(),
             _id: this.Id
-        }
+        };
     }
 
     @action
diff --git a/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts b/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts
index cdae90c8b..6291ec1fc 100644
--- a/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts
+++ b/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts
@@ -1,4 +1,4 @@
-import React = require("react")
+import React = require("react");
 import { AttributeTransformationModel } from "../../northstar/core/attribute/AttributeTransformationModel";
 import { ChartType } from '../../northstar/model/binRanges/VisualBinRange';
 import { AggregateFunction, Bin, Brush, DoubleValueAggregateResult, HistogramResult, MarginAggregateParameters, MarginAggregateResult } from "../../northstar/model/idea/idea";
@@ -29,7 +29,7 @@ export class HistogramBinPrimitiveCollection {
     private _histoBox: HistogramBox;
     private get histoOp() { return this._histoBox.HistoOp; }
     private get histoResult() { return this.histoOp.Result as HistogramResult; }
-    private get sizeConverter() { return this._histoBox.SizeConverter!; }
+    private get sizeConverter() { return this._histoBox.SizeConverter; }
     public BinPrimitives: Array<HistogramBinPrimitive> = new Array<HistogramBinPrimitive>();
     public HitGeom: PIXIRectangle = PIXIRectangle.EMPTY;
 
@@ -99,13 +99,14 @@ export class HistogramBinPrimitiveCollection {
     private createHeatmapBinPrimitives(bin: Bin, brush: Brush, brushFactorSum: number): number {
 
         let unNormalizedValue = this.getBinValue(2, bin, brush.brushIndex!);
-        if (unNormalizedValue === undefined)
+        if (unNormalizedValue === undefined) {
             return brushFactorSum;
+        }
 
         var normalizedValue = (unNormalizedValue - this._histoBox.ValueRange[0]) / (Math.abs((this._histoBox.ValueRange[1] - this._histoBox.ValueRange[0])) < HistogramBinPrimitiveCollection.TOLERANCE ?
             unNormalizedValue : this._histoBox.ValueRange[1] - this._histoBox.ValueRange[0]);
 
-        let allUnNormalizedValue = this.getBinValue(2, bin, ModelHelpers.AllBrushIndex(this.histoResult))
+        let allUnNormalizedValue = this.getBinValue(2, bin, ModelHelpers.AllBrushIndex(this.histoResult));
 
         // bcz: are these calls needed?  
         let [xFrom, xTo] = this.sizeConverter.DataToScreenXAxisRange(this._histoBox.VisualBinRanges, 0, bin);
@@ -145,8 +146,9 @@ export class HistogramBinPrimitiveCollection {
             let [xFrom, xTo] = this.sizeConverter.DataToScreenPointRange(0, bin, ModelHelpers.CreateAggregateKey(this.histoOp.Schema!.distinctAttributeParameters, this.histoOp.X, this.histoResult, brush.brushIndex!));
             let [yFrom, yTo] = this.sizeConverter.DataToScreenPointRange(1, bin, ModelHelpers.CreateAggregateKey(this.histoOp.Schema!.distinctAttributeParameters, this.histoOp.Y, this.histoResult, brush.brushIndex!));
 
-            if (xFrom !== undefined && yFrom !== undefined && xTo !== undefined && yTo !== undefined)
+            if (xFrom !== undefined && yFrom !== undefined && xTo !== undefined && yTo !== undefined) {
                 this.createBinPrimitive(-1, brush, PIXIRectangle.EMPTY, 0, xFrom, xTo, yFrom, yTo, this.baseColorFromBrush(brush), 1, unNormalizedValue);
+            }
         }
         return 0;
     }
diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index ec9413649..7df59ef07 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -1,4 +1,4 @@
-import React = require("react")
+import React = require("react");
 import { action, computed, observable, reaction, runInAction, trace } from "mobx";
 import { observer } from "mobx-react";
 import Measure from "react-measure";
@@ -25,7 +25,7 @@ import { StyleConstants } from "../utils/StyleContants";
 
 @observer
 export class HistogramBox extends React.Component<FieldViewProps> {
-    public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(HistogramBox, fieldStr) }
+    public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(HistogramBox, fieldStr); }
     private _dropXRef = React.createRef<HTMLDivElement>();
     private _dropYRef = React.createRef<HTMLDivElement>();
     private _dropXDisposer?: DragManager.DragDropDisposer;
@@ -92,15 +92,15 @@ export class HistogramBox extends React.Component<FieldViewProps> {
         }
         reaction(() => CurrentUserUtils.NorthstarDBCatalog, (catalog?: Catalog) => this.activateHistogramOperation(catalog), { fireImmediately: true });
         reaction(() => [this.VisualBinRanges && this.VisualBinRanges.slice()], () => this.SizeConverter.SetVisualBinRanges(this.VisualBinRanges));
-        reaction(() => [this.PanelHeight, this.PanelWidth], () => this.SizeConverter.SetIsSmall(this.PanelWidth < 40 && this.PanelHeight < 40))
+        reaction(() => [this.PanelHeight, this.PanelWidth], () => this.SizeConverter.SetIsSmall(this.PanelWidth < 40 && this.PanelHeight < 40));
         reaction(() => this.HistogramResult ? this.HistogramResult.binRanges : undefined,
             (binRanges: BinRange[] | undefined) => {
                 if (binRanges) {
                     this.VisualBinRanges.splice(0, this.VisualBinRanges.length, ...binRanges.map((br, ind) =>
-                        VisualBinRangeHelper.GetVisualBinRange(this.HistoOp.Schema!.distinctAttributeParameters, br, this.HistogramResult!, ind ? this.HistoOp.Y : this.HistoOp.X, this.ChartType)));
+                        VisualBinRangeHelper.GetVisualBinRange(this.HistoOp.Schema!.distinctAttributeParameters, br, this.HistogramResult, ind ? this.HistoOp.Y : this.HistoOp.X, this.ChartType)));
 
-                    let valueAggregateKey = ModelHelpers.CreateAggregateKey(this.HistoOp.Schema!.distinctAttributeParameters, this.HistoOp.V, this.HistogramResult!, ModelHelpers.AllBrushIndex(this.HistogramResult!));
-                    this.ValueRange = Object.values(this.HistogramResult!.bins!).reduce((prev, cur) => {
+                    let valueAggregateKey = ModelHelpers.CreateAggregateKey(this.HistoOp.Schema!.distinctAttributeParameters, this.HistoOp.V, this.HistogramResult, ModelHelpers.AllBrushIndex(this.HistogramResult));
+                    this.ValueRange = Object.values(this.HistogramResult.bins!).reduce((prev, cur) => {
                         let value = ModelHelpers.GetAggregateResult(cur, valueAggregateKey) as DoubleValueAggregateResult;
                         return value && value.hasResult ? [Math.min(prev[0], value.result!), Math.max(prev[1], value.result!)] : prev;
                     }, [Number.MAX_VALUE, Number.MIN_VALUE]);
@@ -109,10 +109,12 @@ export class HistogramBox extends React.Component<FieldViewProps> {
     }
 
     componentWillUnmount() {
-        if (this._dropXDisposer)
+        if (this._dropXDisposer) {
             this._dropXDisposer();
-        if (this._dropYDisposer)
+        }
+        if (this._dropYDisposer) {
             this._dropYDisposer();
+        }
     }
 
     activateHistogramOperation(catalog?: Catalog) {
@@ -128,7 +130,7 @@ export class HistogramBox extends React.Component<FieldViewProps> {
                             this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...brushingDocs.map((brush, i) => {
                                 brush.SetNumber(KeyStore.BackgroundColor, StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length]);
                                 let brushed = brush.GetList(KeyStore.BrushingDocs, [] as Document[]);
-                                return { l: brush, b: brushed[0].Id === proto.Id ? brushed[1] : brushed[0] }
+                                return { l: brush, b: brushed[0].Id === proto.Id ? brushed[1] : brushed[0] };
                             }));
                         }, { fireImmediately: true });
                     reaction(() => this.createOperationParamsCache, () => this.HistoOp.Update(), { fireImmediately: true });
@@ -146,7 +148,7 @@ export class HistogramBox extends React.Component<FieldViewProps> {
         let roff = this.SizeConverter.RightOffset;
         let boff = this.SizeConverter.BottomOffset;
         return (
-            <Measure onResize={(r: any) => runInAction(() => { this.PanelWidth = r.entry.width; this.PanelHeight = r.entry.height })}>
+            <Measure onResize={(r: any) => runInAction(() => { this.PanelWidth = r.entry.width; this.PanelHeight = r.entry.height; })}>
                 {({ measureRef }) =>
                     <div className="histogrambox-container" ref={measureRef} style={{ transform: `translate(-50%, -50%)` }}>
                         <div className="histogrambox-yaxislabel" onPointerDown={this.yLabelPointerDown} ref={this._dropYRef} >
@@ -168,7 +170,7 @@ export class HistogramBox extends React.Component<FieldViewProps> {
                     </div>
                 }
             </Measure>
-        )
+        );
     }
 }
 
diff --git a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
index a78703247..4c5bdb14b 100644
--- a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
@@ -1,4 +1,4 @@
-import React = require("react")
+import React = require("react");
 import { computed, observable, reaction, runInAction, trace, action } from "mobx";
 import { observer } from "mobx-react";
 import { Utils as DashUtils } from '../../../Utils';
@@ -28,17 +28,18 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
     @computed get selectedPrimitives() { return this._selectedPrims.map(bp => this.drawRect(bp.Rect, bp.BarAxis, undefined, "border")); }
     @computed get barPrimitives() {
         let histoResult = this.props.HistoBox.HistogramResult;
-        if (!histoResult || !histoResult.bins || !this.props.HistoBox.VisualBinRanges.length)
+        if (!histoResult || !histoResult.bins || !this.props.HistoBox.VisualBinRanges.length) {
             return (null);
+        }
         let allBrushIndex = ModelHelpers.AllBrushIndex(histoResult);
         return Object.keys(histoResult.bins).reduce((prims: JSX.Element[], key: string) => {
-            let drawPrims = new HistogramBinPrimitiveCollection(histoResult!.bins![key], this.props.HistoBox);
+            let drawPrims = new HistogramBinPrimitiveCollection(histoResult.bins![key], this.props.HistoBox);
             let toggle = this.getSelectionToggle(drawPrims.BinPrimitives, allBrushIndex,
-                ModelHelpers.GetBinFilterModel(histoResult!.bins![key], allBrushIndex, histoResult!, this.histoOp.X, this.histoOp.Y));
+                ModelHelpers.GetBinFilterModel(histoResult.bins![key], allBrushIndex, histoResult, this.histoOp.X, this.histoOp.Y));
             drawPrims.BinPrimitives.filter(bp => bp.DataValue && bp.BrushIndex !== allBrushIndex).map(bp =>
                 prims.push(...[{ r: bp.Rect, c: bp.Color }, { r: bp.MarginRect, c: StyleConstants.MARGIN_BARS_COLOR }].map(pair => this.drawRect(pair.r, bp.BarAxis, pair.c, "bar", toggle))));
             return prims;
-        }, [] as JSX.Element[])
+        }, [] as JSX.Element[]);
     }
 
     componentDidMount() {
@@ -47,8 +48,9 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
 
     private getSelectionToggle(binPrimitives: HistogramBinPrimitive[], allBrushIndex: number, filterModel: FilterModel) {
         let rawAllBrushPrim = ArrayUtil.FirstOrDefault(binPrimitives, bp => bp.BrushIndex === allBrushIndex);
-        if (!rawAllBrushPrim)
-            return () => { }
+        if (!rawAllBrushPrim) {
+            return () => { };
+        }
         let allBrushPrim = rawAllBrushPrim;
         return () => runInAction(() => {
             if (ArrayUtil.Contains(this.histoOp.FilterModels, filterModel)) {
@@ -59,23 +61,25 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
                 this._selectedPrims.push(allBrushPrim);
                 this.histoOp.AddFilterModels([filterModel]);
             }
-        })
+        });
     }
 
     private renderGridLinesAndLabels(axis: number) {
         trace();
-        if (!this.props.HistoBox.SizeConverter.Initialized)
+        if (!this.props.HistoBox.SizeConverter.Initialized) {
             return (null);
+        }
         let labels = this.props.HistoBox.VisualBinRanges[axis].GetLabels();
         return <svg className="histogramboxprimitives-svgContainer">
             {labels.reduce((prims, binLabel, i) => {
                 let r = this.props.HistoBox.SizeConverter.DataToScreenRange(binLabel.minValue!, binLabel.maxValue!, axis);
                 prims.push(this.drawLine(r.xFrom, r.yFrom, axis === 0 ? 0 : r.xTo - r.xFrom, axis === 0 ? r.yTo - r.yFrom : 0));
-                if (i === labels.length - 1)
+                if (i === labels.length - 1) {
                     prims.push(this.drawLine(axis === 0 ? r.xTo : r.xFrom, axis === 0 ? r.yFrom : r.yTo, axis === 0 ? 0 : r.xTo - r.xFrom, axis === 0 ? r.yTo - r.yFrom : 0));
+                }
                 return prims;
             }, [] as JSX.Element[])}
-        </svg>
+        </svg>;
     }
 
     drawLine(xFrom: number, yFrom: number, width: number, height: number) {
@@ -89,9 +93,9 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
         }
         let trans2Xpercent = `${(xFrom + width) / this.renderDimension * 100}%`;
         let trans2Ypercent = `${(yFrom + height) / this.renderDimension * 100}%`;
-        let trans1Xpercent = `${xFrom / this.renderDimension * 100}%`
-        let trans1Ypercent = `${yFrom / this.renderDimension * 100}%`
-        return <line className="histogramboxprimitives-line" key={DashUtils.GenerateGuid()} x1={trans1Xpercent} x2={`${trans2Xpercent}`} y1={trans1Ypercent} y2={`${trans2Ypercent}`} />
+        let trans1Xpercent = `${xFrom / this.renderDimension * 100}%`;
+        let trans1Ypercent = `${yFrom / this.renderDimension * 100}%`;
+        return <line className="histogramboxprimitives-line" key={DashUtils.GenerateGuid()} x1={trans1Xpercent} x2={`${trans2Xpercent}`} y1={trans1Ypercent} y2={`${trans2Ypercent}`} />;
     }
     drawRect(r: PIXIRectangle, barAxis: number, color: number | undefined, classExt: string, tapHandler: () => void = () => { }) {
         if (r.height < 0) {
@@ -102,11 +106,11 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
             r.x += r.width;
             r.width = -r.width;
         }
-        let transXpercent = `${r.x / this.renderDimension * 100}%`
-        let transYpercent = `${r.y / this.renderDimension * 100}%`
+        let transXpercent = `${r.x / this.renderDimension * 100}%`;
+        let transYpercent = `${r.y / this.renderDimension * 100}%`;
         let widthXpercent = `${r.width / this.renderDimension * 100}%`;
         let heightYpercent = `${r.height / this.renderDimension * 100}%`;
-        return (<rect className={`histogramboxprimitives-${classExt}`} key={DashUtils.GenerateGuid()} onPointerDown={(e: React.PointerEvent) => { if (e.button === 0) tapHandler() }}
+        return (<rect className={`histogramboxprimitives-${classExt}`} key={DashUtils.GenerateGuid()} onPointerDown={(e: React.PointerEvent) => { if (e.button === 0) tapHandler(); }}
             x={transXpercent} width={`${widthXpercent}`} y={transYpercent} height={`${heightYpercent}`} fill={color ? `${LABColor.RGBtoHexString(color)}` : "transparent"} />);
     }
     render() {
@@ -118,6 +122,6 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
                 {this.barPrimitives}
                 {this.selectedPrimitives}
             </svg>
-        </div>
+        </div>;
     }
 }
\ No newline at end of file
diff --git a/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx b/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx
index dd62e9146..5785fe838 100644
--- a/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx
+++ b/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx
@@ -1,4 +1,4 @@
-import React = require("react")
+import React = require("react");
 import { action, computed, reaction } from "mobx";
 import { observer } from "mobx-react";
 import { Utils as DashUtils } from '../../../Utils';
@@ -13,7 +13,7 @@ import { HistogramPrimitivesProps } from "./HistogramBoxPrimitives";
 export class HistogramLabelPrimitives extends React.Component<HistogramPrimitivesProps> {
     componentDidMount() {
         reaction(() => [this.props.HistoBox.PanelWidth, this.props.HistoBox.SizeConverter.LeftOffset, this.props.HistoBox.VisualBinRanges.length],
-            (fields) => HistogramLabelPrimitives.computeLabelAngle(fields[0] as number, fields[1] as number, this.props.HistoBox), { fireImmediately: true });
+            (fields) => HistogramLabelPrimitives.computeLabelAngle(fields[0], fields[1], this.props.HistoBox), { fireImmediately: true });
     }
 
     @action
@@ -32,8 +32,9 @@ export class HistogramLabelPrimitives extends React.Component<HistogramPrimitive
     private renderGridLinesAndLabels(axis: number) {
         let sc = this.props.HistoBox.SizeConverter;
         let vb = this.props.HistoBox.VisualBinRanges;
-        if (!vb.length || !sc.Initialized)
+        if (!vb.length || !sc.Initialized) {
             return (null);
+        }
         let dim = (axis === 0 ? this.props.HistoBox.PanelWidth : this.props.HistoBox.PanelHeight) / ((axis === 0 && vb[axis] instanceof NominalVisualBinRange) ?
             (12 + 5) : //  (<number>FontStyles.AxisLabel.fontSize + 5)));
             sc.MaxLabelSizes[axis].coords[axis] + 5);
@@ -52,8 +53,8 @@ export class HistogramLabelPrimitives extends React.Component<HistogramPrimitive
                     xStart += Math.max(textWidth / 2, (1 - textWidth / space) * textWidth / 2) - textHeight / 2;
                 }
 
-                let xPercent = axis === 1 ? `${xStart}px` : `${xStart / sc.RenderDimension * 100}%`
-                let yPercent = axis === 0 ? `${this.props.HistoBox.PanelHeight - sc.BottomOffset - textHeight}px` : `${yStart / sc.RenderDimension * 100}%`
+                let xPercent = axis === 1 ? `${xStart}px` : `${xStart / sc.RenderDimension * 100}%`;
+                let yPercent = axis === 0 ? `${this.props.HistoBox.PanelHeight - sc.BottomOffset - textHeight}px` : `${yStart / sc.RenderDimension * 100}%`;
 
                 prims.push(
                     <div className="histogramLabelPrimitives-placer" key={DashUtils.GenerateGuid()} style={{ transform: `translate(${xPercent}, ${yPercent})` }}>
@@ -61,7 +62,7 @@ export class HistogramLabelPrimitives extends React.Component<HistogramPrimitive
                             {label}
                         </div>
                     </div>
-                )
+                );
             }
             return prims;
         }, [] as JSX.Element[]);
@@ -73,7 +74,7 @@ export class HistogramLabelPrimitives extends React.Component<HistogramPrimitive
         return <div className="histogramLabelPrimitives-container">
             {xaxislines}
             {yaxislines}
-        </div>
+        </div>;
     }
 
 }
\ No newline at end of file
diff --git a/src/client/northstar/manager/Gateway.ts b/src/client/northstar/manager/Gateway.ts
index ba6fe2ad5..1c8d3fd73 100644
--- a/src/client/northstar/manager/Gateway.ts
+++ b/src/client/northstar/manager/Gateway.ts
@@ -1,4 +1,4 @@
-import { Catalog, OperationReference, Result, CompileResults } from "../model/idea/idea"
+import { Catalog, OperationReference, Result, CompileResults } from "../model/idea/idea";
 import { computed, observable, action } from "mobx";
 
 export class Gateway {
diff --git a/src/client/northstar/model/ModelExtensions.ts b/src/client/northstar/model/ModelExtensions.ts
index e4bf77ed8..29f80d2d1 100644
--- a/src/client/northstar/model/ModelExtensions.ts
+++ b/src/client/northstar/model/ModelExtensions.ts
@@ -1,7 +1,7 @@
-import { AttributeParameters, Brush, MarginAggregateParameters, SingleDimensionAggregateParameters, Solution } from '../model/idea/idea'
-import { Utils } from '../utils/Utils'
+import { AttributeParameters, Brush, MarginAggregateParameters, SingleDimensionAggregateParameters, Solution } from '../model/idea/idea';
+import { Utils } from '../utils/Utils';
 
-import { FilterModel } from '../core/filter/FilterModel'
+import { FilterModel } from '../core/filter/FilterModel';
 
 (SingleDimensionAggregateParameters as any).prototype.Equals = function (other: Object) {
     if (!Utils.EqualityHelper(this, other)) return false;
@@ -9,13 +9,13 @@ import { FilterModel } from '../core/filter/FilterModel'
         (other as SingleDimensionAggregateParameters).attributeParameters!)) return false;
     if (!((this as SingleDimensionAggregateParameters).attributeParameters! as any).Equals((other as SingleDimensionAggregateParameters).attributeParameters)) return false;
     return true;
-}
+};
 
 {
     (AttributeParameters as any).prototype.Equals = function (other: AttributeParameters) {
-        return (<any>this).constructor.name === (<any>other).constructor.name &&
+        return (this).constructor.name === (<any>other).constructor.name &&
             this.rawName === other.rawName;
-    }
+    };
 }
 
 {
@@ -23,7 +23,7 @@ import { FilterModel } from '../core/filter/FilterModel'
         if (!Utils.EqualityHelper(this, other)) return false;
         if ((this as Solution).solutionId !== (other as Solution).solutionId) return false;
         return true;
-    }
+    };
 }
 
 {
@@ -35,7 +35,7 @@ import { FilterModel } from '../core/filter/FilterModel'
 
         if ((this as MarginAggregateParameters).aggregateFunction !== (other as MarginAggregateParameters).aggregateFunction) return false;
         return true;
-    }
+    };
 }
 
 {
@@ -44,5 +44,5 @@ import { FilterModel } from '../core/filter/FilterModel'
         if ((this as Brush).brushEnum !== (other as Brush).brushEnum) return false;
         if ((this as Brush).brushIndex !== (other as Brush).brushIndex) return false;
         return true;
-    }
+    };
 }
\ No newline at end of file
diff --git a/src/client/northstar/model/ModelHelpers.ts b/src/client/northstar/model/ModelHelpers.ts
index 1a58e6180..ac807b41f 100644
--- a/src/client/northstar/model/ModelHelpers.ts
+++ b/src/client/northstar/model/ModelHelpers.ts
@@ -64,7 +64,7 @@ export class ModelHelpers {
             if (aggParams) {
                 aggregateParameters.push(aggParams);
 
-                var margin = new MarginAggregateParameters()
+                var margin = new MarginAggregateParameters();
                 margin.aggregateFunction = agg.AggregateFunction;
                 margin.attributeParameters = ModelHelpers.GetAttributeParameters(agg.AttributeModel);
                 margin.distinctAttributeParameters = distinctAttributeParameters;
@@ -106,7 +106,7 @@ export class ModelHelpers {
                 {
                     rawName: am.CodeName,
                     visualizationHints: am.VisualizationHints,
-                    id: (am as BackendAttributeModel).Id
+                    id: (am).Id
                 });
         }
         else if (am instanceof CodeAttributeModel) {
@@ -114,11 +114,11 @@ export class ModelHelpers {
                 {
                     rawName: am.CodeName,
                     visualizationHints: am.VisualizationHints,
-                    code: (am as CodeAttributeModel).Code
+                    code: (am).Code
                 });
         }
         else {
-            throw new Exception()
+            throw new Exception();
         }
     }
 
diff --git a/src/client/northstar/model/binRanges/AlphabeticVisualBinRange.ts b/src/client/northstar/model/binRanges/AlphabeticVisualBinRange.ts
index 995bf4e0b..120b034f2 100644
--- a/src/client/northstar/model/binRanges/AlphabeticVisualBinRange.ts
+++ b/src/client/northstar/model/binRanges/AlphabeticVisualBinRange.ts
@@ -1,5 +1,5 @@
-import { AlphabeticBinRange, BinLabel } from '../../model/idea/idea'
-import { VisualBinRange } from './VisualBinRange'
+import { AlphabeticBinRange, BinLabel } from '../../model/idea/idea';
+import { VisualBinRange } from './VisualBinRange';
 
 export class AlphabeticVisualBinRange extends VisualBinRange {
     public DataBinRange: AlphabeticBinRange;
diff --git a/src/client/northstar/model/binRanges/DateTimeVisualBinRange.ts b/src/client/northstar/model/binRanges/DateTimeVisualBinRange.ts
index f5872aa4c..776e643cd 100644
--- a/src/client/northstar/model/binRanges/DateTimeVisualBinRange.ts
+++ b/src/client/northstar/model/binRanges/DateTimeVisualBinRange.ts
@@ -1,5 +1,5 @@
-import { DateTimeBinRange, DateTimeStep, DateTimeStepGranularity } from '../idea/idea'
-import { VisualBinRange } from './VisualBinRange'
+import { DateTimeBinRange, DateTimeStep, DateTimeStepGranularity } from '../idea/idea';
+import { VisualBinRange } from './VisualBinRange';
 
 export class DateTimeVisualBinRange extends VisualBinRange {
     public DataBinRange: DateTimeBinRange;
diff --git a/src/client/northstar/model/binRanges/NominalVisualBinRange.ts b/src/client/northstar/model/binRanges/NominalVisualBinRange.ts
index 407ff3ea6..42509d797 100644
--- a/src/client/northstar/model/binRanges/NominalVisualBinRange.ts
+++ b/src/client/northstar/model/binRanges/NominalVisualBinRange.ts
@@ -1,5 +1,5 @@
-import { NominalBinRange, BinLabel } from '../../model/idea/idea'
-import { VisualBinRange } from './VisualBinRange'
+import { NominalBinRange, BinLabel } from '../../model/idea/idea';
+import { VisualBinRange } from './VisualBinRange';
 
 export class NominalVisualBinRange extends VisualBinRange {
     public DataBinRange: NominalBinRange;
diff --git a/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts b/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts
index 80886416b..c579c8e5f 100644
--- a/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts
+++ b/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts
@@ -1,4 +1,4 @@
-import { QuantitativeBinRange } from '../idea/idea'
+import { QuantitativeBinRange } from '../idea/idea';
 import { VisualBinRange } from './VisualBinRange';
 import { format } from "d3-format";
 
diff --git a/src/client/northstar/model/binRanges/VisualBinRange.ts b/src/client/northstar/model/binRanges/VisualBinRange.ts
index a0766e494..449a22e91 100644
--- a/src/client/northstar/model/binRanges/VisualBinRange.ts
+++ b/src/client/northstar/model/binRanges/VisualBinRange.ts
@@ -1,4 +1,4 @@
-import { BinLabel } from '../../model/idea/idea'
+import { BinLabel } from '../../model/idea/idea';
 
 export abstract class VisualBinRange {
 
diff --git a/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts b/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts
index 63ee61909..9671e55f8 100644
--- a/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts
+++ b/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts
@@ -16,18 +16,18 @@ export class VisualBinRangeHelper {
 
     public static GetNonAggregateVisualBinRange(dataBinRange: BinRange): VisualBinRange {
         if (dataBinRange instanceof NominalBinRange) {
-            return new NominalVisualBinRange(dataBinRange as NominalBinRange);
+            return new NominalVisualBinRange(dataBinRange);
         }
         else if (dataBinRange instanceof QuantitativeBinRange) {
-            return new QuantitativeVisualBinRange(dataBinRange as QuantitativeBinRange);
+            return new QuantitativeVisualBinRange(dataBinRange);
         }
         else if (dataBinRange instanceof AlphabeticBinRange) {
-            return new AlphabeticVisualBinRange(dataBinRange as AlphabeticBinRange);
+            return new AlphabeticVisualBinRange(dataBinRange);
         }
         else if (dataBinRange instanceof DateTimeBinRange) {
-            return new DateTimeVisualBinRange(dataBinRange as DateTimeBinRange);
+            return new DateTimeVisualBinRange(dataBinRange);
         }
-        throw new Exception()
+        throw new Exception();
     }
 
     public static GetVisualBinRange(distinctAttributeParameters: AttributeParameters | undefined, dataBinRange: BinRange, histoResult: HistogramResult, attr: AttributeTransformationModel, chartType: ChartType): VisualBinRange {
@@ -51,13 +51,13 @@ export class VisualBinRangeHelper {
                         }
                     }
                 }
-            };
+            }
 
             let visualBinRange = QuantitativeVisualBinRange.Initialize(minValue, maxValue, 10, false);
 
             if (chartType === ChartType.HorizontalBar || chartType === ChartType.VerticalBar) {
                 visualBinRange = QuantitativeVisualBinRange.Initialize(Math.min(0, minValue),
-                    Math.max(0, (visualBinRange as QuantitativeVisualBinRange).DataBinRange.maxValue!),
+                    Math.max(0, (visualBinRange).DataBinRange.maxValue!),
                     SETTINGS_X_BINS, false);
             }
             else if (chartType === ChartType.SinglePoint) {
diff --git a/src/client/northstar/operations/BaseOperation.ts b/src/client/northstar/operations/BaseOperation.ts
index a14337763..c6d5f0a15 100644
--- a/src/client/northstar/operations/BaseOperation.ts
+++ b/src/client/northstar/operations/BaseOperation.ts
@@ -1,4 +1,4 @@
-import { FilterModel } from '../core/filter/FilterModel'
+import { FilterModel } from '../core/filter/FilterModel';
 import { ErrorResult, Exception, OperationParameters, OperationReference, Result, ResultParameters } from '../model/idea/idea';
 import { action, computed, observable } from "mobx";
 import { Gateway } from '../manager/Gateway';
@@ -62,8 +62,9 @@ export abstract class BaseOperation {
             }
 
             let operationParameters = this.CreateOperationParameters();
-            if (this.Result)
-                this.Result.progress = 0; // bcz: used to set Result to undefined, but that causes the display to blink
+            if (this.Result) {
+                this.Result.progress = 0;
+            } // bcz: used to set Result to undefined, but that causes the display to blink
             this.Error = "";
             let salt = Math.random().toString();
             this.RequestSalt = salt;
@@ -97,7 +98,7 @@ export abstract class BaseOperation {
                 pollPromise.Start(async () => {
                     let result = await Gateway.Instance.GetResult(resultParameters.toJSON());
                     if (result instanceof ErrorResult) {
-                        throw new Error((result as ErrorResult).message);
+                        throw new Error((result).message);
                     }
                     if (this.RequestSalt === pollPromise.RequestSalt) {
                         if (result && (!this.Result || this.Result.progress !== result.progress)) {
diff --git a/src/client/northstar/operations/HistogramOperation.ts b/src/client/northstar/operations/HistogramOperation.ts
index a3ddc1c98..760106023 100644
--- a/src/client/northstar/operations/HistogramOperation.ts
+++ b/src/client/northstar/operations/HistogramOperation.ts
@@ -62,7 +62,7 @@ export class HistogramOperation extends BaseOperation implements IBaseFilterCons
     @computed
     public get FilterString(): string {
         let filterModels: FilterModel[] = [];
-        return FilterModel.GetFilterModelsRecursive(this, new Set<IBaseFilterProvider>(), filterModels, true)
+        return FilterModel.GetFilterModelsRecursive(this, new Set<IBaseFilterProvider>(), filterModels, true);
     }
 
     @computed
diff --git a/src/client/northstar/utils/ArrayUtil.ts b/src/client/northstar/utils/ArrayUtil.ts
index a52ca6d96..12b8d8e77 100644
--- a/src/client/northstar/utils/ArrayUtil.ts
+++ b/src/client/northstar/utils/ArrayUtil.ts
@@ -50,7 +50,7 @@ export class ArrayUtil {
         if (filtered.length > 0) {
             return filtered[0];
         }
-        throw new Exception()
+        throw new Exception();
     }
 
     public static FirstOrDefault<T>(arr: T[], predicate: (x: any) => boolean): T | undefined {
diff --git a/src/client/northstar/utils/Extensions.ts b/src/client/northstar/utils/Extensions.ts
index 7c2b7fc9d..df14d4da0 100644
--- a/src/client/northstar/utils/Extensions.ts
+++ b/src/client/northstar/utils/Extensions.ts
@@ -6,7 +6,7 @@ interface String {
 String.prototype.ReplaceAll = function (toReplace: string, replacement: string): string {
     var target = this;
     return target.split(toReplace).join(replacement);
-}
+};
 
 String.prototype.Truncate = function (length: number, replacement: string): String {
     var target = this;
@@ -14,7 +14,7 @@ String.prototype.Truncate = function (length: number, replacement: string): Stri
         target = target.slice(0, Math.max(0, length - replacement.length)) + replacement;
     }
     return target;
-}
+};
 
 interface Math {
     log10(val: number): number;
@@ -22,7 +22,7 @@ interface Math {
 
 Math.log10 = function (val: number): number {
     return Math.log(val) / Math.LN10;
-}
+};
 
 declare interface ObjectConstructor {
     assign(...objects: Object[]): Object;
diff --git a/src/client/northstar/utils/GeometryUtil.ts b/src/client/northstar/utils/GeometryUtil.ts
index 6d8acea20..d5220c479 100644
--- a/src/client/northstar/utils/GeometryUtil.ts
+++ b/src/client/northstar/utils/GeometryUtil.ts
@@ -8,15 +8,19 @@ export class GeometryUtil {
         let minY: number = Number.MAX_VALUE;
         let maxX: number = Number.MIN_VALUE;
         let maxY: number = Number.MIN_VALUE;
-        for (var i = 0; i < points.length; i++) {
-            if (points[i].x < minX)
-                minX = points[i].x;
-            if (points[i].y < minY)
-                minY = points[i].y;
-            if (points[i].x > maxX)
-                maxX = points[i].x;
-            if (points[i].y > maxY)
-                maxY = points[i].y;
+        for (const point of points) {
+            if (point.x < minX) {
+                minX = point.x;
+            }
+            if (point.y < minY) {
+                minY = point.y;
+            }
+            if (point.x > maxX) {
+                maxX = point.x;
+            }
+            if (point.y > maxY) {
+                maxY = point.y;
+            }
         }
         return new PIXIRectangle(minX * scale - padding, minY * scale - padding, (maxX - minX) * scale + padding * 2, (maxY - minY) * scale + padding * 2);
     }
@@ -35,7 +39,7 @@ export class GeometryUtil {
                 nx = (cos * (x - cx)) + (sin * (y - cy)) + cx,
                 ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
             return new PIXIPoint(nx, ny);
-        }
+        };
         return points.map(p => rotate(center.x, center.y, p.x, p.y, angle));
     }
 
@@ -54,9 +58,9 @@ export class GeometryUtil {
             return [];
         }
 
-        for (let v = 0; v < points.length; v++) {
-            x = points[v].x;
-            y = points[v].y;
+        for (const point of points) {
+            x = point.x;
+            y = point.y;
             sum_x += x;
             sum_y += y;
             sum_xx += x * x;
@@ -68,8 +72,8 @@ export class GeometryUtil {
         let b = (sum_y / count) - (m * sum_x) / count;
         let result: PIXIPoint[] = new Array<PIXIPoint>();
 
-        for (let v = 0; v < points.length; v++) {
-            x = points[v].x;
+        for (const point of points) {
+            x = point.x;
             y = x * m + b;
             result.push(new PIXIPoint(x, y));
         }
diff --git a/src/client/northstar/utils/MathUtil.ts b/src/client/northstar/utils/MathUtil.ts
index 7aa255096..4b44f40c3 100644
--- a/src/client/northstar/utils/MathUtil.ts
+++ b/src/client/northstar/utils/MathUtil.ts
@@ -16,11 +16,11 @@ export class PIXIRectangle {
     public x: number;
     public y: number;
     public width: number;
-    public height: number
-    public get left() { return this.x }
+    public height: number;
+    public get left() { return this.x; }
     public get right() { return this.x + this.width; }
-    public get top() { return this.y }
-    public get bottom() { return this.top + this.height }
+    public get top() { return this.y; }
+    public get bottom() { return this.top + this.height; }
     public static get EMPTY() { return new PIXIRectangle(0, 0, -1, -1); }
     constructor(x: number, y: number, width: number, height: number) {
         this.x = x;
@@ -127,14 +127,18 @@ export class MathUtil {
     }
 
     public static PointInPIXIRectangle(p: PIXIPoint, rect: PIXIRectangle): boolean {
-        if (p.x < rect.left - this.EPSILON)
+        if (p.x < rect.left - this.EPSILON) {
             return false;
-        if (p.x > rect.right + this.EPSILON)
+        }
+        if (p.x > rect.right + this.EPSILON) {
             return false;
-        if (p.y < rect.top - this.EPSILON)
+        }
+        if (p.y < rect.top - this.EPSILON) {
             return false;
-        if (p.y > rect.bottom + this.EPSILON)
+        }
+        if (p.y > rect.bottom + this.EPSILON) {
             return false;
+        }
 
         return true;
     }
@@ -145,23 +149,27 @@ export class MathUtil {
         var r3 = new PIXIPoint(rect.right, rect.bottom);
         var r4 = new PIXIPoint(rect.left, rect.bottom);
         var ret = new Array<PIXIPoint>();
-        var dist = this.Dist(lineFrom, lineTo)
+        var dist = this.Dist(lineFrom, lineTo);
         var inter = this.LineSegmentIntersection(lineFrom, lineTo, r1, r2);
         if (inter && this.PointInPIXIRectangle(inter, rect) &&
-            this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist)
+            this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) {
             ret.push(inter);
+        }
         inter = this.LineSegmentIntersection(lineFrom, lineTo, r2, r3);
         if (inter && this.PointInPIXIRectangle(inter, rect) &&
-            this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist)
+            this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) {
             ret.push(inter);
+        }
         inter = this.LineSegmentIntersection(lineFrom, lineTo, r3, r4);
         if (inter && this.PointInPIXIRectangle(inter, rect) &&
-            this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist)
+            this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) {
             ret.push(inter);
+        }
         inter = this.LineSegmentIntersection(lineFrom, lineTo, r4, r1);
         if (inter && this.PointInPIXIRectangle(inter, rect) &&
-            this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist)
+            this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) {
             ret.push(inter);
+        }
         return ret;
     }
 
@@ -178,7 +186,7 @@ export class MathUtil {
     }
 
     public static Dot(p1: PIXIPoint, p2: PIXIPoint): number {
-        return p1.x * p2.x + p1.y * p2.y
+        return p1.x * p2.x + p1.y * p2.y;
     }
 
     public static Normalize(p1: PIXIPoint) {
@@ -193,7 +201,7 @@ export class MathUtil {
     public static DistSquared(p1: PIXIPoint, p2: PIXIPoint): number {
         const a = p1.x - p2.x;
         const b = p1.y - p2.y;
-        return (a * a + b * b)
+        return (a * a + b * b);
     }
 
     public static RectIntersectsRect(r1: PIXIRectangle, r2: PIXIRectangle): boolean {
diff --git a/src/client/northstar/utils/SizeConverter.ts b/src/client/northstar/utils/SizeConverter.ts
index b5c4a16ab..a52890ed9 100644
--- a/src/client/northstar/utils/SizeConverter.ts
+++ b/src/client/northstar/utils/SizeConverter.ts
@@ -32,8 +32,8 @@ export class SizeConverter {
         this.Initialized++;
         var xLabels = visualBinRanges[0].GetLabels();
         var yLabels = visualBinRanges[1].GetLabels();
-        var xLabelStrings = xLabels.map(l => l.label!).sort(function (a, b) { return b.length - a.length });
-        var yLabelStrings = yLabels.map(l => l.label!).sort(function (a, b) { return b.length - a.length });
+        var xLabelStrings = xLabels.map(l => l.label!).sort(function (a, b) { return b.length - a.length; });
+        var yLabelStrings = yLabels.map(l => l.label!).sort(function (a, b) { return b.length - a.length; });
 
         var metricsX = { width: 75 }; // RenderUtils.MeasureText(FontStyles.Default.fontFamily.toString(), 12, // FontStyles.AxisLabel.fontSize as number,
         //xLabelStrings[0]!.slice(0, 20)) // StyleConstants.MAX_CHAR_FOR_HISTOGRAM_LABELS));
@@ -62,19 +62,20 @@ export class SizeConverter {
 
     public DataToScreenPointRange(axis: number, bin: Bin, aggregateKey: AggregateKey) {
         var value = ModelHelpers.GetAggregateResult(bin, aggregateKey) as DoubleValueAggregateResult;
-        if (value && value.hasResult)
+        if (value && value.hasResult) {
             return [this.DataToScreenCoord(value.result!, axis) - 5,
             this.DataToScreenCoord(value.result!, axis) + 5];
+        }
         return [undefined, undefined];
     }
 
     public DataToScreenXAxisRange(visualBinRanges: VisualBinRange[], index: number, bin: Bin) {
         var value = visualBinRanges[0].GetValueFromIndex(bin.binIndex!.indices![index]);
-        return [this.DataToScreenX(value), this.DataToScreenX(visualBinRanges[index].AddStep(value))]
+        return [this.DataToScreenX(value), this.DataToScreenX(visualBinRanges[index].AddStep(value))];
     }
     public DataToScreenYAxisRange(visualBinRanges: VisualBinRange[], index: number, bin: Bin) {
         var value = visualBinRanges[1].GetValueFromIndex(bin.binIndex!.indices![index]);
-        return [this.DataToScreenY(value), this.DataToScreenY(visualBinRanges[index].AddStep(value))]
+        return [this.DataToScreenY(value), this.DataToScreenY(visualBinRanges[index].AddStep(value))];
     }
 
     public DataToScreenX(x: number): number {
@@ -85,8 +86,9 @@ export class SizeConverter {
         return flip ? (this.RenderDimension) - retY : retY;
     }
     public DataToScreenCoord(v: number, axis: number) {
-        if (axis === 0)
+        if (axis === 0) {
             return this.DataToScreenX(v);
+        }
         return this.DataToScreenY(v);
     }
     public DataToScreenRange(minVal: number, maxVal: number, axis: number) {
@@ -94,6 +96,6 @@ export class SizeConverter {
         let xTo = this.DataToScreenX(axis === 0 ? maxVal : this.DataMaxs[0]);
         let yFrom = this.DataToScreenY(axis === 1 ? minVal : this.DataMins[1]);
         let yTo = this.DataToScreenY(axis === 1 ? maxVal : this.DataMaxs[1]);
-        return { xFrom, yFrom, xTo, yTo }
+        return { xFrom, yFrom, xTo, yTo };
     }
 }
\ No newline at end of file
diff --git a/src/client/northstar/utils/StyleContants.ts b/src/client/northstar/utils/StyleContants.ts
index ac8617e3b..e9b6e0297 100644
--- a/src/client/northstar/utils/StyleContants.ts
+++ b/src/client/northstar/utils/StyleContants.ts
@@ -11,7 +11,7 @@ export class StyleConstants {
 
     static OPERATOR_MENU_LARGE: number = 35;
     static OPERATOR_MENU_SMALL: number = 25;
-    static BRUSH_PALETTE: number[] = [0x42b43c, 0xfa217f, 0x6a9c75, 0xfb5de7, 0x25b8ea, 0x9b5bc4, 0xda9f63, 0xe23209, 0xfb899b, 0x94a6fd]
+    static BRUSH_PALETTE: number[] = [0x42b43c, 0xfa217f, 0x6a9c75, 0xfb5de7, 0x25b8ea, 0x9b5bc4, 0xda9f63, 0xe23209, 0xfb899b, 0x94a6fd];
     static GAP: number = 3;
 
     static BACKGROUND_COLOR: number = 0xF3F3F3;
diff --git a/src/client/northstar/utils/Utils.ts b/src/client/northstar/utils/Utils.ts
index c96b4cbd9..d071dec62 100644
--- a/src/client/northstar/utils/Utils.ts
+++ b/src/client/northstar/utils/Utils.ts
@@ -1,7 +1,7 @@
-import { IBaseBrushable } from '../core/brusher/IBaseBrushable'
-import { IBaseFilterConsumer } from '../core/filter/IBaseFilterConsumer'
-import { IBaseFilterProvider } from '../core/filter/IBaseFilterProvider'
-import { AggregateFunction } from '../model/idea/idea'
+import { IBaseBrushable } from '../core/brusher/IBaseBrushable';
+import { IBaseFilterConsumer } from '../core/filter/IBaseFilterConsumer';
+import { IBaseFilterProvider } from '../core/filter/IBaseFilterProvider';
+import { AggregateFunction } from '../model/idea/idea';
 
 export class Utils {
 
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index fb489edb6..f38b8ca75 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,7 +1,7 @@
-import React = require('react')
+import React = require('react');
 import { observer } from 'mobx-react';
 import { observable, action, computed } from 'mobx';
-import { Document } from "../../fields/Document"
+import { Document } from "../../fields/Document";
 import { DocumentView } from '../views/nodes/DocumentView';
 import { KeyStore } from '../../fields/KeyStore';
 import { FieldWaiting } from '../../fields/Field';
@@ -50,7 +50,7 @@ export class DocumentManager {
             if (docSrc && docSrc !== FieldWaiting && Object.is(docSrc, toFind)) {
                 toReturn = view;
             }
-        })
+        });
 
         return (toReturn);
     }
@@ -71,7 +71,7 @@ export class DocumentManager {
                     toReturn.push(view);
                 }
             }
-        })
+        });
 
         return (toReturn);
     }
@@ -86,8 +86,8 @@ export class DocumentManager {
                         let linkToDoc = link.GetT(KeyStore.LinkedToDocs, Document);
                         if (linkToDoc && linkToDoc !== FieldWaiting) {
                             DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
-                                pairs.push({ a: dv, b: docView1, l: link })
-                            })
+                                pairs.push({ a: dv, b: docView1, l: link });
+                            });
                         }
                     }
                     return pairs;
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index f95b2c29d..4849ae9f7 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -103,7 +103,7 @@ export namespace DragManager {
         element.addEventListener("dashOnDrop", handler);
         return () => {
             element.removeEventListener("dashOnDrop", handler);
-            delete element.dataset.canDrop
+            delete element.dataset.canDrop;
         };
     }
 
@@ -216,8 +216,9 @@ export namespace DragManager {
         const moveHandler = (e: PointerEvent) => {
             e.stopPropagation();
             e.preventDefault();
-            if (dragData instanceof DocumentDragData)
+            if (dragData instanceof DocumentDragData) {
                 dragData.aliasOnDrop = e.ctrlKey || e.altKey;
+            }
             if (e.shiftKey) {
                 abortDrag();
                 CollectionDockingView.Instance.StartOtherDrag(docs, {
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 2a3c1da6e..92944bec0 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -1,13 +1,13 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray, NodeType } from "prosemirror-model"
-import { joinUp, lift, setBlockType, toggleMark, wrapIn } from 'prosemirror-commands'
-import { redo, undo } from 'prosemirror-history'
-import { orderedList, bulletList, listItem, } from 'prosemirror-schema-list'
+import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray, NodeType } from "prosemirror-model";
+import { joinUp, lift, setBlockType, toggleMark, wrapIn } from 'prosemirror-commands';
+import { redo, undo } from 'prosemirror-history';
+import { orderedList, bulletList, listItem, } from 'prosemirror-schema-list';
 import { EditorState, Transaction, NodeSelection, } from "prosemirror-state";
 import { EditorView, } from "prosemirror-view";
 
 const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
-  preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0]
+  preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
 
 
 // :: Object
@@ -24,7 +24,7 @@ export const nodes: { [index: string]: NodeSpec } = {
     content: "inline*",
     group: "block",
     parseDOM: [{ tag: "p" }],
-    toDOM() { return pDOM }
+    toDOM() { return pDOM; }
   },
 
   // :: NodeSpec A blockquote (`<blockquote>`) wrapping one or more blocks.
@@ -33,14 +33,14 @@ export const nodes: { [index: string]: NodeSpec } = {
     group: "block",
     defining: true,
     parseDOM: [{ tag: "blockquote" }],
-    toDOM() { return blockquoteDOM }
+    toDOM() { return blockquoteDOM; }
   },
 
   // :: NodeSpec A horizontal rule (`<hr>`).
   horizontal_rule: {
     group: "block",
     parseDOM: [{ tag: "hr" }],
-    toDOM() { return hrDOM }
+    toDOM() { return hrDOM; }
   },
 
   // :: NodeSpec A heading textblock, with a `level` attribute that
@@ -57,7 +57,7 @@ export const nodes: { [index: string]: NodeSpec } = {
     { tag: "h4", attrs: { level: 4 } },
     { tag: "h5", attrs: { level: 5 } },
     { tag: "h6", attrs: { level: 6 } }],
-    toDOM(node: any) { return ["h" + node.attrs.level, 0] }
+    toDOM(node: any) { return ["h" + node.attrs.level, 0]; }
   },
 
   // :: NodeSpec A code listing. Disallows marks or non-text inline
@@ -70,7 +70,7 @@ export const nodes: { [index: string]: NodeSpec } = {
     code: true,
     defining: true,
     parseDOM: [{ tag: "pre", preserveWhitespace: "full" }],
-    toDOM() { return preDOM }
+    toDOM() { return preDOM; }
   },
 
   // :: NodeSpec The text node.
@@ -96,10 +96,10 @@ export const nodes: { [index: string]: NodeSpec } = {
           src: dom.getAttribute("src"),
           title: dom.getAttribute("title"),
           alt: dom.getAttribute("alt")
-        }
+        };
       }
     }],
-    toDOM(node: any) { return ["img", node.attrs] }
+    toDOM(node: any) { return ["img", node.attrs]; }
   },
 
   // :: NodeSpec A hard line break, represented in the DOM as `<br>`.
@@ -108,7 +108,7 @@ export const nodes: { [index: string]: NodeSpec } = {
     group: "inline",
     selectable: false,
     parseDOM: [{ tag: "br" }],
-    toDOM() { return brDOM }
+    toDOM() { return brDOM; }
   },
 
   ordered_list: {
@@ -136,7 +136,7 @@ export const nodes: { [index: string]: NodeSpec } = {
     ...listItem,
     content: 'paragraph block*'
   }
-}
+};
 
 const emDOM: DOMOutputSpecArray = ["em", 0];
 const strongDOM: DOMOutputSpecArray = ["strong", 0];
@@ -156,17 +156,17 @@ export const marks: { [index: string]: MarkSpec } = {
     inclusive: false,
     parseDOM: [{
       tag: "a[href]", getAttrs(dom: any) {
-        return { href: dom.getAttribute("href"), title: dom.getAttribute("title") }
+        return { href: dom.getAttribute("href"), title: dom.getAttribute("title") };
       }
     }],
-    toDOM(node: any) { return ["a", node.attrs, 0] }
+    toDOM(node: any) { return ["a", node.attrs, 0]; }
   },
 
   // :: MarkSpec An emphasis mark. Rendered as an `<em>` element.
   // Has parse rules that also match `<i>` and `font-style: italic`.
   em: {
     parseDOM: [{ tag: "i" }, { tag: "em" }, { style: "font-style=italic" }],
-    toDOM() { return emDOM }
+    toDOM() { return emDOM; }
   },
 
   // :: MarkSpec A strong mark. Rendered as `<strong>`, parse rules
@@ -175,7 +175,7 @@ export const marks: { [index: string]: MarkSpec } = {
     parseDOM: [{ tag: "strong" },
     { tag: "b" },
     { style: "font-weight" }],
-    toDOM() { return strongDOM }
+    toDOM() { return strongDOM; }
   },
 
   underline: {
@@ -221,9 +221,9 @@ export const marks: { [index: string]: MarkSpec } = {
   // :: MarkSpec Code font mark. Represented as a `<code>` element.
   code: {
     parseDOM: [{ tag: "code" }],
-    toDOM() { return codeDOM }
+    toDOM() { return codeDOM; }
   }
-}
+};
 
 // :: Schema
 // This schema rougly corresponds to the document schema used by
@@ -233,4 +233,4 @@ export const marks: { [index: string]: MarkSpec } = {
 //
 // To reuse elements from this schema, extend or read from its
 // `spec.nodes` and `spec.marks` [properties](#model.Schema.spec).
-export const schema = new Schema({ nodes, marks })
\ No newline at end of file
+export const schema = new Schema({ nodes, marks });
\ No newline at end of file
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index bf9b8266f..468484928 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -14,7 +14,7 @@ import { ListField } from "../../fields/ListField";
 // import * as typescriptes5 from '!!raw-loader!../../../node_modules/typescript/lib/lib.es5.d.ts'
 
 // @ts-ignore
-import * as typescriptlib from '!!raw-loader!./type_decls.d'
+import * as typescriptlib from '!!raw-loader!./type_decls.d';
 import { Documents } from "../documents/Documents";
 import { Key } from "../../fields/Key";
 
@@ -50,7 +50,7 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
 
     let fieldTypes = [Document, NumberField, TextField, ImageField, RichTextField, ListField, Key];
     let paramNames = ["KeyStore", "Documents", ...fieldTypes.map(fn => fn.name)];
-    let params: any[] = [KeyStore, Documents, ...fieldTypes]
+    let params: any[] = [KeyStore, Documents, ...fieldTypes];
     let compiledFunction = new Function(...paramNames, `return ${script}`);
     let run = (args: { [name: string]: any } = {}): ScriptResult => {
         let argsArray: any[] = [];
@@ -67,7 +67,7 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
         } catch (error) {
             return { success: false, error };
         }
-    }
+    };
     return { compiled: true, run };
 }
 
@@ -90,14 +90,14 @@ class ScriptingCompilerHost {
     }
     // getDefaultLibFileName(options: ts.CompilerOptions): string {
     getDefaultLibFileName(options: any): string {
-        return 'node_modules/typescript/lib/lib.d.ts' // No idea what this means...
+        return 'node_modules/typescript/lib/lib.d.ts'; // No idea what this means...
     }
     writeFile(fileName: string, content: string) {
         const file = this.files.find(file => file.fileName === fileName);
         if (file) {
             file.content = content;
         } else {
-            this.files.push({ fileName, content })
+            this.files.push({ fileName, content });
         }
     }
     getCurrentDirectory(): string {
diff --git a/src/client/util/ScrollBox.tsx b/src/client/util/ScrollBox.tsx
index b6b088170..a209874a3 100644
--- a/src/client/util/ScrollBox.tsx
+++ b/src/client/util/ScrollBox.tsx
@@ -1,4 +1,4 @@
-import React = require("react")
+import React = require("react");
 
 export class ScrollBox extends React.Component {
     onWheel = (e: React.WheelEvent) => {
@@ -16,6 +16,6 @@ export class ScrollBox extends React.Component {
             }} onWheel={this.onWheel}>
                 {this.props.children}
             </div>
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 1bb15c86a..5ddaafc72 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -23,7 +23,7 @@ export namespace SelectionManager {
 
         @action
         DeselectAll(): void {
-            manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false))
+            manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false));
             manager.SelectedDocuments = [];
         }
     }
@@ -46,7 +46,7 @@ export namespace SelectionManager {
             }
         }
 
-        manager.DeselectAll()
+        manager.DeselectAll();
         if (found) manager.SelectDoc(found, false);
         Main.Instance.SetTextDoc(undefined, undefined);
     }
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 913472aa0..bd5753093 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -5,12 +5,12 @@ import { keymap } from "prosemirror-keymap";
 import { EditorState, Transaction, NodeSelection } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
 import { schema } from "./RichTextSchema";
-import { Schema, NodeType } from "prosemirror-model"
-import React = require("react")
+import { Schema, NodeType } from "prosemirror-model";
+import React = require("react");
 import "./TooltipTextMenu.scss";
 const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands");
-import { library } from '@fortawesome/fontawesome-svg-core'
-import { wrapInList, bulletList } from 'prosemirror-schema-list'
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { wrapInList, bulletList } from 'prosemirror-schema-list';
 import { faListUl } from '@fortawesome/free-solid-svg-icons';
 
 
@@ -39,7 +39,7 @@ export class TooltipTextMenu {
             { command: toggleMark(schema.marks.subscript), dom: this.icon("s", "subscript") },
             //this doesn't work currently - look into notion of active block
             { command: wrapInList(schema.nodes.bullet_list), dom: this.icon(":", "bullets") },
-        ]
+        ];
         items.forEach(({ dom }) => this.tooltip.appendChild(dom));
 
         //pointer down handler to activate button effects
@@ -52,8 +52,8 @@ export class TooltipTextMenu {
                     //uncomment this if we want the bullet button to disappear if current selection is bulleted
                     // dom.style.display = active ? "" : "none"
                 }
-            })
-        })
+            });
+        });
 
         this.update(view, undefined);
     }
@@ -99,32 +99,32 @@ export class TooltipTextMenu {
         return {
             command: setBlockType(schema.nodes.heading, { level }),
             dom: this.icon("H" + level, "heading")
-        }
+        };
     }
 
     //updates the tooltip menu when the selection changes
     update(view: EditorView, lastState: EditorState | undefined) {
-        let state = view.state
+        let state = view.state;
         // Don't do anything if the document/selection didn't change
         if (lastState && lastState.doc.eq(state.doc) &&
-            lastState.selection.eq(state.selection)) return
+            lastState.selection.eq(state.selection)) return;
 
         // Hide the tooltip if the selection is empty
         if (state.selection.empty) {
-            this.tooltip.style.display = "none"
-            return
+            this.tooltip.style.display = "none";
+            return;
         }
 
         // Otherwise, reposition it and update its content
-        this.tooltip.style.display = ""
-        let { from, to } = state.selection
-        let start = view.coordsAtPos(from), end = view.coordsAtPos(to)
+        this.tooltip.style.display = "";
+        let { from, to } = state.selection;
+        let start = view.coordsAtPos(from), end = view.coordsAtPos(to);
         // The box in which the tooltip is positioned, to use as base
-        let box = this.tooltip.offsetParent!.getBoundingClientRect()
+        let box = this.tooltip.offsetParent!.getBoundingClientRect();
         // Find a center-ish x position from the selection endpoints (when
         // crossing lines, end may be more to the left)
-        let left = Math.max((start.left + end.left) / 2, start.left + 3)
-        this.tooltip.style.left = (left - box.left) + "px"
+        let left = Math.max((start.left + end.left) / 2, start.left + 3);
+        this.tooltip.style.left = (left - box.left) + "px";
         let width = Math.abs(start.left - end.left) / 2;
         let mid = Math.min(start.left, end.left) + width;
 
@@ -133,5 +133,5 @@ export class TooltipTextMenu {
         this.tooltip.style.bottom = (box.bottom - start.top) + "px";
     }
 
-    destroy() { this.tooltip.remove() }
+    destroy() { this.tooltip.remove(); }
 }
\ No newline at end of file
diff --git a/src/client/util/Transform.ts b/src/client/util/Transform.ts
index ed4282874..e9170ec36 100644
--- a/src/client/util/Transform.ts
+++ b/src/client/util/Transform.ts
@@ -62,19 +62,19 @@ export class Transform {
         return this;
     }
 
-    translated = (x: number, y: number): Transform => this.copy().translate(x, y)
+    translated = (x: number, y: number): Transform => this.copy().translate(x, y);
 
-    preTranslated = (x: number, y: number): Transform => this.copy().preTranslate(x, y)
+    preTranslated = (x: number, y: number): Transform => this.copy().preTranslate(x, y);
 
-    scaled = (scale: number): Transform => this.copy().scale(scale)
+    scaled = (scale: number): Transform => this.copy().scale(scale);
 
-    scaledAbout = (scale: number, x: number, y: number): Transform => this.copy().scaleAbout(scale, x, y)
+    scaledAbout = (scale: number, x: number, y: number): Transform => this.copy().scaleAbout(scale, x, y);
 
-    preScaled = (scale: number): Transform => this.copy().preScale(scale)
+    preScaled = (scale: number): Transform => this.copy().preScale(scale);
 
-    transformed = (transform: Transform): Transform => this.copy().transform(transform)
+    transformed = (transform: Transform): Transform => this.copy().transform(transform);
 
-    preTransformed = (transform: Transform): Transform => this.copy().preTransform(transform)
+    preTransformed = (transform: Transform): Transform => this.copy().preTransform(transform);
 
     transformPoint = (x: number, y: number): [number, number] => {
         x *= this._scale;
@@ -84,7 +84,7 @@ export class Transform {
         return [x, y];
     }
 
-    transformDirection = (x: number, y: number): [number, number] => [x * this._scale, y * this._scale]
+    transformDirection = (x: number, y: number): [number, number] => [x * this._scale, y * this._scale];
 
     transformBounds(x: number, y: number, width: number, height: number): { x: number, y: number, width: number, height: number } {
         [x, y] = this.transformPoint(x, y);
@@ -92,8 +92,8 @@ export class Transform {
         return { x, y, width, height };
     }
 
-    inverse = () => new Transform(-this._translateX / this._scale, -this._translateY / this._scale, 1 / this._scale)
+    inverse = () => new Transform(-this._translateX / this._scale, -this._translateY / this._scale, 1 / this._scale);
 
-    copy = () => new Transform(this._translateX, this._translateY, this._scale)
+    copy = () => new Transform(this._translateX, this._translateY, this._scale);
 
 }
\ No newline at end of file
diff --git a/src/client/util/TypedEvent.ts b/src/client/util/TypedEvent.ts
index 1b251da25..532ba78eb 100644
--- a/src/client/util/TypedEvent.ts
+++ b/src/client/util/TypedEvent.ts
@@ -36,5 +36,5 @@ export class TypedEvent<T> {
     this.listenersOncer = [];
   }
 
-  pipe = (te: TypedEvent<T>): Disposable => this.on((e) => te.emit(e))
+  pipe = (te: TypedEvent<T>): Disposable => this.on((e) => te.emit(e));
 }
\ No newline at end of file
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index b4ea4acae..27aed4bac 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -1,5 +1,5 @@
 import { observable, action } from "mobx";
-import 'source-map-support/register'
+import 'source-map-support/register';
 import { Without } from "../../Utils";
 import { string } from "prop-types";
 
@@ -31,9 +31,9 @@ function propertyDecorator(target: any, key: string | symbol) {
                         batch.end();
                     }
                 }
-            })
+            });
         }
-    })
+    });
 }
 
 export function undoBatch(target: any, key: string | symbol, descriptor?: TypedPropertyDescriptor<any>): any;
@@ -43,11 +43,11 @@ export function undoBatch(target: any, key?: string | symbol, descriptor?: Typed
         return function () {
             let batch = UndoManager.StartBatch("");
             try {
-                return target.apply(undefined, arguments)
+                return target.apply(undefined, arguments);
             } finally {
                 batch.end();
             }
-        }
+        };
     }
     if (!descriptor) {
         propertyDecorator(target, key);
@@ -58,11 +58,11 @@ export function undoBatch(target: any, key?: string | symbol, descriptor?: Typed
     descriptor.value = function (...args: any[]) {
         let batch = UndoManager.StartBatch(getBatchName(target, key));
         try {
-            return oldFunction.apply(this, args)
+            return oldFunction.apply(this, args);
         } finally {
             batch.end();
         }
-    }
+    };
 
     return descriptor;
 }
@@ -117,8 +117,8 @@ export namespace UndoManager {
             EndBatch(cancel);
         }
 
-        end = () => { this.dispose(false); }
-        cancel = () => { this.dispose(true); }
+        end = () => { this.dispose(false); };
+        cancel = () => { this.dispose(true); };
     }
 
     export function StartBatch(batchName: string): Batch {
@@ -138,7 +138,7 @@ export namespace UndoManager {
             redoStack.length = 0;
             currentBatch = undefined;
         }
-    })
+    });
 
     export function RunInBatch(fn: () => void, batchName: string) {
         let batch = StartBatch(batchName);
@@ -166,7 +166,7 @@ export namespace UndoManager {
         undoing = false;
 
         redoStack.push(commands);
-    })
+    });
 
     export const Redo = action(() => {
         if (redoStack.length === 0) {
@@ -185,6 +185,6 @@ export namespace UndoManager {
         undoing = false;
 
         undoStack.push(commands);
-    })
+    });
 
 }
\ No newline at end of file
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index cfa8ea7b7..615a928ad 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -2,11 +2,11 @@ import React = require("react");
 import { ContextMenuItem, ContextMenuProps } from "./ContextMenuItem";
 import { observable, action } from "mobx";
 import { observer } from "mobx-react";
-import "./ContextMenu.scss"
+import "./ContextMenu.scss";
 
 @observer
 export class ContextMenu extends React.Component {
-    static Instance: ContextMenu
+    static Instance: ContextMenu;
 
     @observable private _items: Array<ContextMenuProps> = [{ description: "test", event: (e: React.MouseEvent) => e.preventDefault() }];
     @observable private _pageX: number = 0;
@@ -22,15 +22,15 @@ export class ContextMenu extends React.Component {
     constructor(props: Readonly<{}>) {
         super(props);
 
-        this.ref = React.createRef()
+        this.ref = React.createRef();
 
         ContextMenu.Instance = this;
     }
 
     @action
     clearItems() {
-        this._items = []
-        this._display = "none"
+        this._items = [];
+        this._display = "none";
     }
 
     @action
@@ -56,7 +56,7 @@ export class ContextMenu extends React.Component {
 
         this._searchString = "";
 
-        this._display = "flex"
+        this._display = "flex";
     }
 
     intersects = (x: number, y: number): boolean => {
@@ -86,7 +86,7 @@ export class ContextMenu extends React.Component {
                 {this._items.filter(prop => prop.description.toLowerCase().indexOf(this._searchString.toLowerCase()) !== -1).
                     map(prop => <ContextMenuItem {...prop} key={prop.description} />)}
             </div>
-        )
+        );
     }
 
     @action
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index 4801c1555..70813f0dd 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -11,7 +11,7 @@ export interface SubmenuProps {
 }
 
 export interface ContextMenuItemProps {
-    type: ContextMenuProps | SubmenuProps
+    type: ContextMenuProps | SubmenuProps;
 }
 
 export class ContextMenuItem extends React.Component<ContextMenuProps> {
@@ -20,6 +20,6 @@ export class ContextMenuItem extends React.Component<ContextMenuProps> {
             <div className="contextMenu-item" onClick={this.props.event}>
                 <div className="contextMenu-description">{this.props.description}</div>
             </div>
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 285d145a2..b29fb6a2b 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -21,8 +21,8 @@ export const Flyout = higflyout.default;
 
 @observer
 export class DocumentDecorations extends React.Component<{}, { value: string }> {
-    static Instance: DocumentDecorations
-    private _resizer = ""
+    static Instance: DocumentDecorations;
+    private _resizer = "";
     private _isPointerDown = false;
     private keyinput: React.RefObject<HTMLInputElement>;
     private _documents: DocumentView[] = SelectionManager.SelectedDocuments();
@@ -40,8 +40,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
 
     constructor(props: Readonly<{}>) {
-        super(props)
-        DocumentDecorations.Instance = this
+        super(props);
+        DocumentDecorations.Instance = this;
         this.handleChange = this.handleChange.bind(this);
         this.keyinput = React.createRef();
     }
@@ -49,7 +49,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @action
     handleChange = (event: any) => {
         this._title = event.target.value;
-    };
+    }
 
     @action
     enterPressed = (e: any) => {
@@ -59,14 +59,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             var text = e.target.value;
             if (text[0] === '#') {
                 let command = text.slice(1, text.length);
-                this._fieldKey = new Key(command)
+                this._fieldKey = new Key(command);
                 // if (command === "Title" || command === "title") {
                 //     this._fieldKey = KeyStore.Title;
                 // }
                 // else if (command === "Width" || command === "width") {
                 //     this._fieldKey = KeyStore.Width;
                 // }
-                this._title = "changed"
+                this._title = "changed";
                 // TODO: Change field with switch statement
             }
             else {
@@ -89,7 +89,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             return {
                 x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y),
                 r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b)
-            }
+            };
         }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE });
     }
 
@@ -104,7 +104,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         document.addEventListener("pointermove", this.onBackgroundMove);
         document.removeEventListener("pointerup", this.onBackgroundUp);
         document.addEventListener("pointerup", this.onBackgroundUp);
-        this._lastDrag = [e.clientX, e.clientY]
+        this._lastDrag = [e.clientX, e.clientY];
         e.stopPropagation();
         e.preventDefault();
     }
@@ -127,7 +127,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 dragComplete: action(() => this._dragging = false),
             },
             hideSource: true
-        })
+        });
         e.stopPropagation();
     }
 
@@ -199,53 +199,53 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
     onLinkerButtonDown = (e: React.PointerEvent): void => {
         e.stopPropagation();
-        document.removeEventListener("pointermove", this.onLinkerButtonMoved)
+        document.removeEventListener("pointermove", this.onLinkerButtonMoved);
         document.addEventListener("pointermove", this.onLinkerButtonMoved);
-        document.removeEventListener("pointerup", this.onLinkerButtonUp)
+        document.removeEventListener("pointerup", this.onLinkerButtonUp);
         document.addEventListener("pointerup", this.onLinkerButtonUp);
     }
     onLinkerButtonUp = (e: PointerEvent): void => {
-        document.removeEventListener("pointermove", this.onLinkerButtonMoved)
-        document.removeEventListener("pointerup", this.onLinkerButtonUp)
+        document.removeEventListener("pointermove", this.onLinkerButtonMoved);
+        document.removeEventListener("pointerup", this.onLinkerButtonUp);
         e.stopPropagation();
     }
 
     onLinkerButtonMoved = (e: PointerEvent): void => {
         if (this._linkerButton.current !== null) {
-            document.removeEventListener("pointermove", this.onLinkerButtonMoved)
-            document.removeEventListener("pointerup", this.onLinkerButtonUp)
+            document.removeEventListener("pointermove", this.onLinkerButtonMoved);
+            document.removeEventListener("pointerup", this.onLinkerButtonUp);
             let dragData = new DragManager.LinkDragData(SelectionManager.SelectedDocuments()[0]);
             DragManager.StartLinkDrag(this._linkerButton.current, dragData, e.pageX, e.pageY, {
                 handlers: {
                     dragComplete: action(() => { }),
                 },
                 hideSource: false
-            })
+            });
         }
         e.stopPropagation();
     }
 
     onLinkButtonDown = (e: React.PointerEvent): void => {
         e.stopPropagation();
-        document.removeEventListener("pointermove", this.onLinkButtonMoved)
+        document.removeEventListener("pointermove", this.onLinkButtonMoved);
         document.addEventListener("pointermove", this.onLinkButtonMoved);
-        document.removeEventListener("pointerup", this.onLinkButtonUp)
+        document.removeEventListener("pointerup", this.onLinkButtonUp);
         document.addEventListener("pointerup", this.onLinkButtonUp);
     }
 
     onLinkButtonUp = (e: PointerEvent): void => {
-        document.removeEventListener("pointermove", this.onLinkButtonMoved)
-        document.removeEventListener("pointerup", this.onLinkButtonUp)
+        document.removeEventListener("pointermove", this.onLinkButtonMoved);
+        document.removeEventListener("pointerup", this.onLinkButtonUp);
         e.stopPropagation();
     }
 
     onLinkButtonMoved = async (e: PointerEvent) => {
         if (this._linkButton.current !== null) {
-            document.removeEventListener("pointermove", this.onLinkButtonMoved)
+            document.removeEventListener("pointermove", this.onLinkButtonMoved);
             document.removeEventListener("pointerup", this.onLinkButtonUp);
 
             let sourceDoc = SelectionManager.SelectedDocuments()[0].props.Document;
-            let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document)
+            let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document);
             let draggedDocs = (srcTarg && srcTarg !== FieldWaiting) ?
                 srcTarg.GetList(KeyStore.LinkedToDocs, [] as Document[]).map(linkDoc =>
                     (linkDoc.GetT(KeyStore.LinkedToDocs, Document)) as Document) : [];
@@ -265,7 +265,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                         dragComplete: action(() => { }),
                     },
                     hideSource: false
-                })
+                });
             }
         }
         e.stopPropagation();
@@ -285,38 +285,38 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             case "":
                 break;
             case "documentDecorations-topLeftResizer":
-                dX = -1
-                dY = -1
-                dW = -(e.movementX)
-                dH = -(e.movementY)
+                dX = -1;
+                dY = -1;
+                dW = -(e.movementX);
+                dH = -(e.movementY);
                 break;
             case "documentDecorations-topRightResizer":
-                dW = e.movementX
-                dY = -1
-                dH = -(e.movementY)
+                dW = e.movementX;
+                dY = -1;
+                dH = -(e.movementY);
                 break;
             case "documentDecorations-topResizer":
-                dY = -1
-                dH = -(e.movementY)
+                dY = -1;
+                dH = -(e.movementY);
                 break;
             case "documentDecorations-bottomLeftResizer":
-                dX = -1
-                dW = -(e.movementX)
-                dH = e.movementY
+                dX = -1;
+                dW = -(e.movementX);
+                dH = e.movementY;
                 break;
             case "documentDecorations-bottomRightResizer":
-                dW = e.movementX
-                dH = e.movementY
+                dW = e.movementX;
+                dH = e.movementY;
                 break;
             case "documentDecorations-bottomResizer":
-                dH = e.movementY
+                dH = e.movementY;
                 break;
             case "documentDecorations-leftResizer":
-                dX = -1
-                dW = -(e.movementX)
+                dX = -1;
+                dW = -(e.movementX);
                 break;
             case "documentDecorations-rightResizer":
-                dW = e.movementX
+                dW = e.movementX;
                 break;
         }
 
@@ -338,14 +338,15 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 var nativeWidth = doc.GetNumber(KeyStore.NativeWidth, 0);
                 var nativeHeight = doc.GetNumber(KeyStore.NativeHeight, 0);
                 if (nativeWidth > 0 && nativeHeight > 0) {
-                    if (Math.abs(dW) > Math.abs(dH))
+                    if (Math.abs(dW) > Math.abs(dH)) {
                         actualdH = nativeHeight / nativeWidth * actualdW;
+                    }
                     else actualdW = nativeWidth / nativeHeight * actualdH;
                 }
                 doc.SetNumber(KeyStore.Width, actualdW);
                 doc.SetNumber(KeyStore.Height, actualdH);
             }
-        })
+        });
     }
 
     onPointerUp = (e: PointerEvent): void => {
@@ -362,10 +363,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         if (this._title === "changed" && this._documents.length > 0) {
             let field = this._documents[0].props.Document.Get(this._fieldKey);
             if (field instanceof TextField) {
-                return (field as TextField).GetValue();
+                return (field).GetValue();
             }
             else if (field instanceof NumberField) {
-                return (field as NumberField).GetValue().toString();
+                return (field).GetValue().toString();
             }
         }
         return this._title;
@@ -388,7 +389,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             return (null);
         }
         if (isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
-            console.log("DocumentDecorations: Bounds Error")
+            console.log("DocumentDecorations: Bounds Error");
             return (null);
         }
 
@@ -414,7 +415,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 top: bounds.y - this._resizeBorderWidth / 2,
                 pointerEvents: this._dragging ? "none" : "all",
                 zIndex: SelectionManager.SelectedDocuments().length > 1 ? 1000 : 0,
-            }} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation() }} >
+            }} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); }} >
             </div>
             <div id="documentDecorations-container" style={{
                 width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
@@ -440,6 +441,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 <div className="linkButton-linker" ref={this._linkerButton} onPointerDown={this.onLinkerButtonDown}>∞</div>
             </div >
         </div>
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 982aacdea..2f17c6c51 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -1,7 +1,7 @@
-import React = require('react')
+import React = require('react');
 import { observer } from 'mobx-react';
 import { observable, action, trace } from 'mobx';
-import "./EditableView.scss"
+import "./EditableView.scss";
 
 export interface EditableProps {
     /**
@@ -22,7 +22,7 @@ export interface EditableProps {
      * The contents to render when not editing
      */
     contents: any;
-    height: number
+    height: number;
     display?: string;
 }
 
@@ -55,14 +55,14 @@ export class EditableView extends React.Component<EditableProps> {
     render() {
         if (this.editing) {
             return <input defaultValue={this.props.GetValue()} onKeyDown={this.onKeyDown} autoFocus onBlur={action(() => this.editing = false)}
-                style={{ display: this.props.display }}></input>
+                style={{ display: this.props.display }}></input>;
         } else {
             return (
                 <div className="editableView-container-editing" style={{ display: this.props.display, height: "auto", maxHeight: `${this.props.height}` }}
                     onClick={action(() => this.editing = true)} >
                     {this.props.contents}
                 </div>
-            )
+            );
         }
     }
 }
\ No newline at end of file
diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx
index 4ecc44119..47ee8eb85 100644
--- a/src/client/views/InkingCanvas.tsx
+++ b/src/client/views/InkingCanvas.tsx
@@ -82,7 +82,7 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
             });
             this.inkData = data;
         }
-    };
+    }
 
     @action
     onPointerUp = (e: PointerEvent): void => {
@@ -109,7 +109,7 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
 
     @action
     onPointerMove = (e: PointerEvent): void => {
-        e.stopPropagation()
+        e.stopPropagation();
         e.preventDefault();
         if (InkingControl.Instance.selectedTool !== InkTool.Eraser) {
             let data = this.inkData;  // add points to new line as it is being drawn
@@ -120,7 +120,7 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
             }
             this.inkData = data;
         }
-    };
+    }
 
     relativeCoordinatesForEvent = (ex: number, ey: number): { x: number, y: number } => {
         let [x, y] = this.props.getScreenTransform().transformPoint(ex, ey);
@@ -137,14 +137,15 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
 
     @computed
     get drawnPaths() {
-        let curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1)
+        let curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
         let paths = Array.from(this.inkData).reduce((paths, [id, strokeData]) => {
-            if (strokeData.page === -1 || strokeData.page === curPage)
+            if (strokeData.page === -1 || strokeData.page === curPage) {
                 paths.push(<InkingStroke key={id} id={id} line={strokeData.pathData}
                     offsetX={this.maxCanvasDim - this.inkMidX}
                     offsetY={this.maxCanvasDim - this.inkMidY}
                     color={strokeData.color} width={strokeData.width}
-                    tool={strokeData.tool} deleteCallback={this.removeLine} />)
+                    tool={strokeData.tool} deleteCallback={this.removeLine} />);
+            }
             return paths;
         }, [] as JSX.Element[]);
         return [<svg className={`inkingCanvas-paths-markers`} key="Markers"
@@ -165,6 +166,6 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
                 {this.props.children()}
                 {this.drawnPaths}
             </div >
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 64ee66ec7..9a68f0671 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -1,10 +1,10 @@
 import { observable, action, computed } from "mobx";
 
-import { CirclePicker, ColorResult } from 'react-color'
+import { CirclePicker, ColorResult } from 'react-color';
 import React = require("react");
 import { InkTool } from "../../fields/InkField";
 import { observer } from "mobx-react";
-import "./InkingControl.scss"
+import "./InkingControl.scss";
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { faPen, faHighlighter, faEraser, faBan } from '@fortawesome/free-solid-svg-icons';
@@ -25,7 +25,7 @@ export class InkingControl extends React.Component {
 
     constructor(props: Readonly<{}>) {
         super(props);
-        InkingControl.Instance = this
+        InkingControl.Instance = this;
     }
 
     @action
@@ -66,9 +66,9 @@ export class InkingControl extends React.Component {
 
     selected = (tool: InkTool) => {
         if (this._selectedTool === tool) {
-            return { color: "#61aaa3" }
+            return { color: "#61aaa3" };
         }
-        return {}
+        return {};
     }
 
     @action
@@ -111,6 +111,6 @@ export class InkingControl extends React.Component {
                         onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.switchWidth(e.target.value)} />
                 </li>
             </ul >
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 12b15a3f0..0f05da22c 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -30,7 +30,7 @@ export class InkingStroke extends React.Component<StrokeProps> {
     }
 
     parseData = (line: Array<{ x: number, y: number }>): string => {
-        return !line.length ? "" : "M " + line.map(p => (p.x + this.props.offsetX) + " " + (p.y + this.props.offsetY)).join(" L ")
+        return !line.length ? "" : "M " + line.map(p => (p.x + this.props.offsetX) + " " + (p.y + this.props.offsetY)).join(" L ");
     }
 
     createStyle() {
@@ -41,7 +41,7 @@ export class InkingStroke extends React.Component<StrokeProps> {
                     fill: "none",
                     stroke: this._strokeColor,
                     strokeWidth: this._strokeWidth + "px",
-                }
+                };
         }
     }
 
@@ -53,6 +53,6 @@ export class InkingStroke extends React.Component<StrokeProps> {
         return (
             <path d={pathData} style={{ ...pathStyle, pointerEvents: pointerEvents }} strokeLinejoin="round" strokeLinecap="round"
                 onPointerOver={this.deleteStroke} onPointerDown={this.deleteStroke} />
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 6376fd694..fd2e23c91 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -16,7 +16,7 @@ import { WorkspacesMenu } from '../../server/authentication/controllers/Workspac
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import { MessageStore } from '../../server/Message';
 import { Utils, returnTrue, emptyFunction } from '../../Utils';
-import * as rp from 'request-promise'
+import * as rp from 'request-promise';
 import { RouteStore } from '../../server/RouteStore';
 import { ServerUtils } from '../../server/ServerUtil';
 import { Documents } from '../documents/Documents';
@@ -74,7 +74,7 @@ export class Main extends React.Component {
             if (pathname.length > 1 && pathname[pathname.length - 2] === 'doc') {
                 CurrentUserUtils.MainDocId = pathname[pathname.length - 1];
             }
-        };
+        }
 
         CurrentUserUtils.loadCurrentUser();
 
@@ -119,8 +119,8 @@ export class Main extends React.Component {
 
     initEventListeners = () => {
         // window.addEventListener("pointermove", (e) => this.reportLocation(e))
-        window.addEventListener("drop", (e) => e.preventDefault(), false) // drop event handler
-        window.addEventListener("dragover", (e) => e.preventDefault(), false) // drag event handler
+        window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler
+        window.addEventListener("dragover", (e) => e.preventDefault(), false); // drag event handler
         // click interactions for the context menu
         document.addEventListener("pointerdown", action(function (e: PointerEvent) {
             if (!ContextMenu.Instance.intersects(e.pageX, e.pageY)) {
@@ -139,15 +139,15 @@ export class Main extends React.Component {
                 } else {
                     this.createNewWorkspace();
                 }
-            })
+            });
         } else {
             Server.GetField(CurrentUserUtils.MainDocId).then(field => {
                 if (field instanceof Document) {
-                    this.openWorkspace(field)
+                    this.openWorkspace(field);
                 } else {
                     this.createNewWorkspace(CurrentUserUtils.MainDocId);
                 }
-            })
+            });
         }
     }
 
@@ -163,7 +163,7 @@ export class Main extends React.Component {
                 // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
                 setTimeout(() => {
                     this.openWorkspace(mainDoc);
-                    let pendingDocument = Documents.SchemaDocument([], { title: "New Mobile Uploads" })
+                    let pendingDocument = Documents.SchemaDocument([], { title: "New Mobile Uploads" });
                     mainDoc.Set(KeyStore.OptionalRightCollection, pendingDocument);
                 }, 0);
             }
@@ -182,7 +182,7 @@ export class Main extends React.Component {
                         if (f && f.Data.length > 0) {
                             CollectionDockingView.Instance.AddRightSplit(col);
                         }
-                    })
+                    });
                 }
             }, 100);
         });
@@ -191,7 +191,7 @@ export class Main extends React.Component {
     @observable
     workspacesShown: boolean = false;
 
-    areWorkspacesShown = () => this.workspacesShown
+    areWorkspacesShown = () => this.workspacesShown;
     @action
     toggleWorkspaces = () => {
         this.workspacesShown = !this.workspacesShown;
@@ -199,7 +199,7 @@ export class Main extends React.Component {
 
     pwidthFunc = () => this.pwidth;
     pheightFunc = () => this.pheight;
-    focusDocument = (doc: Document) => { }
+    focusDocument = (doc: Document) => { };
     noScaling = () => 1;
 
     @observable _textDoc?: Document = undefined;
@@ -208,8 +208,9 @@ export class Main extends React.Component {
     SetTextDoc(textDoc?: Document, div?: HTMLDivElement) {
         this._textDoc = undefined;
         this._textDoc = textDoc;
-        if (div)
+        if (div) {
             this._textRect = div.getBoundingClientRect();
+        }
     }
 
     @computed
@@ -221,7 +222,7 @@ export class Main extends React.Component {
             let h: number = this._textRect.height;
             return <div className="mainDiv-textInput" style={{ transform: `translate(${x}px, ${y}px)`, width: `${w}px`, height: `${h}px` }} >
                 <FormattedTextBox fieldKey={KeyStore.Archives} Document={this._textDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true} selectOnLoad={true} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={Transform.Identity} focus={(doc) => { }} />
-            </ div>
+            </ div>;
         }
         else return (null);
     }
@@ -241,19 +242,19 @@ export class Main extends React.Component {
                 focus={this.focusDocument}
                 parentActive={returnTrue}
                 onActiveChanged={emptyFunction}
-                ContainingCollectionView={undefined} />
+                ContainingCollectionView={undefined} />;
     }
 
     /* 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. */
     @computed
     get nodesMenu() {
         let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg";
-        let pdfurl = "http://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf"
+        let pdfurl = "http://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf";
         let weburl = "https://cs.brown.edu/courses/cs166/";
         let audiourl = "http://techslides.com/demos/samples/sample.mp3";
         let videourl = "http://techslides.com/demos/sample-videos/small.mp4";
 
-        let addTextNode = action(() => Documents.TextDocument({ width: 200, height: 200, title: "a text note" }))
+        let addTextNode = action(() => Documents.TextDocument({ width: 200, height: 200, title: "a text note" }));
         let addColNode = action(() => Documents.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" }));
         let addSchemaNode = action(() => Documents.SchemaDocument([], { width: 200, height: 200, title: "a schema collection" }));
         let addTreeNode = action(() => Documents.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", copyDraggedItems: true }));
@@ -261,7 +262,7 @@ export class Main extends React.Component {
         let addPDFNode = action(() => Documents.PdfDocument(pdfurl, { width: 200, height: 200, title: "a schema collection" }));
         let addImageNode = action(() => Documents.ImageDocument(imgurl, { width: 200, height: 200, title: "an image of a cat" }));
         let addWebNode = action(() => Documents.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" }));
-        let addAudioNode = action(() => Documents.AudioDocument(audiourl, { width: 200, height: 200, title: "audio node" }))
+        let addAudioNode = action(() => Documents.AudioDocument(audiourl, { width: 200, height: 200, title: "audio node" }));
 
         let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Document][] = [
             [React.createRef<HTMLDivElement>(), "font", "Add Textbox", addTextNode],
@@ -273,7 +274,7 @@ export class Main extends React.Component {
             [React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode],
             [React.createRef<HTMLDivElement>(), "tree", "Add Tree", addTreeNode],
             [React.createRef<HTMLDivElement>(), "table", "Add Schema", addSchemaNode],
-        ]
+        ];
 
         return < div id="add-nodes-menu" >
             <input type="checkbox" id="add-menu-toggle" />
@@ -289,7 +290,7 @@ export class Main extends React.Component {
                         </div></li>)}
                 </ul>
             </div>
-        </div >
+        </div >;
     }
 
     /* @TODO this should really be moved into a moveable toolbar component, but for now let's put it here to meet the deadline */
@@ -298,7 +299,7 @@ export class Main extends React.Component {
         let workspacesRef = React.createRef<HTMLDivElement>();
         let logoutRef = React.createRef<HTMLDivElement>();
 
-        let clearDatabase = action(() => Utils.Emit(Server.Socket, MessageStore.DeleteAll, {}))
+        let clearDatabase = action(() => Utils.Emit(Server.Socket, MessageStore.DeleteAll, {}));
         return [
             <button className="clear-db-button" key="clear-db" onClick={clearDatabase}>Clear Database</button>,
             <div id="toolbar" key="toolbar">
@@ -310,7 +311,7 @@ export class Main extends React.Component {
                 <button onClick={this.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), () => { })}>Log Out</button></div>
-        ]
+        ];
     }
 
     render() {
@@ -318,7 +319,7 @@ export class Main extends React.Component {
         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} />
+                isShown={this.areWorkspacesShown} toggle={this.toggleWorkspaces} />;
         }
         return (
             <>
@@ -359,7 +360,7 @@ export class Main extends React.Component {
                             schemaDocuments.push(field);
                         } else {
                             var atmod = new ColumnAttributeModel(attr);
-                            let histoOp = new HistogramOperation(schema!.displayName!,
+                            let histoOp = new HistogramOperation(schema.displayName!,
                                 new AttributeTransformationModel(atmod, AggregateFunction.None),
                                 new AttributeTransformationModel(atmod, AggregateFunction.Count),
                                 new AttributeTransformationModel(atmod, AggregateFunction.Count));
@@ -368,7 +369,7 @@ export class Main extends React.Component {
                     }));
                 });
                 return schemaDoc;
-            })
+            });
         }
     }
     async initializeNorthstar(): Promise<void> {
@@ -386,7 +387,7 @@ export class Main extends React.Component {
 }
 
 (async () => {
-    await Documents.initProtos()
-    await CurrentUserUtils.loadCurrentUser()
+    await Documents.initProtos();
+    await CurrentUserUtils.loadCurrentUser();
     ReactDOM.render(<Main />, document.getElementById('root'));
-})()
+})();
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 7a5ab6e3c..32462a4c1 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -66,17 +66,20 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
     createsCycle(documentToAdd: Document, containerDocument: Document): boolean {
         let data = documentToAdd.GetList<Document>(KeyStore.Data, []);
         for (const doc of data) {
-            if (this.createsCycle(doc, containerDocument))
+            if (this.createsCycle(doc, containerDocument)) {
                 return true;
+            }
         }
         let annots = documentToAdd.GetList<Document>(KeyStore.Annotations, []);
         for (const annot of annots) {
-            if (this.createsCycle(annot, containerDocument))
+            if (this.createsCycle(annot, containerDocument)) {
                 return true;
+            }
         }
         for (let containerProto: FieldValue<Document> = containerDocument; containerProto && containerProto !== FieldWaiting; containerProto = containerProto.GetPrototype()) {
-            if (containerProto.Id === documentToAdd.Id)
+            if (containerProto.Id === documentToAdd.Id) {
                 return true;
+            }
         }
         return false;
     }
@@ -91,20 +94,23 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         }
         if (props.Document.Get(props.fieldKey) instanceof Field) {
             //TODO This won't create the field if it doesn't already exist
-            const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>())
+            const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>());
             if (!this.createsCycle(doc, props.Document)) {
-                if (!value.some(v => v.Id === doc.Id) || allowDuplicates)
+                if (!value.some(v => v.Id === doc.Id) || allowDuplicates) {
                     value.push(doc);
+                }
             }
-            else
+            else {
                 return false;
+            }
         } else {
             let proto = props.Document.GetPrototype();
             if (!proto || proto === FieldWaiting || !this.createsCycle(proto, doc)) {
                 props.Document.SetOnPrototype(props.fieldKey, new ListField([doc]));
             }
-            else
+            else {
                 return false;
+            }
         }
         return true;
     }
@@ -113,7 +119,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
     removeDocument(doc: Document): boolean {
         const props = this.props;
         //TODO This won't create the field if it doesn't already exist
-        const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>())
+        const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>());
         let index = -1;
         for (let i = 0; i < value.length; i++) {
             if (value[i].Id === doc.Id) {
@@ -125,16 +131,16 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
             if (annotationOn === props.Document) {
                 doc.Set(KeyStore.AnnotationOn, undefined, true);
             }
-        })
+        });
 
         if (index !== -1) {
-            value.splice(index, 1)
+            value.splice(index, 1);
 
             // SelectionManager.DeselectAll()
-            ContextMenu.Instance.clearItems()
+            ContextMenu.Instance.clearItems();
             return true;
         }
-        return false
+        return false;
     }
 
     @action.bound
@@ -155,12 +161,12 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
             moveDocument: this.moveDocument,
             active: this.active,
             onActiveChanged: this.onActiveChanged,
-        }
+        };
         return (
             <div className={this.props.className || "collectionView-cont"} onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}>
                 {this.props.children(this.collectionViewType, props)}
             </div>
-        )
+        );
     }
 
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index c3757a377..ea6d3a247 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -32,7 +32,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                 documentId: document.Id,
                 //collectionDockingView: CollectionDockingView.Instance
             }
-        }
+        };
     }
 
     private _goldenLayout: any = null;
@@ -58,7 +58,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         let newItemStackConfig = {
             type: 'stack',
             content: [CollectionDockingView.makeDocumentConfig(document)]
-        }
+        };
         var docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
         this._goldenLayout.root.contentItems[0].addChild(docconfig);
         docconfig.callDownwards('_$init');
@@ -86,7 +86,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         let newItemStackConfig = {
             type: 'stack',
             content: [CollectionDockingView.makeDocumentConfig(document)]
-        }
+        };
 
         var newContentItem = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
 
@@ -124,8 +124,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                 this._goldenLayout = new GoldenLayout(JSON.parse(config));
             }
             else {
-                if (config === JSON.stringify(this._goldenLayout.toConfig()))
+                if (config === JSON.stringify(this._goldenLayout.toConfig())) {
                     return;
+                }
                 try {
                     this._goldenLayout.unbind('itemDropped', this.itemDropped);
                     this._goldenLayout.unbind('tabCreated', this.tabCreated);
@@ -199,14 +200,15 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             let docid = (e.target as any).DashDocId;
             let tab = (e.target as any).parentElement as HTMLElement;
             Server.GetField(docid, action((f: Opt<Field>) => {
-                if (f instanceof Document)
-                    DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f as Document]), e.pageX, e.pageY,
+                if (f instanceof Document) {
+                    DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f]), e.pageX, e.pageY,
                         {
                             handlers: {
                                 dragComplete: action(() => { }),
                             },
                             hideSource: false
-                        })
+                        });
+                }
             }));
         }
         if (className === "lm_drag_handle" || className === "lm_close" || className === "lm_maximise" || className === "lm_minimise" || className === "lm_close_tab") {
@@ -220,7 +222,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     @undoBatch
     stateChanged = () => {
         var json = JSON.stringify(this._goldenLayout.toConfig());
-        this.props.Document.SetText(KeyStore.Data, json)
+        this.props.Document.SetText(KeyStore.Data, json);
     }
 
     itemDropped = () => {
@@ -236,7 +238,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                             if (tfield !== undefined) {
                                 tab.titleElement[0].textContent = f.Title;
                             }
-                        })
+                        });
                     }
                 }));
                 tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
@@ -281,7 +283,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
 }
 
 interface DockedFrameProps {
-    documentId: FieldId,
+    documentId: FieldId;
     //collectionDockingView: CollectionDockingView
 }
 @observer
@@ -297,18 +299,19 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         Server.GetField(this.props.documentId, action((f: Opt<Field>) => this._document = f as Document));
     }
 
-    private _nativeWidth = () => this._document!.GetNumber(KeyStore.NativeWidth, this._panelWidth)
-    private _nativeHeight = () => this._document!.GetNumber(KeyStore.NativeHeight, this._panelHeight)
-    private _contentScaling = () => this._panelWidth / (this._nativeWidth() ? this._nativeWidth() : this._panelWidth)
+    private _nativeWidth = () => this._document!.GetNumber(KeyStore.NativeWidth, this._panelWidth);
+    private _nativeHeight = () => this._document!.GetNumber(KeyStore.NativeHeight, this._panelHeight);
+    private _contentScaling = () => this._panelWidth / (this._nativeWidth() ? this._nativeWidth() : this._panelWidth);
 
     ScreenToLocalTransform = () => {
         let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current!);
-        return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale / this._contentScaling())
+        return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale / this._contentScaling());
     }
 
     render() {
-        if (!this._document)
+        if (!this._document) {
             return (null);
+        }
         var content =
             <div className="collectionDockingView-content" ref={this._mainCont}>
                 <DocumentView key={this._document.Id} Document={this._document}
@@ -324,10 +327,10 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                     onActiveChanged={emptyFunction}
                     focus={(doc: Document) => { }}
                     ContainingCollectionView={undefined} />
-            </div>
+            </div>;
 
         return <Measure onResize={action((r: any) => { this._panelWidth = r.entry.width; this._panelHeight = r.entry.height; })}>
             {({ measureRef }) => <div ref={measureRef}>  {content} </div>}
-        </Measure>
+        </Measure>;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 32f50afc0..97bac745c 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -2,7 +2,7 @@ import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ContextMenu } from "../ContextMenu";
-import "./CollectionPDFView.scss"
+import "./CollectionPDFView.scss";
 import React = require("react");
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
@@ -44,7 +44,7 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
                 <CollectionFreeFormView {...props} />
                 {this.props.isSelected() ? this.uIButtons : (null)}
             </>
-        )
+        );
     }
 
     render() {
@@ -52,6 +52,6 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
             <CollectionBaseView {...this.props} className="collectionPdfView-cont" onContextMenu={this.onContextMenu}>
                 {this.subView}
             </CollectionBaseView>
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 9b780f29b..52933818f 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -1,4 +1,4 @@
-import React = require("react")
+import React = require("react");
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faCog, faPlus } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -41,7 +41,7 @@ class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggl
             if (field instanceof Key) {
                 this.key = field;
             }
-        }))
+        }));
     }
 
     render() {
@@ -49,7 +49,7 @@ class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggl
             return (<div key={this.key.Id}>
                 <input type="checkbox" checked={this.props.checked} onChange={() => this.key && this.props.toggle(this.key)} />
                 {this.key.Name}
-            </div>)
+            </div>);
         }
         return (null);
     }
@@ -85,10 +85,10 @@ export class CollectionSchemaView extends CollectionSubView {
             focus: emptyFunction,
             active: returnFalse,
             onActiveChanged: emptyFunction,
-        }
+        };
         let contents = (
             <FieldView {...props} />
-        )
+        );
         let reference = React.createRef<HTMLDivElement>();
         let onItemDown = setupDrag(reference, () => props.Document, this.props.moveDocument);
         let applyToDoc = (doc: Document, run: (args?: { [name: string]: any }) => any) => {
@@ -106,7 +106,7 @@ export class CollectionSchemaView extends CollectionSubView {
                 }
             }
             return false;
-        }
+        };
         return (
             <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "56px" }} key={props.Document.Id} ref={reference}>
                 <EditableView
@@ -138,11 +138,11 @@ export class CollectionSchemaView extends CollectionSubView {
                             if (val) {
                                 val.Data.forEach(doc => applyToDoc(doc, run));
                             }
-                        })
+                        });
                     }}>
                 </EditableView>
             </div>
-        )
+        );
     }
 
     private getTrProps: ComponentPropsGetterR = (state, rowInfo) => {
@@ -156,7 +156,7 @@ export class CollectionSchemaView extends CollectionSubView {
                 that._selectedIndex = rowInfo.index;
 
                 if (handleOriginal) {
-                    handleOriginal()
+                    handleOriginal();
                 }
             }),
             style: {
@@ -182,7 +182,7 @@ export class CollectionSchemaView extends CollectionSubView {
                     this.columns.splice(index, 1);
                 }
 
-            })
+            });
     }
 
     //toggles preview side-panel of schema
@@ -197,7 +197,7 @@ export class CollectionSchemaView extends CollectionSubView {
     @computed
     get findAllDocumentKeys(): { [id: string]: boolean } {
         const docs = this.props.Document.GetList<Document>(this.props.fieldKey, []);
-        let keys: { [id: string]: boolean } = {}
+        let keys: { [id: string]: boolean } = {};
         if (this._optionsActivated > -1) {
             // bcz: ugh.  this is untracked since otherwise a large collection of documents will blast the server for all their fields.
             //  then as each document's fields come back, we update the documents _proxies.  Each time we do this, the whole schema will be
@@ -206,7 +206,7 @@ export class CollectionSchemaView extends CollectionSubView {
             //  is displayed (unlikely) it won't show up until something else changes.
             untracked(() => docs.map(doc => doc.GetAllPrototypes().map(proto => proto._proxies.forEach((val: any, key: string) => keys[key] = false))));
         }
-        this.columns.forEach(key => keys[key.Id] = true)
+        this.columns.forEach(key => keys[key.Id] = true);
         return keys;
     }
 
@@ -248,10 +248,10 @@ export class CollectionSchemaView extends CollectionSubView {
     getContentScaling = (): number => this._contentScaling;
     getPanelWidth = (): number => this._panelWidth;
     getPanelHeight = (): number => this._panelHeight;
-    getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling)
-    getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX - this._tableWidth, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling)
+    getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling);
+    getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX - this._tableWidth, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling);
 
-    focusDocument = (doc: Document) => { }
+    focusDocument = (doc: Document) => { };
 
     onPointerDown = (e: React.PointerEvent): void => {
         if (this.props.isSelected()) {
@@ -273,8 +273,9 @@ export class CollectionSchemaView extends CollectionSubView {
         this.newKeyName = e.currentTarget.value;
     }
     onWheel = (e: React.WheelEvent): void => {
-        if (this.props.active())
+        if (this.props.active()) {
             e.stopPropagation();
+        }
     }
 
     @observable _optionsActivated: number = 0;
@@ -322,9 +323,9 @@ export class CollectionSchemaView extends CollectionSubView {
                     </div>
                 }
             </Measure>
-        )
+        );
         let dividerDragger = this.splitPercentage === 0 ? (null) :
-            <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />
+            <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />;
 
         //options button and menu
         let optionsMenu = !this.props.active() ? (null) : (<Flyout
@@ -379,6 +380,6 @@ export class CollectionSchemaView extends CollectionSubView {
                     {optionsMenu}
                 </div>
             </div >
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 59b89e119..bd385c27e 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -14,7 +14,7 @@ import { NumberField } from "../../../fields/NumberField";
 import { ServerUtils } from "../../../server/ServerUtil";
 import { Server } from "../../Server";
 import { FieldViewProps } from "../nodes/FieldView";
-import * as rp from 'request-promise'
+import * as rp from 'request-promise';
 
 export interface CollectionViewProps extends FieldViewProps {
     addDocument: (document: Document, allowDuplicates?: boolean) => boolean;
@@ -58,8 +58,8 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
                         let entry = new TupleField<[string, string], [number, number]>([textInfo, position]);
                         cursors.push(entry);
                     }
-                }))
-            })
+                }));
+            });
         }
     }
 
@@ -77,17 +77,17 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
                 added = de.data.droppedDocuments.reduce((added: boolean, d) => added || this.props.addDocument(d), false);
             } else if (de.data.moveDocument) {
                 const move = de.data.moveDocument;
-                added = de.data.droppedDocuments.reduce((added: boolean, d) => added || move(d, this.props.Document, this.props.addDocument), false)
+                added = de.data.droppedDocuments.reduce((added: boolean, d) => added || move(d, this.props.Document, this.props.addDocument), false);
             } else {
-                added = de.data.droppedDocuments.reduce((added: boolean, d) => added || this.props.addDocument(d), false)
+                added = de.data.droppedDocuments.reduce((added: boolean, d) => added || this.props.addDocument(d), false);
             }
             e.stopPropagation();
             return added;
         }
         if (de.data instanceof DragManager.LinkDragData) {
             let sourceDoc: Document = de.data.linkSourceDocumentView.props.Document;
-            if (sourceDoc) runInAction(() => {
-                let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document)
+            if (sourceDoc) { runInAction(() => {
+                let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document);
                 if (srcTarg && srcTarg !== FieldWaiting) {
                     let linkDocs = srcTarg.GetList(KeyStore.LinkedToDocs, [] as Document[]);
                     linkDocs.map(linkDoc => {
@@ -97,9 +97,10 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
                             de.data.droppedDocuments.push(dropdoc);
                             this.props.addDocument(dropdoc, false);
                         }
-                    })
+                    });
                 }
-            })
+            });
+            }
             return true;
         }
         return false;
@@ -133,7 +134,7 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
                         alias.SetNumber(KeyStore.Height, options.height || options.width || 300);
                         this.props.addDocument(alias, false);
                     }
-                })
+                });
                 return undefined;
             }
             ctor = Documents.WebDocument;
@@ -151,8 +152,8 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
         if (text && text.startsWith("<div")) {
             return;
         }
-        e.stopPropagation()
-        e.preventDefault()
+        e.stopPropagation();
+        e.preventDefault();
 
         if (html && html.indexOf("<img") !== 0 && !html.startsWith("<a")) {
             console.log("not good");
@@ -173,11 +174,11 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
                 let prom = new Promise<string>(res =>
                     e.dataTransfer.items[i].getAsString(res)).then(action((s: string) => {
                         str = s;
-                        return rp.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + s))
+                        return rp.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + s));
                     })).then(res => {
                         let type = res.headers["content-type"];
                         if (type) {
-                            let doc = this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 })
+                            let doc = this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 });
                             if (doc) {
                                 this.props.addDocument(doc, false);
                             }
@@ -186,13 +187,13 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
                 promises.push(prom);
                 // this.props.addDocument(Documents.WebDocument(s, { ...options, width: 300, height: 300 }), false)
             }
-            let type = item.type
+            let type = item.type;
             if (item.kind === "file") {
                 let file = item.getAsFile();
-                let formData = new FormData()
+                let formData = new FormData();
 
                 if (file) {
-                    formData.append('file', file)
+                    formData.append('file', file);
                 }
 
                 let prom = fetch(upload, {
@@ -201,15 +202,15 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
                 }).then(async (res: Response) => {
                     const json = await res.json();
                     json.map((file: any) => {
-                        let path = window.location.origin + file
+                        let path = window.location.origin + file;
                         runInAction(() => {
-                            let doc = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300 })
+                            let doc = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300 });
 
                             let docs = this.props.Document.GetT(KeyStore.Data, ListField);
                             if (docs !== FieldWaiting) {
                                 if (!docs) {
                                     docs = new ListField<Document>();
-                                    this.props.Document.Set(KeyStore.Data, docs)
+                                    this.props.Document.Set(KeyStore.Data, docs);
                                 }
                                 if (doc) {
                                     docs.Data.push(doc);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 2bdf0baa6..659cff9fe 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -12,7 +12,7 @@ import { EditableView } from "../EditableView";
 import "./CollectionTreeView.scss";
 import { CollectionView } from "./CollectionView";
 import { CollectionSubView } from "./CollectionSubView";
-import React = require("react")
+import React = require("react");
 import { COLLECTION_BORDER_WIDTH } from './CollectionBaseView';
 import { props } from 'bluebird';
 
@@ -58,7 +58,7 @@ class TreeView extends React.Component<TreeViewProps> {
             return true;
         }
         //TODO This should check if it was removed
-        this.remove(document)
+        this.remove(document);
         return addDoc(document);
     }
 
@@ -69,7 +69,7 @@ class TreeView extends React.Component<TreeViewProps> {
             case BulletType.Collapsed: bullet = "caret-right"; break;
             case BulletType.Collapsible: bullet = "caret-down"; break;
         }
-        return <div className="bullet" onClick={onClicked}>{bullet ? <FontAwesomeIcon icon={bullet} /> : ""} </div>
+        return <div className="bullet" onClick={onClicked}>{bullet ? <FontAwesomeIcon icon={bullet} /> : ""} </div>;
     }
 
     /**
@@ -93,7 +93,7 @@ class TreeView extends React.Component<TreeViewProps> {
             <div className="docContainer" ref={reference} onPointerDown={onItemDown}>
                 {editableView(this.props.document.Title)}
                 <div className="delete-button" onClick={this.delete}><FontAwesomeIcon icon="trash-alt" size="xs" /></div>
-            </div >)
+            </div >);
     }
 
     render() {
@@ -105,7 +105,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 bulletType = BulletType.Collapsible;
                 childElements = <ul>
                     {children.Data.map(value => <TreeView key={value.Id} document={value} deleteDoc={this.remove} moveDocument={this.move} copyOnDrag={this.props.copyOnDrag} />)}
-                </ul >
+                </ul >;
             }
             else bulletType = BulletType.Collapsed;
         }
@@ -115,7 +115,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 {this.renderTitle()}
                 {childElements ? childElements : (null)}
             </li>
-        </div>
+        </div>;
     }
 }
 
@@ -136,7 +136,7 @@ export class CollectionTreeView extends CollectionSubView {
         let childrenElement = !children || children === FieldWaiting ? (null) :
             (children.Data.map(value =>
                 <TreeView document={value} key={value.Id} deleteDoc={this.remove} moveDocument={this.props.moveDocument} copyOnDrag={copyOnDrag} />)
-            )
+            );
 
         return (
             <div id="body" className="collectionTreeView-dropTarget" onWheel={(e: React.WheelEvent) => e.stopPropagation()} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}>
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 1acc76c85..b02983a2e 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -4,7 +4,7 @@ import { KeyStore } from "../../../fields/KeyStore";
 import { ContextMenu } from "../ContextMenu";
 import { CollectionViewType, CollectionBaseView, CollectionRenderProps } from "./CollectionBaseView";
 import React = require("react");
-import "./CollectionVideoView.scss"
+import "./CollectionVideoView.scss";
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 
@@ -111,7 +111,7 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
                 <CollectionFreeFormView {...props} />
                 {this.props.isSelected() ? this.uIButtons : (null)}
             </>
-        )
+        );
     }
 
     render() {
@@ -119,6 +119,6 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
         return (
             <CollectionBaseView {...this.props} className="collectionVideoView-cont" contentRef={this.mainCont} onContextMenu={this.onContextMenu}>
                 {this.subView}
-            </CollectionBaseView>)
+            </CollectionBaseView>);
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index b31dcc888..8abd0a02d 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,4 +1,4 @@
-import * as React from 'react'
+import * as React from 'react';
 import { FieldViewProps, FieldView } from '../nodes/FieldView';
 import { CollectionBaseView, CollectionViewType, CollectionRenderProps } from './CollectionBaseView';
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
@@ -13,26 +13,26 @@ import { undoBatch } from '../../util/UndoManager';
 
 @observer
 export class CollectionView extends React.Component<FieldViewProps> {
-    public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(CollectionView, fieldStr) }
+    public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(CollectionView, fieldStr); }
 
     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} />);
+            case CollectionViewType.Docking: return (<CollectionDockingView {...props} />);
+            case CollectionViewType.Tree: return (<CollectionTreeView {...props} />);
             case CollectionViewType.Freeform:
             default:
-                return (<CollectionFreeFormView {...props} />)
+                return (<CollectionFreeFormView {...props} />);
         }
         return (null);
     }
 
     onContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped() && this.props.Document.Id !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
-            ContextMenu.Instance.addItem({ description: "Freeform", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform)) })
-            ContextMenu.Instance.addItem({ description: "Schema", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema)) })
-            ContextMenu.Instance.addItem({ description: "Treeview", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Tree)) })
+            ContextMenu.Instance.addItem({ description: "Freeform", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform)) });
+            ContextMenu.Instance.addItem({ description: "Schema", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema)) });
+            ContextMenu.Instance.addItem({ description: "Treeview", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Tree)) });
         }
     }
 
@@ -41,6 +41,6 @@ export class CollectionView extends React.Component<FieldViewProps> {
             <CollectionBaseView {...this.props} onContextMenu={this.onContextMenu}>
                 {this.SubView}
             </CollectionBaseView>
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 3dfd74ec8..081b3eb6c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -32,6 +32,6 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
                 style={{ strokeWidth: `${l.length * 5}` }}
                 x1={`${x1}`} y1={`${y1}`}
                 x2={`${x2}`} y2={`${y2}`} />
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index c288e7abf..cf058090d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -17,7 +17,7 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
 
     HackToAvoidReactionFiringUnnecessarily?: Document = undefined;
     componentDidMount() {
-        this.HackToAvoidReactionFiringUnnecessarily = this.props.Document
+        this.HackToAvoidReactionFiringUnnecessarily = this.props.Document;
         reaction(() =>
             DocumentManager.Instance.getAllDocumentViews(this.HackToAvoidReactionFiringUnnecessarily!).
                 map(dv => dv.props.Document.GetNumber(KeyStore.X, 0)),
@@ -31,13 +31,14 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
                         let x1w = srcDoc.GetNumber(KeyStore.Width, -1);
                         let x2 = dstDoc.GetNumber(KeyStore.X, 0);
                         let x2w = dstDoc.GetNumber(KeyStore.Width, -1);
-                        if (x1w < 0 || x2w < 0 || i === j)
+                        if (x1w < 0 || x2w < 0 || i === j) {
                             continue;
+                        }
                         let dstTarg = dstDoc;
                         let srcTarg = srcDoc;
                         let findBrush = (field: ListField<Document>) => field.Data.findIndex(brush => {
                             let bdocs = brush ? brush.GetList(KeyStore.BrushingDocs, [] as Document[]) : [];
-                            return (bdocs.length && ((bdocs[0] === dstTarg && bdocs[1] === srcTarg)) ? true : false)
+                            return (bdocs.length && ((bdocs[0] === dstTarg && bdocs[1] === srcTarg)) ? true : false);
                         });
                         let brushAction = (field: ListField<Document>) => {
                             let found = findBrush(field);
@@ -64,13 +65,13 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
 
                     }
                 }
-            })
+            });
     }
     documentAnchors(view: DocumentView) {
         let equalViews = [view];
         let containerDoc = view.props.Document.GetT(KeyStore.AnnotationOn, Document);
         if (containerDoc && containerDoc instanceof Document) {
-            equalViews = DocumentManager.Instance.getDocumentViews(containerDoc.GetPrototype()!)
+            equalViews = DocumentManager.Instance.getDocumentViews(containerDoc.GetPrototype()!);
         }
         return equalViews.filter(sv => sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === this.props.Document);
     }
@@ -86,15 +87,16 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
                 if (!drawnPairs.reduce((found, drawnPair) => {
                     let match = (possiblePair.a === drawnPair.a && possiblePair.b === drawnPair.b);
                     if (match) {
-                        if (!drawnPair.l.reduce((found, link) => found || link.Id === connection.l.Id, false))
+                        if (!drawnPair.l.reduce((found, link) => found || link.Id === connection.l.Id, false)) {
                             drawnPair.l.push(connection.l);
+                        }
                     }
                     return match || found;
                 }, false)) {
                     drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] });
                 }
-            })
-            return drawnPairs
+            });
+            return drawnPairs;
         }, [] as { a: Document, b: Document, l: Document[] }[]);
         return connections.map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l} />);
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index 17656e4ae..fc832264d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -57,7 +57,7 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
                 let id = entry.Data[0][0];
                 let email = entry.Data[0][1];
                 let point = entry.Data[1];
-                this.drawCrosshairs("#" + v5(id, v5.URL).substring(0, 6).toUpperCase() + "22")
+                this.drawCrosshairs("#" + v5(id, v5.URL).substring(0, 6).toUpperCase() + "22");
                 return (
                     <div
                         key={id}
@@ -69,7 +69,7 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
                         }}
                     >
                         <canvas
-                            ref={(el) => { if (el) this.crosshairs = el }}
+                            ref={(el) => { if (el) this.crosshairs = el; }}
                             width={20}
                             height={20}
                             style={{
@@ -93,7 +93,7 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
                     </div>
                 );
             }
-        })
+        });
     }
 
     render() {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 1c3899b24..e694bc7a7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -49,7 +49,7 @@ export class CollectionFreeFormView extends CollectionSubView {
             if (dv) {
                 SelectionManager.SelectDoc(dv, true);
             }
-        })
+        });
     }
 
     public getActiveDocuments = () => {
@@ -70,8 +70,8 @@ export class CollectionFreeFormView extends CollectionSubView {
 
     private outerElement?: HTMLDivElement;
 
-    @computed get panX(): number { return this.props.Document.GetNumber(KeyStore.PanX, 0) }
-    @computed get panY(): number { return this.props.Document.GetNumber(KeyStore.PanY, 0) }
+    @computed get panX(): number { return this.props.Document.GetNumber(KeyStore.PanX, 0); }
+    @computed get panY(): number { return this.props.Document.GetNumber(KeyStore.PanY, 0); }
     @computed get scale(): number { return this.props.Document.GetNumber(KeyStore.Scale, 1); }
     @computed get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey.Id === KeyStore.Annotations.Id; } // bcz: ? Why do we need to compare Id's?
     @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
@@ -109,7 +109,7 @@ export class CollectionFreeFormView extends CollectionSubView {
                             d.SetNumber(KeyStore.Height, 300);
                         }
                         this.bringToFront(d);
-                    })
+                    });
                 }
             }
             return true;
@@ -133,8 +133,9 @@ export class CollectionFreeFormView extends CollectionSubView {
             document.addEventListener("pointerup", this.onPointerUp);
             this._lastX = this.DownX = e.pageX;
             this._lastY = this.DownY = e.pageY;
-            if (this.props.isSelected())
+            if (this.props.isSelected()) {
                 e.stopPropagation();
+            }
         }
     }
 
@@ -182,12 +183,13 @@ export class CollectionFreeFormView extends CollectionSubView {
             let transform = this.getTransform();
 
             let deltaScale = (1 - (e.deltaY / coefficient));
-            if (deltaScale * this.zoomScaling < 1 && this.isAnnotationOverlay)
+            if (deltaScale * this.zoomScaling < 1 && this.isAnnotationOverlay) {
                 deltaScale = 1 / this.zoomScaling;
+            }
             let [x, y] = transform.transformPoint(e.clientX, e.clientY);
 
-            let localTransform = this.getLocalTransform()
-            localTransform = localTransform.inverse().scaleAbout(deltaScale, x, y)
+            let localTransform = this.getLocalTransform();
+            localTransform = localTransform.inverse().scaleAbout(deltaScale, x, y);
             // console.log(localTransform)
 
             this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale);
@@ -209,7 +211,7 @@ export class CollectionFreeFormView extends CollectionSubView {
     onDrop = (e: React.DragEvent): void => {
         var pt = this.getTransform().transformPoint(e.pageX, e.pageY);
         super.onDrop(e, { x: pt[0], y: pt[1] });
-    };
+    }
 
     onDragOver = (): void => {
     }
@@ -228,7 +230,7 @@ export class CollectionFreeFormView extends CollectionSubView {
             }
             return doc1.GetNumber(KeyStore.ZIndex, 0) - doc2.GetNumber(KeyStore.ZIndex, 0);
         }).map((doc, index) => {
-            doc.SetNumber(KeyStore.ZIndex, index + 1)
+            doc.SetNumber(KeyStore.ZIndex, index + 1);
         });
     }
 
@@ -268,7 +270,7 @@ export class CollectionFreeFormView extends CollectionSubView {
             focus: this.focusDocument,
             parentActive: this.props.active,
             onActiveChanged: this.props.active,
-        }
+        };
     }
 
     @computed
@@ -276,10 +278,11 @@ export class CollectionFreeFormView extends CollectionSubView {
         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) => {
             var page = doc.GetNumber(KeyStore.Page, -1);
-            if (page === curPage || page === -1)
+            if (page === curPage || page === -1) {
                 prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
+            }
             return prev;
-        }, [] as JSX.Element[])
+        }, [] as JSX.Element[]);
     }
 
     @computed
@@ -295,8 +298,8 @@ export class CollectionFreeFormView extends CollectionSubView {
                 layoutKey={KeyStore.OverlayLayout} isTopMost={this.props.isTopMost} isSelected={() => false} select={() => { }} />);
     }
 
-    getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform())
-    getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH)
+    getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform());
+    getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH);
     getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.scale).translate(this.panX, this.panY);
     noScaling = () => 1;
     childViews = () => this.views;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 704db1d4a..1e6faafb3 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -37,7 +37,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     @action
     cleanupInteractions = (all: boolean = false) => {
         if (all) {
-            document.removeEventListener("pointermove", this.onPointerMove, true)
+            document.removeEventListener("pointermove", this.onPointerMove, true);
             document.removeEventListener("pointerup", this.onPointerUp, true);
         } else {
             this._used = true;
@@ -52,7 +52,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             this._downX = this._lastX = e.pageX;
             this._downY = this._lastY = e.pageY;
             this._used = false;
-            document.addEventListener("pointermove", this.onPointerMove, true)
+            document.addEventListener("pointermove", this.onPointerMove, true);
             document.addEventListener("pointerup", this.onPointerUp, true);
             document.addEventListener("keydown", this.marqueeCommand, true);
         }
@@ -94,7 +94,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         let top = this._downY < this._lastY ? this._downY : this._lastY;
         let topLeft = this.props.getTransform().transformPoint(left, top);
         let size = this.props.getTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
-        return { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) }
+        return { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
     }
 
     @action
@@ -180,9 +180,10 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             var y = doc.GetNumber(KeyStore.Y, 0);
             var w = doc.GetNumber(KeyStore.Width, 0);
             var h = doc.GetNumber(KeyStore.Height, 0);
-            if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect))
-                selection.push(doc)
-        })
+            if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
+                selection.push(doc);
+            }
+        });
         return selection;
     }
 
@@ -190,7 +191,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     get marqueeDiv() {
         let p = this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
         let v = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
-        return <div className="marquee" style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, width: `${Math.abs(v[0])}`, height: `${Math.abs(v[1])}` }} />
+        return <div className="marquee" style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, width: `${Math.abs(v[0])}`, height: `${Math.abs(v[1])}` }} />;
     }
 
     render() {
diff --git a/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx b/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx
index 599461f85..8eabb020a 100644
--- a/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx
+++ b/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx
@@ -90,7 +90,7 @@ export class PreviewCursor extends React.Component<PreviewCursorProps>  {
                 {this.props.children}
                 <PreviewCursorPrompt setVisible={this.setVisible} getPoint={this.getPoint} getVisible={this.getVisible} />
             </div>
-        )
+        );
     }
 }
 
@@ -109,8 +109,9 @@ export class PreviewCursorPrompt extends React.Component<PromptProps> {
 
     render() {
         let p = this.props.getPoint();
-        if (this.props.getVisible() && this._promptRef.current)
+        if (this.props.getVisible() && this._promptRef.current) {
             this._promptRef.current.focus();
+        }
         return <div className="previewCursor" id="previewCursor" onBlur={this.onBlur} tabIndex={0} ref={this._promptRef}
             style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, opacity: this.props.getVisible() ? 1 : 0 }}>
             I
diff --git a/src/client/views/nodes/Annotation.tsx b/src/client/views/nodes/Annotation.tsx
index e4f17940c..3e4ed6bf1 100644
--- a/src/client/views/nodes/Annotation.tsx
+++ b/src/client/views/nodes/Annotation.tsx
@@ -1,8 +1,8 @@
 import "./ImageBox.scss";
-import React = require("react")
-import { observer } from "mobx-react"
+import React = require("react");
+import { observer } from "mobx-react";
 import { observable, action } from 'mobx';
-import 'react-pdf/dist/Page/AnnotationLayer.css'
+import 'react-pdf/dist/Page/AnnotationLayer.css';
 
 interface IProps {
     Span: HTMLSpanElement;
@@ -29,13 +29,13 @@ export class Annotation extends React.Component<IProps> {
      */
     onColorChange = (e: React.PointerEvent) => {
         if (e.currentTarget.innerHTML === "r") {
-            this.props.Span.style.backgroundColor = "rgba(255,0,0, 0.3)"
+            this.props.Span.style.backgroundColor = "rgba(255,0,0, 0.3)";
         } else if (e.currentTarget.innerHTML === "b") {
-            this.props.Span.style.backgroundColor = "rgba(0,255, 255, 0.3)"
+            this.props.Span.style.backgroundColor = "rgba(0,255, 255, 0.3)";
         } else if (e.currentTarget.innerHTML === "y") {
-            this.props.Span.style.backgroundColor = "rgba(255,255,0, 0.3)"
+            this.props.Span.style.backgroundColor = "rgba(255,255,0, 0.3)";
         } else if (e.currentTarget.innerHTML === "g") {
-            this.props.Span.style.backgroundColor = "rgba(76, 175, 80, 0.3)"
+            this.props.Span.style.backgroundColor = "rgba(76, 175, 80, 0.3)";
         }
 
     }
@@ -54,11 +54,11 @@ export class Annotation extends React.Component<IProps> {
                     this.props.Highlights.splice(index, 1);
                 }
             }
-        })
+        });
 
         //removing from CurrAnno and Annotation array 
         this.props.Annotations.splice(index, 1);
-        this.props.CurrAnno.pop()
+        this.props.CurrAnno.pop();
 
         //removing span from div
         if (this.props.Span.parentElement) {
@@ -70,11 +70,11 @@ export class Annotation extends React.Component<IProps> {
                             if (item === e) {
                                 item.remove();
                             }
-                        })
+                        });
                         e.remove();
                     }
                 }
-            })
+            });
         }
 
 
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 1bd934c25..1493ff25b 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -1,18 +1,18 @@
-import React = require("react")
+import React = require("react");
 import { FieldViewProps, FieldView } from './FieldView';
 import { FieldWaiting } from '../../../fields/Field';
-import { observer } from "mobx-react"
+import { observer } from "mobx-react";
 import { ContextMenu } from "../../views/ContextMenu";
 import { observable, action } from 'mobx';
 import { KeyStore } from '../../../fields/KeyStore';
 import { AudioField } from "../../../fields/AudioField";
-import "./AudioBox.scss"
+import "./AudioBox.scss";
 import { NumberField } from "../../../fields/NumberField";
 
 @observer
 export class AudioBox extends React.Component<FieldViewProps> {
 
-    public static LayoutString() { return FieldView.LayoutString(AudioBox) }
+    public static LayoutString() { return FieldView.LayoutString(AudioBox); }
 
     constructor(props: FieldViewProps) {
         super(props);
@@ -28,7 +28,7 @@ export class AudioBox extends React.Component<FieldViewProps> {
 
 
     render() {
-        let field = this.props.Document.Get(this.props.fieldKey)
+        let field = this.props.Document.Get(this.props.fieldKey);
         let path = field === FieldWaiting ? "http://techslides.com/demos/samples/sample.mp3" :
             field instanceof AudioField ? field.Data.href : "http://techslides.com/demos/samples/sample.mp3";
 
@@ -39,6 +39,6 @@ export class AudioBox extends React.Component<FieldViewProps> {
                     Not supported.
                 </audio>
             </div>
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index dcf82ccd4..77f41105f 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -35,24 +35,24 @@ export class CollectionFreeFormDocumentView extends React.Component<DocumentView
     @computed get nativeHeight(): number { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
 
     set width(w: number) {
-        this.props.Document.SetData(KeyStore.Width, w, NumberField)
+        this.props.Document.SetData(KeyStore.Width, w, NumberField);
         if (this.nativeWidth && this.nativeHeight) {
-            this.props.Document.SetNumber(KeyStore.Height, this.nativeHeight / this.nativeWidth * w)
+            this.props.Document.SetNumber(KeyStore.Height, this.nativeHeight / this.nativeWidth * w);
         }
     }
 
     set height(h: number) {
         this.props.Document.SetData(KeyStore.Height, h, NumberField);
         if (this.nativeWidth && this.nativeHeight) {
-            this.props.Document.SetNumber(KeyStore.Width, this.nativeWidth / this.nativeHeight * h)
+            this.props.Document.SetNumber(KeyStore.Width, this.nativeWidth / this.nativeHeight * h);
         }
     }
 
     set zIndex(h: number) {
-        this.props.Document.SetData(KeyStore.ZIndex, h, NumberField)
+        this.props.Document.SetData(KeyStore.ZIndex, h, NumberField);
     }
 
-    contentScaling = () => this.nativeWidth > 0 ? this.width / this.nativeWidth : 1
+    contentScaling = () => this.nativeWidth > 0 ? this.width / this.nativeWidth : 1;
 
     getTransform = (): Transform =>
         this.props.ScreenToLocalTransform()
@@ -66,7 +66,7 @@ export class CollectionFreeFormDocumentView extends React.Component<DocumentView
             ScreenToLocalTransform={this.getTransform}
             PanelWidth={this.panelWidth}
             PanelHeight={this.panelHeight}
-        />
+        />;
     }
     panelWidth = () => this.props.Document.GetBoolean(KeyStore.Minimized, false) ? 10 : this.props.PanelWidth();
     panelHeight = () => this.props.Document.GetBoolean(KeyStore.Minimized, false) ? 10 : this.props.PanelHeight();
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 34cc326aa..5836da396 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -26,7 +26,7 @@ import { FieldViewProps } from "./FieldView";
 import { Without } from "../../../Utils";
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
-type BindingProps = Without<FieldViewProps, 'fieldKey'>
+type BindingProps = Without<FieldViewProps, 'fieldKey'>;
 export interface JsxBindings {
     props: BindingProps;
     [keyName: string]: BindingProps | Field;
@@ -92,7 +92,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
             bindings={this.CreateBindings()}
             jsx={this.layout}
             showWarnings={true}
-            onError={(test: any) => { console.log(test) }}
-        />
+            onError={(test: any) => { console.log(test); }}
+        />;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index ab9cd2d53..9670ca6b2 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -100,8 +100,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         if (e.shiftKey && e.buttons === 2) {
             if (this.props.isTopMost) {
                 this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey);
-            } else
+            } else {
                 CollectionDockingView.Instance.StartOtherDrag([this.props.Document], e);
+            }
             e.stopPropagation();
         } else {
             if (this.active && !e.isDefaultPrevented()) {
@@ -112,7 +113,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                 document.addEventListener("pointerup", this.onPointerUp);
             }
         }
-    };
+    }
 
     private dropDisposer?: DragManager.DragDropDisposer;
 
@@ -179,7 +180,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         }
         e.stopPropagation();
         e.preventDefault();
-    };
+    }
     onPointerUp = (e: PointerEvent): void => {
         document.removeEventListener("pointermove", this.onPointerMove);
         document.removeEventListener("pointerup", this.onPointerUp);
@@ -190,22 +191,22 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         ) {
             SelectionManager.SelectDoc(this, e.ctrlKey);
         }
-    };
+    }
     stopPropogation = (e: React.SyntheticEvent) => {
         e.stopPropagation();
-    };
+    }
 
     deleteClicked = (): void => {
         if (this.props.removeDocument) {
             this.props.removeDocument(this.props.Document);
         }
-    };
+    }
 
     fieldsClicked = (e: React.MouseEvent): void => {
         if (this.props.addDocument) {
             this.props.addDocument(Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }), false);
         }
-    };
+    }
     fullScreenClicked = (e: React.MouseEvent): void => {
         CollectionDockingView.Instance.OpenFullScreen(this.props.Document);
         ContextMenu.Instance.clearItems();
@@ -214,7 +215,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             event: this.closeFullScreenClicked
         });
         ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
-    };
+    }
 
     closeFullScreenClicked = (e: React.MouseEvent): void => {
         CollectionDockingView.Instance.CloseFullScreen();
@@ -224,7 +225,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             event: this.fullScreenClicked
         });
         ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
-    };
+    }
 
     @action
     public minimize = (): void => {
@@ -234,7 +235,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             BooleanField
         );
         SelectionManager.DeselectAll();
-    };
+    }
 
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
@@ -276,7 +277,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             );
             e.stopPropagation();
         }
-    };
+    }
 
     onDrop = (e: React.DragEvent) => {
         if (e.isDefaultPrevented()) {
@@ -290,7 +291,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             e.stopPropagation();
             e.preventDefault();
         }
-    };
+    }
 
     @action
     onContextMenu = (e: React.MouseEvent): void => {
@@ -352,14 +353,14 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         });
         ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
         SelectionManager.SelectDoc(this, e.ctrlKey);
-    };
+    }
 
     isMinimized = () => {
         let field = this.props.Document.GetT(KeyStore.Minimized, BooleanField);
         if (field && field !== FieldWaiting) {
             return field.Data;
         }
-    };
+    }
 
     @action
     expand = () => {
@@ -368,13 +369,13 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             false as boolean,
             BooleanField
         );
-    };
+    }
 
-    isSelected = () => SelectionManager.IsSelected(this)
+    isSelected = () => SelectionManager.IsSelected(this);
 
     select = (ctrlPressed: boolean) => {
         SelectionManager.SelectDoc(this, ctrlPressed);
-    };
+    }
 
     render() {
         if (!this.props.Document) {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index d9422ae9b..07c5b332c 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -1,4 +1,4 @@
-import React = require("react")
+import React = require("react");
 import { observer } from "mobx-react";
 import { computed } from "mobx";
 import { Field, FieldWaiting, FieldValue } from "../../../fields/Field";
@@ -7,7 +7,7 @@ import { TextField } from "../../../fields/TextField";
 import { NumberField } from "../../../fields/NumberField";
 import { RichTextField } from "../../../fields/RichTextField";
 import { ImageField } from "../../../fields/ImageField";
-import { VideoField } from "../../../fields/VideoField"
+import { VideoField } from "../../../fields/VideoField";
 import { Key } from "../../../fields/Key";
 import { FormattedTextBox } from "./FormattedTextBox";
 import { ImageBox } from "./ImageBox";
@@ -57,22 +57,22 @@ export class FieldView extends React.Component<FieldViewProps> {
     render() {
         const field = this.field;
         if (!field) {
-            return <p>{'<null>'}</p>
+            return <p>{'<null>'}</p>;
         }
         if (field instanceof TextField) {
-            return <p>{field.Data}</p>
+            return <p>{field.Data}</p>;
         }
         else if (field instanceof RichTextField) {
-            return <FormattedTextBox {...this.props} />
+            return <FormattedTextBox {...this.props} />;
         }
         else if (field instanceof ImageField) {
-            return <ImageBox {...this.props} />
+            return <ImageBox {...this.props} />;
         }
         else if (field instanceof VideoField) {
-            return <VideoBox {...this.props} />
+            return <VideoBox {...this.props} />;
         }
         else if (field instanceof AudioField) {
-            return <AudioBox {...this.props} />
+            return <AudioBox {...this.props} />;
         }
         else if (field instanceof Document) {
             return (
@@ -92,25 +92,26 @@ export class FieldView extends React.Component<FieldViewProps> {
                     ContainingCollectionView={undefined}
                     parentActive={this.props.active}
                     onActiveChanged={this.props.onActiveChanged} />
-            )
+            );
         }
         else if (field instanceof ListField) {
             return (<div>
                 {(field as ListField<Field>).Data.map(f => f instanceof Document ? f.Title : f.GetValue().toString()).join(", ")}
-            </div>)
+            </div>);
         }
         // bcz: this belongs here, but it doesn't render well so taking it out for now
         // else if (field instanceof HtmlField) {
         //     return <WebBox {...this.props} />
         // }
         else if (field instanceof NumberField) {
-            return <p>{field.Data}</p>
+            return <p>{field.Data}</p>;
         }
         else if (field !== FieldWaiting) {
-            return <p>{JSON.stringify(field.GetValue())}</p>
+            return <p>{JSON.stringify(field.GetValue())}</p>;
         }
-        else
-            return <p> {"Waiting for server..."} </p>
+        else {
+            return <p> {"Waiting for server..."} </p>;
+ }
     }
 
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 7a94be12b..beca6cdc6 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -61,7 +61,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
             );
             // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
         }
-    };
+    }
 
     get FieldDoc() { return this.props.fieldKey === KeyStore.Archives ? Main.Instance._textDoc! : this.props.Document; }
     get FieldKey() { return this.props.fieldKey === KeyStore.Archives ? KeyStore.Data : this.props.fieldKey; }
@@ -81,12 +81,13 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         if (this.props.fieldKey === KeyStore.Archives) {
             this._inputReactionDisposer = reaction(() => Main.Instance._textDoc && Main.Instance._textDoc.Id,
                 () => {
-                    if (this._editorView)
-                        this._editorView!.destroy();
+                    if (this._editorView) {
+                        this._editorView.destroy();
+                    }
 
                     this.setupEditor(config);
                 }
-            )
+            );
         }
 
         this._reactionDisposer = reaction(
@@ -153,7 +154,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
             e.stopPropagation();
         }
-    };
+    }
     onPointerUp = (e: React.PointerEvent): void => {
         if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
             e.stopPropagation();
@@ -162,7 +163,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
             e.preventDefault();
             Main.Instance.SetTextDoc(this.props.Document, this._ref.current!);
         }
-    };
+    }
 
     onFocused = (e: React.FocusEvent): void => {
         if (this.props.fieldKey !== KeyStore.Archives) {
@@ -191,11 +192,11 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         //     ]
         // })
         // e.stopPropagation()
-    };
+    }
 
     onPointerWheel = (e: React.WheelEvent): void => {
         e.stopPropagation();
-    };
+    }
 
     tooltipMenuPlugin() {
         return new Plugin({
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index c5f29f7b0..6b0a3a799 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -9,13 +9,13 @@ import { KeyStore } from '../../../fields/KeyStore';
 import { ContextMenu } from "../../views/ContextMenu";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./ImageBox.scss";
-import React = require("react")
+import React = require("react");
 import { Utils } from '../../../Utils';
 
 @observer
 export class ImageBox extends React.Component<FieldViewProps> {
 
-    public static LayoutString() { return FieldView.LayoutString(ImageBox) }
+    public static LayoutString() { return FieldView.LayoutString(ImageBox); }
     private _ref: React.RefObject<HTMLDivElement>;
     private _imgRef: React.RefObject<HTMLImageElement>;
     private _downX: number = 0;
@@ -39,7 +39,7 @@ export class ImageBox extends React.Component<FieldViewProps> {
     onLoad = (target: any) => {
         var h = this._imgRef.current!.naturalHeight;
         var w = this._imgRef.current!.naturalWidth;
-        this.props.Document.SetNumber(KeyStore.NativeHeight, this.props.Document.GetNumber(KeyStore.NativeWidth, 0) * h / w)
+        this.props.Document.SetNumber(KeyStore.NativeHeight, this.props.Document.GetNumber(KeyStore.NativeWidth, 0) * h / w);
     }
 
     componentDidMount() {
@@ -86,7 +86,7 @@ export class ImageBox extends React.Component<FieldViewProps> {
                 onMoveNextRequest={action(() =>
                     this._photoIndex = (this._photoIndex + 1) % images.length
                 )}
-            />)
+            />);
         }
     }
 
@@ -96,7 +96,7 @@ export class ImageBox extends React.Component<FieldViewProps> {
             let url = field.Data.href;
             ContextMenu.Instance.addItem({
                 description: "Copy path", event: () => {
-                    Utils.CopyText(url)
+                    Utils.CopyText(url);
                 }
             });
         }
@@ -111,6 +111,6 @@ export class ImageBox extends React.Component<FieldViewProps> {
             <div className="imageBox-cont" onPointerDown={this.onPointerDown} ref={this._ref} onContextMenu={this.specificContextMenu}>
                 <img src={path} width={nativeWidth} alt="Image not found" ref={this._imgRef} onLoad={this.onLoad} />
                 {this.lightbox(path)}
-            </div>)
+            </div>);
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 8a7f32e6d..bcac113f0 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -7,7 +7,7 @@ import { KeyStore } from '../../../fields/KeyStore';
 import { FieldView, FieldViewProps } from './FieldView';
 import "./KeyValueBox.scss";
 import { KeyValuePair } from "./KeyValuePair";
-import React = require("react")
+import React = require("react");
 import { CompileScript, ToField } from "../../util/Scripting";
 import { Key } from '../../../fields/Key';
 import { observable, action } from "mobx";
@@ -15,7 +15,7 @@ import { observable, action } from "mobx";
 @observer
 export class KeyValueBox extends React.Component<FieldViewProps> {
 
-    public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(KeyValueBox, fieldStr) }
+    public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(KeyValueBox, fieldStr); }
     @observable private _keyInput: string = "";
     @observable private _valueInput: string = "";
 
@@ -36,7 +36,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
             if (this._keyInput && this._valueInput) {
                 let doc = this.props.Document.GetT(KeyStore.Data, Document);
                 if (!doc || doc === FieldWaiting) {
-                    return
+                    return;
                 }
                 let realDoc = doc;
 
@@ -55,8 +55,8 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
                         realDoc.Set(new Key(this._keyInput), dataField);
                     }
                 }
-                this._keyInput = ""
-                this._valueInput = ""
+                this._keyInput = "";
+                this._valueInput = "";
             }
         }
     }
@@ -73,7 +73,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     createTable = () => {
         let doc = this.props.Document.GetT(KeyStore.Data, Document);
         if (!doc || doc === FieldWaiting) {
-            return <tr><td>Loading...</td></tr>
+            return <tr><td>Loading...</td></tr>;
         }
         let realDoc = doc;
 
@@ -84,13 +84,13 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
                 if (!(key in ids)) {
                     ids[key] = key;
                 }
-            })
+            });
         }
 
         let rows: JSX.Element[] = [];
         let i = 0;
         for (let key in ids) {
-            rows.push(<KeyValuePair doc={realDoc} rowStyle={"keyValueBox-" + (i++ % 2 ? "oddRow" : "evenRow")} fieldId={key} key={key} />)
+            rows.push(<KeyValuePair doc={realDoc} rowStyle={"keyValueBox-" + (i++ % 2 ? "oddRow" : "evenRow")} fieldId={key} key={key} />);
         }
         return rows;
     }
@@ -125,6 +125,6 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
                     {this.newKeyValue()}
                 </tbody>
             </table>
-        </div>)
+        </div>);
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index c6a0a7296..a1050dc6e 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -1,14 +1,14 @@
 import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
 import "./KeyValueBox.scss";
 import "./KeyValuePair.scss";
-import React = require("react")
+import React = require("react");
 import { FieldViewProps, FieldView } from './FieldView';
 import { Opt, Field } from '../../../fields/Field';
-import { observer } from "mobx-react"
+import { observer } from "mobx-react";
 import { observable, action } from 'mobx';
 import { Document } from '../../../fields/Document';
 import { Key } from '../../../fields/Key';
-import { Server } from "../../Server"
+import { Server } from "../../Server";
 import { EditableView } from "../EditableView";
 import { CompileScript, ToField } from "../../util/Scripting";
 import { Transform } from '../../util/Transform';
@@ -25,7 +25,7 @@ export interface KeyValuePairProps {
 export class KeyValuePair extends React.Component<KeyValuePairProps> {
 
     @observable
-    private key: Opt<Key>
+    private key: Opt<Key>;
 
     constructor(props: KeyValuePairProps) {
         super(props);
@@ -41,7 +41,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
 
     render() {
         if (!this.key) {
-            return <tr><td>error</td><td></td></tr>
+            return <tr><td>error</td><td></td></tr>;
 
         }
         let props: FieldViewProps = {
@@ -55,7 +55,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             onActiveChanged: emptyFunction,
             ScreenToLocalTransform: Transform.Identity,
             focus: emptyFunction,
-        }
+        };
         let contents = (
             <FieldView {...props} />
         );
@@ -101,6 +101,6 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
                         return false;
                     }}></EditableView></td>
             </tr>
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 4791d6029..b016a3d48 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -2,8 +2,8 @@ import { observable, computed, action } from "mobx";
 import React = require("react");
 import { SelectionManager } from "../../util/SelectionManager";
 import { observer } from "mobx-react";
-import './LinkBox.scss'
-import { KeyStore } from '../../../fields/KeyStore'
+import './LinkBox.scss';
+import { KeyStore } from '../../../fields/KeyStore';
 import { props } from "bluebird";
 import { DocumentView } from "./DocumentView";
 import { Document } from "../../../fields/Document";
@@ -30,7 +30,7 @@ interface Props {
     linkName: String;
     pairedDoc: Document;
     type: String;
-    showEditor: () => void
+    showEditor: () => void;
 }
 
 @observer
@@ -49,15 +49,16 @@ export class LinkBox extends React.Component<Props> {
                 } else if (contextDoc instanceof Document) {
                     this.props.pairedDoc.GetTAsync(KeyStore.Page, NumberField).then((pfield: any) => {
                         contextDoc.GetTAsync(KeyStore.CurPage, NumberField).then((cfield: any) => {
-                            if (pfield !== cfield)
+                            if (pfield !== cfield) {
                                 contextDoc.SetNumber(KeyStore.CurPage, pfield.Data);
+                            }
                             let contextView = DocumentManager.Instance.getDocumentView(contextDoc);
                             if (contextView) {
                                 contextView.props.focus(contextDoc);
                             } else {
                                 CollectionDockingView.Instance.AddRightSplit(contextDoc);
                             }
-                        })
+                        });
                     });
                 }
             });
@@ -80,7 +81,7 @@ export class LinkBox extends React.Component<Props> {
                     if (field) {
                         field.Data.splice(field.Data.indexOf(this.props.linkDoc));
                     }
-                })
+                });
             }
         });
         this.props.linkDoc.GetTAsync(KeyStore.LinkedToDocs, Document, field => {
@@ -89,7 +90,7 @@ export class LinkBox extends React.Component<Props> {
                     if (field) {
                         field.Data.splice(field.Data.indexOf(this.props.linkDoc));
                     }
-                })
+                });
             }
         });
     }
@@ -117,6 +118,6 @@ export class LinkBox extends React.Component<Props> {
                         <FontAwesomeIcon className="fa-icon-delete" icon="times" size="sm" /></div>
                 </div>
             </div>
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx
index 3f7b4bf2d..bde50fed8 100644
--- a/src/client/views/nodes/LinkEditor.tsx
+++ b/src/client/views/nodes/LinkEditor.tsx
@@ -2,8 +2,8 @@ import { observable, computed, action } from "mobx";
 import React = require("react");
 import { SelectionManager } from "../../util/SelectionManager";
 import { observer } from "mobx-react";
-import './LinkEditor.scss'
-import { KeyStore } from '../../../fields/KeyStore'
+import './LinkEditor.scss';
+import { KeyStore } from '../../../fields/KeyStore';
 import { props } from "bluebird";
 import { DocumentView } from "./DocumentView";
 import { Document } from "../../../fields/Document";
@@ -43,7 +43,7 @@ export class LinkEditor extends React.Component<Props> {
                 <div className="save-button" onPointerDown={this.onSaveButtonPressed}>SAVE</div>
             </div>
 
-        )
+        );
     }
 
     @action
diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx
index 6c2d24630..ac09da305 100644
--- a/src/client/views/nodes/LinkMenu.tsx
+++ b/src/client/views/nodes/LinkMenu.tsx
@@ -13,7 +13,7 @@ import React = require("react");
 
 interface Props {
     docView: DocumentView;
-    changeFlyout: () => void
+    changeFlyout: () => void;
 }
 
 @observer
@@ -25,9 +25,9 @@ export class LinkMenu extends React.Component<Props> {
         return links.map(link => {
             let doc = link.GetT(key, Document);
             if (doc && doc !== FieldWaiting) {
-                return <LinkBox key={doc.Id} linkDoc={link} linkName={link.Title} pairedDoc={doc} showEditor={action(() => this._editingLink = link)} type={type} />
+                return <LinkBox key={doc.Id} linkDoc={link} linkName={link.Title} pairedDoc={doc} showEditor={action(() => this._editingLink = link)} type={type} />;
             }
-        })
+        });
     }
 
     render() {
@@ -43,11 +43,11 @@ export class LinkMenu extends React.Component<Props> {
                         {this.renderLinkItems(linkFrom, KeyStore.LinkedFromDocs, "Source: ")}
                     </div>
                 </div>
-            )
+            );
         } else {
             return (
                 <LinkEditor linkDoc={this._editingLink} showLinks={action(() => this._editingLink = undefined)}></LinkEditor>
-            )
+            );
         }
 
     }
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 774d9be3e..81ceb37f6 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -17,7 +17,7 @@ import { FieldView, FieldViewProps } from './FieldView';
 import "./ImageBox.scss";
 import "./PDFBox.scss";
 import { Sticky } from './Sticky'; //you should look at sticky and annotation, because they are used here
-import React = require("react")
+import React = require("react");
 import { SelectionManager } from "../../util/SelectionManager";
 
 /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx
@@ -55,7 +55,7 @@ import { SelectionManager } from "../../util/SelectionManager";
 export class PDFBox extends React.Component<FieldViewProps> {
     public static LayoutString() { return FieldView.LayoutString(PDFBox); }
 
-    private _mainDiv = React.createRef<HTMLDivElement>()
+    private _mainDiv = React.createRef<HTMLDivElement>();
     private _pdf = React.createRef<HTMLCanvasElement>();
 
     @observable private _renderAsSvg = true;
@@ -72,7 +72,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
 
     private _currTool: any; //keeps track of current tool button reference
     private _drawToolOn: boolean = false; //boolean that keeps track of the drawing tool 
-    private _drawTool = React.createRef<HTMLButtonElement>()//drawing tool button reference
+    private _drawTool = React.createRef<HTMLButtonElement>();//drawing tool button reference
 
     private _colorTool = React.createRef<HTMLButtonElement>(); //color button reference
     private _currColor: string = "black"; //current color that user selected (for ink/pen)
@@ -85,7 +85,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
     @observable private _perPageInfo: Object[] = []; //stores pageInfo
     @observable private _pageInfo: any = { area: [], divs: [], anno: [] }; //divs is array of objects linked to anno
 
-    @observable private _currAnno: any = []
+    @observable private _currAnno: any = [];
     @observable private _interactive: boolean = false;
     @observable private _loaded: boolean = false;
 
@@ -168,24 +168,24 @@ export class PDFBox extends React.Component<FieldViewProps> {
             let obj: Object = { parentDivs: [], spans: [] };
             //@ts-ignore
             if (range.commonAncestorContainer.className === 'react-pdf__Page__textContent') { //multiline highlighting case
-                obj = this.highlightNodes(range.commonAncestorContainer.childNodes)
+                obj = this.highlightNodes(range.commonAncestorContainer.childNodes);
             } else { //single line highlighting case
-                let parentDiv = range.commonAncestorContainer.parentElement
+                let parentDiv = range.commonAncestorContainer.parentElement;
                 if (parentDiv) {
                     if (parentDiv.className === 'react-pdf__Page__textContent') { //when highlight is overwritten
-                        obj = this.highlightNodes(parentDiv.childNodes)
+                        obj = this.highlightNodes(parentDiv.childNodes);
                     } else {
                         parentDiv.childNodes.forEach((child) => {
                             if (child.nodeName === 'SPAN') {
                                 //@ts-ignore
-                                obj.parentDivs.push(parentDiv)
+                                obj.parentDivs.push(parentDiv);
                                 //@ts-ignore
-                                child.id = "highlighted"
+                                child.id = "highlighted";
                                 //@ts-ignore
-                                obj.spans.push(child)
+                                obj.spans.push(child);
                                 child.addEventListener("mouseover", this.onEnter); //adds mouseover annotation handler
                             }
-                        })
+                        });
                     }
                 }
             }
@@ -196,21 +196,21 @@ export class PDFBox extends React.Component<FieldViewProps> {
     }
 
     highlightNodes = (nodes: NodeListOf<ChildNode>) => {
-        let temp = { parentDivs: [], spans: [] }
+        let temp = { parentDivs: [], spans: [] };
         nodes.forEach((div) => {
             div.childNodes.forEach((child) => {
                 if (child.nodeName === 'SPAN') {
                     //@ts-ignore
-                    temp.parentDivs.push(div)
+                    temp.parentDivs.push(div);
                     //@ts-ignore
-                    child.id = "highlighted"
+                    child.id = "highlighted";
                     //@ts-ignore
-                    temp.spans.push(child)
+                    temp.spans.push(child);
                     child.addEventListener("mouseover", this.onEnter); //adds mouseover annotation handler
                 }
-            })
+            });
 
-        })
+        });
         return temp;
     }
 
@@ -228,8 +228,8 @@ export class PDFBox extends React.Component<FieldViewProps> {
                         index = this._pageInfo.divs.indexOf(obj);
                     }
                 }
-            })
-        })
+            });
+        });
 
         if (this._pageInfo.anno.length >= index + 1) {
             if (this._currAnno.length === 0) {
@@ -239,13 +239,13 @@ export class PDFBox extends React.Component<FieldViewProps> {
             if (this._currAnno.length === 0) { //if there are no current annotation
                 let div = span.offsetParent;
                 //@ts-ignore
-                let divX = div.style.left
+                let divX = div.style.left;
                 //@ts-ignore
-                let divY = div.style.top
+                let divY = div.style.top;
                 //slicing "px" from the end
                 divX = divX.slice(0, divX.length - 2); //gets X of the DIV element (parent of Span)
                 divY = divY.slice(0, divY.length - 2); //gets Y of the DIV element (parent of Span)
-                let annotation = <Annotation key={Utils.GenerateGuid()} Span={span} X={divX} Y={divY - 300} Highlights={this._pageInfo.divs} Annotations={this._pageInfo.anno} CurrAnno={this._currAnno} />
+                let annotation = <Annotation key={Utils.GenerateGuid()} Span={span} X={divX} Y={divY - 300} Highlights={this._pageInfo.divs} Annotations={this._pageInfo.anno} CurrAnno={this._currAnno} />;
                 this._pageInfo.anno.push(annotation);
                 this._currAnno.push(annotation);
             }
@@ -263,7 +263,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
                     this.makeEditableAndHighlight(color);
                 }
             } catch (ex) {
-                this.makeEditableAndHighlight(color)
+                this.makeEditableAndHighlight(color);
             }
         }
     }
@@ -305,7 +305,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
             }
 
             if (this._mainDiv.current) {
-                let sticky = <Sticky key={Utils.GenerateGuid()} Height={height} Width={width} X={this.initX} Y={this.initY} />
+                let sticky = <Sticky key={Utils.GenerateGuid()} Height={height} Width={width} X={this.initX} Y={this.initY} />;
                 this._pageInfo.area.push(sticky);
             }
             this._toolOn = false;
@@ -404,19 +404,19 @@ export class PDFBox extends React.Component<FieldViewProps> {
 
                         if (e instanceof HTMLCanvasElement) {
                             this._pdfCanvas = e;
-                            this._pdfContext = e.getContext("2d")
+                            this._pdfContext = e.getContext("2d");
 
                         }
 
-                    })
+                    });
                 }
-            })
+            });
         }
 
         // bcz: the number of pages should really be set when the document is imported.
         this.props.Document.SetNumber(KeyStore.NumPages, page._transport.numPages);
         if (this._perPageInfo.length === 0) { //Makes sure it only runs once
-            this._perPageInfo = [...Array(page._transport.numPages)]
+            this._perPageInfo = [...Array(page._transport.numPages)];
         }
         this._loaded = true;
     }
diff --git a/src/client/views/nodes/Sticky.tsx b/src/client/views/nodes/Sticky.tsx
index 4a4d69e90..11719831b 100644
--- a/src/client/views/nodes/Sticky.tsx
+++ b/src/client/views/nodes/Sticky.tsx
@@ -39,7 +39,7 @@ export class Sticky extends React.Component<IProps> {
       document.addEventListener("pointermove", this.drawMove);
       document.addEventListener("pointerup", this.drawUp);
     }
-  };
+  }
 
   //when user drags
   drawMove = (e: PointerEvent): void => {
@@ -49,7 +49,7 @@ export class Sticky extends React.Component<IProps> {
     //connects the point
     this.ctx.lineTo(x, y);
     this.ctx.stroke();
-  };
+  }
 
   /**
    * when user lifts the mouse, the drawing ends
@@ -58,7 +58,7 @@ export class Sticky extends React.Component<IProps> {
     this.ctx.closePath();
     console.log(this.ctx);
     document.removeEventListener("pointermove", this.drawMove);
-  };
+  }
 
   render() {
     return (
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index a08b320e8..314af64c9 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1,4 +1,4 @@
-import React = require("react")
+import React = require("react");
 import { observer } from "mobx-react";
 import { FieldWaiting, Opt } from '../../../fields/Field';
 import { VideoField } from '../../../fields/VideoField';
@@ -13,8 +13,8 @@ import { number } from "prop-types";
 export class VideoBox extends React.Component<FieldViewProps> {
 
     private _reactionDisposer: Opt<IReactionDisposer>;
-    private _videoRef = React.createRef<HTMLVideoElement>()
-    public static LayoutString() { return FieldView.LayoutString(VideoBox) }
+    private _videoRef = React.createRef<HTMLVideoElement>();
+    public static LayoutString() { return FieldView.LayoutString(VideoBox); }
 
     constructor(props: FieldViewProps) {
         super(props);
@@ -58,7 +58,7 @@ export class VideoBox extends React.Component<FieldViewProps> {
     render() {
         let field = this.props.Document.GetT(this.props.fieldKey, VideoField);
         if (!field || field === FieldWaiting) {
-            return <div>Loading</div>
+            return <div>Loading</div>;
         }
         let path = field.Data.href;
         trace();
@@ -73,6 +73,6 @@ export class VideoBox extends React.Component<FieldViewProps> {
                     </div>
                 }
             </Measure>
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index c1d389001..90ce72c41 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,9 +1,9 @@
 import "./WebBox.scss";
-import React = require("react")
+import React = require("react");
 import { WebField } from '../../../fields/WebField';
 import { FieldViewProps, FieldView } from './FieldView';
 import { FieldWaiting } from '../../../fields/Field';
-import { observer } from "mobx-react"
+import { observer } from "mobx-react";
 import { computed } from 'mobx';
 import { KeyStore } from '../../../fields/KeyStore';
 
@@ -33,6 +33,6 @@ export class WebBox extends React.Component<FieldViewProps> {
         return (
             <div className="webBox-cont"  >
                 {content}
-            </div>)
+            </div>);
     }
 }
\ No newline at end of file
diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx
index c8de33f41..11f2b0c4e 100644
--- a/src/debug/Test.tsx
+++ b/src/debug/Test.tsx
@@ -1,10 +1,10 @@
 import * as React from 'react';
 import * as ReactDOM from 'react-dom';
-import JsxParser from 'react-jsx-parser'
+import JsxParser from 'react-jsx-parser';
 
 class Hello extends React.Component<{ firstName: string, lastName: string }> {
     render() {
-        return <div>Hello {this.props.firstName} {this.props.lastName}</div>
+        return <div>Hello {this.props.firstName} {this.props.lastName}</div>;
     }
 }
 
@@ -16,8 +16,8 @@ class Test extends React.Component {
                 firstName: "First",
                 lastName: "Last"
             }
-        }
-        return <JsxParser jsx={jsx} bindings={bindings} components={{ Hello }}></JsxParser>
+        };
+        return <JsxParser jsx={jsx} bindings={bindings} components={{ Hello }}></JsxParser>;
     }
 }
 
diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx
index f13cccd24..857da1ebb 100644
--- a/src/debug/Viewer.tsx
+++ b/src/debug/Viewer.tsx
@@ -40,22 +40,22 @@ class ListViewer extends React.Component<{ field: ListField<Field> }>{
                 <div>
                     {this.props.field.Data.map(field => <DebugViewer fieldId={field.Id} key={field.Id} />)}
                 </div>
-            )
+            );
         } else {
-            content = <>[...] ({this.props.field.Id})</>
+            content = <>[...] ({this.props.field.Id})</>;
         }
         return (
             <div>
                 <button onClick={action(() => this.expanded = !this.expanded)}>Toggle</button>
                 {content}
             </div >
-        )
+        );
     }
 }
 
 @observer
 class DocumentViewer extends React.Component<{ field: Document }> {
-    private keyMap: ObservableMap<string, Key> = new ObservableMap
+    private keyMap: ObservableMap<string, Key> = new ObservableMap;
 
     private disposer?: Lambda;
 
@@ -67,12 +67,12 @@ class DocumentViewer extends React.Component<{ field: Document }> {
                         if (field && field instanceof Key) {
                             this.keyMap.set(id, field);
                         }
-                    })
+                    });
                 }
             });
-        }
-        this.disposer = this.props.field._proxies.observe(f)
-        f()
+        };
+        this.disposer = this.props.field._proxies.observe(f);
+        f();
     }
 
     componentWillUnmount() {
@@ -89,8 +89,8 @@ class DocumentViewer extends React.Component<{ field: Document }> {
                     <b>({key ? key.Name : kv[0]}): </b>
                     <DebugViewer fieldId={kv[1]}></DebugViewer>
                 </div>
-            )
-        })
+            );
+        });
         return (
             <div>
                 Document ({this.props.field.Id})
@@ -98,7 +98,7 @@ class DocumentViewer extends React.Component<{ field: Document }> {
                     {fields}
                 </div>
             </div>
-        )
+        );
     }
 }
 
@@ -111,15 +111,15 @@ class DebugViewer extends React.Component<{ fieldId: string }> {
     private error?: string;
 
     constructor(props: { fieldId: string }) {
-        super(props)
-        this.update()
+        super(props);
+        this.update();
     }
 
     update() {
         Server.GetField(this.props.fieldId, action((field: Opt<Field>) => {
             this.field = field;
             if (!field) {
-                this.error = `Field with id ${this.props.fieldId} not found`
+                this.error = `Field with id ${this.props.fieldId} not found`;
             }
         }));
 
@@ -130,20 +130,20 @@ class DebugViewer extends React.Component<{ fieldId: string }> {
         if (this.field) {
             // content = this.field.ToJson();
             if (this.field instanceof ListField) {
-                content = (<ListViewer field={this.field} />)
+                content = (<ListViewer field={this.field} />);
             } else if (this.field instanceof Document) {
-                content = (<DocumentViewer field={this.field} />)
+                content = (<DocumentViewer field={this.field} />);
             } else if (this.field instanceof BasicField) {
-                content = (<FieldViewer field={this.field} />)
+                content = (<FieldViewer field={this.field} />);
             } else if (this.field instanceof Key) {
-                content = (<KeyViewer field={this.field} />)
+                content = (<KeyViewer field={this.field} />);
             } else {
-                content = (<span>Unrecognized field type</span>)
+                content = (<span>Unrecognized field type</span>);
             }
         } else if (this.error) {
-            content = <span>Field <b>{this.props.fieldId}</b> not found <button onClick={() => this.update()}>Refresh</button></span>
+            content = <span>Field <b>{this.props.fieldId}</b> not found <button onClick={() => this.update()}>Refresh</button></span>;
         } else {
-            content = <span>Field loading: {this.props.fieldId}</span>
+            content = <span>Field loading: {this.props.fieldId}</span>;
         }
         return content;
     }
@@ -165,8 +165,8 @@ class Viewer extends React.Component {
     @action
     onKeyPress = (e: React.KeyboardEvent<HTMLDivElement>) => {
         if (e.key === "Enter") {
-            this.ids.push(this.idToAdd)
-            this.idToAdd = ""
+            this.ids.push(this.idToAdd);
+            this.idToAdd = "";
         }
     }
 
@@ -180,7 +180,7 @@ class Viewer extends React.Component {
                     {this.ids.map(id => <DebugViewer fieldId={id} key={id}></DebugViewer>)}
                 </div>
             </>
-        )
+        );
     }
 }
 
diff --git a/src/fields/AudioField.ts b/src/fields/AudioField.ts
index 252a5b74e..996d2556d 100644
--- a/src/fields/AudioField.ts
+++ b/src/fields/AudioField.ts
@@ -25,7 +25,7 @@ export class AudioField extends BasicField<URL> {
             type: Types.Audio,
             data: this.Data.href,
             _id: this.Id
-        }
+        };
     }
 
 }
\ No newline at end of file
diff --git a/src/fields/BasicField.ts b/src/fields/BasicField.ts
index 3083a1937..17b1fc4e8 100644
--- a/src/fields/BasicField.ts
+++ b/src/fields/BasicField.ts
@@ -1,4 +1,4 @@
-import { Field, FieldId } from "./Field"
+import { Field, FieldId } from "./Field";
 import { observable, computed, action } from "mobx";
 import { Server } from "../client/Server";
 import { UndoManager } from "../client/util/UndoManager";
@@ -9,7 +9,7 @@ export abstract class BasicField<T> extends Field {
 
         this.data = data;
         if (save) {
-            Server.UpdateField(this)
+            Server.UpdateField(this);
         }
     }
 
@@ -36,7 +36,7 @@ export abstract class BasicField<T> extends Field {
         UndoManager.AddEvent({
             undo: () => this.Data = oldValue,
             redo: () => this.Data = value
-        })
+        });
         Server.UpdateField(this);
     }
 
diff --git a/src/fields/Document.ts b/src/fields/Document.ts
index 4fa478f32..4584660fb 100644
--- a/src/fields/Document.ts
+++ b/src/fields/Document.ts
@@ -37,21 +37,24 @@ export class Document extends Field {
         }
     }
 
-    public Width = () => this.GetNumber(KeyStore.Width, 0)
+    public Width = () => this.GetNumber(KeyStore.Width, 0);
     public Height = () => this.GetNumber(KeyStore.Height, this.GetNumber(KeyStore.NativeWidth, 0) ? (this.GetNumber(KeyStore.NativeHeight, 0) / this.GetNumber(KeyStore.NativeWidth, 0)) * this.GetNumber(KeyStore.Width, 0) : 0);
-    public Scale = () => this.GetNumber(KeyStore.Scale, 1)
+    public Scale = () => this.GetNumber(KeyStore.Scale, 1);
 
     @computed
     public get Title(): string {
         let title = this.Get(KeyStore.Title, true);
-        if (title)
-            if (title !== FieldWaiting && title instanceof TextField)
+        if (title) {
+            if (title !== FieldWaiting && title instanceof TextField) {
                 return title.Data;
+            }
             else return "-waiting-";
+        }
         let parTitle = this.GetT(KeyStore.Title, TextField);
-        if (parTitle)
+        if (parTitle) {
             if (parTitle !== FieldWaiting) return parTitle.Data + ".alias";
             else return "-waiting-.alias";
+        }
         return "-untitled-";
     }
 
@@ -398,11 +401,13 @@ export class Document extends Field {
                         }
                     }
                     else
-                        if (field instanceof Document)  // ... TODO bcz: should we copy documents or reference them
-                            copy.Set(key!, field)
-                        else if (field)
-                            copy.Set(key!, field.Copy())
-                })
+                        if (field instanceof Document) {  // ... TODO bcz: should we copy documents or reference them
+                            copy.Set(key!, field);
+                        }
+                        else if (field) {
+                            copy.Set(key!, field.Copy());
+ }
+                });
             }
         });
         return copy;
diff --git a/src/fields/DocumentReference.ts b/src/fields/DocumentReference.ts
index 9d3c209b4..6c0c1ef82 100644
--- a/src/fields/DocumentReference.ts
+++ b/src/fields/DocumentReference.ts
@@ -52,6 +52,6 @@ export class DocumentReference extends Field {
             type: Types.DocumentReference,
             data: this.document.Id,
             _id: this.Id
-        }
+        };
     }
 }
\ No newline at end of file
diff --git a/src/fields/Field.ts b/src/fields/Field.ts
index 0d0e56f77..d9db23b9e 100644
--- a/src/fields/Field.ts
+++ b/src/fields/Field.ts
@@ -65,5 +65,5 @@ export abstract class Field {
 
     abstract Copy(): Field;
 
-    abstract ToJson(): { _id: string, type: Types, data: any }
+    abstract ToJson(): { _id: string, type: Types, data: any };
 }
\ No newline at end of file
diff --git a/src/fields/HtmlField.ts b/src/fields/HtmlField.ts
index 7cbdf7e58..65665cf7a 100644
--- a/src/fields/HtmlField.ts
+++ b/src/fields/HtmlField.ts
@@ -20,6 +20,6 @@ export class HtmlField extends BasicField<string> {
             type: Types.Html,
             data: this.Data,
             _id: this.Id,
-        }
+        };
     }
 }
\ No newline at end of file
diff --git a/src/fields/ImageField.ts b/src/fields/ImageField.ts
index ef616b2ad..dd843026f 100644
--- a/src/fields/ImageField.ts
+++ b/src/fields/ImageField.ts
@@ -24,6 +24,6 @@ export class ImageField extends BasicField<URL> {
             type: Types.Image,
             data: this.Data.href,
             _id: this.Id
-        }
+        };
     }
 }
\ No newline at end of file
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index 2a4ed18e7..ab706ee30 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -36,7 +36,7 @@ export class InkField extends BasicField<StrokeMap> {
             type: Types.Ink,
             data: this.Data,
             _id: this.Id,
-        }
+        };
     }
 
     UpdateFromServer(data: any) {
diff --git a/src/fields/Key.ts b/src/fields/Key.ts
index 00d78d516..c7f806b88 100644
--- a/src/fields/Key.ts
+++ b/src/fields/Key.ts
@@ -1,4 +1,4 @@
-import { Field, FieldId } from "./Field"
+import { Field, FieldId } from "./Field";
 import { Utils } from "../Utils";
 import { observable } from "mobx";
 import { Types } from "../server/Message";
@@ -16,7 +16,7 @@ export class Key extends Field {
 
         this.name = name;
         if (save) {
-            Server.UpdateField(this)
+            Server.UpdateField(this);
         }
     }
 
@@ -45,6 +45,6 @@ export class Key extends Field {
             type: Types.Key,
             data: this.name,
             _id: this.Id
-        }
+        };
     }
 }
\ No newline at end of file
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index e24e9c8ec..425408273 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -56,8 +56,9 @@ export namespace KeyStore {
     ];
     export function KeyLookup(keyid: string) {
         for (const key of KeyList) {
-            if (key.Id === keyid)
+            if (key.Id === keyid) {
                 return key;
+            }
         }
         return undefined;
     }
diff --git a/src/fields/ListField.ts b/src/fields/ListField.ts
index 815a3df73..b6eab5f86 100644
--- a/src/fields/ListField.ts
+++ b/src/fields/ListField.ts
@@ -7,7 +7,7 @@ import { Field, FieldId } from "./Field";
 import { FieldMap } from "../client/SocketStub";
 
 export class ListField<T extends Field> extends BasicField<T[]> {
-    private _proxies: string[] = []
+    private _proxies: string[] = [];
     constructor(data: T[] = [], id?: FieldId, save: boolean = true) {
         super(data, save, id);
         this.updateProxies();
@@ -22,23 +22,24 @@ export class ListField<T extends Field> extends BasicField<T[]> {
     private observeDisposer: Lambda | undefined;
     private observeList(): void {
         if (this.observeDisposer) {
-            this.observeDisposer()
+            this.observeDisposer();
         }
         this.observeDisposer = observe(this.Data as IObservableArray<T>, (change: IArrayChange<T> | IArraySplice<T>) => {
-            this.updateProxies()
+            this.updateProxies();
             if (change.type === "splice") {
                 UndoManager.AddEvent({
                     undo: () => this.Data.splice(change.index, change.addedCount, ...change.removed),
                     redo: () => this.Data.splice(change.index, change.removedCount, ...change.added)
-                })
+                });
             } else {
                 UndoManager.AddEvent({
                     undo: () => this.Data[change.index] = change.oldValue,
                     redo: () => this.Data[change.index] = change.newValue
-                })
+                });
             }
-            if (!this._processingServerUpdate)
+            if (!this._processingServerUpdate) {
                 Server.UpdateField(this);
+            }
         });
     }
 
@@ -78,19 +79,23 @@ export class ListField<T extends Field> extends BasicField<T[]> {
                 var proxies = this._proxies.map(p => p);
                 var added = this.data.length < this._proxies.length;
                 var deleted = this.data.length > this._proxies.length;
-                for (let i = 0; i < dataids.length && added; i++)
+                for (let i = 0; i < dataids.length && added; i++) {
                     added = proxies.indexOf(dataids[i]) !== -1;
-                for (let i = 0; i < this._proxies.length && deleted; i++)
+                }
+                for (let i = 0; i < this._proxies.length && deleted; i++) {
                     deleted = dataids.indexOf(proxies[i]) !== -1;
+                }
 
                 this._processingServerUpdate = true;
                 for (let i = 0; i < proxies.length && added; i++) {
-                    if (dataids.indexOf(proxies[i]) === -1)
+                    if (dataids.indexOf(proxies[i]) === -1) {
                         this.Data.splice(i, 0, fields[proxies[i]] as T);
+                    }
                 }
                 for (let i = dataids.length - 1; i >= 0 && deleted; i--) {
-                    if (proxies.indexOf(dataids[i]) === -1)
+                    if (proxies.indexOf(dataids[i]) === -1) {
                         this.Data.splice(i, 1);
+                    }
                 }
                 if (!added && !deleted) {// otherwise, just rebuild the whole list
                     this.setData(proxies.map(id => fields[id] as T));
@@ -98,7 +103,7 @@ export class ListField<T extends Field> extends BasicField<T[]> {
                 this._processingServerUpdate = false;
             }
             callback(this);
-        }))
+        }));
     }
 
     ToScriptString(): string {
@@ -114,12 +119,12 @@ export class ListField<T extends Field> extends BasicField<T[]> {
             type: Types.List,
             data: this._proxies || [],
             _id: this.Id
-        }
+        };
     }
 
     static FromJson(id: string, ids: string[]): ListField<Field> {
         let list = new ListField([], id, false);
         list._proxies = ids;
-        return list
+        return list;
     }
 }
\ No newline at end of file
diff --git a/src/fields/NumberField.ts b/src/fields/NumberField.ts
index e0c8648de..45b920e31 100644
--- a/src/fields/NumberField.ts
+++ b/src/fields/NumberField.ts
@@ -1,4 +1,4 @@
-import { BasicField } from "./BasicField"
+import { BasicField } from "./BasicField";
 import { Types } from "../server/Message";
 import { FieldId } from "./Field";
 
@@ -20,6 +20,6 @@ export class NumberField extends BasicField<number> {
             _id: this.Id,
             type: Types.Number,
             data: this.Data
-        }
+        };
     }
 }
\ No newline at end of file
diff --git a/src/fields/PDFField.ts b/src/fields/PDFField.ts
index 436c1cf2b..65e179894 100644
--- a/src/fields/PDFField.ts
+++ b/src/fields/PDFField.ts
@@ -1,6 +1,6 @@
 import { BasicField } from "./BasicField";
 import { Field, FieldId } from "./Field";
-import { observable } from "mobx"
+import { observable } from "mobx";
 import { Types } from "../server/Message";
 
 
@@ -27,7 +27,7 @@ export class PDFField extends BasicField<URL> {
             type: Types.PDF,
             data: this.Data.href,
             _id: this.Id
-        }
+        };
     }
 
     @observable
diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts
index 5efb43314..6f7b3074a 100644
--- a/src/fields/RichTextField.ts
+++ b/src/fields/RichTextField.ts
@@ -20,7 +20,7 @@ export class RichTextField extends BasicField<string> {
             type: Types.RichText,
             data: this.Data,
             _id: this.Id
-        }
+        };
     }
 
 }
\ No newline at end of file
diff --git a/src/fields/TextField.ts b/src/fields/TextField.ts
index 71d8ea310..69d26f42f 100644
--- a/src/fields/TextField.ts
+++ b/src/fields/TextField.ts
@@ -1,4 +1,4 @@
-import { BasicField } from "./BasicField"
+import { BasicField } from "./BasicField";
 import { FieldId } from "./Field";
 import { Types } from "../server/Message";
 
@@ -20,6 +20,6 @@ export class TextField extends BasicField<string> {
             type: Types.Text,
             data: this.Data,
             _id: this.Id
-        }
+        };
     }
 }
\ No newline at end of file
diff --git a/src/fields/TupleField.ts b/src/fields/TupleField.ts
index e2162c751..ad0f6f350 100644
--- a/src/fields/TupleField.ts
+++ b/src/fields/TupleField.ts
@@ -21,7 +21,7 @@ export class TupleField<T, U> extends BasicField<[T, U]> {
                 UndoManager.AddEvent({
                     undo: () => this.Data[change.index] = change.oldValue,
                     redo: () => this.Data[change.index] = change.newValue
-                })
+                });
                 Server.UpdateField(this);
             } else {
                 throw new Error("Why are you messing with the length of a tuple, huh?");
@@ -31,7 +31,7 @@ export class TupleField<T, U> extends BasicField<[T, U]> {
 
     protected setData(value: [T, U]) {
         if (this.observeDisposer) {
-            this.observeDisposer()
+            this.observeDisposer();
         }
         this.data = observable(value) as (T | U)[] as [T, U];
         this.observeTuple();
@@ -54,6 +54,6 @@ export class TupleField<T, U> extends BasicField<[T, U]> {
             type: Types.Tuple,
             data: this.Data,
             _id: this.Id
-        }
+        };
     }
 }
\ No newline at end of file
diff --git a/src/fields/VideoField.ts b/src/fields/VideoField.ts
index 992cc1641..d7cd7e968 100644
--- a/src/fields/VideoField.ts
+++ b/src/fields/VideoField.ts
@@ -24,7 +24,7 @@ export class VideoField extends BasicField<URL> {
             type: Types.Video,
             data: this.Data.href,
             _id: this.Id
-        }
+        };
     }
 
 }
\ No newline at end of file
diff --git a/src/fields/WebField.ts b/src/fields/WebField.ts
index 0cbcc6d33..6023e9e6b 100644
--- a/src/fields/WebField.ts
+++ b/src/fields/WebField.ts
@@ -24,7 +24,7 @@ export class WebField extends BasicField<URL> {
             type: Types.Web,
             data: this.Data.href,
             _id: this.Id
-        }
+        };
     }
 
 }
\ No newline at end of file
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
index c3684a0eb..ec89a1194 100644
--- a/src/mobile/ImageUpload.tsx
+++ b/src/mobile/ImageUpload.tsx
@@ -22,7 +22,7 @@ import { Opt } from '../fields/Field';
 // }
 
 const onFileLoad = async (file: any) => {
-    let imgPrev = document.getElementById("img_preview")
+    let imgPrev = document.getElementById("img_preview");
     if (imgPrev) {
         let files: File[] = file.target.files;
         if (files.length !== 0) {
@@ -30,15 +30,15 @@ const onFileLoad = async (file: any) => {
             let formData = new FormData();
             formData.append("file", files[0]);
 
-            const upload = window.location.origin + "/upload"
+            const upload = window.location.origin + "/upload";
             const res = await fetch(upload, {
                 method: 'POST',
                 body: formData
             });
             const json = await res.json();
             json.map(async (file: any) => {
-                let path = window.location.origin + file
-                var doc: Document = Documents.ImageDocument(path, { nativeWidth: 200, width: 200 })
+                let path = window.location.origin + file;
+                var doc: Document = Documents.ImageDocument(path, { nativeWidth: 200, width: 200 });
 
                 const res = await rp.get(ServerUtils.prepend(RouteStore.getUserDocumentId));
                 if (!res) {
@@ -47,12 +47,12 @@ const onFileLoad = async (file: any) => {
                 const field = await Server.GetField(res);
                 let pending: Opt<Document>;
                 if (field instanceof Document) {
-                    pending = await field.GetTAsync(KeyStore.OptionalRightCollection, Document)
+                    pending = await field.GetTAsync(KeyStore.OptionalRightCollection, Document);
                 }
                 if (pending) {
                     pending.GetOrCreateAsync(KeyStore.Data, ListField, list => {
                         list.Data.push(doc);
-                    })
+                    });
                 }
             });
 
@@ -61,7 +61,7 @@ const onFileLoad = async (file: any) => {
             //imgPrev.setAttribute("src", window.location.origin + files[0].name)
         }
     }
-}
+};
 
 ReactDOM.render((
     <div className="imgupload_cont">
diff --git a/src/server/Client.ts b/src/server/Client.ts
index 6b8841658..02402a5a0 100644
--- a/src/server/Client.ts
+++ b/src/server/Client.ts
@@ -2,14 +2,14 @@ import { computed } from "mobx";
 
 export class Client {
     constructor(guid: string) {
-        this.guid = guid
+        this.guid = guid;
     }
 
     private guid: string;
 
     @computed
     public get GUID(): string {
-        return this.guid
+        return this.guid;
     }
 
 }
\ No newline at end of file
diff --git a/src/server/authentication/config/passport.ts b/src/server/authentication/config/passport.ts
index b6fe15655..d42741410 100644
--- a/src/server/authentication/config/passport.ts
+++ b/src/server/authentication/config/passport.ts
@@ -1,4 +1,4 @@
-import * as passport from 'passport'
+import * as passport from 'passport';
 import * as passportLocal from 'passport-local';
 import * as mongodb from 'mongodb';
 import * as _ from "lodash";
@@ -22,7 +22,7 @@ passport.deserializeUser<any, any>((id, done) => {
 passport.use(new LocalStrategy({ usernameField: 'email', passReqToCallback: true }, (req, email, password, done) => {
     User.findOne({ email: email.toLowerCase() }, (error: any, user: any) => {
         if (error) return done(error);
-        if (!user) return done(undefined, false, { message: "Invalid email or password" }) // invalid email
+        if (!user) return done(undefined, false, { message: "Invalid email or password" }); // invalid email
         user.comparePassword(password, (error: Error, isMatch: boolean) => {
             if (error) return done(error);
             if (!isMatch) return done(undefined, false, { message: "Invalid email or password" }); // invalid password
@@ -37,7 +37,7 @@ export let isAuthenticated = (req: Request, res: Response, next: NextFunction) =
         return next();
     }
     return res.redirect(RouteStore.login);
-}
+};
 
 export let isAuthorized = (req: Request, res: Response, next: NextFunction) => {
     const provider = req.path.split("/").slice(-1)[0];
diff --git a/src/server/authentication/controllers/WorkspacesMenu.tsx b/src/server/authentication/controllers/WorkspacesMenu.tsx
index 835432c8e..b08c1aebe 100644
--- a/src/server/authentication/controllers/WorkspacesMenu.tsx
+++ b/src/server/authentication/controllers/WorkspacesMenu.tsx
@@ -1,7 +1,7 @@
 import * as React from 'react';
 import { observable, action, configure, reaction, computed, ObservableMap, runInAction } from 'mobx';
 import { observer } from "mobx-react";
-import './WorkspacesMenu.css'
+import './WorkspacesMenu.css';
 import { Document } from '../../../fields/Document';
 import { EditableView } from '../../../client/views/EditableView';
 import { KeyStore } from '../../../fields/KeyStore';
diff --git a/src/server/authentication/controllers/user_controller.ts b/src/server/authentication/controllers/user_controller.ts
index 2bbb334b5..1dacdf3fa 100644
--- a/src/server/authentication/controllers/user_controller.ts
+++ b/src/server/authentication/controllers/user_controller.ts
@@ -109,12 +109,12 @@ export let postLogin = (req: Request, res: Response, next: NextFunction) => {
     }
 
     passport.authenticate("local", (err: Error, user: DashUserModel, info: IVerifyOptions) => {
-        if (err) { next(err); return }
+        if (err) { next(err); return; }
         if (!user) {
             return res.redirect(RouteStore.signup);
         }
         req.logIn(user, (err) => {
-            if (err) { next(err); return }
+            if (err) { next(err); return; }
             res.redirect(RouteStore.home);
         });
     })(req, res, next);
@@ -132,14 +132,14 @@ export let getLogout = (req: Request, res: Response) => {
         sess.destroy((err) => { if (err) { console.log(err); } });
     }
     res.redirect(RouteStore.login);
-}
+};
 
 export let getForgot = function (req: Request, res: Response) {
     res.render("forgot.pug", {
         title: "Recover Password",
         user: req.user,
     });
-}
+};
 
 export let postForgot = function (req: Request, res: Response, next: NextFunction) {
     const email = req.body.email;
@@ -152,14 +152,14 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio
                     return;
                 }
                 done(null, buffer.toString('hex'));
-            })
+            });
         },
         function (token: string, done: any) {
             User.findOne({ email }, function (err, user: DashUserModel) {
                 if (!user) {
                     // NO ACCOUNT WITH SUBMITTED EMAIL
                     res.redirect(RouteStore.forgot);
-                    return
+                    return;
                 }
                 user.passwordResetToken = token;
                 user.passwordResetExpires = new Date(Date.now() + 3600000); // 1 HOUR
@@ -193,8 +193,8 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio
     ], function (err) {
         if (err) return next(err);
         res.redirect(RouteStore.forgot);
-    })
-}
+    });
+};
 
 export let getReset = function (req: Request, res: Response) {
     User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err, user: DashUserModel) {
@@ -206,7 +206,7 @@ export let getReset = function (req: Request, res: Response) {
             user: req.user,
         });
     });
-}
+};
 
 export let postReset = function (req: Request, res: Response) {
     async.waterfall([
@@ -263,4 +263,4 @@ export let postReset = function (req: Request, res: Response) {
     ], function (err) {
         res.redirect(RouteStore.login);
     });
-}
\ No newline at end of file
+};
\ No newline at end of file
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 0ac85b446..13eddafbf 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -70,7 +70,7 @@ export class CurrentUserUtils {
         let doc = new Document(id);
 
         doc.Set(KeyStore.Workspaces, new ListField<Document>());
-        doc.Set(KeyStore.OptionalRightCollection, Documents.SchemaDocument([], { title: "Pending documents" }))
+        doc.Set(KeyStore.OptionalRightCollection, Documents.SchemaDocument([], { title: "Pending documents" }));
         return doc;
     }
 
@@ -81,7 +81,7 @@ export class CurrentUserUtils {
                 CurrentUserUtils.curr_id = obj.id as string;
                 CurrentUserUtils.curr_email = obj.email as string;
             } else {
-                throw new Error("There should be a user! Why does Dash think there isn't one?")
+                throw new Error("There should be a user! Why does Dash think there isn't one?");
             }
         });
         let userDocPromise = rp.get(ServerUtils.prepend(RouteStore.getUserDocumentId)).then(id => {
@@ -92,9 +92,9 @@ export class CurrentUserUtils {
                     } else {
                         this.user_document = this.createUserDocument(id);
                     }
-                })
+                });
             } else {
-                throw new Error("There should be a user id! Why does Dash think there isn't one?")
+                throw new Error("There should be a user id! Why does Dash think there isn't one?");
             }
         });
         return Promise.all([userPromise, userDocPromise]);
diff --git a/src/server/authentication/models/user_model.ts b/src/server/authentication/models/user_model.ts
index 81580aad5..1c6926517 100644
--- a/src/server/authentication/models/user_model.ts
+++ b/src/server/authentication/models/user_model.ts
@@ -2,7 +2,7 @@
 import * as bcrypt from "bcrypt-nodejs";
 //@ts-ignore
 import * as mongoose from "mongoose";
-var url = 'mongodb://localhost:27017/Dash'
+var url = 'mongodb://localhost:27017/Dash';
 
 mongoose.connect(url, { useNewUrlParser: true });
 
diff --git a/src/server/database.ts b/src/server/database.ts
index 415acc09a..0bc806253 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -1,15 +1,15 @@
 import * as mongodb from 'mongodb';
 
 export class Database {
-    public static Instance = new Database()
+    public static Instance = new Database();
     private MongoClient = mongodb.MongoClient;
     private url = 'mongodb://localhost:27017/Dash';
     private db?: mongodb.Db;
 
     constructor() {
         this.MongoClient.connect(this.url, (err, client) => {
-            this.db = client.db()
-        })
+            this.db = client.db();
+        });
     }
 
     private currentWrites: { [_id: string]: Promise<void> } = {};
@@ -30,19 +30,19 @@ export class Database {
                     //     console.log(JSON.stringify(res.result));
                     // }
                     if (this.currentWrites[id] === promise) {
-                        delete this.currentWrites[id]
+                        delete this.currentWrites[id];
                     }
                     if (resolve) {
                         resolve();
                     }
                     callback();
                 });
-            }
+            };
             if (prom) {
                 const newProm: Promise<void> = prom.then(() => run(newProm));
                 this.currentWrites[id] = newProm;
             } else {
-                const newProm: Promise<void> = new Promise<void>(res => run(newProm, res))
+                const newProm: Promise<void> = new Promise<void>(res => run(newProm, res));
                 this.currentWrites[id] = newProm;
             }
         }
@@ -61,7 +61,7 @@ export class Database {
                 let collection = this.db.collection(collectionName);
                 collection.deleteMany({}, res);
             }
-        })
+        });
     }
 
     public insert(kvpairs: any) {
@@ -70,7 +70,7 @@ export class Database {
             collection.insertOne(kvpairs, (err: any, res: any) => {
                 if (err) {
                     // console.log(err)
-                    return
+                    return;
                 }
             });
         }
@@ -81,30 +81,30 @@ export class Database {
         if (this.db) {
             let collection = this.db.collection('documents');
             collection.findOne({ _id: id }, (err: any, res: any) => {
-                result = res
+                result = res;
                 if (!result) {
-                    fn(undefined)
+                    fn(undefined);
                 }
-                fn(result)
-            })
-        };
+                fn(result);
+            });
+        }
     }
 
     public getDocuments(ids: string[], fn: (res: any) => void) {
         if (this.db) {
             let collection = this.db.collection('documents');
-            let cursor = collection.find({ _id: { "$in": ids } })
+            let cursor = collection.find({ _id: { "$in": ids } });
             cursor.toArray((err, docs) => {
                 if (err) {
                     console.log(err.message);
                     console.log(err.errmsg);
                 }
                 fn(docs);
-            })
-        };
+            });
+        }
     }
 
     public print() {
-        console.log("db says hi!")
+        console.log("db says hi!");
     }
 }
diff --git a/src/server/index.ts b/src/server/index.ts
index f60e6e293..b9c7448b4 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -1,10 +1,10 @@
-import * as express from 'express'
-const app = express()
-import * as webpack from 'webpack'
+import * as express from 'express';
+const app = express();
+import * as webpack from 'webpack';
 import * as wdm from 'webpack-dev-middleware';
 import * as whm from 'webpack-hot-middleware';
-import * as path from 'path'
-import * as formidable from 'formidable'
+import * as path from 'path';
+import * as formidable from 'formidable';
 import * as passport from 'passport';
 import { MessageStore, Transferable } from "./Message";
 import { Client } from './Client';
@@ -13,7 +13,7 @@ import { Utils } from '../Utils';
 import { ObservableMap } from 'mobx';
 import { FieldId, Field } from '../fields/Field';
 import { Database } from './database';
-import * as io from 'socket.io'
+import * as io from 'socket.io';
 import { getLogin, postLogin, getSignup, postSignup, getLogout, postReset, getForgot, postForgot, getReset } from './authentication/controllers/user_controller';
 const config = require('../../webpack.config');
 const compiler = webpack(config);
@@ -31,19 +31,19 @@ const MongoStore = require('connect-mongo')(session);
 const mongoose = require('mongoose');
 import { DashUserModel } from './authentication/models/user_model';
 import * as fs from 'fs';
-import * as request from 'request'
+import * as request from 'request';
 import { RouteStore } from './RouteStore';
-import { exec } from 'child_process'
+import { exec } from 'child_process';
 
 const download = (url: string, dest: fs.PathLike) => {
     request.get(url).pipe(fs.createWriteStream(dest));
-}
+};
 
 const mongoUrl = 'mongodb://localhost:27017/Dash';
-mongoose.connect(mongoUrl)
+mongoose.connect(mongoUrl);
 mongoose.connection.on('connected', function () {
     console.log("connected");
-})
+});
 
 // SESSION MANAGEMENT AND AUTHENTICATION MIDDLEWARE
 // ORDER OF IMPORTS MATTERS
@@ -73,7 +73,7 @@ app.use((req, res, next) => {
 
 app.get("/hello", (req, res) => {
     res.send("<p>Hello</p>");
-})
+});
 
 enum Method {
     GET,
@@ -98,7 +98,7 @@ function addSecureRoute(method: Method,
         const dashUser: DashUserModel = req.user;
         if (!dashUser) return onRejection(res);
         handler(dashUser, res, req);
-    }
+    };
     subscribers.forEach(route => {
         switch (method) {
             case Method.GET:
@@ -116,7 +116,7 @@ function addSecureRoute(method: Method,
 let FieldStore: ObservableMap<FieldId, Field> = new ObservableMap();
 
 app.use(express.static(__dirname + RouteStore.public));
-app.use(RouteStore.images, express.static(__dirname + RouteStore.public))
+app.use(RouteStore.images, express.static(__dirname + RouteStore.public));
 
 app.get("/pull", (req, res) => {
     exec('"C:\\Program Files\\Git\\git-bash.exe" -c "git pull"', (err, stdout, stderr) => {
@@ -125,7 +125,7 @@ app.get("/pull", (req, res) => {
             return;
         }
         res.redirect("/");
-    })
+    });
 });
 
 // GETTERS
@@ -178,13 +178,13 @@ addSecureRoute(
 addSecureRoute(
     Method.POST,
     (user, res, req) => {
-        let form = new formidable.IncomingForm()
-        form.uploadDir = __dirname + "/public/files/"
-        form.keepExtensions = true
+        let form = new formidable.IncomingForm();
+        form.uploadDir = __dirname + "/public/files/";
+        form.keepExtensions = true;
         // let path = req.body.path;
-        console.log("upload")
+        console.log("upload");
         form.parse(req, (err, fields, files) => {
-            console.log("parsing")
+            console.log("parsing");
             let names: any[] = [];
             for (const name in files) {
                 let file = files[name];
@@ -211,8 +211,8 @@ app.post(RouteStore.login, postLogin);
 app.get(RouteStore.logout, getLogout);
 
 // FORGOT PASSWORD EMAIL HANDLING
-app.get(RouteStore.forgot, getForgot)
-app.post(RouteStore.forgot, postForgot)
+app.get(RouteStore.forgot, getForgot);
+app.post(RouteStore.forgot, postForgot);
 
 // RESET PASSWORD EMAIL HANDLING
 app.get(RouteStore.reset, getReset);
@@ -232,32 +232,32 @@ app.get(RouteStore.deleteAll, (req, res) => {
 
 app.use(wdm(compiler, {
     publicPath: config.output.publicPath
-}))
+}));
 
-app.use(whm(compiler))
+app.use(whm(compiler));
 
 // start the Express server
 app.listen(port, () => {
     console.log(`server started at http://localhost:${port}`);
-})
+});
 
 const server = io();
 interface Map {
     [key: string]: Client;
 }
-let clients: Map = {}
+let clients: Map = {};
 
 server.on("connection", function (socket: Socket) {
-    console.log("a user has connected")
+    console.log("a user has connected");
 
-    Utils.Emit(socket, MessageStore.Foo, "handshooken")
+    Utils.Emit(socket, MessageStore.Foo, "handshooken");
 
-    Utils.AddServerHandler(socket, MessageStore.Bar, barReceived)
-    Utils.AddServerHandler(socket, MessageStore.SetField, (args) => setField(socket, args))
-    Utils.AddServerHandlerCallback(socket, MessageStore.GetField, getField)
-    Utils.AddServerHandlerCallback(socket, MessageStore.GetFields, getFields)
-    Utils.AddServerHandler(socket, MessageStore.DeleteAll, deleteFields)
-})
+    Utils.AddServerHandler(socket, MessageStore.Bar, barReceived);
+    Utils.AddServerHandler(socket, MessageStore.SetField, (args) => setField(socket, args));
+    Utils.AddServerHandlerCallback(socket, MessageStore.GetField, getField);
+    Utils.AddServerHandlerCallback(socket, MessageStore.GetFields, getFields);
+    Utils.AddServerHandler(socket, MessageStore.DeleteAll, deleteFields);
+});
 
 function deleteFields() {
     return Database.Instance.deleteAll();
@@ -276,12 +276,12 @@ function barReceived(guid: String) {
 function getField([id, callback]: [string, (result: any) => void]) {
     Database.Instance.getDocument(id, (result: any) => {
         if (result) {
-            callback(result)
+            callback(result);
         }
         else {
-            callback(undefined)
+            callback(undefined);
         }
-    })
+    });
 }
 
 function getFields([ids, callback]: [string[], (result: any) => void]) {
@@ -291,7 +291,7 @@ function getFields([ids, callback]: [string[], (result: any) => void]) {
 function setField(socket: Socket, newValue: Transferable) {
     Database.Instance.update(newValue._id, newValue, () => {
         socket.broadcast.emit(MessageStore.SetField.Message, newValue);
-    })
+    });
 }
 
 server.listen(serverPort);
diff --git a/test/test.ts b/test/test.ts
index db24cae5f..16cace026 100644
--- a/test/test.ts
+++ b/test/test.ts
@@ -1,6 +1,6 @@
 import { NumberField } from "../src/fields/NumberField";
 import { expect } from 'chai';
-import 'mocha'
+import 'mocha';
 import { Key } from "../src/fields/Key";
 import { Document } from "../src/fields/Document";
 import { autorun, reaction } from "mobx";
@@ -17,7 +17,7 @@ describe('Number Controller', () => {
     it('Should update', () => {
         const numController = new NumberField(15);
         let ran = false;
-        reaction(() => numController.Data, (data) => { ran = true; })
+        reaction(() => numController.Data, (data) => { ran = true; });
         expect(ran).to.equal(false);
         numController.Data = 5;
         expect(ran).to.equal(true);
@@ -42,7 +42,7 @@ describe("Document", () => {
         let key = new Key("Test");
         let key2 = new Key("Test2");
         let ran = false;
-        reaction(() => doc.Get(key), (field) => { ran = true });
+        reaction(() => doc.Get(key), (field) => { ran = true; });
         expect(ran).to.equal(false);
 
         doc.Set(key2, new NumberField(4));
diff --git a/tslint.json b/tslint.json
index 54876916e..aa4dee4e5 100644
--- a/tslint.json
+++ b/tslint.json
@@ -27,14 +27,14 @@
         "arrow-return-shorthand": true,
         // "object-literal-shorthand": true,
         // "object-literal-sort-keys": true,
-        // "semicolon": [
-        //     true,
-        //     "always"
-        // ],
-        // "curly": [
-        //     true,
-        //     "ignore-same-line"
-        // ],
+        "semicolon": [
+            true,
+            "always"
+        ],
+        "curly": [
+            true,
+            "ignore-same-line"
+        ],
         // "quotemark": [
         //     true,
         //     "double",
-- 
cgit v1.2.3-70-g09d2


From 89fd4327db1536990b4a4dc218819a1077f82445 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 9 Apr 2019 15:14:46 -0400
Subject: fixed layout of collection freeforms.

---
 .../collectionFreeForm/CollectionFreeFormView.scss |  8 ++-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 60 ++++++++++++----------
 2 files changed, 40 insertions(+), 28 deletions(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 79d520069..81f2146e4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -1,5 +1,11 @@
 @import "../../global_variables";
-
+.collectionfreeformview-measure {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+  }
 .collectionfreeformview {
   position: absolute;
   top: 0;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index e694bc7a7..1feb30db1 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, trace } from "mobx";
+import { action, computed, observable, trace, ObservableSet, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { Document } from "../../../../fields/Document";
 import { FieldWaiting } from "../../../../fields/Field";
@@ -24,6 +24,7 @@ import { DocumentManager } from "../../../util/DocumentManager";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { NumberField } from "../../../../fields/NumberField";
 import { Main } from "../../Main";
+import Measure from "react-measure";
 
 @observer
 export class CollectionFreeFormView extends CollectionSubView {
@@ -67,8 +68,8 @@ export class CollectionFreeFormView extends CollectionSubView {
     @observable public DownY: number = 0;
     @observable private _lastX: number = 0;
     @observable private _lastY: number = 0;
-
-    private outerElement?: HTMLDivElement;
+    @observable private _pwidth: number = 0;
+    @observable private _pheight: number = 0;
 
     @computed get panX(): number { return this.props.Document.GetNumber(KeyStore.PanX, 0); }
     @computed get panY(): number { return this.props.Document.GetNumber(KeyStore.PanY, 0); }
@@ -77,8 +78,8 @@ export class CollectionFreeFormView extends CollectionSubView {
     @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
     @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
     @computed get zoomScaling() { return this.props.Document.GetNumber(KeyStore.Scale, 1); }
-    @computed get centeringShiftX() { return !this.props.Document.GetNumber(KeyStore.NativeWidth, 0) && this.outerElement ? this.outerElement.clientWidth / 2 : 0; }  // shift so pan position is at center of window for non-overlay collections
-    @computed get centeringShiftY() { return !this.props.Document.GetNumber(KeyStore.NativeHeight, 0) && this.outerElement ? this.outerElement.clientHeight / 2 : 0; }// shift so pan position is at center of window for non-overlay collections
+    @computed get centeringShiftX() { return !this.props.Document.GetNumber(KeyStore.NativeWidth, 0) ? this._pwidth / 2 : 0; }  // shift so pan position is at center of window for non-overlay collections
+    @computed get centeringShiftY() { return !this.props.Document.GetNumber(KeyStore.NativeHeight, 0) ? this._pheight / 2 : 0; }// shift so pan position is at center of window for non-overlay collections
 
     @undoBatch
     @action
@@ -311,29 +312,34 @@ export class CollectionFreeFormView extends CollectionSubView {
         const pany: number = -this.props.Document.GetNumber(KeyStore.PanY, 0);
 
         return (
-            <div className={`collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`}
-                onPointerDown={this.onPointerDown} onPointerMove={(e) => super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY))}
-                onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} onWheel={this.onPointerWheel}
-                style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} ref={this.createDropTarget}>
-                <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
-                    addDocument={this.addDocument} removeDocument={this.props.removeDocument}
-                    getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
-                    <PreviewCursor container={this} addLiveTextDocument={this.addLiveTextBox}
-                        getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} >
-                        <div className="collectionfreeformview" ref={this._canvasRef}
-                            style={{ transform: `translate(${dx}px, ${dy}px) scale(${this.zoomScaling}, ${this.zoomScaling}) translate(${panx}px, ${pany}px)` }}>
-                            {this.backgroundView}
-                            <CollectionFreeFormLinksView {...this.props}>
-                                <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
-                                    {this.childViews}
-                                </InkingCanvas>
-                            </CollectionFreeFormLinksView>
-                            <CollectionFreeFormRemoteCursors {...this.props} />
+            <Measure onResize={(r: any) => runInAction(() => { this._pwidth = r.entry.width; this._pheight = r.entry.height })}>
+                {({ measureRef }) => (
+                    <div className={`collectionfreeformview-measure`} ref={measureRef}>
+                        <div className={`collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`}
+                            onPointerDown={this.onPointerDown} onPointerMove={(e) => super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY))}
+                            onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} onWheel={this.onPointerWheel}
+                            style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} ref={this.createDropTarget}>
+                            <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
+                                addDocument={this.addDocument} removeDocument={this.props.removeDocument}
+                                getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
+                                <PreviewCursor container={this} addLiveTextDocument={this.addLiveTextBox}
+                                    getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} >
+                                    <div className="collectionfreeformview" ref={this._canvasRef}
+                                        style={{ transform: `translate(${dx}px, ${dy}px) scale(${this.zoomScaling}, ${this.zoomScaling}) translate(${panx}px, ${pany}px)` }}>
+                                        {this.backgroundView}
+                                        <CollectionFreeFormLinksView {...this.props}>
+                                            <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
+                                                {this.childViews}
+                                            </InkingCanvas>
+                                        </CollectionFreeFormLinksView>
+                                        <CollectionFreeFormRemoteCursors {...this.props} />
+                                    </div>
+                                    {this.overlayView}
+                                </PreviewCursor>
+                            </MarqueeView>
                         </div>
-                        {this.overlayView}
-                    </PreviewCursor>
-                </MarqueeView>
-            </div>
+                    </div>)}
+            </Measure>
         );
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 54a89c59905b74ab71aa7366b3f1b7d653000547 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Wed, 10 Apr 2019 01:53:56 -0400
Subject: Fixed a couple server bugs

---
 src/client/Server.ts                                 | 20 ++++++++++----------
 src/client/views/Main.tsx                            |  3 ++-
 .../collectionFreeForm/CollectionFreeFormView.tsx    |  2 +-
 3 files changed, 13 insertions(+), 12 deletions(-)

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

diff --git a/src/client/Server.ts b/src/client/Server.ts
index 857101a33..b3cca0f4c 100644
--- a/src/client/Server.ts
+++ b/src/client/Server.ts
@@ -59,14 +59,14 @@ export class Server {
     public static GetFields(fieldIds: FieldId[]): Promise<{ [id: string]: Field }>;
     public static GetFields(fieldIds: FieldId[], callback: (fields: FieldMap) => any): void;
     public static GetFields(fieldIds: FieldId[], callback?: (fields: FieldMap) => any): Promise<FieldMap> | void {
-        let fn = (cb: (fields: FieldMap) => void) => {
+        let fn = action((cb: (fields: FieldMap) => void) => {
 
             let neededFieldIds: FieldId[] = [];
             let waitingFieldIds: FieldId[] = [];
-            let existingFields: { [id: string]: Field } = {};
+            let existingFields: FieldMap = {};
             for (let id of fieldIds) {
                 let field = this.ClientFieldsCached.get(id);
-                if (!field) {
+                if (field === undefined) {
                     neededFieldIds.push(id);
                     this.ClientFieldsCached.set(id, FieldWaiting);
                 } else if (field === FieldWaiting) {
@@ -79,7 +79,7 @@ export class Server {
                 for (let id of neededFieldIds) {
                     let field = fields[id];
                     if (field) {
-                        if (!(this.ClientFieldsCached.get(field.Id) instanceof Field)) {
+                        if (this.ClientFieldsCached.get(field.Id) === FieldWaiting) {
                             this.ClientFieldsCached.set(field.Id, field);
                         } else {
                             throw new Error("we shouldn't be trying to replace things that are already in the cache");
@@ -94,17 +94,17 @@ export class Server {
                 }
                 reaction(() => waitingFieldIds.map(id => this.ClientFieldsCached.get(id)),
                     (cachedFields, reaction) => {
-                        if (!cachedFields.some(field => !field)) {
+                        if (!cachedFields.some(field => field === FieldWaiting)) {
+                            const realFields = cachedFields as Opt<Field>[];
                             reaction.dispose();
-                            for (let field of cachedFields) {
-                                let realField = field as Field;
-                                existingFields[realField.Id] = realField;
-                            }
+                            waitingFieldIds.forEach((id, index) => {
+                                existingFields[id] = realFields[index];
+                            });
                             cb({ ...fields, ...existingFields });
                         }
                     }, { fireImmediately: true });
             }));
-        };
+        });
         if (callback) {
             fn(callback);
         } else {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 32798631d..6e7701d89 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -384,8 +384,9 @@ export class Main extends React.Component {
         let cat = Gateway.Instance.ClearCatalog();
         cat.then(async () => {
             this.AddToNorthstarCatalog(await Gateway.Instance.GetCatalog());
-            if (!CurrentUserUtils.GetNorthstarSchema("Book1"))
+            if (!CurrentUserUtils.GetNorthstarSchema("Book1")) {
                 this.AddToNorthstarCatalog(await Gateway.Instance.GetSchema("http://www.cs.brown.edu/~bcz/Book1.csv"));
+            }
         });
 
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 1feb30db1..9914f3793 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -312,7 +312,7 @@ export class CollectionFreeFormView extends CollectionSubView {
         const pany: number = -this.props.Document.GetNumber(KeyStore.PanY, 0);
 
         return (
-            <Measure onResize={(r: any) => runInAction(() => { this._pwidth = r.entry.width; this._pheight = r.entry.height })}>
+            <Measure onResize={(r: any) => runInAction(() => { this._pwidth = r.entry.width; this._pheight = r.entry.height; })}>
                 {({ measureRef }) => (
                     <div className={`collectionfreeformview-measure`} ref={measureRef}>
                         <div className={`collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`}
-- 
cgit v1.2.3-70-g09d2


From e0b3c759880639bf56f9b8b39ea2e38c5cbad8a6 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 10 Apr 2019 14:28:25 -0400
Subject: fixed extra rendering for collections.

---
 src/client/Server.ts                               | 10 +++----
 .../dash-nodes/HistogramBoxPrimitives.tsx          |  6 ++--
 src/client/util/DragManager.ts                     |  4 +--
 src/client/util/SelectionManager.ts                |  2 +-
 src/client/views/DocumentDecorations.tsx           |  5 ++--
 src/client/views/Main.scss                         | 24 +++++++--------
 src/client/views/Main.tsx                          | 19 ++++++++----
 .../views/collections/CollectionDockingView.tsx    |  4 +--
 src/client/views/collections/CollectionPDFView.tsx |  3 +-
 .../views/collections/CollectionSchemaView.tsx     |  4 +--
 src/client/views/collections/CollectionSubView.tsx |  3 +-
 .../views/collections/CollectionVideoView.tsx      |  3 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  7 +++--
 src/client/views/nodes/DocumentView.tsx            | 35 ++++++++++++++--------
 src/client/views/nodes/FieldView.tsx               | 10 +++----
 src/client/views/nodes/FormattedTextBox.tsx        |  4 +--
 src/client/views/nodes/VideoBox.tsx                |  2 +-
 17 files changed, 84 insertions(+), 61 deletions(-)

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

diff --git a/src/client/Server.ts b/src/client/Server.ts
index 857101a33..228d94a7b 100644
--- a/src/client/Server.ts
+++ b/src/client/Server.ts
@@ -1,10 +1,10 @@
 import { Key } from "../fields/Key";
-import { ObservableMap, action, reaction } from "mobx";
+import { ObservableMap, action, reaction, runInAction } from "mobx";
 import { Field, FieldWaiting, FIELD_WAITING, Opt, FieldId } from "../fields/Field";
 import { Document } from "../fields/Document";
 import { SocketStub, FieldMap } from "./SocketStub";
 import * as OpenSocket from 'socket.io-client';
-import { Utils } from "./../Utils";
+import { Utils, emptyFunction } from "./../Utils";
 import { MessageStore, Types } from "./../server/Message";
 
 export class Server {
@@ -59,7 +59,7 @@ export class Server {
     public static GetFields(fieldIds: FieldId[]): Promise<{ [id: string]: Field }>;
     public static GetFields(fieldIds: FieldId[], callback: (fields: FieldMap) => any): void;
     public static GetFields(fieldIds: FieldId[], callback?: (fields: FieldMap) => any): Promise<FieldMap> | void {
-        let fn = (cb: (fields: FieldMap) => void) => {
+        let fn = (cb: (fields: FieldMap) => void) => runInAction(() => {
 
             let neededFieldIds: FieldId[] = [];
             let waitingFieldIds: FieldId[] = [];
@@ -104,7 +104,7 @@ export class Server {
                         }
                     }, { fireImmediately: true });
             }));
-        };
+        });
         if (callback) {
             fn(callback);
         } else {
@@ -167,7 +167,7 @@ export class Server {
             if (f) {
                 // console.log("Applying        : " + field._id);
                 f.UpdateFromServer(field.data);
-                f.init(() => { });
+                f.init(emptyFunction);
             } else {
                 // console.log("Not applying wa : " + field._id);
             }
diff --git a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
index 4c5bdb14b..721bf6a89 100644
--- a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
@@ -1,7 +1,7 @@
 import React = require("react");
 import { computed, observable, reaction, runInAction, trace, action } from "mobx";
 import { observer } from "mobx-react";
-import { Utils as DashUtils } from '../../../Utils';
+import { Utils as DashUtils, emptyFunction } from '../../../Utils';
 import { FilterModel } from "../../northstar/core/filter/FilterModel";
 import { ModelHelpers } from "../../northstar/model/ModelHelpers";
 import { ArrayUtil } from "../../northstar/utils/ArrayUtil";
@@ -49,7 +49,7 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
     private getSelectionToggle(binPrimitives: HistogramBinPrimitive[], allBrushIndex: number, filterModel: FilterModel) {
         let rawAllBrushPrim = ArrayUtil.FirstOrDefault(binPrimitives, bp => bp.BrushIndex === allBrushIndex);
         if (!rawAllBrushPrim) {
-            return () => { };
+            return emptyFunction;
         }
         let allBrushPrim = rawAllBrushPrim;
         return () => runInAction(() => {
@@ -97,7 +97,7 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
         let trans1Ypercent = `${yFrom / this.renderDimension * 100}%`;
         return <line className="histogramboxprimitives-line" key={DashUtils.GenerateGuid()} x1={trans1Xpercent} x2={`${trans2Xpercent}`} y1={trans1Ypercent} y2={`${trans2Ypercent}`} />;
     }
-    drawRect(r: PIXIRectangle, barAxis: number, color: number | undefined, classExt: string, tapHandler: () => void = () => { }) {
+    drawRect(r: PIXIRectangle, barAxis: number, color: number | undefined, classExt: string, tapHandler: () => void = emptyFunction) {
         if (r.height < 0) {
             r.y += r.height;
             r.height = -r.height;
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 4849ae9f7..f7395578f 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -4,7 +4,7 @@ import { CollectionDockingView } from "../views/collections/CollectionDockingVie
 import { CollectionView } from "../views/collections/CollectionView";
 import { DocumentDecorations } from "../views/DocumentDecorations";
 import { DocumentView } from "../views/nodes/DocumentView";
-import { returnFalse } from "../../Utils";
+import { returnFalse, emptyFunction } from "../../Utils";
 
 export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) {
     let onRowMove = action((e: PointerEvent): void => {
@@ -224,7 +224,7 @@ export namespace DragManager {
                 CollectionDockingView.Instance.StartOtherDrag(docs, {
                     pageX: e.pageX,
                     pageY: e.pageY,
-                    preventDefault: () => { },
+                    preventDefault: emptyFunction,
                     button: 0
                 });
             }
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 5ddaafc72..c6b8c7b0d 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -48,7 +48,7 @@ export namespace SelectionManager {
 
         manager.DeselectAll();
         if (found) manager.SelectDoc(found, false);
-        Main.Instance.SetTextDoc(undefined, undefined);
+        Main.Instance.SetTextDoc(undefined, undefined, undefined);
     }
 
     export function SelectedDocuments(): Array<DocumentView> {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 28af46358..b7bf727f1 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -15,6 +15,7 @@ import { DocumentView } from "./nodes/DocumentView";
 import { LinkMenu } from "./nodes/LinkMenu";
 import React = require("react");
 import { FieldWaiting } from "../../fields/Field";
+import { emptyFunction } from "../../Utils";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -217,7 +218,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             let dragData = new DragManager.LinkDragData(SelectionManager.SelectedDocuments()[0]);
             DragManager.StartLinkDrag(this._linkerButton.current, dragData, e.pageX, e.pageY, {
                 handlers: {
-                    dragComplete: action(() => { }),
+                    dragComplete: action(emptyFunction),
                 },
                 hideSource: false
             });
@@ -262,7 +263,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 let dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs);
                 DragManager.StartDocumentDrag([this._linkButton.current], dragData, e.x, e.y, {
                     handlers: {
-                        dragComplete: action(() => { }),
+                        dragComplete: action(emptyFunction),
                     },
                     hideSource: false
                 });
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index fe7f007b0..8ef6b83f1 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -169,21 +169,21 @@ button:hover {
     overflow: scroll;
 }
 .mainDiv-textInput {
-    background:pink;
-        width: 200px;
-        height: 200px;
+    background-color: rgba(248, 6, 6, 0.1);
+    width: 200px;
+    height: 200px;
+    position:absolute;
+    overflow: visible;
+    top: 0;
+    left: 0;
+    .formattedTextBox-cont {
+        background-color: rgba(248, 6, 6, 0.1);
+        width: 100%;
+        height: 100%;
         position:absolute;
-        overflow: visible;
         top: 0;
         left: 0;
-        .formattedTextBox-cont {
-            background:pink;
-            width: 100%;
-            height: 100%;
-            position:absolute;
-            top: 0;
-            left: 0;
-    }
+}
 }
 #mainContent-div {
     width:100%;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 012633bed..2751b1899 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -23,7 +23,7 @@ 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 } from '../northstar/model/idea/idea';
+import { AggregateFunction, Catalog, Point } from '../northstar/model/idea/idea';
 import '../northstar/model/ModelExtensions';
 import { HistogramOperation } from '../northstar/operations/HistogramOperation';
 import '../northstar/utils/Extensions';
@@ -204,10 +204,12 @@ export class Main extends React.Component {
 
     @observable _textDoc?: Document = undefined;
     _textRect: any;
+    _textXf: Transform = Transform.Identity();
     @action
-    SetTextDoc(textDoc?: Document, div?: HTMLDivElement) {
+    SetTextDoc(textDoc?: Document, div?: HTMLDivElement, tx?: Transform) {
         this._textDoc = undefined;
         this._textDoc = textDoc;
+        this._textXf = tx ? tx : Transform.Identity();
         if (div) {
             this._textRect = div.getBoundingClientRect();
         }
@@ -220,8 +222,15 @@ export class Main extends React.Component {
             let y: number = this._textRect.y;
             let w: number = this._textRect.width;
             let h: number = this._textRect.height;
-            return <div className="mainDiv-textInput" style={{ transform: `translate(${x}px, ${y}px)`, width: `${w}px`, height: `${h}px` }} >
-                <FormattedTextBox fieldKey={KeyStore.Archives} Document={this._textDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true} selectOnLoad={true} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={Transform.Identity} focus={(doc) => { }} />
+            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={{ transform: `translate(${x}px, ${y}px) scale(${s[0]},${s[0]})` }} >
+                <div className="mainDiv-textInput" style={{ transform: `scale(${1 / s[0]}, ${1 / s[0]})`, width: `${w / s[0]}px`, height: `${h / s[0]}px` }}>
+
+                    <FormattedTextBox fieldKey={KeyStore.Archives} Document={this._textDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true} selectOnLoad={true} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={Transform.Identity} focus={(doc) => { }} />
+
+                </div>
             </ div>;
         }
         else return (null);
@@ -310,7 +319,7 @@ export class Main extends React.Component {
             <div className="main-buttonDiv" key="workspaces" style={{ top: '34px', left: '2px', position: 'absolute' }} ref={workspacesRef}>
                 <button onClick={this.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), () => { })}>Log Out</button></div>
+                <button onClick={() => request.get(ServerUtils.prepend(RouteStore.logout), emptyFunction)}>Log Out</button></div>
         ];
     }
 
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index ea6d3a247..212cf8a69 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -50,7 +50,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     public StartOtherDrag(dragDocs: Document[], e: any) {
         dragDocs.map(dragDoc =>
             this.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener.
-                onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: () => { }, button: 0 }));
+                onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 }));
     }
 
     @action
@@ -204,7 +204,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                     DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f]), e.pageX, e.pageY,
                         {
                             handlers: {
-                                dragComplete: action(() => { }),
+                                dragComplete: action(emptyFunction),
                             },
                             hideSource: false
                         });
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 97bac745c..6cbe59012 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -7,6 +7,7 @@ import React = require("react");
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from "./CollectionBaseView";
+import { emptyFunction } from "../../../Utils";
 
 
 @observer
@@ -33,7 +34,7 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
 
     onContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped() && this.props.Document.Id !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
-            ContextMenu.Instance.addItem({ description: "PDFOptions", event: () => { } });
+            ContextMenu.Instance.addItem({ description: "PDFOptions", event: emptyFunction });
         }
     }
 
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 587f60b3d..c67f8cd97 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -77,8 +77,8 @@ export class CollectionSchemaView extends CollectionSubView {
         let props: FieldViewProps = {
             Document: rowProps.value[0],
             fieldKey: rowProps.value[1],
-            isSelected: () => false,
-            select: () => { },
+            isSelected: returnFalse,
+            select: emptyFunction,
             isTopMost: false,
             selectOnLoad: false,
             ScreenToLocalTransform: Transform.Identity,
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 588ff9483..ef0684955 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -15,6 +15,7 @@ import { ServerUtils } from "../../../server/ServerUtil";
 import { Server } from "../../Server";
 import { FieldViewProps } from "../nodes/FieldView";
 import * as rp from 'request-promise';
+import { emptyFunction } from "../../../Utils";
 
 export interface CollectionViewProps extends FieldViewProps {
     addDocument: (document: Document, allowDuplicates?: boolean) => boolean;
@@ -213,7 +214,7 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
         }
 
         if (promises.length) {
-            Promise.all(promises).catch(() => { }).then(() => batch.end());
+            Promise.all(promises).catch(emptyFunction).then(() => batch.end());
         } else {
             batch.end();
         }
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index b02983a2e..6c9780adb 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -7,6 +7,7 @@ import React = require("react");
 import "./CollectionVideoView.scss";
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
+import { emptyFunction } from "../../../Utils";
 
 
 @observer
@@ -100,7 +101,7 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
 
     onContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped() && this.props.Document.Id !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
-            ContextMenu.Instance.addItem({ description: "VideoOptions", event: () => { } });
+            ContextMenu.Instance.addItem({ description: "VideoOptions", event: emptyFunction });
         }
     }
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 1feb30db1..102455d27 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -25,6 +25,7 @@ import { SelectionManager } from "../../../util/SelectionManager";
 import { NumberField } from "../../../../fields/NumberField";
 import { Main } from "../../Main";
 import Measure from "react-measure";
+import { returnFalse, emptyFunction } from "../../../../Utils";
 
 @observer
 export class CollectionFreeFormView extends CollectionSubView {
@@ -200,7 +201,7 @@ export class CollectionFreeFormView extends CollectionSubView {
 
     @action
     private SetPan(panX: number, panY: number) {
-        Main.Instance.SetTextDoc(undefined, undefined);
+        Main.Instance.SetTextDoc(undefined, undefined, undefined);
         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));
@@ -290,13 +291,13 @@ export class CollectionFreeFormView extends CollectionSubView {
     get backgroundView() {
         return !this.backgroundLayout ? (null) :
             (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document)}
-                layoutKey={KeyStore.BackgroundLayout} isTopMost={this.props.isTopMost} isSelected={() => false} select={() => { }} />);
+                layoutKey={KeyStore.BackgroundLayout} isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
     }
     @computed
     get overlayView() {
         return !this.overlayLayout ? (null) :
             (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document)}
-                layoutKey={KeyStore.OverlayLayout} isTopMost={this.props.isTopMost} isSelected={() => false} select={() => { }} />);
+                layoutKey={KeyStore.OverlayLayout} isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
     }
 
     getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform());
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9670ca6b2..42967eb22 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, IReactionDisposer, reaction, runInAction } from "mobx";
+import { action, computed, IReactionDisposer, reaction, runInAction, trace } from "mobx";
 import { observer } from "mobx-react";
 import { Document } from "../../../fields/Document";
 import { Field, FieldWaiting, Opt } from "../../../fields/Field";
@@ -8,7 +8,7 @@ import { ListField } from "../../../fields/ListField";
 import { BooleanField } from "../../../fields/BooleanField";
 import { TextField } from "../../../fields/TextField";
 import { ServerUtils } from "../../../server/ServerUtil";
-import { Utils } from "../../../Utils";
+import { Utils, emptyFunction } from "../../../Utils";
 import { Documents } from "../../documents/Documents";
 import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager } from "../../util/DragManager";
@@ -62,12 +62,12 @@ export function FakeJsxArgs(keys: string[], fields: string[] = []): JsxArgs {
     let Keys: { [name: string]: any } = {};
     let Fields: { [name: string]: any } = {};
     for (const key of keys) {
-        let fn = () => { };
+        let fn = emptyFunction;
         Object.defineProperty(fn, "name", { value: key + "Key" });
         Keys[key] = fn;
     }
     for (const field of fields) {
-        let fn = () => { };
+        let fn = emptyFunction;
         Object.defineProperty(fn, "name", { value: field });
         Fields[field] = fn;
     }
@@ -157,7 +157,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             dragData.moveDocument = this.props.moveDocument;
             DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, {
                 handlers: {
-                    dragComplete: action(() => { })
+                    dragComplete: action(emptyFunction)
                 },
                 hideSource: !dropAliasOfDraggedDoc
             });
@@ -377,14 +377,28 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         SelectionManager.SelectDoc(this, ctrlPressed);
     }
 
+    @computed get nativeWidth(): number { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
+    @computed get nativeHeight(): number { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
+    @computed
+    get contents() {
+        trace();
+        return (<DocumentContentsView
+            {...this.props}
+            isSelected={this.isSelected}
+            select={this.select}
+            layoutKey={KeyStore.Layout}
+        />);
+    }
+
     render() {
         if (!this.props.Document) {
             return null;
         }
+        trace();
 
         var scaling = this.props.ContentScaling();
-        var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
-        var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
+        var nativeWidth = this.nativeWidth;
+        var nativeHeight = this.nativeHeight;
 
         if (this.isMinimized()) {
             return (
@@ -420,12 +434,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                     onContextMenu={this.onContextMenu}
                     onPointerDown={this.onPointerDown}
                 >
-                    <DocumentContentsView
-                        {...this.props}
-                        isSelected={this.isSelected}
-                        select={this.select}
-                        layoutKey={KeyStore.Layout}
-                    />
+                    {this.contents}
                 </div>
             );
         }
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 07c5b332c..40b44aae5 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -19,7 +19,7 @@ import { ListField } from "../../../fields/ListField";
 import { DocumentContentsView } from "./DocumentContentsView";
 import { Transform } from "../../util/Transform";
 import { KeyStore } from "../../../fields/KeyStore";
-import { returnFalse } from "../../../Utils";
+import { returnFalse, emptyFunction } from "../../../Utils";
 
 
 //
@@ -85,9 +85,9 @@ export class FieldView extends React.Component<FieldViewProps> {
                     PanelHeight={() => 100}
                     isTopMost={true} //TODO Why is this top most?
                     selectOnLoad={false}
-                    focus={() => { }}
-                    isSelected={() => false}
-                    select={() => false}
+                    focus={emptyFunction}
+                    isSelected={returnFalse}
+                    select={returnFalse}
                     layoutKey={KeyStore.Layout}
                     ContainingCollectionView={undefined}
                     parentActive={this.props.active}
@@ -111,7 +111,7 @@ export class FieldView extends React.Component<FieldViewProps> {
         }
         else {
             return <p> {"Waiting for server..."} </p>;
- }
+        }
     }
 
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index beca6cdc6..bb1bc4fb0 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -161,13 +161,13 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         }
         if (this.props.fieldKey !== KeyStore.Archives) {
             e.preventDefault();
-            Main.Instance.SetTextDoc(this.props.Document, this._ref.current!);
+            Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform());
         }
     }
 
     onFocused = (e: React.FocusEvent): void => {
         if (this.props.fieldKey !== KeyStore.Archives) {
-            Main.Instance.SetTextDoc(this.props.Document, this._ref.current!);
+            Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform());
         }
     }
 
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 314af64c9..9d7c2bc56 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -66,7 +66,7 @@ export class VideoBox extends React.Component<FieldViewProps> {
             <Measure onResize={this.setScaling}>
                 {({ measureRef }) =>
                     <div style={{ width: "100%", height: "auto" }} ref={measureRef}>
-                        <video className="videobox-cont" onClick={() => { }} ref={this.setVideoRef}>
+                        <video className="videobox-cont" ref={this.setVideoRef}>
                             <source src={path} type="video/mp4" />
                             Not supported.
                         </video>
-- 
cgit v1.2.3-70-g09d2


From e5411598c4d2ecc7a0b4d8584b576ff5f45ad2a0 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 11 Apr 2019 01:05:12 -0400
Subject: tried to fix some context menu and other conflicting interactions

---
 src/client/util/SelectionManager.ts                          |  2 +-
 src/client/views/Main.tsx                                    |  4 ++--
 .../collectionFreeForm/CollectionFreeFormView.tsx            |  3 ---
 src/client/views/nodes/DocumentView.tsx                      |  3 ++-
 src/client/views/nodes/FormattedTextBox.scss                 |  1 +
 src/client/views/nodes/FormattedTextBox.tsx                  | 12 ++++++++----
 6 files changed, 14 insertions(+), 11 deletions(-)

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

diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index c6b8c7b0d..2638e3b7d 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -25,6 +25,7 @@ export namespace SelectionManager {
         DeselectAll(): void {
             manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false));
             manager.SelectedDocuments = [];
+            Main.Instance.SetTextDoc(undefined, undefined, undefined);
         }
     }
 
@@ -48,7 +49,6 @@ export namespace SelectionManager {
 
         manager.DeselectAll();
         if (found) manager.SelectDoc(found, false);
-        Main.Instance.SetTextDoc(undefined, undefined, undefined);
     }
 
     export function SelectedDocuments(): Array<DocumentView> {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 8153250c7..b9e209b3c 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -274,8 +274,8 @@ export class Main extends React.Component {
             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={{ 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={{ transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}>
+            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={KeyStore.Archives} Document={this._textDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true} selectOnLoad={true} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={(doc) => { }} />
                 </div>
             </ div>;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index db8c370df..4e1164ee5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -135,9 +135,6 @@ export class CollectionFreeFormView extends CollectionSubView {
             document.addEventListener("pointerup", this.onPointerUp);
             this._lastX = this.DownX = e.pageX;
             this._lastY = this.DownY = e.pageY;
-            if (this.props.isSelected()) {
-                e.stopPropagation();
-            }
         }
     }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6b585ec4b..1465d589c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -105,7 +105,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             }
             e.stopPropagation();
         } else {
-            if (this.active && !e.isDefaultPrevented()) {
+            if (this.active) {
                 e.stopPropagation();
                 document.removeEventListener("pointermove", this.onPointerMove);
                 document.addEventListener("pointermove", this.onPointerMove);
@@ -186,6 +186,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         document.removeEventListener("pointerup", this.onPointerUp);
         e.stopPropagation();
         if (!SelectionManager.IsSelected(this) &&
+            e.button != 2 &&
             Math.abs(e.clientX - this._downX) < 4 &&
             Math.abs(e.clientY - this._downY) < 4
         ) {
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 32da2632e..d2ba52cf9 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -22,6 +22,7 @@
   overflow-x: hidden;
   color: initial;
   height: 100%;
+  pointer-events: all;
 }
 
 .menuicon {
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 17125a298..14b22e9f5 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -14,6 +14,7 @@ import { ContextMenu } from "../../views/ContextMenu";
 import { Main } from "../Main";
 import { FieldView, FieldViewProps } from "./FieldView";
 import "./FormattedTextBox.scss";
+import { emptyFunction } from '../../../Utils';
 import React = require("react");
 const { buildMenuItems } = require("prosemirror-example-setup");
 const { menuBar } = require("prosemirror-menu");
@@ -95,8 +96,10 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
             );
         } else {
             this._proxyReactionDisposer = reaction(() => this.props.isSelected(),
-                () =>
-                    this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform())
+                () => {
+                    if (this.props.isSelected())
+                        Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform());
+                }
             );
         }
 
@@ -166,6 +169,8 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         if (e.button === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
             e.stopPropagation();
         }
+        if (e.button === 2)
+            e.preventDefault();
     }
     onPointerUp = (e: React.PointerEvent): void => {
         if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
@@ -227,12 +232,11 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
     render() {
         return (
             <div
-                className="formattedTextBox-cont"
+                className={`formattedTextBox-cont`}
                 onKeyDown={this.onKeyPress}
                 onKeyPress={this.onKeyPress}
                 onPointerUp={this.onPointerUp}
                 onPointerDown={this.onPointerDown}
-                onFocus={this.onFocused}
                 onContextMenu={this.specificContextMenu}
                 // tfs: do we need this event handler
                 onWheel={this.onPointerWheel}
-- 
cgit v1.2.3-70-g09d2


From cbe23091b72c472be3e9a6de1cac5325171c282f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 11 Apr 2019 08:51:39 -0400
Subject: fixed zoom not always rendering.   fixed documents (images, pdfs) not
 appearing.  fixed lint warnings.

---
 src/client/views/Main.tsx                                    |  7 ++++---
 .../collectionFreeForm/CollectionFreeFormView.tsx            | 12 +++++++-----
 src/client/views/nodes/DocumentView.tsx                      |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx                  |  9 +++------
 4 files changed, 15 insertions(+), 15 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 b9e209b3c..5fd778c7e 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -213,8 +213,9 @@ export class Main extends React.Component {
     _textProxyDiv: React.RefObject<HTMLDivElement>;
     @action
     SetTextDoc(textDoc?: Document, div?: HTMLDivElement, tx?: Transform) {
-        if (this._textTargetDiv)
+        if (this._textTargetDiv) {
             this._textTargetDiv.style.color = this._textColor;
+        }
 
         this._textDoc = undefined;
         this._textDoc = textDoc;
@@ -231,12 +232,12 @@ export class Main extends React.Component {
     @action
     textScroll = (e: React.UIEvent) => {
         if (this._textProxyDiv.current && this._textTargetDiv) {
-            this._textTargetDiv.scrollTop = this._textScroll = this._textProxyDiv.current.children[0].scrollTop
+            this._textTargetDiv.scrollTop = this._textScroll = this._textProxyDiv.current.children[0].scrollTop;
         }
     }
 
     textBoxDown = (e: React.PointerEvent) => {
-        if (e.button != 0 || e.metaKey || e.altKey) {
+        if (e.button !== 0 || e.metaKey || e.altKey) {
             document.addEventListener("pointermove", this.textBoxMove);
             document.addEventListener('pointerup', this.textBoxUp);
         }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4e1164ee5..b40e36e77 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -304,10 +304,12 @@ export class CollectionFreeFormView extends CollectionSubView {
     childViews = () => this.views;
 
     render() {
-        let [dx, dy] = [this.centeringShiftX, this.centeringShiftY];
-
+        const [dx, dy] = [this.centeringShiftX, this.centeringShiftY];
         const panx: number = -this.props.Document.GetNumber(KeyStore.PanX, 0);
         const pany: number = -this.props.Document.GetNumber(KeyStore.PanY, 0);
+        const zoom: number = this.zoomScaling;
+        const blay = this.backgroundView;
+        const olay = this.overlayView;
 
         return (
             <Measure onResize={(r: any) => runInAction(() => { this._pwidth = r.entry.width; this._pheight = r.entry.height; })}>
@@ -323,8 +325,8 @@ export class CollectionFreeFormView extends CollectionSubView {
                                 <PreviewCursor container={this} addLiveTextDocument={this.addLiveTextBox}
                                     getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} >
                                     <div className="collectionfreeformview" ref={this._canvasRef}
-                                        style={{ transform: `translate(${dx}px, ${dy}px) scale(${this.zoomScaling}, ${this.zoomScaling}) translate(${panx}px, ${pany}px)` }}>
-                                        {this.backgroundView}
+                                        style={{ transform: `translate(${dx}px, ${dy}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}>
+                                        {blay}
                                         <CollectionFreeFormLinksView {...this.props}>
                                             <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
                                                 {this.childViews}
@@ -332,7 +334,7 @@ export class CollectionFreeFormView extends CollectionSubView {
                                         </CollectionFreeFormLinksView>
                                         <CollectionFreeFormRemoteCursors {...this.props} />
                                     </div>
-                                    {this.overlayView}
+                                    {olay}
                                 </PreviewCursor>
                             </MarqueeView>
                         </div>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1465d589c..9c31a83c1 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -186,7 +186,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         document.removeEventListener("pointerup", this.onPointerUp);
         e.stopPropagation();
         if (!SelectionManager.IsSelected(this) &&
-            e.button != 2 &&
+            e.button !== 2 &&
             Math.abs(e.clientX - this._downX) < 4 &&
             Math.abs(e.clientY - this._downY) < 4
         ) {
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 14b22e9f5..468cbcd1e 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -96,11 +96,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
             );
         } else {
             this._proxyReactionDisposer = reaction(() => this.props.isSelected(),
-                () => {
-                    if (this.props.isSelected())
-                        Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform());
-                }
-            );
+                () => this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform()));
         }
 
         this._reactionDisposer = reaction(
@@ -169,8 +165,9 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         if (e.button === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
             e.stopPropagation();
         }
-        if (e.button === 2)
+        if (e.button === 2) {
             e.preventDefault();
+        }
     }
     onPointerUp = (e: React.PointerEvent): void => {
         if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
-- 
cgit v1.2.3-70-g09d2


From 50be8cb7a93110821c972c679567ddb6aae8bc6f Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 11 Apr 2019 11:56:40 -0400
Subject: made css globals avabile from code.  cleaned up formattedText overlay
 api..

---
 src/client/util/DragManager.ts                     |  12 +-
 src/client/util/SelectionManager.ts                |   2 +-
 src/client/views/DocumentDecorations.tsx           |   2 +-
 src/client/views/Main.tsx                          |   7 +-
 src/client/views/_global_variables.scss            |   2 +-
 src/client/views/_global_variables.scss.d.ts       |  11 +-
 src/client/views/_global_variables.ts              |   8 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   2 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  31 ++--
 tsconfig.json                                      |  42 ++---
 webpack.config.js                                  | 180 ++++++++++-----------
 11 files changed, 154 insertions(+), 145 deletions(-)
 create mode 100644 src/client/views/_global_variables.ts

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index e500b0274..d66c6e90f 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,12 +1,12 @@
 import { action } from "mobx";
 import { Document } from "../../fields/Document";
+import { emptyFunction } from "../../Utils";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
-import { CollectionView } from "../views/collections/CollectionView";
 import { DocumentDecorations } from "../views/DocumentDecorations";
-import { DocumentView } from "../views/nodes/DocumentView";
-import { returnFalse, emptyFunction } from "../../Utils";
 import { Main } from "../views/Main";
-import globalStyles from '../views/_global_variables.scss';
+import { DocumentView } from "../views/nodes/DocumentView";
+import globalStyles from "../views/_global_variables";
+// import globalStyleVariables from "../views/_global_variables.scss"; // bcz: why doesn't this work?
 
 export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) {
     let onRowMove = action((e: PointerEvent): void => {
@@ -149,7 +149,7 @@ export namespace DragManager {
             dragDiv.className = "dragManager-dragDiv";
             DragManager.Root().appendChild(dragDiv);
         }
-        Main.Instance.SetTextDoc(undefined, undefined, undefined);
+        Main.Instance.SetTextDoc();
 
         let scaleXs: number[] = [];
         let scaleYs: number[] = [];
@@ -178,7 +178,7 @@ export namespace DragManager {
             dragElement.style.bottom = "";
             dragElement.style.left = "0";
             dragElement.style.transformOrigin = "0 0";
-            dragElement.style.zIndex = "1000";// globalStyles.contextMenuZindex.toString();
+            dragElement.style.zIndex = globalStyles.contextMenuZindex;// "1000";
             dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
             dragElement.style.width = `${rect.width / scaleX}px`;
             dragElement.style.height = `${rect.height / scaleY}px`;
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 2638e3b7d..2fa45a086 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -25,7 +25,7 @@ export namespace SelectionManager {
         DeselectAll(): void {
             manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false));
             manager.SelectedDocuments = [];
-            Main.Instance.SetTextDoc(undefined, undefined, undefined);
+            Main.Instance.SetTextDoc();
         }
     }
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index fa521b7e2..29cca286d 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -322,7 +322,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 break;
         }
 
-        Main.Instance.SetTextDoc(undefined, undefined, undefined);
+        Main.Instance.SetTextDoc();
         SelectionManager.SelectedDocuments().forEach(element => {
             const rect = element.screenRect();
             if (rect.width !== 0) {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 5fd778c7e..c4c4a6bf9 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -39,6 +39,7 @@ import "./Main.scss";
 import { DocumentView } from './nodes/DocumentView';
 import { FormattedTextBox } from './nodes/FormattedTextBox';
 import { REPLCommand } from 'repl';
+import { Key } from '../../fields/Key';
 
 @observer
 export class Main extends React.Component {
@@ -208,17 +209,19 @@ export class Main extends React.Component {
     _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, div?: HTMLDivElement, tx?: Transform) {
+    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) {
@@ -277,7 +280,7 @@ export class Main extends React.Component {
             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={KeyStore.Archives} Document={this._textDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true} selectOnLoad={true} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={(doc) => { }} />
+                    <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={(doc) => { }} />
                 </div>
             </ div>;
         }
diff --git a/src/client/views/_global_variables.scss b/src/client/views/_global_variables.scss
index 238351a77..cd6af2dac 100644
--- a/src/client/views/_global_variables.scss
+++ b/src/client/views/_global_variables.scss
@@ -24,5 +24,5 @@ $docDecorations-zindex: 998; // then doc decorations appear over everything else
 $remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right?
 
 :export  {
-    contextMenuZindex: $contextMenu-zindex
+    contextMenuZindex: $contextMenu-zindex;
 }
\ No newline at end of file
diff --git a/src/client/views/_global_variables.scss.d.ts b/src/client/views/_global_variables.scss.d.ts
index 12008aeef..c902d473f 100644
--- a/src/client/views/_global_variables.scss.d.ts
+++ b/src/client/views/_global_variables.scss.d.ts
@@ -1,10 +1,7 @@
+
 export interface I_globalScss {
-    contextMenuZindex: number;  // context menu shows up over everything
-    mainTextInputZindex: number; // then text input overlay so that it's context menu will appear over decorations, etc
-    docDecorationsZindex: number; // then doc decorations appear over everything else
-    remoteCursorsZindex: number; // ... not sure what level the remote cursors should go -- is this right?
+    contextMenuZindex: string;  // context menu shows up over everything
 }
+export const globalStyleVariables: I_globalScss;
 
-export const globalStyles: I_globalScss;
-
-export default globalStyles;
\ No newline at end of file
+export default globalStyleVariables;
\ No newline at end of file
diff --git a/src/client/views/_global_variables.ts b/src/client/views/_global_variables.ts
new file mode 100644
index 000000000..e70bfd56c
--- /dev/null
+++ b/src/client/views/_global_variables.ts
@@ -0,0 +1,8 @@
+import * as globalStyleVariables from "../views/_global_variables.scss"
+
+export interface I_globalScss {
+    contextMenuZindex: string;  // context menu shows up over everything
+}
+let globalStyles = globalStyleVariables as any as I_globalScss;
+
+export default globalStyles;
\ 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 b40e36e77..01ebbe0e1 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -198,7 +198,7 @@ export class CollectionFreeFormView extends CollectionSubView {
 
     @action
     private SetPan(panX: number, panY: number) {
-        Main.Instance.SetTextDoc(undefined, undefined, undefined);
+        Main.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 468cbcd1e..8ea747b1c 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -5,7 +5,6 @@ import { keymap } from "prosemirror-keymap";
 import { EditorState, Plugin, Transaction } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
 import { FieldWaiting, Opt } from "../../../fields/Field";
-import { KeyStore } from "../../../fields/KeyStore";
 import { RichTextField } from "../../../fields/RichTextField";
 import { inpRules } from "../../util/RichTextRules";
 import { schema } from "../../util/RichTextSchema";
@@ -14,7 +13,6 @@ import { ContextMenu } from "../../views/ContextMenu";
 import { Main } from "../Main";
 import { FieldView, FieldViewProps } from "./FieldView";
 import "./FormattedTextBox.scss";
-import { emptyFunction } from '../../../Utils';
 import React = require("react");
 const { buildMenuItems } = require("prosemirror-example-setup");
 const { menuBar } = require("prosemirror-menu");
@@ -35,7 +33,12 @@ const { menuBar } = require("prosemirror-menu");
 //  specified Key and assigns it to an HTML input node.  When changes are made to this node,
 //  this will edit the document and assign the new value to that field.
 //]
-export class FormattedTextBox extends React.Component<FieldViewProps> {
+
+export interface FormattedTextBoxOverlay {
+    isOverlay?: boolean;
+}
+
+export class FormattedTextBox extends React.Component<(FieldViewProps & FormattedTextBoxOverlay)> {
     public static LayoutString(fieldStr: string = "DataKey") {
         return FieldView.LayoutString(FormattedTextBox, fieldStr);
     }
@@ -56,8 +59,8 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         if (this._editorView) {
             const state = this._editorView.state.apply(tx);
             this._editorView.updateState(state);
-            this.FieldDoc.SetDataOnPrototype(
-                this.FieldKey,
+            this.props.Document.SetDataOnPrototype(
+                this.props.fieldKey,
                 JSON.stringify(state.toJSON()),
                 RichTextField
             );
@@ -65,14 +68,11 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         }
     }
 
-    get FieldDoc() { return this.props.fieldKey === KeyStore.Archives ? Main.Instance._textDoc! : this.props.Document; }
-    get FieldKey() { return this.props.fieldKey === KeyStore.Archives ? KeyStore.Data : this.props.fieldKey; }
-
     componentDidMount() {
         const config = {
             schema,
             inpRules, //these currently don't do anything, but could eventually be helpful
-            plugins: this.props.fieldKey === KeyStore.Archives ? [
+            plugins: this.props.isOverlay ? [
                 history(),
                 keymap({ "Mod-z": undo, "Mod-y": redo }),
                 keymap(baseKeymap),
@@ -84,7 +84,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
                 ]
         };
 
-        if (this.props.fieldKey === KeyStore.Archives) {
+        if (this.props.isOverlay) {
             this._inputReactionDisposer = reaction(() => Main.Instance._textDoc && Main.Instance._textDoc.Id,
                 () => {
                     if (this._editorView) {
@@ -96,12 +96,12 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
             );
         } else {
             this._proxyReactionDisposer = reaction(() => this.props.isSelected(),
-                () => this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform()));
+                () => this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform()));
         }
 
         this._reactionDisposer = reaction(
             () => {
-                const field = this.FieldDoc ? this.FieldDoc.GetT(this.FieldKey, RichTextField) : undefined;
+                const field = this.props.Document ? this.props.Document.GetT(this.props.fieldKey, RichTextField) : undefined;
                 return field && field !== FieldWaiting ? field.Data : undefined;
             },
             field => {
@@ -117,7 +117,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
 
     private setupEditor(config: any) {
         let state: EditorState;
-        let field = this.FieldDoc ? this.FieldDoc.GetT(this.FieldKey, RichTextField) : undefined;
+        let field = this.props.Document ? this.props.Document.GetT(this.props.fieldKey, RichTextField) : undefined;
         if (field && field !== FieldWaiting && field.Data) {
             state = EditorState.fromJSON(config, JSON.parse(field.Data));
         } else {
@@ -176,8 +176,8 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
     }
 
     onFocused = (e: React.FocusEvent): void => {
-        if (this.props.fieldKey !== KeyStore.Archives) {
-            Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform());
+        if (!this.props.isOverlay) {
+            Main.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;
@@ -232,6 +232,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
                 className={`formattedTextBox-cont`}
                 onKeyDown={this.onKeyPress}
                 onKeyPress={this.onKeyPress}
+                onFocus={this.onFocused}
                 onPointerUp={this.onPointerUp}
                 onPointerDown={this.onPointerDown}
                 onContextMenu={this.specificContextMenu}
diff --git a/tsconfig.json b/tsconfig.json
index 41db1d0a7..0d4d77002 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,23 +1,23 @@
 {
-  "compilerOptions": {
-    "target": "es5",
-    "removeComments": true,
-    "experimentalDecorators": true,
-    "strict": true,
-    "jsx": "react",
-    "sourceMap": true,
-    "outDir": "dist",
-    "lib": [
-      "dom",
-      "es2015"
-    ],
-  },
-  // "exclude": [
-  //   "node_modules",
-  //   "static"
-  // ],
-  "typeRoots": [
-    "./node_modules/@types",
-    "./src/typings"
-  ]
+    "compilerOptions": {
+        "target": "es5",
+        "removeComments": true,
+        "experimentalDecorators": true,
+        "strict": true,
+        "jsx": "react",
+        "sourceMap": true,
+        "outDir": "dist",
+        "lib": [
+            "dom",
+            "es2015"
+        ],
+    },
+    // "exclude": [
+    //   "node_modules",
+    //   "static"
+    // ],
+    "typeRoots": [
+        "./node_modules/@types",
+        "./src/typings"
+    ]
 }
\ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
index 50079255f..574401807 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -3,95 +3,95 @@ var webpack = require('webpack');
 const CopyWebpackPlugin = require("copy-webpack-plugin");
 
 module.exports = {
-  mode: 'development',
-  entry: {
-    bundle: ["./src/client/views/Main.tsx", 'webpack-hot-middleware/client?reload=true'],
-    viewer: ["./src/debug/Viewer.tsx", 'webpack-hot-middleware/client?reload=true'],
-    test: ["./src/debug/Test.tsx", 'webpack-hot-middleware/client?reload=true'],
-    inkControls: ["./src/mobile/InkControls.tsx", 'webpack-hot-middleware/client?reload=true'],
-    imageUpload: ["./src/mobile/ImageUpload.tsx", 'webpack-hot-middleware/client?reload=true'],
-  },
-  devtool: "source-map",
-  node: {
-    fs: 'empty',
-    module: 'empty',
-    dns: 'mock',
-    tls: 'mock',
-    net: 'mock'
-  },
-  output: {
-    filename: "[name].js",
-    path: path.resolve(__dirname, "build"),
-    publicPath: "/"
-  },
-  resolve: {
-    extensions: ['.js', '.ts', '.tsx']
-  },
-  module: {
-    rules: [
-      {
-        test: [/\.tsx?$/, /\.ts?$/,],
-        enforce: 'pre',
-        use: [
-          {
-            loader: "tslint-loader",
-          }
-        ]
-      }, {
-        test: [/\.tsx?$/, /\.ts?$/,],
-        loader: "awesome-typescript-loader",
-        include: path.join(__dirname, 'src')
-      },
-      {
-        test: /\.scss|css$/,
-        use: [
-          {
-            loader: "style-loader"
-          },
-          {
-            loader: "css-loader"
-          },
-          {
-            loader: "sass-loader"
-          }
-        ]
-      },
-      {
-        test: /\.(jpg|png|pdf)$/,
-        use: [
-          {
-            loader: 'file-loader'
-          }
-        ]
-      },
-      {
-        test: /\.(png|jpg|gif)$/i,
-        use: [
-          {
-            loader: 'url-loader',
-            options: {
-              limit: 8192
-            }
-          }
-        ]
-      }]
-  },
-  plugins: [
-    new CopyWebpackPlugin([{ from: "deploy", to: path.join(__dirname, "build") }]),
-    new webpack.optimize.OccurrenceOrderPlugin(),
-    new webpack.HotModuleReplacementPlugin(),
-    new webpack.NoEmitOnErrorsPlugin()
-  ],
-  devServer: {
-    compress: false,
-    host: "localhost",
-    contentBase: path.join(__dirname, 'deploy'),
-    port: 4321,
-    hot: true,
-    https: false,
-    overlay: {
-      warnings: true,
-      errors: true
+    mode: 'development',
+    entry: {
+        bundle: ["./src/client/views/Main.tsx", 'webpack-hot-middleware/client?reload=true'],
+        viewer: ["./src/debug/Viewer.tsx", 'webpack-hot-middleware/client?reload=true'],
+        test: ["./src/debug/Test.tsx", 'webpack-hot-middleware/client?reload=true'],
+        inkControls: ["./src/mobile/InkControls.tsx", 'webpack-hot-middleware/client?reload=true'],
+        imageUpload: ["./src/mobile/ImageUpload.tsx", 'webpack-hot-middleware/client?reload=true'],
+    },
+    devtool: "source-map",
+    node: {
+        fs: 'empty',
+        module: 'empty',
+        dns: 'mock',
+        tls: 'mock',
+        net: 'mock'
+    },
+    output: {
+        filename: "[name].js",
+        path: path.resolve(__dirname, "build"),
+        publicPath: "/"
+    },
+    resolve: {
+        extensions: ['.js', '.ts', '.tsx']
+    },
+    module: {
+        rules: [
+            {
+                test: [/\.tsx?$/, /\.ts?$/,],
+                enforce: 'pre',
+                use: [
+                    {
+                        loader: "tslint-loader",
+                    }
+                ]
+            }, {
+                test: [/\.tsx?$/, /\.ts?$/,],
+                loader: "awesome-typescript-loader",
+                include: path.join(__dirname, 'src')
+            },
+            {
+                test: /\.scss|css$/,
+                use: [
+                    {
+                        loader: "style-loader"
+                    },
+                    {
+                        loader: "css-loader"
+                    },
+                    {
+                        loader: "sass-loader"
+                    }
+                ]
+            },
+            {
+                test: /\.(jpg|png|pdf)$/,
+                use: [
+                    {
+                        loader: 'file-loader'
+                    }
+                ]
+            },
+            {
+                test: /\.(png|jpg|gif)$/i,
+                use: [
+                    {
+                        loader: 'url-loader',
+                        options: {
+                            limit: 8192
+                        }
+                    }
+                ]
+            }]
+    },
+    plugins: [
+        new CopyWebpackPlugin([{ from: "deploy", to: path.join(__dirname, "build") }]),
+        new webpack.optimize.OccurrenceOrderPlugin(),
+        new webpack.HotModuleReplacementPlugin(),
+        new webpack.NoEmitOnErrorsPlugin()
+    ],
+    devServer: {
+        compress: false,
+        host: "localhost",
+        contentBase: path.join(__dirname, 'deploy'),
+        port: 4321,
+        hot: true,
+        https: false,
+        overlay: {
+            warnings: true,
+            errors: true
+        }
     }
-  }
 };
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From fd8e6bf2cacf8bed7c3363eeb777c731741266d5 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 11 Apr 2019 20:19:57 -0400
Subject: made COLLECTION_BORDER_WIDTH a CSS variable accessible from code. 
 changed global css variable file name

---
 src/client/util/DragManager.ts                     |  5 ++--
 src/client/util/TooltipTextMenu.scss               |  2 +-
 src/client/views/ContextMenu.scss                  |  2 +-
 src/client/views/DocumentDecorations.scss          |  2 +-
 src/client/views/InkingCanvas.scss                 |  2 +-
 src/client/views/InkingControl.scss                |  2 +-
 src/client/views/Main.scss                         |  2 +-
 src/client/views/_global_variables.scss            | 28 -------------------
 src/client/views/_global_variables.scss.d.ts       |  7 -----
 src/client/views/_global_variables.ts              |  8 ------
 .../views/collections/CollectionBaseView.tsx       |  1 -
 .../views/collections/CollectionDockingView.scss   |  6 +++++
 .../views/collections/CollectionDockingView.tsx    |  9 +------
 .../views/collections/CollectionSchemaView.scss    |  6 +++--
 .../views/collections/CollectionSchemaView.tsx     | 10 ++++---
 .../views/collections/CollectionTreeView.scss      |  6 +++--
 .../views/collections/CollectionTreeView.tsx       |  4 +--
 .../CollectionFreeFormRemoteCursors.scss           |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.scss | 10 ++++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 31 ++++++++++++----------
 src/client/views/globalCssVariables.scss           | 29 ++++++++++++++++++++
 src/client/views/globalCssVariables.scss.d.ts      |  8 ++++++
 src/client/views/nodes/DocumentView.scss           |  2 +-
 src/client/views/nodes/FormattedTextBox.scss       |  2 +-
 src/client/views/nodes/KeyValueBox.scss            |  2 +-
 src/client/views/nodes/KeyValuePair.scss           |  2 +-
 src/client/views/nodes/LinkBox.scss                |  2 +-
 src/client/views/nodes/LinkEditor.scss             |  2 +-
 28 files changed, 99 insertions(+), 95 deletions(-)
 delete mode 100644 src/client/views/_global_variables.scss
 delete mode 100644 src/client/views/_global_variables.scss.d.ts
 delete mode 100644 src/client/views/_global_variables.ts
 create mode 100644 src/client/views/globalCssVariables.scss
 create mode 100644 src/client/views/globalCssVariables.scss.d.ts

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 333c474c1..3d8bbb49c 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -5,8 +5,7 @@ import { CollectionDockingView } from "../views/collections/CollectionDockingVie
 import { DocumentDecorations } from "../views/DocumentDecorations";
 import { Main } from "../views/Main";
 import { DocumentView } from "../views/nodes/DocumentView";
-// import globalStyles from "../views/_global_variables";
-import * as globalStyles from "../views/_global_variables.scss"; // bcz: why doesn't this work?
+import * as globalCssVariables from "../views/globalCssVariables.scss";
 
 export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) {
     let onRowMove = action((e: PointerEvent): void => {
@@ -178,7 +177,7 @@ export namespace DragManager {
             dragElement.style.bottom = "";
             dragElement.style.left = "0";
             dragElement.style.transformOrigin = "0 0";
-            dragElement.style.zIndex = globalStyles.contextMenuZindex;// "1000";
+            dragElement.style.zIndex = globalCssVariables.contextMenuZindex;// "1000";
             dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
             dragElement.style.width = `${rect.width / scaleX}px`;
             dragElement.style.height = `${rect.height / scaleY}px`;
diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss
index ea580d104..7deea3be6 100644
--- a/src/client/util/TooltipTextMenu.scss
+++ b/src/client/util/TooltipTextMenu.scss
@@ -1,4 +1,4 @@
-@import "../views/global_variables";
+@import "../views/globalCssVariables";
 
 .tooltipMenu {
     position: absolute;
diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss
index 5acf598cf..fe884ca85 100644
--- a/src/client/views/ContextMenu.scss
+++ b/src/client/views/ContextMenu.scss
@@ -1,4 +1,4 @@
-@import "global_variables";
+@import "globalCssVariables";
 .contextMenu-cont {
   position: absolute;
   display: flex;
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 321bda384..ce84b6106 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -1,4 +1,4 @@
-@import "global_variables";
+@import "globalCssVariables";
 
 .documentDecorations {
     position: absolute;
diff --git a/src/client/views/InkingCanvas.scss b/src/client/views/InkingCanvas.scss
index 42ae38c73..2c550051c 100644
--- a/src/client/views/InkingCanvas.scss
+++ b/src/client/views/InkingCanvas.scss
@@ -1,4 +1,4 @@
-@import "global_variables";
+@import "globalCssVariables";
 
 .inkingCanvas {
     opacity:0.99;
diff --git a/src/client/views/InkingControl.scss b/src/client/views/InkingControl.scss
index 0d8fd8784..ba4ec41af 100644
--- a/src/client/views/InkingControl.scss
+++ b/src/client/views/InkingControl.scss
@@ -1,4 +1,4 @@
-@import "global_variables";
+@import "globalCssVariables";
 .inking-control {
     position: absolute;
     left: 70px;
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 7329b8eb6..f3af26f37 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -1,4 +1,4 @@
-@import "global_variables";
+@import "globalCssVariables";
 @import "nodeModuleOverrides";
 html,
 body {
diff --git a/src/client/views/_global_variables.scss b/src/client/views/_global_variables.scss
deleted file mode 100644
index cd6af2dac..000000000
--- a/src/client/views/_global_variables.scss
+++ /dev/null
@@ -1,28 +0,0 @@
-@import url("https://fonts.googleapis.com/css?family=Noto+Sans:400,700|Crimson+Text:400,400i,700");
-// colors
-$light-color: #fcfbf7;
-$light-color-secondary: rgb(241, 239, 235);
-$main-accent: #61aaa3;
-// $alt-accent: #cdd5ec;
-// $alt-accent: #cdeceb;
-$alt-accent: #59dff7;
-$lighter-alt-accent: rgb(207, 220, 240);
-$intermediate-color: #9c9396;
-$dark-color: #121721;
-// fonts
-$sans-serif: "Noto Sans", sans-serif;
-// $sans-serif: "Roboto Slab", sans-serif;
-$serif: "Crimson Text", serif;
-// misc values
-$border-radius: 0.3em;
-//
-
-                            // dragged items
-$contextMenu-zindex: 1000;  // context menu shows up over everything
-$mainTextInput-zindex: 999; // then text input overlay so that it's context menu will appear over decorations, etc
-$docDecorations-zindex: 998; // then doc decorations appear over everything else
-$remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right?
-
-:export  {
-    contextMenuZindex: $contextMenu-zindex;
-}
\ No newline at end of file
diff --git a/src/client/views/_global_variables.scss.d.ts b/src/client/views/_global_variables.scss.d.ts
deleted file mode 100644
index ebee7dbce..000000000
--- a/src/client/views/_global_variables.scss.d.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-
-interface IGlobalScss {
-    contextMenuZindex: string;  // context menu shows up over everything
-}
-declare const globalStyleVariables: IGlobalScss;
-
-export = globalStyleVariables;
\ No newline at end of file
diff --git a/src/client/views/_global_variables.ts b/src/client/views/_global_variables.ts
deleted file mode 100644
index caedc7aad..000000000
--- a/src/client/views/_global_variables.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import * as globalStyleVariables from "../views/_global_variables.scss";
-
-export interface IGlobalScss {
-    contextMenuZindex: string;  // context menu shows up over everything
-}
-let globalStyles = globalStyleVariables as any as IGlobalScss;
-
-export default globalStyles;
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index ac320eda3..0ace700ce 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -34,7 +34,6 @@ export interface CollectionViewProps extends FieldViewProps {
     contentRef?: React.Ref<HTMLDivElement>;
 }
 
-export const COLLECTION_BORDER_WIDTH = 1;
 
 @observer
 export class CollectionBaseView extends React.Component<CollectionViewProps> {
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 583d50c5b..13dd9faa8 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -1,8 +1,14 @@
+@import "../../views/globalCssVariables.scss";
+
 .collectiondockingview-content {
     height: 100%;
 }
 
 .collectiondockingview-container {
+    width: 100%;
+    height: 100%;
+    border-style: solid;
+    border-width: $COLLECTION_BORDER_WIDTH;
     position: absolute;
     top: 0;
     left: 0;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 212cf8a69..05470f405 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -13,7 +13,6 @@ import { Server } from "../../Server";
 import { undoBatch } from "../../util/UndoManager";
 import { DocumentView } from "../nodes/DocumentView";
 import "./CollectionDockingView.scss";
-import { COLLECTION_BORDER_WIDTH } from "./CollectionBaseView";
 import React = require("react");
 import { SubCollectionViewProps } from "./CollectionSubView";
 import { ServerUtils } from "../../../server/ServerUtil";
@@ -271,13 +270,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     render() {
         return (
             <div className="collectiondockingview-container" id="menuContainer"
-                onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} ref={this._containerRef}
-                style={{
-                    width: "100%",
-                    height: "100%",
-                    borderStyle: "solid",
-                    borderWidth: `${COLLECTION_BORDER_WIDTH}px`,
-                }} />
+                onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} ref={this._containerRef} />
         );
     }
 }
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index c3a2e88ac..40e49bb5f 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -1,4 +1,4 @@
-@import "../global_variables";
+@import "../globalCssVariables";
 
 //options menu styling
 #schemaOptionsMenuBtn {
@@ -53,7 +53,9 @@
   
 
 .collectionSchemaView-container {
-    border: 1px solid $intermediate-color;
+    border-width: $COLLECTION_BORDER_WIDTH;
+    border-color : $intermediate-color;
+    border-style: solid;
     border-radius: $border-radius;
     box-sizing: border-box;
     position: absolute;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index f1b3e1b8f..0cb88eab0 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -25,7 +25,7 @@ import "./CollectionSchemaView.scss";
 import { CollectionView } from "./CollectionView";
 import { CollectionSubView } from "./CollectionSubView";
 import { TextField } from "../../../fields/TextField";
-import { COLLECTION_BORDER_WIDTH } from "./CollectionBaseView";
+import * as globalCssVariables from "../../views/globalCssVariables.scss";
 import { emptyFunction, returnFalse } from "../../../Utils";
 
 
@@ -245,11 +245,13 @@ export class CollectionSchemaView extends CollectionSubView {
         this._contentScaling = r.entry.width / selected!.GetNumber(KeyStore.NativeWidth, r.entry.width);
     }
 
+    @computed
+    get borderWidth() { return globalCssVariables.COLLECTION_BORDER_WIDTH; }
     getContentScaling = (): number => this._contentScaling;
     getPanelWidth = (): number => this._panelWidth;
     getPanelHeight = (): number => this._panelHeight;
-    getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling);
-    getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX - this._tableWidth, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling);
+    getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- this.borderWidth - this.DIVIDER_WIDTH - this._dividerX, - this.borderWidth).scale(1 / this._contentScaling);
+    getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- this.borderWidth - this.DIVIDER_WIDTH - this._dividerX - this._tableWidth, - this.borderWidth).scale(1 / this._contentScaling);
 
     focusDocument = (doc: Document) => { };
 
@@ -349,7 +351,7 @@ export class CollectionSchemaView extends CollectionSubView {
         </Flyout>);
 
         return (
-            <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel} ref={this._mainCont} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} >
+            <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel} ref={this._mainCont}>
                 <div className="collectionSchemaView-dropTarget" onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
                     <Measure onResize={this.setTableDimensions}>
                         {({ measureRef }) =>
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index f2affbf55..973eead97 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -1,7 +1,9 @@
-@import "../global_variables";
+@import "../globalCssVariables";
 
 .collectionTreeView-dropTarget {
-    border: 0px solid transparent;
+    border-width: $COLLECTION_BORDER_WIDTH;
+    border-color: transparent;
+    border-style: solid;
     border-radius: $border-radius;
     box-sizing: border-box;
     height: 100%;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 659cff9fe..20ec16822 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -11,9 +11,9 @@ import { setupDrag, DragManager } from "../../util/DragManager";
 import { EditableView } from "../EditableView";
 import "./CollectionTreeView.scss";
 import { CollectionView } from "./CollectionView";
+import * as globalCssVariables from "../../views/globalCssVariables.scss";
 import { CollectionSubView } from "./CollectionSubView";
 import React = require("react");
-import { COLLECTION_BORDER_WIDTH } from './CollectionBaseView';
 import { props } from 'bluebird';
 
 
@@ -139,7 +139,7 @@ export class CollectionTreeView extends CollectionSubView {
             );
 
         return (
-            <div id="body" className="collectionTreeView-dropTarget" onWheel={(e: React.WheelEvent) => e.stopPropagation()} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}>
+            <div id="body" className="collectionTreeView-dropTarget" onWheel={(e: React.WheelEvent) => e.stopPropagation()} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
                 <div className="coll-title">
                     <EditableView
                         contents={this.props.Document.Title}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss
index c38787802..c5b8fc5e8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss
@@ -1,4 +1,4 @@
-@import "global_variables";
+@import "globalCssVariables";
 
 .collectionFreeFormRemoteCursors-cont {
     
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 81f2146e4..f3ac4a383 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -1,4 +1,4 @@
-@import "../../global_variables";
+@import "../../globalCssVariables";
 .collectionfreeformview-measure {
     position: absolute;
     top: 0;
@@ -28,8 +28,10 @@
     // background-size: 30px 30px;
   // }
 
+  border-width: $COLLECTION_BORDER_WIDTH;
   box-shadow: $intermediate-color 0.2vw 0.2vw 0.8vw;
-  border: 0px solid $light-color-secondary;
+  border-color:  $light-color-secondary;
+  border-style: solid; 
   border-radius: $border-radius;
   box-sizing: border-box;
   position: absolute;
@@ -49,7 +51,9 @@
   }
  
   opacity: 0.99;
-  border: 0px solid transparent;
+  border-width: 0;
+  border-color: transparent;
+  border-style: solid; 
   border-radius: $border-radius;
   box-sizing: border-box;
   position:absolute;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 01ebbe0e1..e38e71a68 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,31 +1,31 @@
-import { action, computed, observable, trace, ObservableSet, runInAction } from "mobx";
+import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
+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";
 import { DragManager } from "../../../util/DragManager";
+import { SelectionManager } from "../../../util/SelectionManager";
 import { Transform } from "../../../util/Transform";
 import { undoBatch } from "../../../util/UndoManager";
+import * as globalCssVariables from "../../../views/globalCssVariables.scss";
 import { InkingCanvas } from "../../InkingCanvas";
+import { Main } from "../../Main";
 import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
 import { DocumentContentsView } from "../../nodes/DocumentContentsView";
 import { DocumentViewProps } from "../../nodes/DocumentView";
-import { COLLECTION_BORDER_WIDTH } from "../CollectionBaseView";
 import { CollectionSubView } from "../CollectionSubView";
 import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView";
+import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
 import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
+import { PreviewCursor } from "./PreviewCursor";
 import React = require("react");
 import v5 = require("uuid/v5");
-import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
-import { PreviewCursor } from "./PreviewCursor";
-import { DocumentManager } from "../../../util/DocumentManager";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { NumberField } from "../../../../fields/NumberField";
-import { Main } from "../../Main";
-import Measure from "react-measure";
-import { returnFalse, emptyFunction } from "../../../../Utils";
 
 @observer
 export class CollectionFreeFormView extends CollectionSubView {
@@ -297,8 +297,12 @@ export class CollectionFreeFormView extends CollectionSubView {
                 layoutKey={KeyStore.OverlayLayout} isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
     }
 
-    getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform());
-    getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH);
+    @computed
+    get borderWidth() {
+        return this.isAnnotationOverlay ? 0 : globalCssVariables.COLLECTION_BORDER_WIDTH;
+    }
+    getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform());
+    getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
     getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.scale).translate(this.panX, this.panY);
     noScaling = () => 1;
     childViews = () => this.views;
@@ -317,8 +321,7 @@ export class CollectionFreeFormView extends CollectionSubView {
                     <div className={`collectionfreeformview-measure`} ref={measureRef}>
                         <div className={`collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`}
                             onPointerDown={this.onPointerDown} onPointerMove={(e) => super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY))}
-                            onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} onWheel={this.onPointerWheel}
-                            style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} ref={this.createDropTarget}>
+                            onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} onWheel={this.onPointerWheel} ref={this.createDropTarget}>
                             <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
                                 addDocument={this.addDocument} removeDocument={this.props.removeDocument}
                                 getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss
new file mode 100644
index 000000000..5c8e9c8fc
--- /dev/null
+++ b/src/client/views/globalCssVariables.scss
@@ -0,0 +1,29 @@
+@import url("https://fonts.googleapis.com/css?family=Noto+Sans:400,700|Crimson+Text:400,400i,700");
+// colors
+$light-color: #fcfbf7;
+$light-color-secondary: rgb(241, 239, 235);
+$main-accent: #61aaa3;
+// $alt-accent: #cdd5ec;
+// $alt-accent: #cdeceb;
+$alt-accent: #59dff7;
+$lighter-alt-accent: rgb(207, 220, 240);
+$intermediate-color: #9c9396;
+$dark-color: #121721;
+// fonts
+$sans-serif: "Noto Sans", sans-serif;
+// $sans-serif: "Roboto Slab", sans-serif;
+$serif: "Crimson Text", serif;
+// misc values
+$border-radius: 0.3em;
+//
+
+                            // dragged items
+$contextMenu-zindex: 1000;  // context menu shows up over everything
+$mainTextInput-zindex: 999; // then text input overlay so that it's context menu will appear over decorations, etc
+$docDecorations-zindex: 998; // then doc decorations appear over everything else
+$remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right?
+$COLLECTION_BORDER_WIDTH: 1;
+:export  {
+    contextMenuZindex: $contextMenu-zindex;
+    COLLECTION_BORDER_WIDTH: $COLLECTION_BORDER_WIDTH;
+}
\ No newline at end of file
diff --git a/src/client/views/globalCssVariables.scss.d.ts b/src/client/views/globalCssVariables.scss.d.ts
new file mode 100644
index 000000000..e874b815d
--- /dev/null
+++ b/src/client/views/globalCssVariables.scss.d.ts
@@ -0,0 +1,8 @@
+
+interface IGlobalScss {
+    contextMenuZindex: string;  // context menu shows up over everything
+    COLLECTION_BORDER_WIDTH: number;
+}
+declare const globalCssVariables: IGlobalScss;
+
+export = globalCssVariables;
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 5126e69f9..039d1bcc6 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -1,4 +1,4 @@
-@import "../global_variables";
+@import "../globalCssVariables";
 
 .documentView-node {
   position: absolute;
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index d2ba52cf9..3978c3d38 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -1,4 +1,4 @@
-@import "../global_variables";
+@import "../globalCssVariables";
 .ProseMirror {
   width: 100%;
   height: auto;
diff --git a/src/client/views/nodes/KeyValueBox.scss b/src/client/views/nodes/KeyValueBox.scss
index 63ae75424..f60edc675 100644
--- a/src/client/views/nodes/KeyValueBox.scss
+++ b/src/client/views/nodes/KeyValueBox.scss
@@ -1,4 +1,4 @@
-@import "../global_variables";
+@import "../globalCssVariables";
 .keyValueBox-cont {
     overflow-y: scroll;
     height: 100%;
diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss
index 64e871e1c..0bb92e5a3 100644
--- a/src/client/views/nodes/KeyValuePair.scss
+++ b/src/client/views/nodes/KeyValuePair.scss
@@ -1,4 +1,4 @@
-@import "../global_variables";
+@import "../globalCssVariables";
 
 .container{
     display: flex;                 
diff --git a/src/client/views/nodes/LinkBox.scss b/src/client/views/nodes/LinkBox.scss
index 5d5f782d2..8bc70b48f 100644
--- a/src/client/views/nodes/LinkBox.scss
+++ b/src/client/views/nodes/LinkBox.scss
@@ -1,4 +1,4 @@
-@import "../global_variables";
+@import "../globalCssVariables";
 .link-container {
     width: 100%;
     height: 35px;
diff --git a/src/client/views/nodes/LinkEditor.scss b/src/client/views/nodes/LinkEditor.scss
index fb0c69cff..ea2e7289c 100644
--- a/src/client/views/nodes/LinkEditor.scss
+++ b/src/client/views/nodes/LinkEditor.scss
@@ -1,4 +1,4 @@
-@import "../global_variables";
+@import "../globalCssVariables";
 .edit-container {
     width: 100%;
     height: auto;
-- 
cgit v1.2.3-70-g09d2


From c694f7d7d9fbb88f2b2fa6ebb6093e9265a0b0b7 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 12 Apr 2019 16:04:08 -0400
Subject: moved previewcursor to main

---
 src/client/views/Main.tsx                          | 37 +++++++++++++++++
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  3 +-
 .../collectionFreeForm/PreviewCursor.tsx           | 47 +++-------------------
 3 files changed, 44 insertions(+), 43 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 419de6fc1..ac00be63c 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -40,7 +40,43 @@ import { DocumentView } from './nodes/DocumentView';
 import { FormattedTextBox } from './nodes/FormattedTextBox';
 import { REPLCommand } from 'repl';
 import { Key } from '../../fields/Key';
+import { truncate } from 'fs';
 
+
+export interface PromptProps {
+}
+
+@observer
+export class PreviewCursorPrompt extends React.Component<PromptProps> {
+    private _prompt = React.createRef<HTMLDivElement>();
+    //when focus is lost, this will remove the preview cursor
+    @action onBlur = (): void => {
+        PreviewCursorPrompt.Visible = false;
+        PreviewCursorPrompt.hide();
+    }
+
+    @observable static clickPoint = [0, 0];
+    @observable public static Visible = false;
+    @observable public static hide = () => { };
+    @action
+    public static Show(hide: any, x: number, y: number) {
+        this.clickPoint = [x, y];
+        this.hide = hide;
+        setTimeout(action(() => this.Visible = true), (1));
+    }
+    render() {
+        if (!PreviewCursorPrompt.clickPoint)
+            return (null);
+        if (PreviewCursorPrompt.Visible && this._prompt.current) {
+            this._prompt.current.focus();
+        }
+        let p = PreviewCursorPrompt.clickPoint;
+        return <div className="previewCursor" id="previewCursor" onBlur={this.onBlur} tabIndex={0} ref={this._prompt}
+            style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, opacity: PreviewCursorPrompt.Visible ? 1 : 0 }}>
+            I
+        </div >;
+    }
+}
 @observer
 export class Main extends React.Component {
     // dummy initializations keep the compiler happy
@@ -392,6 +428,7 @@ export class Main extends React.Component {
                         {({ measureRef }) =>
                             <div ref={measureRef} id="mainContent-div">
                                 {this.mainContent}
+                                <PreviewCursorPrompt />
                             </div>
                         }
                     </Measure>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index e38e71a68..dae62f126 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -325,8 +325,7 @@ export class CollectionFreeFormView extends CollectionSubView {
                             <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
                                 addDocument={this.addDocument} removeDocument={this.props.removeDocument}
                                 getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
-                                <PreviewCursor container={this} addLiveTextDocument={this.addLiveTextBox}
-                                    getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} >
+                                <PreviewCursor container={this} addLiveTextDocument={this.addLiveTextBox} getTransform={this.getTransform} >
                                     <div className="collectionfreeformview" ref={this._canvasRef}
                                         style={{ transform: `translate(${dx}px, ${dy}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}>
                                         {blay}
diff --git a/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx b/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx
index 8eabb020a..64da8ecc6 100644
--- a/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx
+++ b/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx
@@ -6,12 +6,11 @@ import { Transform } from "../../../util/Transform";
 import { CollectionFreeFormView } from "./CollectionFreeFormView";
 import "./PreviewCursor.scss";
 import React = require("react");
-import { interfaceDeclaration } from "babel-types";
+import { Main, PreviewCursorPrompt } from "../../Main";
 
 
 export interface PreviewCursorProps {
     getTransform: () => Transform;
-    getContainerTransform: () => Transform;
     container: CollectionFreeFormView;
     addLiveTextDocument: (doc: Document) => void;
 }
@@ -20,7 +19,6 @@ export interface PreviewCursorProps {
 export class PreviewCursor extends React.Component<PreviewCursorProps>  {
     @observable _lastX: number = 0;
     @observable _lastY: number = 0;
-    @observable public _visible: boolean = false;
     @observable public DownX: number = 0;
     @observable public DownY: number = 0;
     _showOnUp: boolean = false;
@@ -46,17 +44,14 @@ export class PreviewCursor extends React.Component<PreviewCursorProps>  {
     onPointerMove = (e: PointerEvent): void => {
         if (Math.abs(this.DownX - e.clientX) > 4 || Math.abs(this.DownY - e.clientY) > 4) {
             this._showOnUp = false;
-            this._visible = false;
+            PreviewCursorPrompt.Visible = false;
         }
     }
 
-    @action
     onPointerUp = (e: PointerEvent): void => {
         if (this._showOnUp) {
+            PreviewCursorPrompt.Show(this.hideCursor, this.DownX, this.DownY);
             document.addEventListener("keypress", this.onKeyPress, false);
-            this._lastX = this.DownX;
-            this._lastY = this.DownY;
-            this._visible = true;
         }
         this.cleanupInteractions();
     }
@@ -69,52 +64,22 @@ export class PreviewCursor extends React.Component<PreviewCursorProps>  {
         //if not these keys, make a textbox if preview cursor is active!
         if (!e.ctrlKey && !e.altKey && !e.defaultPrevented && !(e as any).DASHFormattedTextBoxHandled) {
             //make textbox and add it to this collection
-            let [x, y] = this.props.getTransform().transformPoint(this._lastX, this._lastY);
+            let [x, y] = this.props.getTransform().transformPoint(this.DownX, this.DownY);
             let newBox = Documents.TextDocument({ width: 200, height: 100, x: x, y: y, title: "typed text" });
             this.props.addLiveTextDocument(newBox);
-            document.removeEventListener("keypress", this.onKeyPress, false);
-            this._visible = false;
+            PreviewCursorPrompt.Visible = false;
             e.stopPropagation();
         }
     }
 
-    getPoint = () => this.props.getContainerTransform().transformPoint(this._lastX, this._lastY);
-    getVisible = () => this._visible;
-    setVisible = (v: boolean) => {
-        this._visible = v;
+    hideCursor = () => {
         document.removeEventListener("keypress", this.onKeyPress, false);
     }
     render() {
         return (
             <div className="previewCursorView" onPointerDown={this.onPointerDown}>
                 {this.props.children}
-                <PreviewCursorPrompt setVisible={this.setVisible} getPoint={this.getPoint} getVisible={this.getVisible} />
             </div>
         );
     }
-}
-
-export interface PromptProps {
-    getPoint: () => number[];
-    getVisible: () => boolean;
-    setVisible: (v: boolean) => void;
-}
-
-@observer
-export class PreviewCursorPrompt extends React.Component<PromptProps> {
-    private _promptRef = React.createRef<HTMLDivElement>();
-
-    //when focus is lost, this will remove the preview cursor
-    @action onBlur = (): void => this.props.setVisible(false);
-
-    render() {
-        let p = this.props.getPoint();
-        if (this.props.getVisible() && this._promptRef.current) {
-            this._promptRef.current.focus();
-        }
-        return <div className="previewCursor" id="previewCursor" onBlur={this.onBlur} tabIndex={0} ref={this._promptRef}
-            style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, opacity: this.props.getVisible() ? 1 : 0 }}>
-            I
-        </div >;
-    }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 83f269b77aa23b641c0ec6b09fe5696936fb221b Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 12 Apr 2019 16:38:13 -0400
Subject: added undo for creating links.  cleaned up some other code.

---
 .../views/collections/CollectionSchemaView.tsx     | 12 +++--
 src/client/views/collections/CollectionSubView.tsx | 51 +++++++++-------------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 +-
 src/client/views/nodes/DocumentView.tsx            | 14 ++++--
 4 files changed, 38 insertions(+), 43 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 4cf187963..162a39170 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -2,16 +2,17 @@ import React = require("react");
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faCog, faPlus } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, trace, untracked } from "mobx";
+import { action, computed, observable, untracked } from "mobx";
 import { observer } from "mobx-react";
 import Measure from "react-measure";
 import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
 import "react-table/react-table.css";
 import { Document } from "../../../fields/Document";
-import { Field, Opt, FieldWaiting } from "../../../fields/Field";
+import { Field, Opt } from "../../../fields/Field";
 import { Key } from "../../../fields/Key";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ListField } from "../../../fields/ListField";
+import { emptyDocFunction, emptyFunction, returnFalse } from "../../../Utils";
 import { Server } from "../../Server";
 import { SetupDrag } from "../../util/DragManager";
 import { CompileScript, ToField } from "../../util/Scripting";
@@ -22,11 +23,8 @@ import { EditableView } from "../EditableView";
 import { DocumentView } from "../nodes/DocumentView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
-import { CollectionView } from "./CollectionView";
 import { CollectionSubView } from "./CollectionSubView";
-import { TextField } from "../../../fields/TextField";
-import * as globalCssVariables from "../../views/globalCssVariables.scss";
-import { emptyFunction, returnFalse, emptyDocFunction } from "../../../Utils";
+import { COLLECTION_BORDER_WIDTH } from "../../views/globalCssVariables.scss";
 
 
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
@@ -246,7 +244,7 @@ export class CollectionSchemaView extends CollectionSubView {
     }
 
     @computed
-    get borderWidth() { return globalCssVariables.COLLECTION_BORDER_WIDTH; }
+    get borderWidth() { return COLLECTION_BORDER_WIDTH; }
     getContentScaling = (): number => this._contentScaling;
     getPanelWidth = (): number => this._panelWidth;
     getPanelHeight = (): number => this._panelHeight;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 6a6a6c900..d91db68bb 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -166,22 +166,16 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
             let item = e.dataTransfer.items[i];
             if (item.kind === "string" && item.type.indexOf("uri") !== -1) {
                 let str: string;
-                let prom = new Promise<string>(res =>
-                    e.dataTransfer.items[i].getAsString(res)).then(action((s: string) => {
-                        str = s;
-                        return rp.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + s));
-                    })).then(res => {
-                        let type = res.headers["content-type"];
+                let prom = new Promise<string>(resolve => e.dataTransfer.items[i].getAsString(resolve))
+                    .then(action((s: string) => rp.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + (str = s)))))
+                    .then(result => {
+                        let type = result.headers["content-type"];
                         if (type) {
-                            this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 }).then(doc => {
-                                if (doc) {
-                                    this.props.addDocument(doc, false);
-                                }
-                            });
+                            this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 })
+                                .then(doc => doc && this.props.addDocument(doc, false));
                         }
                     });
                 promises.push(prom);
-                // this.props.addDocument(Documents.WebDocument(s, { ...options, width: 300, height: 300 }), false)
             }
             let type = item.type;
             if (item.kind === "file") {
@@ -197,33 +191,30 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
                     method: 'POST',
                     body: formData
                 }).then(async (res: Response) => {
-                    const json = await res.json();
-                    json.map((file: any) => {
+                    (await res.json()).map(action((file: any) => {
                         let path = window.location.origin + file;
-                        runInAction(() => {
-                            let docPromise = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300, title: dropFileName });
+                        let docPromise = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300, title: dropFileName });
 
-                            docPromise.then(doc => runInAction(() => {
-                                let docs = this.props.Document.GetT(KeyStore.Data, ListField);
-                                if (docs !== FieldWaiting) {
-                                    if (!docs) {
-                                        docs = new ListField<Document>();
-                                        this.props.Document.Set(KeyStore.Data, docs);
-                                    }
-                                    if (doc) {
-                                        docs.Data.push(doc);
-                                    }
+                        docPromise.then(action((doc?: Document) => {
+                            let docs = this.props.Document.GetT(KeyStore.Data, ListField);
+                            if (docs !== FieldWaiting) {
+                                if (!docs) {
+                                    docs = new ListField<Document>();
+                                    this.props.Document.Set(KeyStore.Data, docs);
                                 }
-                            }));
-                        });
-                    });
+                                if (doc) {
+                                    docs.Data.push(doc);
+                                }
+                            }
+                        }));
+                    }));
                 });
                 promises.push(prom);
             }
         }
 
         if (promises.length) {
-            Promise.all(promises).catch(emptyFunction).then(() => batch.end());
+            Promise.all(promises).finally(() => batch.end());
         } else {
             batch.end();
         }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index dae62f126..98ebbf1b3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -12,7 +12,7 @@ import { DragManager } from "../../../util/DragManager";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { Transform } from "../../../util/Transform";
 import { undoBatch } from "../../../util/UndoManager";
-import * as globalCssVariables from "../../../views/globalCssVariables.scss";
+import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss";
 import { InkingCanvas } from "../../InkingCanvas";
 import { Main } from "../../Main";
 import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
@@ -299,7 +299,7 @@ export class CollectionFreeFormView extends CollectionSubView {
 
     @computed
     get borderWidth() {
-        return this.isAnnotationOverlay ? 0 : globalCssVariables.COLLECTION_BORDER_WIDTH;
+        return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH;
     }
     getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform());
     getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 06182d1a6..4d7a85316 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -20,6 +20,7 @@ import { ContextMenu } from "../ContextMenu";
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
 import React = require("react");
+import { undoBatch, UndoManager } from "../../util/UndoManager";
 
 
 export interface DocumentViewProps {
@@ -238,6 +239,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         SelectionManager.DeselectAll();
     }
 
+    @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (de.data instanceof DragManager.LinkDragData) {
@@ -248,6 +250,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             destDoc.GetTAsync(KeyStore.Prototype, Document).then(protoDest =>
                 sourceDoc.GetTAsync(KeyStore.Prototype, Document).then(protoSrc =>
                     runInAction(() => {
+                        let batch = UndoManager.StartBatch("document view drop");
                         linkDoc.Set(KeyStore.Title, new TextField("New Link"));
                         linkDoc.Set(KeyStore.LinkDescription, new TextField(""));
                         linkDoc.Set(KeyStore.LinkTags, new TextField("Default"));
@@ -256,20 +259,23 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                         let srcTarg = protoSrc ? protoSrc : sourceDoc;
                         linkDoc.Set(KeyStore.LinkedToDocs, dstTarg);
                         linkDoc.Set(KeyStore.LinkedFromDocs, srcTarg);
-                        dstTarg.GetOrCreateAsync(
+                        const prom1 = new Promise(resolve => dstTarg.GetOrCreateAsync(
                             KeyStore.LinkedFromDocs,
                             ListField,
                             field => {
                                 (field as ListField<Document>).Data.push(linkDoc);
+                                resolve();
                             }
-                        );
-                        srcTarg.GetOrCreateAsync(
+                        ));
+                        const prom2 = new Promise(resolve => srcTarg.GetOrCreateAsync(
                             KeyStore.LinkedToDocs,
                             ListField,
                             field => {
                                 (field as ListField<Document>).Data.push(linkDoc);
+                                resolve();
                             }
-                        );
+                        ));
+                        Promise.all([prom1, prom2]).finally(() => batch.end());
                     })
                 )
             );
-- 
cgit v1.2.3-70-g09d2


From 5557c3b87df3119a3e53d2edb4bbdb9ae1deafc4 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 12 Apr 2019 18:28:54 -0400
Subject: fixed brushing

---
 src/client/Server.ts                               |  3 +++
 src/client/northstar/dash-nodes/HistogramBox.tsx   |  4 ----
 src/client/util/DocumentManager.ts                 |  7 ++++--
 src/client/views/Main.tsx                          | 16 ++++++++-----
 .../views/collections/CollectionBaseView.tsx       | 11 +++++----
 .../CollectionFreeFormLinksView.tsx                | 26 +++++++++++++---------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  1 +
 src/fields/Document.ts                             |  4 ++--
 8 files changed, 42 insertions(+), 30 deletions(-)

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

diff --git a/src/client/Server.ts b/src/client/Server.ts
index 3bbbebe72..fb5ad1c52 100644
--- a/src/client/Server.ts
+++ b/src/client/Server.ts
@@ -123,6 +123,9 @@ export class Server {
                             callback(fieldfromserver);
                         }
                     }
+                    if (!fieldfromserver && callback) {
+                        console.log("Why didn't we get a field?")
+                    }
                 }));
         }
     }
diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index 7df59ef07..2084fc346 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -47,10 +47,6 @@ export class HistogramBox extends React.Component<FieldViewProps> {
             this.BinRanges[1] instanceof AggregateBinRange ? ChartType.VerticalBar : ChartType.HeatMap;
     }
 
-    constructor(props: FieldViewProps) {
-        super(props);
-    }
-
     @action
     dropX = (e: Event, de: DragManager.DropEvent) => {
         if (de.data instanceof DragManager.DocumentDragData) {
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index f38b8ca75..b62287de8 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -28,8 +28,11 @@ export class DocumentManager {
     }
 
     public getAllDocumentViews(collection: Document) {
-        return this.DocumentViews.filter(dv =>
-            dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document === collection);
+        return this.DocumentViews.filter(dv => {
+            console.log((dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document ?
+                dv.props.ContainingCollectionView.props.Document.Title : "--") + " = " + collection.Title);
+            return dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document === collection;
+        });
     }
 
     public getDocumentView(toFind: Document): DocumentView | null {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index ac00be63c..3cf8f7c26 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -448,11 +448,11 @@ export class Main extends React.Component {
     @action AddToNorthstarCatalog(ctlog: Catalog) {
         CurrentUserUtils.NorthstarDBCatalog = CurrentUserUtils.NorthstarDBCatalog ? CurrentUserUtils.NorthstarDBCatalog : ctlog;
         if (ctlog && ctlog.schemas) {
-            this._northstarSchemas.push(...ctlog.schemas.map(schema => {
-                let schemaDoc = Documents.TreeDocument([], { width: 50, height: 100, title: schema.displayName! });
-                let schemaDocuments = schemaDoc.GetList(KeyStore.Data, [] as Document[]);
+            ctlog.schemas.map(schema => {
+                let promises: Promise<void>[] = [];
+                let schemaDocuments: Document[] = [];
                 CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => {
-                    Server.GetField(attr.displayName! + ".alias", action((field: Opt<Field>) => {
+                    let prom = Server.GetField(attr.displayName! + ".alias").then(action((field: Opt<Field>) => {
                         if (field instanceof Document) {
                             schemaDocuments.push(field);
                         } else {
@@ -464,9 +464,13 @@ export class Main extends React.Component {
                             schemaDocuments.push(Documents.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! }, undefined, attr.displayName! + ".alias"));
                         }
                     }));
+                    promises.push(prom);
                 });
-                return schemaDoc;
-            }));
+                Promise.all(promises).finally(() => {
+                    let schemaDoc = Documents.TreeDocument(schemaDocuments, { width: 50, height: 100, title: schema.displayName! });
+                    this._northstarSchemas.push(schemaDoc);
+                })
+            })
         }
     }
     async initializeNorthstar(): Promise<void> {
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 0ace700ce..b5eaab349 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -8,8 +8,6 @@ import { ListField } from '../../../fields/ListField';
 import { NumberField } from '../../../fields/NumberField';
 import { ContextMenu } from '../ContextMenu';
 import { FieldViewProps } from '../nodes/FieldView';
-import { CompileScript } from '../../util/Scripting';
-import { ScriptField } from '../../../fields/ScriptField';
 
 export enum CollectionViewType {
     Invalid,
@@ -37,15 +35,15 @@ export interface CollectionViewProps extends FieldViewProps {
 
 @observer
 export class CollectionBaseView extends React.Component<CollectionViewProps> {
-    get collectionViewType(): CollectionViewType {
+    get collectionViewType(): CollectionViewType | undefined {
         let Document = this.props.Document;
         let viewField = Document.GetT(KeyStore.ViewType, NumberField);
         if (viewField === FieldWaiting) {
-            return CollectionViewType.Invalid;
+            return undefined;
         } else if (viewField) {
             return viewField.Data;
         } else {
-            return CollectionViewType.Freeform;
+            return CollectionViewType.Invalid;
         }
     }
 
@@ -181,9 +179,10 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
             active: this.active,
             onActiveChanged: this.onActiveChanged,
         };
+        const viewtype = this.collectionViewType;
         return (
             <div className={this.props.className || "collectionView-cont"} onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}>
-                {this.props.children(this.collectionViewType, props)}
+                {viewtype !== undefined ? this.props.children(viewtype, props) : (null)}
             </div>
         );
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index cf058090d..40d7b25d3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,4 +1,4 @@
-import { computed, reaction } from "mobx";
+import { computed, reaction, trace, IReactionDisposer } from "mobx";
 import { observer } from "mobx-react";
 import { Document } from "../../../../fields/Document";
 import { FieldWaiting } from "../../../../fields/Field";
@@ -15,18 +15,19 @@ import React = require("react");
 @observer
 export class CollectionFreeFormLinksView extends React.Component<CollectionViewProps> {
 
-    HackToAvoidReactionFiringUnnecessarily?: Document = undefined;
+    _brushReactionDisposer?: IReactionDisposer;
     componentDidMount() {
-        this.HackToAvoidReactionFiringUnnecessarily = this.props.Document;
-        reaction(() =>
-            DocumentManager.Instance.getAllDocumentViews(this.HackToAvoidReactionFiringUnnecessarily!).
-                map(dv => dv.props.Document.GetNumber(KeyStore.X, 0)),
+        this._brushReactionDisposer = reaction(() => {
+            // trace();
+            return this.props.Document.GetList<Document>(this.props.fieldKey, []).map(doc => doc.GetNumber(KeyStore.X, 0));
+        },
             () => {
-                let views = DocumentManager.Instance.getAllDocumentViews(this.props.Document);
+                console.log("title = " + this.props.Document.Title);
+                let views = this.props.Document.GetList<Document>(this.props.fieldKey, []);
                 for (let i = 0; i < views.length; i++) {
                     for (let j = 0; j < views.length; j++) {
-                        let srcDoc = views[j].props.Document;
-                        let dstDoc = views[i].props.Document;
+                        let srcDoc = views[j];
+                        let dstDoc = views[i];
                         let x1 = srcDoc.GetNumber(KeyStore.X, 0);
                         let x1w = srcDoc.GetNumber(KeyStore.Width, -1);
                         let x2 = dstDoc.GetNumber(KeyStore.X, 0);
@@ -53,7 +54,7 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
                             linkDoc.SetText(KeyStore.LinkDescription, "Brush between " + srcTarg.Title + " and " + dstTarg.Title);
                             linkDoc.SetData(KeyStore.BrushingDocs, [dstTarg, srcTarg], ListField);
 
-                            brushAction = brushAction = (field: ListField<Document>) => {
+                            brushAction = (field: ListField<Document>) => {
                                 if (findBrush(field) === -1) {
                                     console.log("ADD BRUSH " + srcTarg.Title + " " + dstTarg.Title);
                                     (findBrush(field) === -1) && field.Data.push(linkDoc);
@@ -67,6 +68,11 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
                 }
             });
     }
+    componentWillUnmount() {
+        if (this._brushReactionDisposer) {
+            this._brushReactionDisposer();
+        }
+    }
     documentAnchors(view: DocumentView) {
         let equalViews = [view];
         let containerDoc = view.props.Document.GetT(KeyStore.AnnotationOn, Document);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 98ebbf1b3..6b9f4fdfd 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -315,6 +315,7 @@ export class CollectionFreeFormView extends CollectionSubView {
         const blay = this.backgroundView;
         const olay = this.overlayView;
 
+        console.log("tv = " + this.props.Document.Title);
         return (
             <Measure onResize={(r: any) => runInAction(() => { this._pwidth = r.entry.width; this._pheight = r.entry.height; })}>
                 {({ measureRef }) => (
diff --git a/src/fields/Document.ts b/src/fields/Document.ts
index 60eaf5b51..628fe684c 100644
--- a/src/fields/Document.ts
+++ b/src/fields/Document.ts
@@ -41,14 +41,14 @@ export class Document extends Field {
     @computed
     public get Title(): string {
         let title = this.Get(KeyStore.Title, true);
-        if (title) {
+        if (title || title === FieldWaiting) {
             if (title !== FieldWaiting && title instanceof TextField) {
                 return title.Data;
             }
             else return "-waiting-";
         }
         let parTitle = this.GetT(KeyStore.Title, TextField);
-        if (parTitle) {
+        if (parTitle || parTitle === FieldWaiting) {
             if (parTitle !== FieldWaiting) return parTitle.Data + ".alias";
             else return "-waiting-.alias";
         }
-- 
cgit v1.2.3-70-g09d2


From 9c162a938e861509e2e32e7b63409f037e2da284 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 12 Apr 2019 18:34:17 -0400
Subject: removed log stuff

---
 src/client/Server.ts                                                | 3 ---
 src/client/util/DocumentManager.ts                                  | 6 +-----
 .../collections/collectionFreeForm/CollectionFreeFormLinksView.tsx  | 6 +-----
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 1 -
 4 files changed, 2 insertions(+), 14 deletions(-)

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

diff --git a/src/client/Server.ts b/src/client/Server.ts
index fb5ad1c52..3bbbebe72 100644
--- a/src/client/Server.ts
+++ b/src/client/Server.ts
@@ -123,9 +123,6 @@ export class Server {
                             callback(fieldfromserver);
                         }
                     }
-                    if (!fieldfromserver && callback) {
-                        console.log("Why didn't we get a field?")
-                    }
                 }));
         }
     }
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index b62287de8..3e093c8dc 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -28,11 +28,7 @@ export class DocumentManager {
     }
 
     public getAllDocumentViews(collection: Document) {
-        return this.DocumentViews.filter(dv => {
-            console.log((dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document ?
-                dv.props.ContainingCollectionView.props.Document.Title : "--") + " = " + collection.Title);
-            return dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document === collection;
-        });
+        return this.DocumentViews.filter(dv => dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document === collection);
     }
 
     public getDocumentView(toFind: Document): DocumentView | null {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 40d7b25d3..647c83d4d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -17,12 +17,8 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
 
     _brushReactionDisposer?: IReactionDisposer;
     componentDidMount() {
-        this._brushReactionDisposer = reaction(() => {
-            // trace();
-            return this.props.Document.GetList<Document>(this.props.fieldKey, []).map(doc => doc.GetNumber(KeyStore.X, 0));
-        },
+        this._brushReactionDisposer = reaction(() => this.props.Document.GetList<Document>(this.props.fieldKey, []).map(doc => doc.GetNumber(KeyStore.X, 0)),
             () => {
-                console.log("title = " + this.props.Document.Title);
                 let views = this.props.Document.GetList<Document>(this.props.fieldKey, []);
                 for (let i = 0; i < views.length; i++) {
                     for (let j = 0; j < views.length; j++) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 6b9f4fdfd..98ebbf1b3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -315,7 +315,6 @@ export class CollectionFreeFormView extends CollectionSubView {
         const blay = this.backgroundView;
         const olay = this.overlayView;
 
-        console.log("tv = " + this.props.Document.Title);
         return (
             <Measure onResize={(r: any) => runInAction(() => { this._pwidth = r.entry.width; this._pheight = r.entry.height; })}>
                 {({ measureRef }) => (
-- 
cgit v1.2.3-70-g09d2


From db9ee4f0d710d132e33e48cf6f105fd945941003 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 12 Apr 2019 21:18:30 -0400
Subject: combined preview cursor with marquee.  moved preview cursor prompt to
 its own file.

---
 src/client/views/DocumentDecorations.scss          |  2 +
 src/client/views/Main.scss                         |  2 +-
 src/client/views/Main.tsx                          | 39 +---------
 src/client/views/PreviewCursor.scss                |  9 +++
 src/client/views/PreviewCursor.tsx                 | 37 ++++++++++
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 33 ++++-----
 .../collections/collectionFreeForm/MarqueeView.tsx | 43 +++++++++--
 .../collectionFreeForm/PreviewCursor.scss          | 27 -------
 .../collectionFreeForm/PreviewCursor.tsx           | 85 ----------------------
 9 files changed, 103 insertions(+), 174 deletions(-)
 create mode 100644 src/client/views/PreviewCursor.scss
 create mode 100644 src/client/views/PreviewCursor.tsx
 delete mode 100644 src/client/views/collections/collectionFreeForm/PreviewCursor.scss
 delete mode 100644 src/client/views/collections/collectionFreeForm/PreviewCursor.tsx

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

diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index ce84b6106..c1a949639 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -78,6 +78,7 @@
     grid-column-end: 6;
     pointer-events: all;
     text-align: center;
+    cursor: pointer;
 }
 .documentDecorations-minimizeButton {
     background:$alt-accent;
@@ -86,6 +87,7 @@
     grid-column-end: 3;
     pointer-events: all;
     text-align: center;
+    cursor: pointer;
 }
 .documentDecorations-background {
     background: lightblue;
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index f3af26f37..13cadb10d 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -184,7 +184,7 @@ button:hover {
         position:absolute;
         top: 0;
         left: 0;
-}
+    }
 }
 #mainContent-div {
     width:100%;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 80e19c4f5..ed61aa5a7 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -40,44 +40,9 @@ import { DocumentView } from './nodes/DocumentView';
 import { FormattedTextBox } from './nodes/FormattedTextBox';
 import { REPLCommand } from 'repl';
 import { Key } from '../../fields/Key';
-import { truncate } from 'fs';
+import { PreviewCursor } from './PreviewCursor';
 
 
-export interface PromptProps {
-}
-
-@observer
-export class PreviewCursorPrompt extends React.Component<PromptProps> {
-    private _prompt = React.createRef<HTMLDivElement>();
-    //when focus is lost, this will remove the preview cursor
-    @action onBlur = (): void => {
-        PreviewCursorPrompt.Visible = false;
-        PreviewCursorPrompt.hide();
-    }
-
-    @observable static clickPoint = [0, 0];
-    @observable public static Visible = false;
-    @observable public static hide = () => { };
-    @action
-    public static Show(hide: any, x: number, y: number) {
-        this.clickPoint = [x, y];
-        this.hide = hide;
-        setTimeout(action(() => this.Visible = true), (1));
-    }
-    render() {
-        if (!PreviewCursorPrompt.clickPoint) {
-            return (null);
-        }
-        if (PreviewCursorPrompt.Visible && this._prompt.current) {
-            this._prompt.current.focus();
-        }
-        let p = PreviewCursorPrompt.clickPoint;
-        return <div className="previewCursor" id="previewCursor" onBlur={this.onBlur} tabIndex={0} ref={this._prompt}
-            style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, opacity: PreviewCursorPrompt.Visible ? 1 : 0 }}>
-            I
-        </div >;
-    }
-}
 @observer
 export class Main extends React.Component {
     // dummy initializations keep the compiler happy
@@ -429,7 +394,7 @@ export class Main extends React.Component {
                         {({ measureRef }) =>
                             <div ref={measureRef} id="mainContent-div">
                                 {this.mainContent}
-                                <PreviewCursorPrompt />
+                                <PreviewCursor />
                             </div>
                         }
                     </Measure>
diff --git a/src/client/views/PreviewCursor.scss b/src/client/views/PreviewCursor.scss
new file mode 100644
index 000000000..20f9b9a49
--- /dev/null
+++ b/src/client/views/PreviewCursor.scss
@@ -0,0 +1,9 @@
+
+.previewCursor {
+    color: black;
+    position: absolute;
+    transform-origin: left top;
+    top: 0;
+    left:0;
+    pointer-events: none;
+}
\ No newline at end of file
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
new file mode 100644
index 000000000..ff8434681
--- /dev/null
+++ b/src/client/views/PreviewCursor.tsx
@@ -0,0 +1,37 @@
+import { action, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import "normalize.css";
+import * as React from 'react';
+import "./PreviewCursor.scss";
+
+@observer
+export class PreviewCursor extends React.Component<{}> {
+    private _prompt = React.createRef<HTMLDivElement>();
+    //when focus is lost, this will remove the preview cursor
+    @action onBlur = (): void => {
+        PreviewCursor.Visible = false;
+        PreviewCursor.hide();
+    }
+
+    @observable static clickPoint = [0, 0];
+    @observable public static Visible = false;
+    @observable public static hide = () => { };
+    @action
+    public static Show(hide: any, x: number, y: number) {
+        this.clickPoint = [x, y];
+        this.hide = hide;
+        setTimeout(action(() => this.Visible = true), (1));
+    }
+    render() {
+        if (!PreviewCursor.clickPoint) {
+            return (null);
+        }
+        if (PreviewCursor.Visible && this._prompt.current) {
+            this._prompt.current.focus();
+        }
+        return <div className="previewCursor" id="previewCursor" onBlur={this.onBlur} tabIndex={0} ref={this._prompt}
+            style={{ transform: `translate(${PreviewCursor.clickPoint[0]}px, ${PreviewCursor.clickPoint[1]}px)`, opacity: PreviewCursor.Visible ? 1 : 0 }}>
+            I
+        </div >;
+    }
+}
\ 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 98ebbf1b3..e19dc98fa 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -23,7 +23,6 @@ import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView";
 import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
 import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
-import { PreviewCursor } from "./PreviewCursor";
 import React = require("react");
 import v5 = require("uuid/v5");
 
@@ -311,9 +310,9 @@ export class CollectionFreeFormView extends CollectionSubView {
         const [dx, dy] = [this.centeringShiftX, this.centeringShiftY];
         const panx: number = -this.props.Document.GetNumber(KeyStore.PanX, 0);
         const pany: number = -this.props.Document.GetNumber(KeyStore.PanY, 0);
-        const zoom: number = this.zoomScaling;
-        const blay = this.backgroundView;
-        const olay = this.overlayView;
+        const zoom: number = this.zoomScaling;// needs to be a variable outside of the <Measure> otherwise, reactions won't fire
+        const backgroundView = this.backgroundView; // needs to be a variable outside of the <Measure> otherwise, reactions won't fire
+        const overlayView = this.overlayView;// needs to be a variable outside of the <Measure> otherwise, reactions won't fire
 
         return (
             <Measure onResize={(r: any) => runInAction(() => { this._pwidth = r.entry.width; this._pheight = r.entry.height; })}>
@@ -323,21 +322,19 @@ export class CollectionFreeFormView extends CollectionSubView {
                             onPointerDown={this.onPointerDown} onPointerMove={(e) => super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY))}
                             onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} onWheel={this.onPointerWheel} ref={this.createDropTarget}>
                             <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
-                                addDocument={this.addDocument} removeDocument={this.props.removeDocument}
+                                addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox}
                                 getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
-                                <PreviewCursor container={this} addLiveTextDocument={this.addLiveTextBox} getTransform={this.getTransform} >
-                                    <div className="collectionfreeformview" ref={this._canvasRef}
-                                        style={{ transform: `translate(${dx}px, ${dy}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}>
-                                        {blay}
-                                        <CollectionFreeFormLinksView {...this.props}>
-                                            <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
-                                                {this.childViews}
-                                            </InkingCanvas>
-                                        </CollectionFreeFormLinksView>
-                                        <CollectionFreeFormRemoteCursors {...this.props} />
-                                    </div>
-                                    {olay}
-                                </PreviewCursor>
+                                <div className="collectionfreeformview" ref={this._canvasRef}
+                                    style={{ transform: `translate(${dx}px, ${dy}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}>
+                                    {backgroundView}
+                                    <CollectionFreeFormLinksView {...this.props}>
+                                        <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
+                                            {this.childViews}
+                                        </InkingCanvas>
+                                    </CollectionFreeFormLinksView>
+                                    <CollectionFreeFormRemoteCursors {...this.props} />
+                                </div>
+                                {overlayView}
                             </MarqueeView>
                         </div>
                     </div>)}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 1e6faafb3..9c0d75c22 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable, trace } from "mobx";
+import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Document } from "../../../../fields/Document";
 import { FieldWaiting } from "../../../../fields/Field";
@@ -8,9 +8,9 @@ import { Documents } from "../../../documents/Documents";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { Transform } from "../../../util/Transform";
 import { InkingCanvas } from "../../InkingCanvas";
+import { PreviewCursor } from "../../PreviewCursor";
 import { CollectionFreeFormView } from "./CollectionFreeFormView";
 import "./MarqueeView.scss";
-import { PreviewCursor } from "./PreviewCursor";
 import React = require("react");
 
 interface MarqueeViewProps {
@@ -21,6 +21,7 @@ interface MarqueeViewProps {
     activeDocuments: () => Document[];
     selectDocuments: (docs: Document[]) => void;
     removeDocument: (doc: Document) => boolean;
+    addLiveTextDocument: (doc: Document) => void;
 }
 
 @observer
@@ -32,6 +33,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     @observable _downY: number = 0;
     @observable _used: boolean = false;
     @observable _visible: boolean = false;
+    _showOnUp: boolean = false;
     static DRAG_THRESHOLD = 4;
 
     @action
@@ -46,12 +48,32 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         this._visible = false;
     }
 
+    @action
+    onKeyPress = (e: KeyboardEvent) => {
+        // Mixing events between React and Native is finicky.  In FormattedTextBox, we set the
+        // DASHFormattedTextBoxHandled flag when a text box consumes a key press so that we can ignore
+        // the keyPress here.
+        //if not these keys, make a textbox if preview cursor is active!
+        if (!e.ctrlKey && !e.altKey && !e.defaultPrevented && !(e as any).DASHFormattedTextBoxHandled) {
+            //make textbox and add it to this collection
+            let [x, y] = this.props.getTransform().transformPoint(this._downX, this._downY);
+            let newBox = Documents.TextDocument({ width: 200, height: 100, x: x, y: y, title: "typed text" });
+            this.props.addLiveTextDocument(newBox);
+            PreviewCursor.Visible = false;
+            e.stopPropagation();
+        }
+    }
+    hideCursor = () => {
+        document.removeEventListener("keypress", this.onKeyPress, false);
+    }
     @action
     onPointerDown = (e: React.PointerEvent): void => {
         if (e.buttons === 1 && !e.altKey && !e.metaKey && this.props.container.props.active()) {
             this._downX = this._lastX = e.pageX;
             this._downY = this._lastY = e.pageY;
             this._used = false;
+            this._showOnUp = true;
+            document.removeEventListener("keypress", this.onKeyPress, false);
             document.addEventListener("pointermove", this.onPointerMove, true);
             document.addEventListener("pointerup", this.onPointerUp, true);
             document.addEventListener("keydown", this.marqueeCommand, true);
@@ -63,6 +85,10 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         this._lastX = e.pageX;
         this._lastY = e.pageY;
         if (!e.cancelBubble) {
+            if (Math.abs(this._downX - e.clientX) > 4 || Math.abs(this._downY - e.clientY) > 4) {
+                this._showOnUp = false;
+                PreviewCursor.Visible = false;
+            }
             if (!this._used && e.buttons === 1 && !e.altKey && !e.metaKey &&
                 (Math.abs(this._lastX - this._downX) > MarqueeView.DRAG_THRESHOLD || Math.abs(this._lastY - this._downY) > MarqueeView.DRAG_THRESHOLD)) {
                 this._visible = true;
@@ -76,11 +102,16 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     onPointerUp = (e: PointerEvent): void => {
         this.cleanupInteractions(true);
         this._visible = false;
-        let mselect = this.marqueeSelect();
-        if (!e.shiftKey) {
-            SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document);
+        if (this._showOnUp) {
+            PreviewCursor.Show(this.hideCursor, this._downX, this._downY);
+            document.addEventListener("keypress", this.onKeyPress, false);
+        } else {
+            let mselect = this.marqueeSelect();
+            if (!e.shiftKey) {
+                SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document);
+            }
+            this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document]);
         }
-        this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document]);
     }
 
     intersectRect(r1: { left: number, top: number, width: number, height: number },
diff --git a/src/client/views/collections/collectionFreeForm/PreviewCursor.scss b/src/client/views/collections/collectionFreeForm/PreviewCursor.scss
deleted file mode 100644
index 7a67c29bf..000000000
--- a/src/client/views/collections/collectionFreeForm/PreviewCursor.scss
+++ /dev/null
@@ -1,27 +0,0 @@
-
-.previewCursor {
-    color: black;
-    position: absolute;
-    transform-origin: left top;
-    top: 0;
-    left:0;
-    pointer-events: none;
-}
-.previewCursorView {
-    top: 0;
-    left:0;
-    position: absolute;
-    width:100%;
-    height:100%;
-}
-
-//this is an animation for the blinking cursor!
-// @keyframes blink {
-// 	0% {opacity: 0}
-// 	49%{opacity: 0}
-// 	50% {opacity: 1}
-// }
-
-// #previewCursor {
-//      animation: blink 1s infinite;
-// }
\ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx b/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx
deleted file mode 100644
index 64da8ecc6..000000000
--- a/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import { action, observable, trace, computed, reaction } from "mobx";
-import { observer } from "mobx-react";
-import { Document } from "../../../../fields/Document";
-import { Documents } from "../../../documents/Documents";
-import { Transform } from "../../../util/Transform";
-import { CollectionFreeFormView } from "./CollectionFreeFormView";
-import "./PreviewCursor.scss";
-import React = require("react");
-import { Main, PreviewCursorPrompt } from "../../Main";
-
-
-export interface PreviewCursorProps {
-    getTransform: () => Transform;
-    container: CollectionFreeFormView;
-    addLiveTextDocument: (doc: Document) => void;
-}
-
-@observer
-export class PreviewCursor extends React.Component<PreviewCursorProps>  {
-    @observable _lastX: number = 0;
-    @observable _lastY: number = 0;
-    @observable public DownX: number = 0;
-    @observable public DownY: number = 0;
-    _showOnUp: boolean = false;
-
-    @action
-    cleanupInteractions = () => {
-        document.removeEventListener("pointerup", this.onPointerUp, true);
-        document.removeEventListener("pointermove", this.onPointerMove, true);
-    }
-
-    @action
-    onPointerDown = (e: React.PointerEvent) => {
-        if (e.button === 0 && this.props.container.props.active()) {
-            document.removeEventListener("keypress", this.onKeyPress, false);
-            this._showOnUp = true;
-            this.DownX = e.pageX;
-            this.DownY = e.pageY;
-            document.addEventListener("pointerup", this.onPointerUp, true);
-            document.addEventListener("pointermove", this.onPointerMove, true);
-        }
-    }
-    @action
-    onPointerMove = (e: PointerEvent): void => {
-        if (Math.abs(this.DownX - e.clientX) > 4 || Math.abs(this.DownY - e.clientY) > 4) {
-            this._showOnUp = false;
-            PreviewCursorPrompt.Visible = false;
-        }
-    }
-
-    onPointerUp = (e: PointerEvent): void => {
-        if (this._showOnUp) {
-            PreviewCursorPrompt.Show(this.hideCursor, this.DownX, this.DownY);
-            document.addEventListener("keypress", this.onKeyPress, false);
-        }
-        this.cleanupInteractions();
-    }
-
-    @action
-    onKeyPress = (e: KeyboardEvent) => {
-        // Mixing events between React and Native is finicky.  In FormattedTextBox, we set the
-        // DASHFormattedTextBoxHandled flag when a text box consumes a key press so that we can ignore
-        // the keyPress here.
-        //if not these keys, make a textbox if preview cursor is active!
-        if (!e.ctrlKey && !e.altKey && !e.defaultPrevented && !(e as any).DASHFormattedTextBoxHandled) {
-            //make textbox and add it to this collection
-            let [x, y] = this.props.getTransform().transformPoint(this.DownX, this.DownY);
-            let newBox = Documents.TextDocument({ width: 200, height: 100, x: x, y: y, title: "typed text" });
-            this.props.addLiveTextDocument(newBox);
-            PreviewCursorPrompt.Visible = false;
-            e.stopPropagation();
-        }
-    }
-
-    hideCursor = () => {
-        document.removeEventListener("keypress", this.onKeyPress, false);
-    }
-    render() {
-        return (
-            <div className="previewCursorView" onPointerDown={this.onPointerDown}>
-                {this.props.children}
-            </div>
-        );
-    }
-}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


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

---
 src/client/util/DragManager.ts                     |  6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 85 +++++++++++-----------
 src/server/Client.ts                               |  1 -
 src/server/index.ts                                |  8 +-
 4 files changed, 51 insertions(+), 49 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 6a7047725..2ee36d2ec 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -140,11 +140,13 @@ export namespace DragManager {
         constructor(dragDoc: Document[]) {
             this.draggedDocuments = dragDoc;
             this.droppedDocuments = dragDoc;
+            this.xOffset = 0;
+            this.yOffset = 0;
         }
         draggedDocuments: Document[];
         droppedDocuments: Document[];
-        xOffset?: number;
-        yOffset?: number;
+        xOffset: number;
+        yOffset: number;
         aliasOnDrop?: boolean;
         copyOnDrop?: boolean;
         moveDocument?: MoveFunction;
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 {
diff --git a/src/server/Client.ts b/src/server/Client.ts
index b4c419438..e6f953712 100644
--- a/src/server/Client.ts
+++ b/src/server/Client.ts
@@ -8,5 +8,4 @@ export class Client {
     }
 
     @computed public get GUID(): string { return this._guid; }
-
 }
\ No newline at end of file
diff --git a/src/server/index.ts b/src/server/index.ts
index a86638186..3cbe1ca76 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -87,9 +87,11 @@ function addSecureRoute(method: Method,
     ...subscribers: string[]
 ) {
     let abstracted = (req: express.Request, res: express.Response) => {
-        const dashUser: DashUserModel = req.user;
-        if (!dashUser) return onRejection(res);
-        handler(dashUser, res, req);
+        if (req.user) {
+            handler(req.user, res, req);
+        } else {
+            onRejection(res);
+        }
     };
     subscribers.forEach(route => {
         switch (method) {
-- 
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

