From 872d9a57bdb5bf2b3f3a26acf17d77b72dcb5164 Mon Sep 17 00:00:00 2001
From: Jude <julie_wang1@brown.edu>
Date: Sat, 9 Mar 2019 20:09:11 -0500
Subject: schema view improvements

---
 src/client/views/collections/CollectionSchemaView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 49f95c014..19ed97391 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -24,7 +24,7 @@ import { setupDrag } from "../../util/DragManager";
 @observer
 export class CollectionSchemaView extends CollectionViewBase {
     private _mainCont = React.createRef<HTMLDivElement>();
-    private DIVIDER_WIDTH = 5;
+    private DIVIDER_WIDTH = 4;
 
     @observable _contentScaling = 1; // used to transfer the dimensions of the content pane in the DOM to the ContentScaling prop of the DocumentView
     @observable _dividerX = 0;
-- 
cgit v1.2.3-70-g09d2


From 81c0a8373fd5cb051531762243e200f11f8c7297 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <abdullah_ahmed@brown.edu>
Date: Sat, 9 Mar 2019 23:00:41 -0500
Subject: editable workspace titles now supported, added 'display' prop to
 EditableView

---
 src/client/views/EditableView.tsx                  |  5 ++--
 .../views/collections/CollectionSchemaView.tsx     |  6 ++--
 .../views/collections/CollectionTreeView.tsx       |  5 +++-
 .../authentication/controllers/WorkspacesMenu.tsx  | 32 +++++++++++++++-------
 4 files changed, 33 insertions(+), 15 deletions(-)

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

diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 84b1b91c3..55a49863d 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -20,6 +20,7 @@ export interface EditableProps {
      */
     contents: any;
     height: number
+    display: string;
 }
 
 /**
@@ -46,10 +47,10 @@ 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: "inline" }}></input>
+                style={{ display: this.props.display }}></input>
         } else {
             return (
-                <div className="editableView-container-editing" style={{ display: "inline", height: "100%", maxHeight: `${this.props.height}` }}
+                <div className="editableView-container-editing" style={{ display: this.props.display, height: "100%", maxHeight: `${this.props.height}` }}
                     onClick={action(() => this.editing = true)}>
                     {this.props.contents}
                 </div>
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 49f95c014..2868e1322 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -50,7 +50,9 @@ export class CollectionSchemaView extends CollectionViewBase {
         let onItemDown = setupDrag(reference, () => props.doc);
         return (
             <div onPointerDown={onItemDown} key={props.doc.Id} ref={reference}>
-                <EditableView contents={contents}
+                <EditableView
+                    display={"inline"}
+                    contents={contents}
                     height={36} GetValue={() => {
                         let field = props.doc.Get(props.fieldKey);
                         if (field && field instanceof Field) {
@@ -59,7 +61,7 @@ export class CollectionSchemaView extends CollectionViewBase {
                         return field || "";
                     }}
                     SetValue={(value: string) => {
-                        let script = CompileScript(value, undefined, true);
+                        let script = CompileScript(value);
                         if (!script.compiled) {
                             return false;
                         }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 8b06d9ac4..9c31bdae2 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -69,7 +69,9 @@ class TreeView extends React.Component<TreeViewProps> {
             return <div key={this.props.document.Id}></div>;
         }
 
-        return <div className="docContainer"> <EditableView contents={title.Data}
+        return <div className="docContainer"> <EditableView
+            display={"inline"}
+            contents={title.Data}
             height={36} GetValue={() => {
                 let title = this.props.document.GetT<TextField>(KeyStore.Title, TextField);
                 if (title && title !== "<Waiting>")
@@ -159,6 +161,7 @@ export class CollectionTreeView extends CollectionViewBase {
             <div id="body" className="collectionTreeView-dropTarget" onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}>
                 <h3>
                     <EditableView contents={titleStr}
+                        display={"inline"}
                         height={72} GetValue={() => {
                             return this.props.Document.Title;
                         }} SetValue={(value: string) => {
diff --git a/src/server/authentication/controllers/WorkspacesMenu.tsx b/src/server/authentication/controllers/WorkspacesMenu.tsx
index 70c37774c..ffef2e11c 100644
--- a/src/server/authentication/controllers/WorkspacesMenu.tsx
+++ b/src/server/authentication/controllers/WorkspacesMenu.tsx
@@ -7,6 +7,8 @@ import './WorkspacesMenu.css'
 import { Document } from '../../../fields/Document';
 import { Server } from '../../../client/Server';
 import { Field } from '../../../fields/Field';
+import { EditableView } from '../../../client/views/EditableView';
+import { KeyStore } from '../../../fields/KeyStore';
 
 export interface WorkspaceMenuProps {
     active: Document;
@@ -67,19 +69,29 @@ export class WorkspacesMenu extends React.Component<WorkspaceMenuProps> {
                     }}
                     onClick={this.addNewWorkspace}
                 />
-                {this.props.allWorkspaces.map(s =>
-                    <li className={"ids"}
+                {this.props.allWorkspaces.map((s, i) =>
+                    <div
                         key={s.Id}
-                        style={{
-                            listStyleType: "none",
-                            color: s.Id === this.props.active.Id ? "darkblue" : "black",
-                            cursor: "grab"
-                        }}
-                        onClick={() => {
+                        onContextMenu={(e) => {
+                            e.preventDefault();
                             this.props.open(s);
-                            console.log(this.props.allWorkspaces.length);
                         }}
-                    >{s.Title}</li>
+                        style={{
+                            marginTop: 10
+                        }}
+                    >
+                        <span>{i + 1} - </span>
+                        <EditableView
+                            display={"inline"}
+                            GetValue={() => { return s.Title }}
+                            SetValue={(title: string): boolean => {
+                                s.SetText(KeyStore.Title, title);
+                                return true;
+                            }}
+                            contents={s.Title}
+                            height={20}
+                        />
+                    </div>
                 )}
             </div>
         );
-- 
cgit v1.2.3-70-g09d2


From 43c96800f0a651247fdcaf2c77c710a30cb3f79d Mon Sep 17 00:00:00 2001
From: Jude <julie_wang1@brown.edu>
Date: Sun, 10 Mar 2019 13:06:04 -0400
Subject: changed default sizing

---
 src/client/documents/Documents.ts                  |  8 +++---
 src/client/views/Main.tsx                          | 14 ++++-----
 src/client/views/_global_variables.scss            |  2 +-
 .../views/collections/CollectionFreeFormView.scss  |  2 +-
 .../views/collections/CollectionSchemaView.scss    |  2 ++
 .../views/collections/CollectionSchemaView.tsx     |  2 +-
 .../views/collections/CollectionTreeView.scss      | 33 ++++++++++++----------
 src/client/views/nodes/FormattedTextBox.tsx        |  2 +-
 src/client/views/nodes/ImageBox.scss               | 29 +++++++++----------
 9 files changed, 49 insertions(+), 45 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index fc210e9a2..30a292502 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -113,7 +113,7 @@ export namespace Documents {
     function GetCollectionPrototype(): Document {
         return collProto ? collProto :
             collProto = setupPrototypeOptions(collProtoId, "COLLECTION_PROTO", CollectionView.LayoutString("DataKey"),
-                { panx: 0, pany: 0, scale: 1, layoutKeys: [KeyStore.Data] });
+                { panx: 0, pany: 0, scale: 1, width: 500, height: 500, layoutKeys: [KeyStore.Data] });
     }
 
     function GetKVPPrototype(): Document {
@@ -125,9 +125,9 @@ export namespace Documents {
     export function ImageDocument(url: string, options: DocumentOptions = {}) {
         let doc = SetInstanceOptions(GetImagePrototype(), { ...options, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] },
             new URL(url), ImageField);
-        doc.SetText(KeyStore.Caption, "my caption...");
-        doc.SetText(KeyStore.BackgroundLayout, EmbeddedCaption());
-        doc.SetText(KeyStore.OverlayLayout, FixedCaption());
+        // doc.SetText(KeyStore.Caption, "my caption...");
+        // doc.SetText(KeyStore.BackgroundLayout, EmbeddedCaption());
+        // doc.SetText(KeyStore.OverlayLayout, FixedCaption());
         return doc;
     }
     export function TextDocument(options: DocumentOptions = {}) {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 1caaa1cba..79c5cb5aa 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -76,12 +76,12 @@ Documents.initProtos(mainDocId, (res?: Document) => {
     let pdfurl = "http://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf"
     let weburl = "https://cs.brown.edu/courses/cs166/";
     let clearDatabase = action(() => Utils.Emit(Server.Socket, MessageStore.DeleteAll, {}))
-    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([Documents.TextDocument()], { width: 200, height: 200, title: "a schema collection" }));
-    let addPDFNode = action(() => Documents.PdfDocument(pdfurl, { width: 200, height: 200, title: "a schema collection" }));
+    let addTextNode = action(() => Documents.TextDocument({ width: 230, height: 130, title: "a text note" }))
+    let addColNode = action(() => Documents.FreeformDocument([], { width: 300, height: 300, title: "a freeform collection" }));
+    let addSchemaNode = action(() => Documents.SchemaDocument([Documents.TextDocument()], { width: 450, height: 200, title: "a schema collection" }));
+    let addPDFNode = action(() => Documents.PdfDocument(pdfurl, { width: 300, height: 400, title: "a pdf" }));
     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 addWebNode = action(() => Documents.WebDocument(weburl, { width: 300, height: 400, title: "a sample web page" }));
 
 
     let addClick = (creator: () => Document) => action(() =>
@@ -100,7 +100,7 @@ Documents.initProtos(mainDocId, (res?: Document) => {
 
     ReactDOM.render((
         <div style={{ position: "absolute", width: "100%", height: "100%" }}>
-            <div id="dash-title">— DASH —</div>
+            {/* <div id="dash-title">— DASH —</div> */}
 
             <DocumentView Document={mainContainer}
                 AddDocument={undefined} RemoveDocument={undefined} ScreenToLocalTransform={() => Transform.Identity}
@@ -153,7 +153,7 @@ Documents.initProtos(mainDocId, (res?: Document) => {
 
             </div>
 
-            <InkingControl />
+            {/* <InkingControl /> */}
         </div >),
         document.getElementById('root'));
 })
diff --git a/src/client/views/_global_variables.scss b/src/client/views/_global_variables.scss
index 333fb3122..1c06ab44a 100644
--- a/src/client/views/_global_variables.scss
+++ b/src/client/views/_global_variables.scss
@@ -1,4 +1,4 @@
-@import url("https://fonts.googleapis.com/css?family=Roboto+Slab:300,700|Crimson+Text:400,400i,700");
+@import url("https://fonts.googleapis.com/css?family=Roboto+Slab:,700|Crimson+Text:400,400i,700");
 
 // colors
 $light-color: #fcfbf7;
diff --git a/src/client/views/collections/CollectionFreeFormView.scss b/src/client/views/collections/CollectionFreeFormView.scss
index 90caa0d14..7250c9241 100644
--- a/src/client/views/collections/CollectionFreeFormView.scss
+++ b/src/client/views/collections/CollectionFreeFormView.scss
@@ -41,7 +41,7 @@
     background: $light-color-secondary;
   }
 
-  border: 0px solid $intermediate-color;
+  border: 0px solid transparent;
   border-radius: $border-radius;
   box-sizing: border-box;
   position: absolute;
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index cb0040657..b66fc7981 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -7,6 +7,8 @@
   position: absolute;
   width: 100%;
   height: 100%;
+  overflow: hidden;
+
   .collectionSchemaView-previewRegion {
     position: relative;
     background: $light-color;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 19ed97391..deb307fb3 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -31,7 +31,7 @@ export class CollectionSchemaView extends CollectionViewBase {
     @observable _panelWidth = 0;
     @observable _panelHeight = 0;
     @observable _selectedIndex = 0;
-    @observable _splitPercentage: number = 50;
+    @observable _splitPercentage: number = 100;
 
     renderCell = (rowProps: CellInfo) => {
         let props: FieldViewProps = {
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index f8d580a7b..4b28ded45 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -1,37 +1,40 @@
+@import "../global_variables";
+
 #body {
-    padding: 20px;
-    background: #bbbbbb;
+  padding: 20px;
+  background: $light-color-secondary;
 }
 
 ul {
-    list-style: none;
+  list-style: none;
 }
 
 li {
-    margin: 5px 0;
+  margin: 5px 0;
 }
 
 .no-indent {
-    padding-left: 0;
+  padding-left: 0;
 }
 
 .bullet {
-    width: 1.5em;
-    display: inline-block;
+  width: 1.5em;
+  display: inline-block;
 }
 
 .collectionTreeView-dropTarget {
-    border-style: solid;
-    box-sizing: border-box;
-    height: 100%;
+  border: 0px solid $intermediate-color;
+  border-radius: $border-radius;
+  box-sizing: border-box;
+  height: 100%;
 }
 
 .docContainer {
-    display: inline-table;
+  display: inline-table;
 }
 
 .delete-button {
-    color: #999999;
-    float: right;
-    margin-left: 1em;
-}
\ No newline at end of file
+  color: $intermediate-color;
+  float: right;
+  margin-left: 1em;
+}
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index a6cee9957..a375cfc48 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -31,7 +31,7 @@ import { ContextMenu } from "../../views/ContextMenu";
 //    and 'doc' property to the document that is being rendered
 //
 //  When rendered() by React, this extracts the TextController from the Document stored at the 
-//  specified Key and assigns it to an HTML input node.  When changes are made tot his node, 
+//  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> {
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index ea459b911..2cb460eea 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -1,22 +1,21 @@
-
 .imageBox-cont {
-    padding: 0vw;
-    position: relative;
-    text-align: center;
-    width: 100%;
-    height: auto;
-    max-width: 100%;
-    max-height: 100%
+  padding: 0vw;
+  position: relative;
+  text-align: center;
+  width: 100%;
+  height: auto;
+  max-width: 100%;
+  max-height: 100%;
 }
 
 .imageBox-cont img {
-    object-fit: contain;
-    height: 100%;
+  object-fit: contain;
+  height: 100%;
 }
 
 .imageBox-button {
-    padding : 0vw;
-    border: none;
-    width : 100%;
-    height: 100%;
-}
\ No newline at end of file
+  padding: 0vw;
+  border: none;
+  width: 100%;
+  height: 100%;
+}
-- 
cgit v1.2.3-70-g09d2


From 44d669b46616e561aef874471d2740a4f205d9f0 Mon Sep 17 00:00:00 2001
From: Fawn <fangrui_tong@brown.edu>
Date: Mon, 11 Mar 2019 21:25:13 -0400
Subject: column adding works, but render doesn't get called on add

---
 .../views/collections/CollectionSchemaView.scss    | 91 +++++++++++++++-------
 .../views/collections/CollectionSchemaView.tsx     | 42 +++++++++-
 src/fields/Document.ts                             |  5 ++
 3 files changed, 108 insertions(+), 30 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index d40e6d314..38da3505e 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -1,5 +1,3 @@
-
-
 .collectionSchemaView-container {
     border-style: solid;
     box-sizing: border-box;
@@ -7,36 +5,74 @@
     width: 100%;
     height: 100%;
     .collectionSchemaView-previewRegion {
-        position: relative; 
-        background: black; 
-        float: left; 
+        position: relative;
+        background: black;
+        float: left;
         height: 100%;
     }
     .collectionSchemaView-previewHandle {
         position: absolute;
         height: 37px;
-        width: 20px; 
-        z-index: 20; 
-        right: 0; 
-        top: 0; 
-        background: Black ;
-    }
-    .collectionSchemaView-dividerDragger{
-        position: relative; 
-        background: black; 
-        float: left; 
+        width: 20px;
+        z-index: 20;
+        right: 0;
+        top: 0;
+        background: Black;
+    }
+    .collectionSchemaView-dividerDragger {
+        position: relative;
+        background: black;
+        float: left;
         height: 100%;
     }
     .collectionSchemaView-tableContainer {
         position: relative;
         float: left;
-        height: 100%; 
+        height: 100%;
     }
-
-    .ReactTable {
+    .collectionSchemaView-addColumn {
         position: absolute;
-       // display: inline-block;
-       // overflow: auto;
+        z-index: 20px;
+        right: 10px;
+        bottom: 10px;
+        input {
+            display: none;
+        }
+        label {
+            background: red;
+            display: inline-block;
+            border-radius: 18px;
+            font-size: 25px;
+            width: 36px;
+            height: 36px;
+            margin-right: 10px;
+            text-align: center;
+            cursor: pointer;
+        }
+        input:not(:checked)~.addColumn-options {
+            display: none;
+        }
+        .addColumn-options {
+            position: absolute;
+            right: 70px;
+            bottom: 0;
+            width: 150px;
+            background-color: white;
+            ul {
+                padding: 0;
+                margin: 0;
+            }
+            li {
+                padding: 6px;
+                border: 1px solid gray;
+                width: 100%;
+                margin: 0;
+            }
+        }
+    }
+    .ReactTable {
+        position: absolute; // display: inline-block;
+        // overflow: auto;
         width: 100%;
         height: 100%;
         background: white;
@@ -45,10 +81,8 @@
             overflow-y: auto;
             overflow-x: auto;
             height: 100%;
-
             display: -webkit-inline-box;
-            direction: ltr;
-            // direction:rtl;
+            direction: ltr; // direction:rtl;
             // display:block;
         }
         .rt-tbody {
@@ -63,8 +97,8 @@
             border-width: 1;
             border-right-color: #aaa;
             .imageBox-cont {
-                position:relative;
-                max-height:100%;
+                position: relative;
+                max-height: 100%;
             }
             .imageBox-cont img {
                 object-fit: contain;
@@ -78,9 +112,10 @@
         }
     }
     .ReactTable .rt-thead.-header {
-            background:grey;
-        }
-    .ReactTable .rt-th, .ReactTable .rt-td {
+        background: grey;
+    }
+    .ReactTable .rt-th,
+    .ReactTable .rt-td {
         max-height: 44;
         padding: 3px 7px;
     }
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 04f017378..e1036e9b0 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -1,5 +1,5 @@
 import React = require("react")
-import { action, observable } from "mobx";
+import { action, observable, ObservableMap, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import Measure from "react-measure";
 import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
@@ -17,6 +17,8 @@ import "./CollectionSchemaView.scss";
 import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
 import { CollectionViewBase } from "./CollectionViewBase";
 import { setupDrag } from "../../util/DragManager";
+import { Key } from "./../../../fields/Key";
+
 
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
 
@@ -26,6 +28,7 @@ export class CollectionSchemaView extends CollectionViewBase {
     private _mainCont = React.createRef<HTMLDivElement>();
     private DIVIDER_WIDTH = 5;
 
+    @observable _columns: Array<Key> = [KeyStore.Title, KeyStore.Data, KeyStore.Author];
     @observable _contentScaling = 1; // used to transfer the dimensions of the content pane in the DOM to the ContentScaling prop of the DocumentView
     @observable _dividerX = 0;
     @observable _panelWidth = 0;
@@ -102,6 +105,29 @@ export class CollectionSchemaView extends CollectionViewBase {
         };
     }
 
+    @action
+    addColumn = (k: Key): void => {
+        this._columns.push(k);
+        console.log("adding", this._columns.length);
+    }
+
+    findAllDocumentKeys = (docs: Array<Document>): Set<Key> => {
+        let keys = new Set();
+        docs.forEach(doc => {
+            let fields: ObservableMap<string, { key: Key, field: Field }> = doc.Fields;
+            Array.from(fields).map(item => {
+                let v: { key: Key, field: Field } = item[1];
+                let k = v.key;
+                keys.add(k);
+            })
+        })
+        keys.delete(KeyStore.Title);
+        keys.delete(KeyStore.Data);
+        keys.delete(KeyStore.Author);
+        console.log("key set", keys);
+        return keys;
+    }
+
     _startSplitPercent = 0;
     @action
     onDividerMove = (e: PointerEvent): void => {
@@ -179,7 +205,7 @@ export class CollectionSchemaView extends CollectionViewBase {
     focusDocument = (doc: Document) => { }
 
     render() {
-        const columns = this.props.Document.GetList(KeyStore.ColumnsKey, [KeyStore.Title, KeyStore.Data, KeyStore.Author])
+        const columns = this.props.Document.GetList(KeyStore.ColumnsKey, this._columns)
         const children = this.props.Document.GetList<Document>(this.props.fieldKey, []);
         const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
         let content = this._selectedIndex == -1 || !selected ? (null) : (
@@ -205,6 +231,7 @@ export class CollectionSchemaView extends CollectionViewBase {
             <div className="collectionSchemaView-previewHandle" onPointerDown={this.onExpanderDown} />);
         let dividerDragger = this._splitPercentage == 100 ? (null) :
             <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />
+
         return (
             <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} ref={this._mainCont} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} >
                 <div className="collectionSchemaView-dropTarget" onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
@@ -231,6 +258,16 @@ export class CollectionSchemaView extends CollectionViewBase {
                                     }}
                                     getTrProps={this.getTrProps}
                                 />
+                                <div className="collectionSchemaView-addColumn" >
+                                    <input type="checkbox" id="addColumn-toggle" />
+                                    <label htmlFor="addColumn-toggle" title="Add Column"><p>+</p></label>
+
+                                    <div className="addColumn-options">
+                                        <ul>{Array.from(this.findAllDocumentKeys(children)).map(item => {
+                                            return <li onClick={() => this.addColumn(item)}>{item.Name}</li>
+                                        })}</ul>
+                                    </div>
+                                </div>
                             </div>
                         }
                     </Measure>
@@ -239,6 +276,7 @@ export class CollectionSchemaView extends CollectionViewBase {
                         {content}
                     </div>
                     {previewHandle}
+
                 </div>
             </div >
         )
diff --git a/src/fields/Document.ts b/src/fields/Document.ts
index 25e239417..6cf9c194e 100644
--- a/src/fields/Document.ts
+++ b/src/fields/Document.ts
@@ -38,6 +38,11 @@ export class Document extends Field {
         return this.GetText(KeyStore.Title, "<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
-- 
cgit v1.2.3-70-g09d2


From c17605b4d1fe04429b029ff5a161f31ce19f5fd4 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 15 Mar 2019 10:48:23 -0400
Subject: think video resizing is fixed now.

---
 src/client/views/collections/CollectionSchemaView.tsx | 2 +-
 src/client/views/nodes/VideoBox.tsx                   | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 37620cd7c..14e1df8ca 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -49,7 +49,7 @@ export class CollectionSchemaView extends CollectionViewBase {
         let reference = React.createRef<HTMLDivElement>();
         let onItemDown = setupDrag(reference, () => props.doc);
         return (
-            <div onPointerDown={onItemDown} key={props.doc.Id} ref={reference}>
+            <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "36px" }} key={props.doc.Id} ref={reference}>
                 <EditableView contents={contents}
                     height={36} GetValue={() => {
                         let field = props.doc.Get(props.fieldKey);
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 053d9dd56..fef278cec 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -30,7 +30,7 @@ export class VideoBox extends React.Component<FieldViewProps> {
             var nativeWidth = this.props.doc.GetNumber(KeyStore.NativeWidth, 0);
             var nativeHeight = this.props.doc.GetNumber(KeyStore.NativeHeight, 0);
             var newNativeHeight = nativeWidth * r.entry.height / r.entry.width;
-            if (newNativeHeight != nativeHeight && !isNaN(newNativeHeight)) {
+            if (!nativeHeight && newNativeHeight != nativeHeight && !isNaN(newNativeHeight)) {
                 this.props.doc.SetNumber(KeyStore.Height, newNativeHeight / nativeWidth * this.props.doc.GetNumber(KeyStore.Width, 0));
                 this.props.doc.SetNumber(KeyStore.NativeHeight, newNativeHeight);
             }
@@ -48,7 +48,7 @@ export class VideoBox extends React.Component<FieldViewProps> {
 
         //setTimeout(action(() => this._loaded = true), 500);
         return (
-            <div style={{ width: "100%", height: "Auto" }} >
+            <div style={{ width: "100%", height: "100%" }} >
                 <Measure onResize={this.setScaling}>
                     {({ measureRef }) =>
                         <video className="videobox-cont" ref={measureRef}>
-- 
cgit v1.2.3-70-g09d2


From 487a74d28501cbdc86f0d02fef656754ed22ae19 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 15 Mar 2019 13:30:22 -0400
Subject: fixed various issues with schema/videos

---
 src/client/views/EditableView.tsx                  |  2 +-
 .../views/collections/CollectionFreeFormView.tsx   |  6 ++---
 .../views/collections/CollectionSchemaView.scss    |  2 +-
 .../views/collections/CollectionSchemaView.tsx     | 28 ++++++++++++----------
 src/client/views/collections/MarqueeView.tsx       |  2 +-
 src/client/views/nodes/VideoBox.tsx                | 16 ++++++-------
 src/fields/KeyStore.ts                             |  1 +
 7 files changed, 28 insertions(+), 29 deletions(-)

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

diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 757bfeae6..3b54c0dbb 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -50,7 +50,7 @@ export class EditableView extends React.Component<EditableProps> {
                 style={{ display: "inline" }}></input>
         } else {
             return (
-                <div className="editableView-container-editing" style={{ display: "inline", height: "100%", maxHeight: `${this.props.height}` }}
+                <div className="editableView-container-editing" style={{ display: "inline", height: "auto", maxHeight: `${this.props.height}` }}
                     onClick={action(() => this.editing = true)}>
                     {this.props.contents}
                 </div>
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index c8b745254..a94539312 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -126,7 +126,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
     onPointerUp = (e: PointerEvent): void => {
         e.stopPropagation();
 
-        if (!this.MarqueeVisible && Math.abs(this.DownX - e.clientX) < 3 && Math.abs(this.DownY - e.clientY) < 3) {
+        if (Math.abs(this.DownX - e.clientX) < 4 && Math.abs(this.DownY - e.clientY) < 4) {
             //show preview text cursor on tap
             this.PreviewCursorVisible = true;
             //select is not already selected
@@ -140,9 +140,8 @@ export class CollectionFreeFormView extends CollectionViewBase {
     @action
     onPointerMove = (e: PointerEvent): void => {
         if (!e.cancelBubble && this.props.active()) {
-            if (e.buttons != 2 && !e.altKey && !e.metaKey && !this.MarqueeVisible) {
+            if (e.buttons == 1 && !e.altKey && !e.metaKey) {
                 this.MarqueeVisible = true;
-                this.PreviewCursorVisible = false;
             }
             if (this.MarqueeVisible) {
                 e.stopPropagation();
@@ -152,7 +151,6 @@ export class CollectionFreeFormView extends CollectionViewBase {
                 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);
-                this.PreviewCursorVisible = false;
                 this.SetPan(x - dx, y - dy);
                 this._lastX = e.pageX;
                 this._lastY = e.pageY;
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index fdcad1355..0d615dc01 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -84,7 +84,7 @@
       }
       .videobox-cont  {
         object-fit: contain;
-        max-width: 100%;
+        width:auto;
         height: 100%;
       }
     }
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 14e1df8ca..8c1aeef2c 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -1,5 +1,5 @@
 import React = require("react")
-import { action, observable } from "mobx";
+import { action, observable, computed } from "mobx";
 import { observer } from "mobx-react";
 import Measure from "react-measure";
 import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
@@ -31,7 +31,7 @@ export class CollectionSchemaView extends CollectionViewBase {
     @observable _panelWidth = 0;
     @observable _panelHeight = 0;
     @observable _selectedIndex = 0;
-    @observable _splitPercentage: number = 100;
+    @computed get splitPercentage() { return this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0); }
 
     renderCell = (rowProps: CellInfo) => {
         let props: FieldViewProps = {
@@ -89,7 +89,8 @@ export class CollectionSchemaView extends CollectionViewBase {
         return {
             onClick: action((e: React.MouseEvent, handleOriginal: Function) => {
                 that._selectedIndex = rowInfo.index;
-                this._splitPercentage += 0.05; // bcz - ugh - needed to force Measure to do its thing and call onResize
+                // bcz - ugh - needed to force Measure to do its thing and call onResize
+                this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage - 0.05)
 
                 if (handleOriginal) {
                     handleOriginal()
@@ -106,18 +107,18 @@ export class CollectionSchemaView extends CollectionViewBase {
     @action
     onDividerMove = (e: PointerEvent): void => {
         let nativeWidth = this._mainCont.current!.getBoundingClientRect();
-        this._splitPercentage = Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100);
+        this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100));
     }
     @action
     onDividerUp = (e: PointerEvent): void => {
         document.removeEventListener("pointermove", this.onDividerMove);
         document.removeEventListener('pointerup', this.onDividerUp);
-        if (this._startSplitPercent == this._splitPercentage) {
-            this._splitPercentage = this._splitPercentage == 1 ? 66 : 100;
+        if (this._startSplitPercent == this.splitPercentage) {
+            this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage == 0 ? 33 : 0);
         }
     }
     onDividerDown = (e: React.PointerEvent) => {
-        this._startSplitPercent = this._splitPercentage;
+        this._startSplitPercent = this.splitPercentage;
         e.stopPropagation();
         e.preventDefault();
         document.addEventListener("pointermove", this.onDividerMove);
@@ -134,12 +135,12 @@ export class CollectionSchemaView extends CollectionViewBase {
         e.preventDefault();
         document.removeEventListener("pointermove", this.onExpanderMove);
         document.removeEventListener('pointerup', this.onExpanderUp);
-        if (this._startSplitPercent == this._splitPercentage) {
-            this._splitPercentage = this._splitPercentage == 100 ? 66 : 100;
+        if (this._startSplitPercent == this.splitPercentage) {
+            this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage == 0 ? 33 : 0);
         }
     }
     onExpanderDown = (e: React.PointerEvent) => {
-        this._startSplitPercent = this._splitPercentage;
+        this._startSplitPercent = this.splitPercentage;
         e.stopPropagation();
         e.preventDefault();
         document.addEventListener("pointermove", this.onExpanderMove);
@@ -203,7 +204,7 @@ export class CollectionSchemaView extends CollectionViewBase {
         )
         let previewHandle = !this.props.active() ? (null) : (
             <div className="collectionSchemaView-previewHandle" onPointerDown={this.onExpanderDown} />);
-        let dividerDragger = this._splitPercentage == 100 ? (null) :
+        let dividerDragger = this.splitPercentage == 0 ? (null) :
             <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />
         return (
             <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} ref={this._mainCont} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} >
@@ -213,7 +214,8 @@ export class CollectionSchemaView extends CollectionViewBase {
                         this._panelHeight = r.entry.height;
                     })}>
                         {({ measureRef }) =>
-                            <div ref={measureRef} className="collectionSchemaView-tableContainer" style={{ width: `${this._splitPercentage}%` }}>
+                            <div ref={measureRef} className="collectionSchemaView-tableContainer"
+                                style={{ width: `calc(100% - ${this.splitPercentage}%)` }}>
                                 <ReactTable
                                     data={children}
                                     pageSize={children.length}
@@ -235,7 +237,7 @@ export class CollectionSchemaView extends CollectionViewBase {
                         }
                     </Measure>
                     {dividerDragger}
-                    <div className="collectionSchemaView-previewRegion" style={{ width: `calc(${100 - this._splitPercentage}% - ${this.DIVIDER_WIDTH}px)` }}>
+                    <div className="collectionSchemaView-previewRegion" style={{ width: `calc(${this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0)}% - ${this.DIVIDER_WIDTH}px)` }}>
                         {content}
                     </div>
                     {previewHandle}
diff --git a/src/client/views/collections/MarqueeView.tsx b/src/client/views/collections/MarqueeView.tsx
index 67716f99a..65aaa837f 100644
--- a/src/client/views/collections/MarqueeView.tsx
+++ b/src/client/views/collections/MarqueeView.tsx
@@ -93,7 +93,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
 
     @action
     marqueeCommand = (e: KeyboardEvent) => {
-        if (e.key == "Backspace") {
+        if (e.key == "Backspace" || e.key == "Delete") {
             this.marqueeSelect().map(d => this.props.removeDocument(d));
             this.props.container.props.Document.SetData(KeyStore.Ink, this.marqueeInkSelect(false), InkField);
             this.cleanupInteractions();
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index fef278cec..8c1ee669f 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -48,16 +48,14 @@ export class VideoBox extends React.Component<FieldViewProps> {
 
         //setTimeout(action(() => this._loaded = true), 500);
         return (
-            <div style={{ width: "100%", height: "100%" }} >
-                <Measure onResize={this.setScaling}>
-                    {({ measureRef }) =>
-                        <video className="videobox-cont" ref={measureRef}>
-                            <source src={path} type="video/mp4" />
-                            Not supported.
+            <Measure onResize={this.setScaling}>
+                {({ measureRef }) =>
+                    <video className="videobox-cont" ref={measureRef}>
+                        <source src={path} type="video/mp4" />
+                        Not supported.
                         </video>
-                    }
-                </Measure>
-            </div>
+                }
+            </Measure>
         )
     }
 }
\ No newline at end of file
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index 0c8b7e260..06cdc8fc8 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -25,6 +25,7 @@ export namespace KeyStore {
     export const LayoutKeys = new Key("LayoutKeys");
     export const LayoutFields = new Key("LayoutFields");
     export const ColumnsKey = new Key("SchemaColumns");
+    export const SchemaSplitPercentage = new Key("SchemaSplitPercentage");
     export const Caption = new Key("Caption");
     export const ActiveFrame = new Key("ActiveFrame");
     export const DocumentText = new Key("DocumentText");
-- 
cgit v1.2.3-70-g09d2


From bf257dfcdb106e73e2e64c384f8f2d3646118376 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 17 Mar 2019 23:49:14 -0400
Subject: fixed context menus on schema views.

---
 src/client/views/collections/CollectionSchemaView.tsx | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 8c1aeef2c..f88a62a0f 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -153,11 +153,11 @@ export class CollectionSchemaView extends CollectionViewBase {
         //     e.preventDefault();
         // } else 
         {
-            if (e.buttons === 1) {
-                if (this.props.isSelected()) {
-                    e.stopPropagation();
-                }
-            }
+            // if (e.buttons === 1) {
+            //     if (this.props.isSelected()) {
+            //         e.stopPropagation();
+            //     }
+            // }
         }
     }
 
-- 
cgit v1.2.3-70-g09d2


From 6cc582d85317c35908d313b4f129617073c5ccac Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Mon, 18 Mar 2019 03:37:22 -0400
Subject: Fixed columns, looks horrible though

---
 .../views/collections/CollectionSchemaView.scss    |  2 +-
 .../views/collections/CollectionSchemaView.tsx     | 90 ++++++++++++++++------
 2 files changed, 68 insertions(+), 24 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index 37f4652a9..8d3eba696 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -42,7 +42,7 @@
         height: 100%;
     }
     .ReactTable {
-        position: absolute; // display: inline-block;
+        // position: absolute; // display: inline-block;
         // overflow: auto;
         width: 100%;
         height: 100%;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 16432ebbc..654d13bf1 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -5,7 +5,7 @@ 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 } from "../../../fields/Field";
+import { Field, Opt } from "../../../fields/Field";
 import { KeyStore } from "../../../fields/KeyStore";
 import { CompileScript, ToField } from "../../util/Scripting";
 import { Transform } from "../../util/Transform";
@@ -18,11 +18,37 @@ import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
 import { CollectionViewBase } from "./CollectionViewBase";
 import { setupDrag } from "../../util/DragManager";
 import { Key } from "./../../../fields/Key";
+import { Server } from "../../Server";
+import { ListField } from "../../../fields/ListField";
 
 
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
 
 
+@observer
+class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggle: (key: Key) => void }> {
+    @observable
+    key: Key | undefined;
+
+    componentWillReceiveProps() {
+        Server.GetField(this.props.keyId, action((field: Opt<Field>) => {
+            if (field instanceof Key) {
+                this.key = field;
+            }
+        }))
+    }
+
+    render() {
+        if (this.key) {
+            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>)
+        } else {
+            return <div></div>
+        }
+    }
+}
+
 @observer
 export class CollectionSchemaView extends CollectionViewBase {
     private _mainCont = React.createRef<HTMLDivElement>();
@@ -106,26 +132,40 @@ export class CollectionSchemaView extends CollectionViewBase {
         };
     }
 
+    get columns() {
+        return this.props.Document.GetList<Key>(KeyStore.ColumnsKey, []);
+    }
+
     @action
-    addColumn = (k: Key): void => {
-        this._columns.push(k);
-        console.log("adding", this._columns.length);
+    toggleKey = (key: Key) => {
+        this.props.Document.GetOrCreateAsync<ListField<Key>>(KeyStore.ColumnsKey, ListField,
+            (field) => {
+                const index = field.Data.indexOf(key);
+                if (index === -1) {
+                    this.columns.push(key);
+                } else {
+                    this.columns.splice(index, 1);
+                }
+
+            })
     }
 
-    findAllDocumentKeys = (docs: Array<Document>): Set<Key> => {
-        let keys = new Set();
+    @observable keys: Key[] = [];
+
+    findAllDocumentKeys = (): { [id: string]: boolean } => {
+        const docs = this.props.Document.GetList<Document>(this.props.fieldKey, []);
+        let keys: { [id: string]: boolean } = {}
         docs.forEach(doc => {
-            let fields: ObservableMap<string, { key: Key, field: Field }> = doc.Fields;
-            Array.from(fields).map(item => {
-                let v: { key: Key, field: Field } = item[1];
-                let k = v.key;
-                keys.add(k);
-            })
+            let protos = doc.GetAllPrototypes();
+            for (const proto of protos) {
+                proto._proxies.forEach((val: any, key: string) => {
+                    keys[key] = false
+                })
+            }
+        })
+        this.columns.forEach(key => {
+            keys[key.Id] = true;
         })
-        keys.delete(KeyStore.Title);
-        keys.delete(KeyStore.Data);
-        keys.delete(KeyStore.Author);
-        console.log("key set", keys);
         return keys;
     }
 
@@ -206,9 +246,10 @@ export class CollectionSchemaView extends CollectionViewBase {
     focusDocument = (doc: Document) => { }
 
     render() {
-        const columns = this.props.Document.GetList(KeyStore.ColumnsKey, this._columns)
+        const columns = this.columns;
         const children = this.props.Document.GetList<Document>(this.props.fieldKey, []);
         const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
+        const allKeys = this.findAllDocumentKeys();
         let content = this._selectedIndex == -1 || !selected ? (null) : (
             <Measure onResize={this.setScaling}>
                 {({ measureRef }) =>
@@ -259,15 +300,18 @@ export class CollectionSchemaView extends CollectionViewBase {
 
                                     }}
                                     getTrProps={this.getTrProps}
+                                    style={{ height: "50%" }}
                                 />
-                                <div className="collectionSchemaView-addColumn" >
-                                    <input type="checkbox" id="addColumn-toggle" />
-                                    <label htmlFor="addColumn-toggle" title="Add Column"><p>+</p></label>
+                                <div className="collectionSchemaView-addColumn" style={{ height: "50%" }} >
+                                    {/* <input type="checkbox" id="addColumn-toggle" />
+                                    <label htmlFor="addColumn-toggle" title="Add Column"><p>+</p></label> */}
 
                                     <div className="addColumn-options">
-                                        <ul>{Array.from(this.findAllDocumentKeys(children)).map(item => {
-                                            return <li onClick={() => this.addColumn(item)}>{item.Name}</li>
-                                        })}</ul>
+                                        <ul>
+                                            {Array.from(Object.keys(allKeys)).map(item => {
+                                                return (<KeyToggle checked={allKeys[item]} key={item} keyId={item} toggle={this.toggleKey} />)
+                                            })}
+                                        </ul>
                                     </div>
                                 </div>
                             </div>
-- 
cgit v1.2.3-70-g09d2


From 8418b842b16da68150243e1750aa8b40eb8a8792 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 18 Mar 2019 11:48:24 -0400
Subject: fixed removing from schemas.  fixed dragging link button to move
 quickly. cleaned up drag/drop code a little

---
 src/client/util/DragManager.ts                     | 12 +++-----
 src/client/views/DocumentDecorations.tsx           | 14 ++++-----
 src/client/views/InkingControl.tsx                 |  3 +-
 .../views/collections/CollectionFreeFormView.tsx   |  4 +--
 src/client/views/collections/CollectionPDFView.tsx |  2 +-
 .../views/collections/CollectionSchemaView.tsx     |  4 +--
 .../views/collections/CollectionVideoView.tsx      |  2 +-
 src/client/views/collections/CollectionView.tsx    |  7 +++--
 .../views/collections/CollectionViewBase.tsx       | 36 ++++++++--------------
 src/client/views/collections/MarqueeView.tsx       | 15 +++++++--
 src/client/views/nodes/DocumentView.tsx            | 16 +++++-----
 11 files changed, 57 insertions(+), 58 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index c0abec407..54784f2d2 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -2,18 +2,18 @@ import { DocumentDecorations } from "../views/DocumentDecorations";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import { Document } from "../../fields/Document"
 import { action } from "mobx";
-import { DocumentView } from "../views/nodes/DocumentView";
 import { ImageField } from "../../fields/ImageField";
 import { KeyStore } from "../../fields/KeyStore";
+import { CollectionView } from "../views/collections/CollectionView";
 
-export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document) {
+export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, removeFunc: (containingCollection: CollectionView) => void = () => { }) {
     let onRowMove = action((e: PointerEvent): void => {
         e.stopPropagation();
         e.preventDefault();
 
         document.removeEventListener("pointermove", onRowMove);
         document.removeEventListener('pointerup', onRowUp);
-        DragManager.StartDrag(_reference.current!, { document: docFunc() });
+        DragManager.StartDrag(_reference.current!, { draggedDocument: docFunc(), removeDocument: removeFunc });
     });
     let onRowUp = action((e: PointerEvent): void => {
         document.removeEventListener("pointermove", onRowMove);
@@ -122,9 +122,7 @@ export namespace DragManager {
         // 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
-        const docView: DocumentView = dragData["documentView"];
-        const doc: Document = dragData["document"];
-
+        const doc: Document = dragData["draggedDocument"];
         if (doc) {
             var pdfBox = dragElement.getElementsByClassName("pdfBox-cont")[0] as HTMLElement;
             let thumbnail = doc.GetT(KeyStore.Thumbnail, ImageField);
@@ -138,7 +136,6 @@ export namespace DragManager {
             }
         }
 
-
         dragDiv.appendChild(dragElement);
 
         let hideSource = false;
@@ -191,6 +188,7 @@ export namespace DragManager {
         if (!target) {
             return;
         }
+        dragData["droppedDocument"] = dragData["aliasOnDrop"] ? (dragData["draggedDocument"] as Document).CreateAlias() : dragData["draggedDocument"];
         target.dispatchEvent(new CustomEvent<DropEvent>("dashOnDrop", {
             bubbles: true,
             detail: {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 3bdb7d5b3..1145fbe7f 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -77,7 +77,6 @@ export class DocumentDecorations extends React.Component {
     }
 
     onLinkButtonUp = (e: PointerEvent): void => {
-        console.log("up");
         document.removeEventListener("pointermove", this.onLinkButtonMoved)
         document.removeEventListener("pointerup", this.onLinkButtonUp)
         e.stopPropagation();
@@ -85,10 +84,11 @@ export class DocumentDecorations extends React.Component {
 
 
     onLinkButtonMoved = (e: PointerEvent): void => {
-        console.log("moved");
-        let dragData: { [id: string]: any } = {};
-        dragData["linkSourceDoc"] = SelectionManager.SelectedDocuments()[0];
         if (this._linkButton.current != null) {
+            document.removeEventListener("pointermove", this.onLinkButtonMoved)
+            document.removeEventListener("pointerup", this.onLinkButtonUp)
+            let dragData: { [id: string]: any } = {};
+            dragData["linkSourceDoc"] = SelectionManager.SelectedDocuments()[0];
             DragManager.StartDrag(this._linkButton.current, dragData, {
                 handlers: {
                     dragComplete: action(() => { }),
@@ -96,8 +96,6 @@ export class DocumentDecorations extends React.Component {
                 hideSource: false
             })
         }
-        document.removeEventListener("pointermove", this.onLinkButtonMoved)
-        document.removeEventListener("pointerup", this.onLinkButtonUp)
         e.stopPropagation();
     }
 
@@ -217,7 +215,7 @@ export class DocumentDecorations extends React.Component {
                     <LinkMenu docView={selFirst} changeFlyout={this.changeFlyoutContent}>
                     </LinkMenu>
                 }>
-                <div className={"linkButton-" + (selFirst.props.Document.GetData(KeyStore.LinkedToDocs, ListField, []).length ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} ref={this._linkButton}>{linkCount}</div>
+                <div className={"linkButton-" + (selFirst.props.Document.GetData(KeyStore.LinkedToDocs, ListField, []).length ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >{linkCount}</div>
             </Flyout>);
         }
         return (
@@ -237,7 +235,7 @@ export class DocumentDecorations extends React.Component {
                 <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">{linkButton}</div>
+                <div title="View Links" className="linkFlyout" ref={this._linkButton}>{linkButton}</div>
 
             </div >
         )
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 6616f68d8..ad6bbd476 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -11,6 +11,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { faPen, faHighlighter, faEraser, faBan } from '@fortawesome/free-solid-svg-icons';
 import { SelectionManager } from "../util/SelectionManager";
 import { KeyStore } from "../../fields/KeyStore";
+import { TextField } from "../../fields/TextField";
 
 library.add(faPen, faHighlighter, faEraser, faBan);
 
@@ -39,7 +40,7 @@ export class InkingControl extends React.Component {
         if (SelectionManager.SelectedDocuments().length == 1) {
             var sdoc = SelectionManager.SelectedDocuments()[0];
             if (sdoc.props.ContainingCollectionView && sdoc.props.ContainingCollectionView) {
-                sdoc.props.Document.SetText(KeyStore.BackgroundColor, color.hex);
+                sdoc.props.Document.SetOnPrototype(KeyStore.BackgroundColor, new TextField(color.hex));
             }
         }
     }
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index 728076ef8..85ea2d121 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -28,7 +28,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
         // mark this collection so that when the text box is created we can send it the SelectOnLoad prop to focus itself
         this._selectOnLoaded = newBox.Id;
         //set text to be the typed key and get focus on text box
-        this.props.addDocument(newBox);
+        this.props.addDocument(newBox, false);
         //remove cursor from screen
         this.PreviewCursorVisible = false;
     }
@@ -79,7 +79,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
         let screenX = de.x - (de.data["xOffset"] as number || 0);
         let screenY = de.y - (de.data["yOffset"] as number || 0);
         const [x, y] = this.getTransform().transformPoint(screenX, screenY);
-        let doc: Document = de.data["document"];
+        let doc: Document = de.data["droppedDocument"];
         if (doc) {
             doc.SetNumber(KeyStore.X, x);
             doc.SetNumber(KeyStore.Y, y);
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 124d82c8b..e64b4c945 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -38,7 +38,7 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
     public SelectedDocs: FieldId[] = []
     public active: () => boolean = () => CollectionView.Active(this);
 
-    addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); }
+    addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); }
     removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
 
     specificContextMenu = (e: React.MouseEvent): void => {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index f88a62a0f..ced08a14e 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -14,7 +14,7 @@ import { EditableView } from "../EditableView";
 import { DocumentView } from "../nodes/DocumentView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
-import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
+import { COLLECTION_BORDER_WIDTH, CollectionView } from "./CollectionView";
 import { CollectionViewBase } from "./CollectionViewBase";
 import { setupDrag } from "../../util/DragManager";
 
@@ -47,7 +47,7 @@ export class CollectionSchemaView extends CollectionViewBase {
             <FieldView {...props} />
         )
         let reference = React.createRef<HTMLDivElement>();
-        let onItemDown = setupDrag(reference, () => props.doc);
+        let onItemDown = setupDrag(reference, () => props.doc, (containingCollection: CollectionView) => this.props.removeDocument(props.doc));
         return (
             <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "36px" }} key={props.doc.Id} ref={reference}>
                 <EditableView contents={contents}
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 8fc1a1b4d..05f759967 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -44,7 +44,7 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
     public SelectedDocs: FieldId[] = []
     public active: () => boolean = () => CollectionView.Active(this);
 
-    addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); }
+    addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); }
     removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
 
     specificContextMenu = (e: React.MouseEvent): void => {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 40acf466e..303099c16 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -36,7 +36,7 @@ export class CollectionView extends React.Component<CollectionViewProps> {
     @observable
     public SelectedDocs: FieldId[] = [];
     public active: () => boolean = () => CollectionView.Active(this);
-    addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); }
+    addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); }
     removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
     get subView() { return CollectionView.SubView(this); }
 
@@ -48,12 +48,13 @@ export class CollectionView extends React.Component<CollectionViewProps> {
     }
 
     @action
-    public static AddDocument(props: CollectionViewProps, doc: Document) {
+    public static AddDocument(props: CollectionViewProps, doc: Document, allowDuplicates: boolean) {
         doc.SetNumber(KeyStore.Page, props.Document.GetNumber(KeyStore.CurPage, -1));
         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>())
-            value.push(doc);
+            if (!value.some(v => v.Id == doc.Id) || allowDuplicates)
+                value.push(doc);
         } else {
             props.Document.SetOnPrototype(props.fieldKey, new ListField([doc]));
         }
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index 3d1a76a07..0358b5907 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -27,7 +27,7 @@ export interface CollectionViewProps {
 }
 export interface SubCollectionViewProps extends CollectionViewProps {
     active: () => boolean;
-    addDocument: (doc: Document) => void;
+    addDocument: (doc: Document, allowDuplicates: boolean) => void;
     removeDocument: (doc: Document) => boolean;
     CollectionView: CollectionView;
 }
@@ -46,28 +46,16 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
     @undoBatch
     @action
     protected drop(e: Event, de: DragManager.DropEvent) {
-        let docToAlias = de.data["documentToAlias"];
-        let docView = de.data["documentView"];
-        let doc = docToAlias ? docToAlias.CreateAlias() : de.data["document"];
-        if (docToAlias) {
-            [KeyStore.Width, KeyStore.Height].map(key =>
-                docToAlias.GetTAsync(key, NumberField, (f: Opt<NumberField>) => {
-                    if (f) {
-                        doc.SetNumber(key, f.Data)
-                    }
-                })
-            );
-            de.data["document"] = doc;
-            this.props.addDocument(doc);
-        } else if (docView) {
-            if (doc && docView.props.RemoveDocument && docView.props.ContainingCollectionView !== this.props.CollectionView) {
-                docView.props.RemoveDocument(doc);
-                this.props.removeDocument(doc); // bcz: not good -- want to check if it's there and then add if it isn't
-                this.props.addDocument(doc);
+        let dropped = de.data["droppedDocument"];
+        if (dropped) {
+            if (de.data["aliasOnDrop"]) {
+                let dragged = de.data["draggedDocument"];
+                [KeyStore.Width, KeyStore.Height, KeyStore.CurPage].map(key =>
+                    dragged.GetTAsync(key, NumberField, (f: Opt<NumberField>) => f ? dropped.SetNumber(key, f.Data) : null));
+            } else if (de.data["removeDocument"]) {
+                de.data["removeDocument"](this.props.CollectionView);
             }
-        } else if (doc) {
-            this.props.removeDocument(doc); // bcz: not good -- want to check if it's there and then add if it isn't
-            this.props.addDocument(doc);
+            this.props.addDocument(dropped, false);
         }
         e.stopPropagation();
     }
@@ -89,7 +77,7 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
             console.log("not good");
             let htmlDoc = Documents.HtmlDocument(html, { ...options, width: 300, height: 300 });
             htmlDoc.SetText(KeyStore.DocumentText, text);
-            this.props.addDocument(htmlDoc);
+            this.props.addDocument(htmlDoc, false);
             return;
         }
 
@@ -99,7 +87,7 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
             const upload = window.location.origin + "/upload";
             let item = e.dataTransfer.items[i];
             if (item.kind === "string" && item.type.indexOf("uri") != -1) {
-                e.dataTransfer.items[i].getAsString(action((s: string) => this.props.addDocument(Documents.WebDocument(s, options))))
+                e.dataTransfer.items[i].getAsString(action((s: string) => this.props.addDocument(Documents.WebDocument(s, options), false)))
             }
             let type = item.type
             console.log(type)
diff --git a/src/client/views/collections/MarqueeView.tsx b/src/client/views/collections/MarqueeView.tsx
index f5c83a934..8c2f3443c 100644
--- a/src/client/views/collections/MarqueeView.tsx
+++ b/src/client/views/collections/MarqueeView.tsx
@@ -17,7 +17,7 @@ interface MarqueeViewProps {
     getMarqueeTransform: () => Transform;
     getTransform: () => Transform;
     container: CollectionFreeFormView;
-    addDocument: (doc: Document) => void;
+    addDocument: (doc: Document, allowDuplicates: false) => void;
     activeDocuments: () => Document[];
     selectDocuments: (docs: Document[]) => void;
     removeDocument: (doc: Document) => boolean;
@@ -111,7 +111,18 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             let liftedInk = this.marqueeInkSelect(true);
             this.props.container.props.Document.SetData(KeyStore.Ink, this.marqueeInkSelect(false), InkField);
             //setTimeout(() => {
-            this.props.addDocument(Documents.FreeformDocument(selected, { x: bounds.left, y: bounds.top, panx: 0, pany: 0, width: bounds.width, backgroundColor: "Transparent", height: bounds.height, ink: liftedInk, title: "a nested collection" }));
+            let newCollection = Documents.FreeformDocument(selected, {
+                x: bounds.left,
+                y: bounds.top,
+                panx: 0,
+                pany: 0,
+                width: bounds.width,
+                height: bounds.height,
+                backgroundColor: "Transparent",
+                ink: liftedInk,
+                title: "a nested collection"
+            });
+            this.props.addDocument(newCollection, false);
             // }, 100);
             this.cleanupInteractions();
         }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index fbd58ef7e..e98815fa6 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -23,7 +23,7 @@ import React = require("react");
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView>;
     Document: Document;
-    AddDocument?: (doc: Document) => void;
+    AddDocument?: (doc: Document, allowDuplicates: boolean) => void;
     RemoveDocument?: (doc: Document) => boolean;
     ScreenToLocalTransform: () => Transform;
     isTopMost: boolean;
@@ -164,12 +164,15 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         if (this._mainCont.current) {
             const [left, top] = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
             let dragData: { [id: string]: any } = {};
-            dragData["documentView"] = this;
-            if (dropAliasOfDraggedDoc)
-                dragData["documentToAlias"] = this.props.Document;
-            dragData["document"] = this.props.Document;
+            dragData["aliasOnDrop"] = dropAliasOfDraggedDoc;
+            dragData["draggedDocument"] = this.props.Document;
             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);
+                }
+            }
             DragManager.StartDrag(this._mainCont.current, dragData, {
                 handlers: {
                     dragComplete: action(() => { }),
@@ -213,7 +216,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
 
     fieldsClicked = (e: React.MouseEvent): void => {
         if (this.props.AddDocument) {
-            this.props.AddDocument(Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }));
+            this.props.AddDocument(Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }), false);
         }
     }
     fullScreenClicked = (e: React.MouseEvent): void => {
@@ -232,7 +235,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
 
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
-        console.log("drop");
         const sourceDocView: DocumentView = de.data["linkSourceDoc"];
         if (!sourceDocView) {
             return;
-- 
cgit v1.2.3-70-g09d2


From da576c10538c025f247a50ed8561b112f78f828e Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 18 Mar 2019 14:02:21 -0400
Subject: added column dividers

---
 .../views/collections/CollectionSchemaView.scss    | 17 ++++
 .../views/collections/CollectionSchemaView.tsx     | 90 ++++++++++++++++------
 2 files changed, 84 insertions(+), 23 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index 8d3eba696..863bb256a 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -28,6 +28,23 @@
         top: 0;
         background: $main-accent;
     }
+    .collectionSchemaView-columnsHandle {
+        position: absolute;
+        height: 37px;
+        width: 20px;
+        z-index: 20;
+        left: 0;
+        bottom: 0;
+        background: $main-accent;
+    }
+    .collectionSchemaView-colDividerDragger {
+        position: relative;
+        box-sizing: border-box;
+        border-top: 1px solid $intermediate-color;
+        border-bottom: 1px solid $intermediate-color;
+        float: top;
+        width: 100%;
+    }
     .collectionSchemaView-dividerDragger {
         position: relative;
         box-sizing: border-box;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 654d13bf1..76ee421d6 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -60,6 +60,7 @@ export class CollectionSchemaView extends CollectionViewBase {
     @observable _panelWidth = 0;
     @observable _panelHeight = 0;
     @observable _selectedIndex = 0;
+    @observable _columnsPercentage = 0;
     @computed get splitPercentage() { return this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0); }
 
     renderCell = (rowProps: CellInfo) => {
@@ -190,6 +191,25 @@ export class CollectionSchemaView extends CollectionViewBase {
         document.addEventListener("pointermove", this.onDividerMove);
         document.addEventListener('pointerup', this.onDividerUp);
     }
+
+
+    @action
+    onColDividerMove = (e: PointerEvent): void => {
+        let nativeWidth = this._mainCont.current!.getBoundingClientRect();
+        this._columnsPercentage = 100 - (e.clientY - nativeWidth.top) / nativeWidth.height * 100;
+    }
+    @action
+    onColDividerUp = (e: PointerEvent): void => {
+        document.removeEventListener("pointermove", this.onColDividerMove);
+        document.removeEventListener('pointerup', this.onColDividerUp);
+    }
+    onColDividerDown = (e: React.PointerEvent) => {
+        e.stopPropagation();
+        e.preventDefault();
+        document.addEventListener("pointermove", this.onColDividerMove);
+        document.addEventListener('pointerup', this.onColDividerUp);
+    }
+
     @action
     onExpanderMove = (e: PointerEvent): void => {
         e.stopPropagation();
@@ -201,12 +221,8 @@ export class CollectionSchemaView extends CollectionViewBase {
         e.preventDefault();
         document.removeEventListener("pointermove", this.onExpanderMove);
         document.removeEventListener('pointerup', this.onExpanderUp);
-        if (this._startSplitPercent == this.splitPercentage) {
-            this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage == 0 ? 33 : 0);
-        }
     }
     onExpanderDown = (e: React.PointerEvent) => {
-        this._startSplitPercent = this.splitPercentage;
         e.stopPropagation();
         e.preventDefault();
         document.addEventListener("pointermove", this.onExpanderMove);
@@ -227,6 +243,27 @@ export class CollectionSchemaView extends CollectionViewBase {
         }
     }
 
+    @action
+    onColumnsMove = (e: PointerEvent): void => {
+        e.stopPropagation();
+        e.preventDefault();
+    }
+    @action
+    onColumnsUp = (e: PointerEvent): void => {
+        e.stopPropagation();
+        e.preventDefault();
+        document.removeEventListener("pointermove", this.onColumnsMove);
+        document.removeEventListener('pointerup', this.onColumnsUp);
+        this._columnsPercentage = this._columnsPercentage ? 0 : 50;
+    }
+    onColumnsDown = (e: React.PointerEvent) => {
+        this._startSplitPercent = this.splitPercentage;
+        e.stopPropagation();
+        e.preventDefault();
+        document.addEventListener("pointermove", this.onColumnsMove);
+        document.addEventListener('pointerup', this.onColumnsUp);
+    }
+
     @action
     setScaling = (r: any) => {
         const children = this.props.Document.GetList<Document>(this.props.fieldKey, []);
@@ -271,8 +308,12 @@ export class CollectionSchemaView extends CollectionViewBase {
         )
         let previewHandle = !this.props.active() ? (null) : (
             <div className="collectionSchemaView-previewHandle" onPointerDown={this.onExpanderDown} />);
+        let columnsHandle = !this.props.active() ? (null) : (
+            <div className="collectionSchemaView-columnsHandle" onPointerDown={this.onColumnsDown} />);
         let dividerDragger = this.splitPercentage == 0 ? (null) :
             <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />
+        let colDividerDragger = this._columnsPercentage == 0 ? (null) :
+            <div className="collectionSchemaView-colDividerDragger" onPointerDown={this.onColDividerDown} style={{ height: `${this.DIVIDER_WIDTH}px` }} />
 
         return (
             <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} ref={this._mainCont} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} >
@@ -284,30 +325,32 @@ export class CollectionSchemaView extends CollectionViewBase {
                         {({ measureRef }) =>
                             <div ref={measureRef} className="collectionSchemaView-tableContainer"
                                 style={{ width: `calc(100% - ${this.splitPercentage}%)` }}>
-                                <ReactTable
-                                    data={children}
-                                    pageSize={children.length}
-                                    page={0}
-                                    showPagination={false}
-                                    columns={columns.map(col => ({
-                                        Header: col.Name,
-                                        accessor: (doc: Document) => [doc, col],
-                                        id: col.Id
-                                    }))}
-                                    column={{
-                                        ...ReactTableDefaults.column,
-                                        Cell: this.renderCell,
+                                <div className="collectionSchemaView-reactContainer" style={{ height: `calc(100% - ${this._columnsPercentage}%)` }}>
+                                    <ReactTable
+                                        data={children}
+                                        pageSize={children.length}
+                                        page={0}
+                                        showPagination={false}
+                                        columns={columns.map(col => ({
+                                            Header: col.Name,
+                                            accessor: (doc: Document) => [doc, col],
+                                            id: col.Id
+                                        }))}
+                                        column={{
+                                            ...ReactTableDefaults.column,
+                                            Cell: this.renderCell,
 
-                                    }}
-                                    getTrProps={this.getTrProps}
-                                    style={{ height: "50%" }}
-                                />
-                                <div className="collectionSchemaView-addColumn" style={{ height: "50%" }} >
+                                        }}
+                                        getTrProps={this.getTrProps}
+                                    />
+                                </div>
+                                {colDividerDragger}
+                                <div className="collectionSchemaView-addColumn" style={{ height: `${this._columnsPercentage}%` }} >
                                     {/* <input type="checkbox" id="addColumn-toggle" />
                                     <label htmlFor="addColumn-toggle" title="Add Column"><p>+</p></label> */}
 
                                     <div className="addColumn-options">
-                                        <ul>
+                                        <ul style={{ overflow: "scroll" }}>
                                             {Array.from(Object.keys(allKeys)).map(item => {
                                                 return (<KeyToggle checked={allKeys[item]} key={item} keyId={item} toggle={this.toggleKey} />)
                                             })}
@@ -322,6 +365,7 @@ export class CollectionSchemaView extends CollectionViewBase {
                         {content}
                     </div>
                     {previewHandle}
+                    {columnsHandle}
 
                 </div>
             </div >
-- 
cgit v1.2.3-70-g09d2


From f70ad315167b714f11f7d68f35a46abe9e525a4d Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Mon, 18 Mar 2019 14:51:06 -0400
Subject: Fixed schema stuff

---
 src/client/views/collections/CollectionSchemaView.tsx | 4 ++++
 1 file changed, 4 insertions(+)

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

diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 1c5ff1b12..50f5e9618 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -223,8 +223,12 @@ export class CollectionSchemaView extends CollectionViewBase {
         e.preventDefault();
         document.removeEventListener("pointermove", this.onExpanderMove);
         document.removeEventListener('pointerup', this.onExpanderUp);
+        if (this._startSplitPercent == this.splitPercentage) {
+            this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage == 0 ? 33 : 0);
+        }
     }
     onExpanderDown = (e: React.PointerEvent) => {
+        this._startSplitPercent = this.splitPercentage;
         e.stopPropagation();
         e.preventDefault();
         document.addEventListener("pointermove", this.onExpanderMove);
-- 
cgit v1.2.3-70-g09d2


From 67170e521366f8178645cc85aaf377e53b1a6f21 Mon Sep 17 00:00:00 2001
From: laurawilsonri <laura_wilson@brown.edu>
Date: Mon, 18 Mar 2019 18:07:46 -0400
Subject: ui in place, needs refinement and more backend

---
 deploy/index.html                                  |   2 +
 package-lock.json                                  | 239 ++++++++++++++++++++-
 package.json                                       |   7 +-
 src/client/views/DocumentDecorations.tsx           |   4 +-
 .../views/collections/CollectionSchemaView.scss    |  66 +++++-
 .../views/collections/CollectionSchemaView.tsx     |  68 +++++-
 6 files changed, 373 insertions(+), 13 deletions(-)

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

diff --git a/deploy/index.html b/deploy/index.html
index e0892662a..94699126c 100644
--- a/deploy/index.html
+++ b/deploy/index.html
@@ -3,6 +3,8 @@
 <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>
 </head>
 
diff --git a/package-lock.json b/package-lock.json
index 5b9cb598d..3d66773f2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -174,6 +174,52 @@
       "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
       "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw=="
     },
+    "@react-bootstrap/react-popper": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@react-bootstrap/react-popper/-/react-popper-1.2.1.tgz",
+      "integrity": "sha512-4l3q7LcZEhrSkI4d3Ie3g4CdrXqqTexXX4PFT45CB0z5z2JUbaxgRwKNq7r5j2bLdVpZm+uvUGqxJw8d9vgbJQ==",
+      "requires": {
+        "babel-runtime": "6.x.x",
+        "create-react-context": "^0.2.1",
+        "popper.js": "^1.14.4",
+        "prop-types": "^15.6.1",
+        "typed-styles": "^0.0.5",
+        "warning": "^3.0.0"
+      }
+    },
+    "@trendmicro/react-buttons": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/@trendmicro/react-buttons/-/react-buttons-1.3.1.tgz",
+      "integrity": "sha512-9zvt/fdkqCb9kxUdZnvTZKmbmykM2wDQ3VEJFtztGcKAkm4Wkq4oZOQLJXKfUQ1vX3w+YDJob18LkNOzaHI1UQ==",
+      "requires": {
+        "classnames": "^2.2.5",
+        "prop-types": "^15.5.8"
+      }
+    },
+    "@trendmicro/react-dropdown": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/@trendmicro/react-dropdown/-/react-dropdown-1.3.0.tgz",
+      "integrity": "sha512-KwL0ksEZPay7qNsiGcPQ3aGmyfJCcUuIjiD9HZs6L66ScwSRoFkDlAjMTlRVLFcYVNhpuyUH4pPiFlKQQzDHGQ==",
+      "requires": {
+        "@trendmicro/react-buttons": "^1.3.0",
+        "chained-function": "^0.5.0",
+        "classnames": "^2.2.5",
+        "dom-helpers": "^3.3.1",
+        "prop-types": "^15.6.0",
+        "uncontrollable": "^5.0.0",
+        "warning": "^3.0.0"
+      },
+      "dependencies": {
+        "uncontrollable": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-5.1.0.tgz",
+          "integrity": "sha512-5FXYaFANKaafg4IVZXUNtGyzsnYEvqlr9wQ3WpZxFpEUxl29A3H6Q4G1Dnnorvq9TGOGATBApWR4YpLAh+F5hw==",
+          "requires": {
+            "invariant": "^2.2.4"
+          }
+        }
+      }
+    },
     "@types/anymatch": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz",
@@ -1531,6 +1577,11 @@
         }
       }
     },
+    "bootstrap": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.3.1.tgz",
+      "integrity": "sha512-rXqOmH1VilAt2DyPzluTi2blhk17bO7ef+zLLPlWvG494pDxcM234pJ8wTc/6R40UWizAIIMgxjvxZg5kmsbag=="
+    },
     "boxen": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz",
@@ -1899,6 +1950,11 @@
         "type-detect": "^4.0.5"
       }
     },
+    "chained-function": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/chained-function/-/chained-function-0.5.0.tgz",
+      "integrity": "sha1-JWS73994AxlL6/daayvQQe3iOzo="
+    },
     "character-parser": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz",
@@ -3461,6 +3517,20 @@
         "websocket-driver": ">=0.5.1"
       }
     },
+    "fbjs": {
+      "version": "0.8.17",
+      "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
+      "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
+      "requires": {
+        "core-js": "^1.0.0",
+        "isomorphic-fetch": "^2.1.1",
+        "loose-envify": "^1.0.0",
+        "object-assign": "^4.1.0",
+        "promise": "^7.1.1",
+        "setimmediate": "^1.0.5",
+        "ua-parser-js": "^0.7.18"
+      }
+    },
     "figgy-pudding": {
       "version": "3.5.1",
       "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz",
@@ -3735,14 +3805,12 @@
         "balanced-match": {
           "version": "1.0.0",
           "resolved": false,
-          "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
-          "optional": true
+          "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
         },
         "brace-expansion": {
           "version": "1.1.11",
           "resolved": false,
           "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-          "optional": true,
           "requires": {
             "balanced-match": "^1.0.0",
             "concat-map": "0.0.1"
@@ -3762,8 +3830,7 @@
         "concat-map": {
           "version": "0.0.1",
           "resolved": false,
-          "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-          "optional": true
+          "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
         },
         "console-control-strings": {
           "version": "1.1.0",
@@ -3911,7 +3978,6 @@
           "version": "3.0.4",
           "resolved": false,
           "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
-          "optional": true,
           "requires": {
             "brace-expansion": "^1.1.7"
           }
@@ -4927,6 +4993,14 @@
       "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==",
       "dev": true
     },
+    "invariant": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+      "requires": {
+        "loose-envify": "^1.0.0"
+      }
+    },
     "invert-kv": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
@@ -5440,6 +5514,11 @@
       "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.0.tgz",
       "integrity": "sha512-6hHxsp9e6zQU8nXsP+02HGWXwTkOEw6IROhF2ZA28cYbUk4eJ6QbtZvdqZOdD9YPKghG3apk5eOCvs+tLl3lRg=="
     },
+    "keycode": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz",
+      "integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ="
+    },
     "killable": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
@@ -9983,6 +10062,11 @@
         }
       }
     },
+    "popper.js": {
+      "version": "1.14.7",
+      "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.7.tgz",
+      "integrity": "sha512-4q1hNvoUre/8srWsH7hnoSJ5xVmIL4qgz+s4qf2TnJIMyZFUFMGH+9vE7mXynAlHSZ/NdTmmow86muD0myUkVQ=="
+    },
     "portfinder": {
       "version": "1.0.20",
       "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz",
@@ -10168,6 +10252,15 @@
         "react-is": "^16.8.1"
       }
     },
+    "prop-types-extra": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.0.tgz",
+      "integrity": "sha512-QFyuDxvMipmIVKD2TwxLVPzMnO4e5oOf1vr3tJIomL8E7d0lr6phTHd5nkPhFIzTD1idBLLEPeylL9g+rrTzRg==",
+      "requires": {
+        "react-is": "^16.3.2",
+        "warning": "^3.0.0"
+      }
+    },
     "prosemirror-commands": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.0.7.tgz",
@@ -10605,6 +10698,43 @@
         "scheduler": "^0.13.4"
       }
     },
+    "react-bootstrap": {
+      "version": "1.0.0-beta.5",
+      "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.0.0-beta.5.tgz",
+      "integrity": "sha512-Osm0OtTbYwfsT1rpu88ESWuAHZxfaHFNKFiW8w3w+6YY9/bLEPHbGRZA6W21fg5yvcuKN9hJKT857TTHgY7SoQ==",
+      "requires": {
+        "@babel/runtime": "^7.2.0",
+        "@react-bootstrap/react-popper": "1.2.1",
+        "classnames": "^2.2.6",
+        "dom-helpers": "^3.4.0",
+        "invariant": "^2.2.3",
+        "keycode": "^2.1.2",
+        "popper.js": "^1.14.6",
+        "prop-types": "^15.6.2",
+        "prop-types-extra": "^1.1.0",
+        "react-context-toolbox": "^2.0.2",
+        "react-overlays": "^1.0.0",
+        "react-prop-types": "^0.4.0",
+        "react-transition-group": "^2.5.1",
+        "uncontrollable": "^6.0.0",
+        "warning": "^4.0.1"
+      },
+      "dependencies": {
+        "warning": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+          "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+          "requires": {
+            "loose-envify": "^1.0.0"
+          }
+        }
+      }
+    },
+    "react-bootstrap-dropdown-menu": {
+      "version": "1.1.15",
+      "resolved": "https://registry.npmjs.org/react-bootstrap-dropdown-menu/-/react-bootstrap-dropdown-menu-1.1.15.tgz",
+      "integrity": "sha512-o35fODF4GsNxEzmnRWZuoe29a6x3fXqRsLXOlAvS4d+TvO3yoLTM8iZnSZpJCkHQnjOOWRnGvDh5tryqJCKZ0w=="
+    },
     "react-color": {
       "version": "2.17.0",
       "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.17.0.tgz",
@@ -10618,6 +10748,11 @@
         "tinycolor2": "^1.4.1"
       }
     },
+    "react-context-toolbox": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/react-context-toolbox/-/react-context-toolbox-2.0.2.tgz",
+      "integrity": "sha512-tY4j0imkYC3n5ZlYSgFkaw7fmlCp3IoQQ6DxpqeNHzcD0hf+6V+/HeJxviLUZ1Rv1Yn3N3xyO2EhkkZwHn0m1A=="
+    },
     "react-dimensions": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/react-dimensions/-/react-dimensions-1.3.1.tgz",
@@ -10714,6 +10849,31 @@
         "threads": "^0.8.0"
       }
     },
+    "react-overlays": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-1.2.0.tgz",
+      "integrity": "sha512-i/FCV8wR6aRaI+Kz/dpJhOdyx+ah2tN1RhT9InPrexyC4uzf3N4bNayFTGtUeQVacj57j1Mqh1CwV60/5153Iw==",
+      "requires": {
+        "classnames": "^2.2.6",
+        "dom-helpers": "^3.4.0",
+        "prop-types": "^15.6.2",
+        "prop-types-extra": "^1.1.0",
+        "react-context-toolbox": "^2.0.2",
+        "react-popper": "^1.3.2",
+        "uncontrollable": "^6.0.0",
+        "warning": "^4.0.2"
+      },
+      "dependencies": {
+        "warning": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+          "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+          "requires": {
+            "loose-envify": "^1.0.0"
+          }
+        }
+      }
+    },
     "react-pdf": {
       "version": "4.0.5",
       "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-4.0.5.tgz",
@@ -10791,6 +10951,51 @@
       "resolved": "https://registry.npmjs.org/react-pointable/-/react-pointable-1.1.3.tgz",
       "integrity": "sha512-7skalWy38NLyKk1HcITmuloqCe2INPh69cFHbN7NcCn+Wfb0Ha4XGwGN0iVa4ZzqIHSqFRYOmV0loYNHrZ5/Sg=="
     },
+    "react-popper": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.3.tgz",
+      "integrity": "sha512-ynMZBPkXONPc5K4P5yFWgZx5JGAUIP3pGGLNs58cfAPgK67olx7fmLp+AdpZ0+GoQ+ieFDa/z4cdV6u7sioH6w==",
+      "requires": {
+        "@babel/runtime": "^7.1.2",
+        "create-react-context": "<=0.2.2",
+        "popper.js": "^1.14.4",
+        "prop-types": "^15.6.1",
+        "typed-styles": "^0.0.7",
+        "warning": "^4.0.2"
+      },
+      "dependencies": {
+        "create-react-context": {
+          "version": "0.2.2",
+          "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.2.tgz",
+          "integrity": "sha512-KkpaLARMhsTsgp0d2NA/R94F/eDLbhXERdIq3LvX2biCAXcDvHYoOqHfWCHf1+OLj+HKBotLG3KqaOOf+C1C+A==",
+          "requires": {
+            "fbjs": "^0.8.0",
+            "gud": "^1.0.0"
+          }
+        },
+        "typed-styles": {
+          "version": "0.0.7",
+          "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz",
+          "integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q=="
+        },
+        "warning": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+          "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+          "requires": {
+            "loose-envify": "^1.0.0"
+          }
+        }
+      }
+    },
+    "react-prop-types": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/react-prop-types/-/react-prop-types-0.4.0.tgz",
+      "integrity": "sha1-+ZsL+0AGkpya8gUefBQUpcdbk9A=",
+      "requires": {
+        "warning": "^3.0.0"
+      }
+    },
     "react-rnd": {
       "version": "7.4.3",
       "resolved": "https://registry.npmjs.org/react-rnd/-/react-rnd-7.4.3.tgz",
@@ -10800,6 +11005,15 @@
         "react-draggable": "^3.0.5"
       }
     },
+    "react-simple-dropdown": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmjs.org/react-simple-dropdown/-/react-simple-dropdown-3.2.3.tgz",
+      "integrity": "sha512-NmyyvA0D4wph5ctzkn8U4wmblOacavJMl9gTOhQR3v8I997mc1FL1NFKkj3Mx+HNysBKRD/HI+kpxXCAgXumPw==",
+      "requires": {
+        "classnames": "^2.1.2",
+        "prop-types": "^15.5.8"
+      }
+    },
     "react-split-pane": {
       "version": "0.1.85",
       "resolved": "https://registry.npmjs.org/react-split-pane/-/react-split-pane-0.1.85.tgz",
@@ -12453,6 +12667,11 @@
         "mime-types": "~2.1.18"
       }
     },
+    "typed-styles": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.5.tgz",
+      "integrity": "sha512-ht+rEe5UsdEBAa3gr64+QjUOqjOLJfWLvl5HZR5Ev9uo/OnD3p43wPeFSB1hNFc13GXQF/JU1Bn0YHLUqBRIlw=="
+    },
     "typedarray": {
       "version": "0.0.6",
       "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
@@ -12526,6 +12745,14 @@
         "random-bytes": "~1.0.0"
       }
     },
+    "uncontrollable": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-6.1.0.tgz",
+      "integrity": "sha512-2TzEm0pLKauMBZfAZXsgQvLpZHEp95891frCZdGDrSG7dWYaIQhedwLAzi0X8pR8KHNqlmuYEb2cEgbQzr050A==",
+      "requires": {
+        "invariant": "^2.2.4"
+      }
+    },
     "undefsafe": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz",
diff --git a/package.json b/package.json
index 3bbaf72ad..3a1928632 100644
--- a/package.json
+++ b/package.json
@@ -35,9 +35,10 @@
   "dependencies": {
     "@fortawesome/fontawesome-free-solid": "^5.0.13",
     "@fortawesome/fontawesome-svg-core": "^1.2.15",
-    "@hig/flyout": "^1.0.3",
     "@fortawesome/free-solid-svg-icons": "^5.7.2",
     "@fortawesome/react-fontawesome": "^0.1.4",
+    "@hig/flyout": "^1.0.3",
+    "@trendmicro/react-dropdown": "^1.3.0",
     "@types/bcrypt-nodejs": "0.0.30",
     "@types/bluebird": "^3.5.25",
     "@types/body-parser": "^1.17.0",
@@ -78,6 +79,7 @@
     "bcrypt-nodejs": "0.0.3",
     "bluebird": "^3.5.3",
     "body-parser": "^1.18.3",
+    "bootstrap": "^4.3.1",
     "connect-mongo": "^2.0.3",
     "express": "^4.16.4",
     "express-flash": "0.0.2",
@@ -116,6 +118,8 @@
     "pug": "^2.0.3",
     "raw-loader": "^1.0.0",
     "react": "^16.8.4",
+    "react-bootstrap": "^1.0.0-beta.5",
+    "react-bootstrap-dropdown-menu": "^1.1.15",
     "react-color": "^2.17.0",
     "react-dimensions": "^1.3.1",
     "react-dom": "^16.8.4",
@@ -127,6 +131,7 @@
     "react-pdf": "^4.0.2",
     "react-pdf-highlighter": "^2.1.2",
     "react-pdf-js": "^4.0.2",
+    "react-simple-dropdown": "^3.2.3",
     "react-split-pane": "^0.1.85",
     "react-table": "^6.9.2",
     "request": "^2.88.0",
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index dc62f97cf..1aa6d1936 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -10,8 +10,8 @@ import { DragManager } from "../util/DragManager";
 import { LinkMenu } from "./nodes/LinkMenu";
 import { ListField } from "../../fields/ListField";
 const higflyout = require("@hig/flyout");
-const { anchorPoints } = higflyout;
-const Flyout = higflyout.default;
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
 
 @observer
 export class DocumentDecorations extends React.Component {
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index d40e6d314..ab906cf4b 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -1,5 +1,65 @@
+//options menu styling
+#schemaOptionsMenuBtn {
+    position: absolute;
+    height: 15px;
+    width: 15px;
+    border-radius: 50%;
+    z-index: 21; 
+    right: 3px;
+    top: 3px; 
+    pointer-events: auto;
+    background-color:red;
+}
+
+.dd-item-containter:hover {
+  background-color: rgb(204, 204, 204);
+}
+
+.dd-item {
+    color: black;
+    padding-left: 6px;
+    padding-bottom: 0px;
+    padding-top: 4px;
+    font-size: 12px;
+    margin: 0px;
+    display: block;
+    width: 100%;
+    height: 25px;
+    z-index: 100;
+}
+.dd-item:hover {
+    color: black;
+    font-weight: 500;
+}
+
+.dropdown-menu {
+    height: 100px;
+    overflow-y: scroll;
+  }
 
+#dropdown-basic-button {
+    background-color: #fff;
+    border: .75px solid #bbbbbb;
+    border-radius: 0;
+    -webkit-box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+    margin:10px;
+    margin-top: 8px;
+    color: black;
+    display: inline-block;
+    width: 160px;
+    height: 25px;
+    font-size: 15px;
+    padding: 0px;
+    border-radius: 3px;
+    font-size: 12px;
+  }
 
+  #options-flyout-div {
+    text-align: center;
+    padding:0px;
+  }
+  
 .collectionSchemaView-container {
     border-style: solid;
     box-sizing: border-box;
@@ -14,11 +74,11 @@
     }
     .collectionSchemaView-previewHandle {
         position: absolute;
-        height: 37px;
-        width: 20px; 
+        height: 15px;
+        width: 15px; 
         z-index: 20; 
         right: 0; 
-        top: 0; 
+        top: 20px; 
         background: Black ;
     }
     .collectionSchemaView-dividerDragger{
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 04f017378..7c87ef744 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 { action, observable } from "mobx";
 import { observer } from "mobx-react";
 import Measure from "react-measure";
@@ -17,6 +17,16 @@ import "./CollectionSchemaView.scss";
 import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
 import { CollectionViewBase } from "./CollectionViewBase";
 import { setupDrag } from "../../util/DragManager";
+import '../DocumentDecorations.scss';
+import { Flyout, anchorPoints } from "../DocumentDecorations";
+import { DropdownButton, Dropdown } from 'react-bootstrap';
+import { ListField } from "../../../fields/ListField";
+import { Key } from "../../../fields/Key";
+//import { Server } from "http";
+//import { Server } from "http";
+import { Server } from "../../Server";
+
+//import { MenuItem } from 'react-bootstrap';
 
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
 
@@ -178,6 +188,19 @@ export class CollectionSchemaView extends CollectionViewBase {
 
     focusDocument = (doc: Document) => { }
 
+    keyDropDownOnSelect = (keyId: string) => {
+        const columns = this.props.Document.GetList(KeyStore.ColumnsKey, [KeyStore.Title, KeyStore.Data, KeyStore.Author]);
+        const listOfFields = this.props.Document.GetList<Document>(this.props.fieldKey, [])[1].fields;
+        if (listOfFields.has(keyId)) {
+            let index = columns.push(listOfFields!.get(keyId)!.key);
+            this.props.Document.SetData(KeyStore.ColumnsKey, columns, ListField);
+        }
+    }
+
+    dropdownItemOnPointerDown = (e: React.PointerEvent): void => {
+        //(e.currentTarget.children[0] as Dropdown.Item ).onSelect();
+    }
+
     render() {
         const columns = this.props.Document.GetList(KeyStore.ColumnsKey, [KeyStore.Title, KeyStore.Data, KeyStore.Author])
         const children = this.props.Document.GetList<Document>(this.props.fieldKey, []);
@@ -205,6 +228,47 @@ export class CollectionSchemaView extends CollectionViewBase {
             <div className="collectionSchemaView-previewHandle" onPointerDown={this.onExpanderDown} />);
         let dividerDragger = this._splitPercentage == 100 ? (null) :
             <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />
+
+        //get the union of all childrens' keys
+        let addFields: { id: string, name: string }[] = [];
+        let removeFields: { id: string, name: string }[] = [];
+        //children.forEach((child) => {
+        //   for (var keyId in fields.keys) {
+        //       if (!allFields.includes(keyId)) {
+        //           allFields.push(keyId);
+        //      }
+        //   }
+        // });
+        let optionsMenu = null;
+        if (this.props.active()) {
+            let protos = this.props.Document.GetAllPrototypes();
+            for (const proto of protos) {
+                proto._proxies.forEach((val, key) => {
+                    if (!(key in addFields.keys)) {
+                        let this_name: string = proto.fields!.get(key)!.key.Name;
+                        addFields.push({ id: key, name: this_name });
+                    }
+                })
+            }
+            optionsMenu = !this.props.active() ? (null) : (<Flyout
+                anchorPoint={anchorPoints.LEFT_TOP}
+                content={<div id="options-flyout-div"> <h5>Options</h5>
+                    <DropdownButton id="dropdown-basic-button" className="colDropDown" title="Add Column">
+                        {addFields.map(({ id, name }) => (
+                            <div className="dd-item-containter"><Dropdown.Item className="dd-item" eventKey={id} onSelect={this.keyDropDownOnSelect}>{name} </Dropdown.Item></div>))}
+                    </DropdownButton>
+                    <DropdownButton id="dropdown-basic-button" className="colDropDown" title="Remove Column">
+                        {addFields.map(({ id, name }) => (
+                            <div className="dd-item-containter"><Dropdown.Item className="dd-item" eventKey={id} onSelect={this.keyDropDownOnSelect}>{name} </Dropdown.Item></div>))}
+                    </DropdownButton>
+                </div>
+                }>
+                <div id="schemaOptionsMenuBtn" />
+            </Flyout>);
+        }
+
+
+
         return (
             <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} ref={this._mainCont} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} >
                 <div className="collectionSchemaView-dropTarget" onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
@@ -239,6 +303,8 @@ export class CollectionSchemaView extends CollectionViewBase {
                         {content}
                     </div>
                     {previewHandle}
+                    {optionsMenu}
+
                 </div>
             </div >
         )
-- 
cgit v1.2.3-70-g09d2


From 670cf766c99a8a5de806fd7d6f1d9e357a55071a Mon Sep 17 00:00:00 2001
From: laurawilsonri <laura_wilson@brown.edu>
Date: Mon, 18 Mar 2019 18:29:10 -0400
Subject: not sure

---
 deploy/index.html                                  |   3 -
 package-lock.json                                  | 419 ++++++++++++++++++---
 package.json                                       |   6 -
 .../views/collections/CollectionSchemaView.tsx     |  80 ++--
 4 files changed, 402 insertions(+), 106 deletions(-)

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

diff --git a/deploy/index.html b/deploy/index.html
index dbfc6dee0..ca5c13e98 100644
--- a/deploy/index.html
+++ b/deploy/index.html
@@ -2,12 +2,9 @@
 
 <head>
   <title>Dash Web</title>
-<<<<<<< HEAD
   <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">
-=======
->>>>>>> f70ad315167b714f11f7d68f35a46abe9e525a4d
   <script src="https://cdnjs.cloudflare.com/ajax/libs/typescript/3.3.1/typescript.min.js"></script>
 </head>
 
diff --git a/package-lock.json b/package-lock.json
index 3d66773f2..18365bf9b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -152,11 +152,10 @@
       }
     },
     "@hig/theme-context": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/@hig/theme-context/-/theme-context-2.1.2.tgz",
-      "integrity": "sha512-JTdYJgJpbPX0KPrjBkeKUFq8fLq7PFmMaBQaT71z6vs/ARqdeYC1ZNpksH2HCzDNU7mjGgFineA9He3eR3sgpg==",
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/@hig/theme-context/-/theme-context-2.1.3.tgz",
+      "integrity": "sha512-c0Ju+Z8C532ZZtjwOLzN+XeO+pL3kqUawu6ZG3J084MH5RM9W8JCKyMf4D9Qr38jFWoiX6u8yiSxxjV/mz9Sqw==",
       "requires": {
-        "@hig/theme-data": "^2.3.2",
         "create-react-context": "^0.2.3",
         "prop-types": "^15.6.1"
       }
@@ -225,6 +224,11 @@
       "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz",
       "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA=="
     },
+    "@types/async": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/@types/async/-/async-2.4.1.tgz",
+      "integrity": "sha512-C09BK/wXzbW+/JK9zckhe+FeSbg7NmvVjUWwApnw7ksRpUq3ecGLiq2Aw1LlY4Z/VmtdhSaIs7jO5/MWRYMcOA=="
+    },
     "@types/babel-types": {
       "version": "7.0.6",
       "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.6.tgz",
@@ -292,6 +296,22 @@
         "@types/express": "*"
       }
     },
+    "@types/cookie-parser": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.1.tgz",
+      "integrity": "sha512-iJY6B3ZGufLiDf2OCAgiAAQuj1sMKC/wz/7XCEjZ+/MDuultfFJuSwrBKcLSmJ5iYApLzCCYBYJZs0Ws8GPmwA==",
+      "requires": {
+        "@types/express": "*"
+      }
+    },
+    "@types/cookie-session": {
+      "version": "2.0.36",
+      "resolved": "https://registry.npmjs.org/@types/cookie-session/-/cookie-session-2.0.36.tgz",
+      "integrity": "sha512-vnKXnz9vGdOF6Gi9mvaMEtSn2PlDotvde1u9xVzzBRAVsPcufEmDfmas7023T7PpK9Awv1JqQDwN1ThjPSAoTA==",
+      "requires": {
+        "@types/express": "*"
+      }
+    },
     "@types/events": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
@@ -400,6 +420,14 @@
       "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz",
       "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw=="
     },
+    "@types/mobile-detect": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/@types/mobile-detect/-/mobile-detect-1.3.4.tgz",
+      "integrity": "sha512-MGBTvT5c7aH8eX6szFYP3dWPryNLt5iGlo31XNaJtt8o6jsg6tjn99eEMq9l8T6cPZymsr+J4Jth8+/G/04ZDw==",
+      "requires": {
+        "mobile-detect": "*"
+      }
+    },
     "@types/mocha": {
       "version": "5.2.6",
       "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.6.tgz",
@@ -429,6 +457,14 @@
       "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.30.tgz",
       "integrity": "sha512-nsqTN6zUcm9xtdJiM9OvOJ5EF0kOI8f1Zuug27O/rgtxCRJHGqncSWfCMZUP852dCKPsDsYXGvBhxfRjDBkF5Q=="
     },
+    "@types/nodemailer": {
+      "version": "4.6.6",
+      "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-4.6.6.tgz",
+      "integrity": "sha512-N2czhXs7fbQhvoquEGzmHAWttnxLfrM3+cWMRFX4hTQq4GE3VyaSE3MOOse4VoNgvtti/H5ow/Hq9KXu/UMWqA==",
+      "requires": {
+        "@types/node": "*"
+      }
+    },
     "@types/orderedmap": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/@types/orderedmap/-/orderedmap-1.0.0.tgz",
@@ -485,6 +521,15 @@
         "@types/prosemirror-state": "*"
       }
     },
+    "@types/prosemirror-inputrules": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@types/prosemirror-inputrules/-/prosemirror-inputrules-1.0.2.tgz",
+      "integrity": "sha512-bKFneQUPnkZmzCJ1uoitpKH6PFW0hc4q55NsC7mFUCvX0eZl0GRKxyfV47jkJbsbyUQoO/QFv0WwLDz2bo15sA==",
+      "requires": {
+        "@types/prosemirror-model": "*",
+        "@types/prosemirror-state": "*"
+      }
+    },
     "@types/prosemirror-keymap": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/@types/prosemirror-keymap/-/prosemirror-keymap-1.0.1.tgz",
@@ -1160,7 +1205,6 @@
       "version": "4.10.1",
       "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
       "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
-      "dev": true,
       "requires": {
         "bn.js": "^4.0.0",
         "inherits": "^2.0.1",
@@ -1215,11 +1259,11 @@
       "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk="
     },
     "async": {
-      "version": "2.6.1",
-      "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
-      "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
+      "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
       "requires": {
-        "lodash": "^4.17.10"
+        "lodash": "^4.17.11"
       }
     },
     "async-each": {
@@ -1525,8 +1569,7 @@
     "bn.js": {
       "version": "4.11.8",
       "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
-      "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
-      "dev": true
+      "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
     },
     "body-parser": {
       "version": "1.18.3",
@@ -1695,8 +1738,7 @@
     "brorand": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
-      "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
-      "dev": true
+      "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
     },
     "browser-stdout": {
       "version": "1.3.1",
@@ -1708,7 +1750,6 @@
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
       "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
-      "dev": true,
       "requires": {
         "buffer-xor": "^1.0.3",
         "cipher-base": "^1.0.0",
@@ -1722,7 +1763,6 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
       "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
-      "dev": true,
       "requires": {
         "browserify-aes": "^1.0.4",
         "browserify-des": "^1.0.0",
@@ -1733,7 +1773,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
       "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
-      "dev": true,
       "requires": {
         "cipher-base": "^1.0.1",
         "des.js": "^1.0.0",
@@ -1745,7 +1784,6 @@
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
       "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
-      "dev": true,
       "requires": {
         "bn.js": "^4.1.0",
         "randombytes": "^2.0.1"
@@ -1755,7 +1793,6 @@
       "version": "4.0.4",
       "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
       "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
-      "dev": true,
       "requires": {
         "bn.js": "^4.1.1",
         "browserify-rsa": "^4.0.0",
@@ -1816,8 +1853,7 @@
     "buffer-xor": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
-      "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
-      "dev": true
+      "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk="
     },
     "builtin-status-codes": {
       "version": "3.0.0",
@@ -2021,7 +2057,6 @@
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
       "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
-      "dev": true,
       "requires": {
         "inherits": "^2.0.1",
         "safe-buffer": "^5.0.1"
@@ -2359,11 +2394,60 @@
       "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
       "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
     },
+    "cookie-parser": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.4.tgz",
+      "integrity": "sha512-lo13tqF3JEtFO7FyA49CqbhaFkskRJ0u/UAiINgrIXeRCY41c88/zxtrECl8AKH3B0hj9q10+h3Kt8I7KlW4tw==",
+      "requires": {
+        "cookie": "0.3.1",
+        "cookie-signature": "1.0.6"
+      }
+    },
+    "cookie-session": {
+      "version": "2.0.0-beta.3",
+      "resolved": "https://registry.npmjs.org/cookie-session/-/cookie-session-2.0.0-beta.3.tgz",
+      "integrity": "sha512-zyqm5tA0z9yMEB/xyP7lnRnqp8eLR2e0dap+9+rBwVigla9yPKn8XTL1jJymog8xjfrowqW2o5LUjixQChkqrw==",
+      "requires": {
+        "cookies": "0.7.1",
+        "debug": "3.1.0",
+        "on-headers": "~1.0.1",
+        "safe-buffer": "5.1.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        },
+        "safe-buffer": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
+          "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
+        }
+      }
+    },
     "cookie-signature": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
       "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
     },
+    "cookies": {
+      "version": "0.7.1",
+      "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.7.1.tgz",
+      "integrity": "sha1-fIphX1SBxhq58WyDNzG8uPZjuZs=",
+      "requires": {
+        "depd": "~1.1.1",
+        "keygrip": "~1.0.2"
+      }
+    },
     "copy-concurrently": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
@@ -2430,7 +2514,6 @@
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
       "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==",
-      "dev": true,
       "requires": {
         "bn.js": "^4.1.0",
         "elliptic": "^6.0.0"
@@ -2459,7 +2542,6 @@
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
       "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
-      "dev": true,
       "requires": {
         "cipher-base": "^1.0.1",
         "inherits": "^2.0.1",
@@ -2472,7 +2554,6 @@
       "version": "1.1.7",
       "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
       "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
-      "dev": true,
       "requires": {
         "cipher-base": "^1.0.3",
         "create-hash": "^1.1.0",
@@ -2516,7 +2597,6 @@
       "version": "3.12.0",
       "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
       "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
-      "dev": true,
       "requires": {
         "browserify-cipher": "^1.0.0",
         "browserify-sign": "^4.0.0",
@@ -2779,7 +2859,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
       "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
-      "dev": true,
       "requires": {
         "inherits": "^2.0.1",
         "minimalistic-assert": "^1.0.0"
@@ -2821,7 +2900,6 @@
       "version": "5.0.3",
       "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
       "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
-      "dev": true,
       "requires": {
         "bn.js": "^4.1.0",
         "miller-rabin": "^4.0.0",
@@ -2954,7 +3032,6 @@
       "version": "6.4.1",
       "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
       "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
-      "dev": true,
       "requires": {
         "bn.js": "^4.4.0",
         "brorand": "^1.0.1",
@@ -3222,7 +3299,6 @@
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
       "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
-      "dev": true,
       "requires": {
         "md5.js": "^1.3.4",
         "safe-buffer": "^5.1.1"
@@ -4559,6 +4635,21 @@
         "ansi-regex": "^2.0.0"
       }
     },
+    "has-binary": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz",
+      "integrity": "sha1-aOYesWIQyVRaClzOBqhzkS/h5ow=",
+      "requires": {
+        "isarray": "0.0.1"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+        }
+      }
+    },
     "has-binary2": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
@@ -4628,7 +4719,6 @@
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
       "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
-      "dev": true,
       "requires": {
         "inherits": "^2.0.1",
         "safe-buffer": "^5.0.1"
@@ -4638,7 +4728,6 @@
       "version": "1.1.7",
       "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
       "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
-      "dev": true,
       "requires": {
         "inherits": "^2.0.3",
         "minimalistic-assert": "^1.0.1"
@@ -4654,7 +4743,6 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
       "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
-      "dev": true,
       "requires": {
         "hash.js": "^1.0.3",
         "minimalistic-assert": "^1.0.0",
@@ -5365,8 +5453,7 @@
     "json3": {
       "version": "3.3.2",
       "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz",
-      "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=",
-      "dev": true
+      "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE="
     },
     "json5": {
       "version": "1.0.1",
@@ -5519,6 +5606,11 @@
       "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz",
       "integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ="
     },
+    "keygrip": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.3.tgz",
+      "integrity": "sha512-/PpesirAIfaklxUzp4Yb7xBper9MwP6hNRA6BGGUFCgbJ+BM5CKBtsoxinNXkLHAr+GXS1/lSlF2rP7cv5Fl+g=="
+    },
     "killable": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
@@ -5845,7 +5937,6 @@
       "version": "1.3.5",
       "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
       "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
-      "dev": true,
       "requires": {
         "hash-base": "^3.0.0",
         "inherits": "^2.0.1",
@@ -5940,7 +6031,6 @@
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
       "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
-      "dev": true,
       "requires": {
         "bn.js": "^4.0.0",
         "brorand": "^1.0.1"
@@ -5973,14 +6063,12 @@
     "minimalistic-assert": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
-      "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
-      "dev": true
+      "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
     },
     "minimalistic-crypto-utils": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
-      "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
-      "dev": true
+      "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
     },
     "minimatch": {
       "version": "3.0.4",
@@ -6065,6 +6153,11 @@
         }
       }
     },
+    "mobile-detect": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/mobile-detect/-/mobile-detect-1.4.3.tgz",
+      "integrity": "sha512-UaahPNLllQsstHOEHAmVnTHCMQrAS9eL5Qgdi50QrYz6UgGk+Xziz2udz2GN6NYcyODcPLnasC7a7s6R2DjiaQ=="
+    },
     "mobx": {
       "version": "5.9.0",
       "resolved": "https://registry.npmjs.org/mobx/-/mobx-5.9.0.tgz",
@@ -6189,6 +6282,14 @@
         "sliced": "1.0.1"
       },
       "dependencies": {
+        "async": {
+          "version": "2.6.1",
+          "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
+          "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+          "requires": {
+            "lodash": "^4.17.10"
+          }
+        },
         "bson": {
           "version": "1.1.1",
           "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.1.tgz",
@@ -6494,6 +6595,11 @@
         }
       }
     },
+    "nodemailer": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-5.1.1.tgz",
+      "integrity": "sha512-hKGCoeNdFL2W7S76J/Oucbw0/qRlfG815tENdhzcqTpSjKgAN91mFOqU2lQUflRRxFM7iZvCyaFcAR9noc/CqQ=="
+    },
     "nodemon": {
       "version": "1.18.10",
       "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.18.10.tgz",
@@ -9750,6 +9856,11 @@
         "is-wsl": "^1.1.0"
       }
     },
+    "options": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz",
+      "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8="
+    },
     "orderedmap": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-1.0.0.tgz",
@@ -9868,7 +9979,6 @@
       "version": "5.1.4",
       "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz",
       "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==",
-      "dev": true,
       "requires": {
         "asn1.js": "^4.0.0",
         "browserify-aes": "^1.0.0",
@@ -9893,6 +10003,14 @@
       "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
       "dev": true
     },
+    "parsejson": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz",
+      "integrity": "sha1-q343WfIJ7OmUN5c/fQ8fZK4OZKs=",
+      "requires": {
+        "better-assert": "~1.0.0"
+      }
+    },
     "parseqs": {
       "version": "0.0.5",
       "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
@@ -10000,7 +10118,6 @@
       "version": "3.0.17",
       "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
       "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
-      "dev": true,
       "requires": {
         "create-hash": "^1.1.2",
         "create-hmac": "^1.1.4",
@@ -10433,7 +10550,6 @@
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
       "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
-      "dev": true,
       "requires": {
         "bn.js": "^4.1.0",
         "browserify-rsa": "^4.0.0",
@@ -10621,7 +10737,6 @@
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
       "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
-      "dev": true,
       "requires": {
         "safe-buffer": "^5.1.0"
       }
@@ -10630,7 +10745,6 @@
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
       "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
-      "dev": true,
       "requires": {
         "randombytes": "^2.0.5",
         "safe-buffer": "^5.1.0"
@@ -11344,7 +11458,6 @@
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
       "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
-      "dev": true,
       "requires": {
         "hash-base": "^3.0.0",
         "inherits": "^2.0.1"
@@ -11595,7 +11708,6 @@
       "version": "2.4.11",
       "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
       "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
-      "dev": true,
       "requires": {
         "inherits": "^2.0.1",
         "safe-buffer": "^5.0.1"
@@ -11845,6 +11957,204 @@
         }
       }
     },
+    "socketio": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/socketio/-/socketio-1.0.0.tgz",
+      "integrity": "sha1-aOoVmP0kBWFgFxLldJl2ftjrqMo=",
+      "requires": {
+        "express": "^4.13.3",
+        "socket.io": "^1.3.7"
+      },
+      "dependencies": {
+        "accepts": {
+          "version": "1.3.3",
+          "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz",
+          "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=",
+          "requires": {
+            "mime-types": "~2.1.11",
+            "negotiator": "0.6.1"
+          }
+        },
+        "arraybuffer.slice": {
+          "version": "0.0.6",
+          "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz",
+          "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco="
+        },
+        "blob": {
+          "version": "0.0.4",
+          "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz",
+          "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE="
+        },
+        "component-emitter": {
+          "version": "1.1.2",
+          "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz",
+          "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM="
+        },
+        "debug": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz",
+          "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=",
+          "requires": {
+            "ms": "0.7.2"
+          }
+        },
+        "engine.io": {
+          "version": "1.8.5",
+          "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.5.tgz",
+          "integrity": "sha512-j1DWIcktw4hRwrv6nWx++5nFH2X64x16MAG2P0Lmi5Dvdfi3I+Jhc7JKJIdAmDJa+5aZ/imHV7dWRPy2Cqjh3A==",
+          "requires": {
+            "accepts": "1.3.3",
+            "base64id": "1.0.0",
+            "cookie": "0.3.1",
+            "debug": "2.3.3",
+            "engine.io-parser": "1.3.2",
+            "ws": "~1.1.5"
+          }
+        },
+        "engine.io-client": {
+          "version": "1.8.5",
+          "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.5.tgz",
+          "integrity": "sha512-AYTgHyeVUPitsseqjoedjhYJapNVoSPShbZ+tEUX9/73jgZ/Z3sUlJf9oYgdEBBdVhupUpUqSxH0kBCXlQnmZg==",
+          "requires": {
+            "component-emitter": "1.2.1",
+            "component-inherit": "0.0.3",
+            "debug": "2.3.3",
+            "engine.io-parser": "1.3.2",
+            "has-cors": "1.1.0",
+            "indexof": "0.0.1",
+            "parsejson": "0.0.3",
+            "parseqs": "0.0.5",
+            "parseuri": "0.0.5",
+            "ws": "~1.1.5",
+            "xmlhttprequest-ssl": "1.5.3",
+            "yeast": "0.1.2"
+          },
+          "dependencies": {
+            "component-emitter": {
+              "version": "1.2.1",
+              "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+              "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
+            }
+          }
+        },
+        "engine.io-parser": {
+          "version": "1.3.2",
+          "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz",
+          "integrity": "sha1-k3sHnwAH0Ik+xW1GyyILjLQ1Igo=",
+          "requires": {
+            "after": "0.8.2",
+            "arraybuffer.slice": "0.0.6",
+            "base64-arraybuffer": "0.1.5",
+            "blob": "0.0.4",
+            "has-binary": "0.1.7",
+            "wtf-8": "1.0.0"
+          }
+        },
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+        },
+        "ms": {
+          "version": "0.7.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz",
+          "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U="
+        },
+        "object-assign": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz",
+          "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A="
+        },
+        "socket.io": {
+          "version": "1.7.4",
+          "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-1.7.4.tgz",
+          "integrity": "sha1-L37O3DORvy1cc+KR/iM+bjTU3QA=",
+          "requires": {
+            "debug": "2.3.3",
+            "engine.io": "~1.8.4",
+            "has-binary": "0.1.7",
+            "object-assign": "4.1.0",
+            "socket.io-adapter": "0.5.0",
+            "socket.io-client": "1.7.4",
+            "socket.io-parser": "2.3.1"
+          }
+        },
+        "socket.io-adapter": {
+          "version": "0.5.0",
+          "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz",
+          "integrity": "sha1-y21LuL7IHhB4uZZ3+c7QBGBmu4s=",
+          "requires": {
+            "debug": "2.3.3",
+            "socket.io-parser": "2.3.1"
+          }
+        },
+        "socket.io-client": {
+          "version": "1.7.4",
+          "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.4.tgz",
+          "integrity": "sha1-7J+CA1btme9tNX8HVtZIcXvdQoE=",
+          "requires": {
+            "backo2": "1.0.2",
+            "component-bind": "1.0.0",
+            "component-emitter": "1.2.1",
+            "debug": "2.3.3",
+            "engine.io-client": "~1.8.4",
+            "has-binary": "0.1.7",
+            "indexof": "0.0.1",
+            "object-component": "0.0.3",
+            "parseuri": "0.0.5",
+            "socket.io-parser": "2.3.1",
+            "to-array": "0.1.4"
+          },
+          "dependencies": {
+            "component-emitter": {
+              "version": "1.2.1",
+              "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+              "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
+            }
+          }
+        },
+        "socket.io-parser": {
+          "version": "2.3.1",
+          "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz",
+          "integrity": "sha1-3VMgJRA85Clpcya+/WQAX8/ltKA=",
+          "requires": {
+            "component-emitter": "1.1.2",
+            "debug": "2.2.0",
+            "isarray": "0.0.1",
+            "json3": "3.3.2"
+          },
+          "dependencies": {
+            "debug": {
+              "version": "2.2.0",
+              "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+              "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
+              "requires": {
+                "ms": "0.7.1"
+              }
+            },
+            "ms": {
+              "version": "0.7.1",
+              "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
+              "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg="
+            }
+          }
+        },
+        "ws": {
+          "version": "1.1.5",
+          "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz",
+          "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==",
+          "requires": {
+            "options": ">=0.0.5",
+            "ultron": "1.0.x"
+          }
+        },
+        "xmlhttprequest-ssl": {
+          "version": "1.5.3",
+          "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz",
+          "integrity": "sha1-GFqIjATspGw+QHDZn3tJ3jUomS0="
+        }
+      }
+    },
     "sockjs": {
       "version": "0.3.19",
       "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz",
@@ -12745,6 +13055,11 @@
         "random-bytes": "~1.0.0"
       }
     },
+    "ultron": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
+      "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po="
+    },
     "uncontrollable": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-6.1.0.tgz",
@@ -13884,6 +14199,11 @@
         "async-limiter": "~1.0.0"
       }
     },
+    "wtf-8": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/wtf-8/-/wtf-8-1.0.0.tgz",
+      "integrity": "sha1-OS2LotDxw00e4tYw8V0O+2jhBIo="
+    },
     "xdg-basedir": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz",
@@ -13894,6 +14214,11 @@
       "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
       "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
     },
+    "xoauth2": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/xoauth2/-/xoauth2-1.2.0.tgz",
+      "integrity": "sha1-8u76wRRyyXHqO8RuVU60sSMhRuU="
+    },
     "xregexp": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz",
diff --git a/package.json b/package.json
index 640f16437..a4a328859 100644
--- a/package.json
+++ b/package.json
@@ -38,13 +38,10 @@
     "@fortawesome/free-solid-svg-icons": "^5.7.2",
     "@fortawesome/react-fontawesome": "^0.1.4",
     "@hig/flyout": "^1.0.3",
-<<<<<<< HEAD
     "@trendmicro/react-dropdown": "^1.3.0",
-=======
     "@types/async": "^2.4.1",
     "@hig/theme-context": "^2.1.3",
     "@hig/theme-data": "^2.3.3",
->>>>>>> f70ad315167b714f11f7d68f35a46abe9e525a4d
     "@types/bcrypt-nodejs": "0.0.30",
     "@types/bluebird": "^3.5.25",
     "@types/body-parser": "^1.17.0",
@@ -92,11 +89,8 @@
     "bcrypt-nodejs": "0.0.3",
     "bluebird": "^3.5.3",
     "body-parser": "^1.18.3",
-<<<<<<< HEAD
     "bootstrap": "^4.3.1",
-=======
     "connect-flash": "^0.1.1",
->>>>>>> f70ad315167b714f11f7d68f35a46abe9e525a4d
     "connect-mongo": "^2.0.3",
     "cookie-parser": "^1.4.4",
     "cookie-session": "^2.0.0-beta.3",
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 2598dd3a9..da38d54c4 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -1,10 +1,5 @@
-<<<<<<< HEAD
-import React = require("react");
-import { action, observable } from "mobx";
-=======
 import React = require("react")
 import { action, observable, ObservableMap, computed } from "mobx";
->>>>>>> f70ad315167b714f11f7d68f35a46abe9e525a4d
 import { observer } from "mobx-react";
 import Measure from "react-measure";
 import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
@@ -22,23 +17,13 @@ import "./CollectionSchemaView.scss";
 import { COLLECTION_BORDER_WIDTH, CollectionView } from "./CollectionView";
 import { CollectionViewBase } from "./CollectionViewBase";
 import { setupDrag } from "../../util/DragManager";
-<<<<<<< HEAD
 import '../DocumentDecorations.scss';
 import { Flyout, anchorPoints } from "../DocumentDecorations";
 import { DropdownButton, Dropdown } from 'react-bootstrap';
 import { ListField } from "../../../fields/ListField";
 import { Key } from "../../../fields/Key";
-//import { Server } from "http";
-//import { Server } from "http";
 import { Server } from "../../Server";
 
-//import { MenuItem } from 'react-bootstrap';
-=======
-import { Key } from "./../../../fields/Key";
-import { Server } from "../../Server";
-import { ListField } from "../../../fields/ListField";
-
->>>>>>> f70ad315167b714f11f7d68f35a46abe9e525a4d
 
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
 
@@ -349,18 +334,10 @@ export class CollectionSchemaView extends CollectionViewBase {
             <div className="collectionSchemaView-columnsHandle" onPointerDown={this.onColumnsDown} />);
         let dividerDragger = this.splitPercentage == 0 ? (null) :
             <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />
-<<<<<<< HEAD
 
         //get the union of all childrens' keys
         let addFields: { id: string, name: string }[] = [];
         let removeFields: { id: string, name: string }[] = [];
-        //children.forEach((child) => {
-        //   for (var keyId in fields.keys) {
-        //       if (!allFields.includes(keyId)) {
-        //           allFields.push(keyId);
-        //      }
-        //   }
-        // });
         let optionsMenu = null;
         if (this.props.active()) {
             let protos = this.props.Document.GetAllPrototypes();
@@ -372,28 +349,32 @@ export class CollectionSchemaView extends CollectionViewBase {
                     }
                 })
             }
-            optionsMenu = !this.props.active() ? (null) : (<Flyout
-                anchorPoint={anchorPoints.LEFT_TOP}
-                content={<div id="options-flyout-div"> <h5>Options</h5>
-                    <DropdownButton id="dropdown-basic-button" className="colDropDown" title="Add Column">
+            /*<DropdownButton id="dropdown-basic-button" className="colDropDown" title="Add Column">
                         {addFields.map(({ id, name }) => (
                             <div className="dd-item-containter"><Dropdown.Item className="dd-item" eventKey={id} onSelect={this.keyDropDownOnSelect}>{name} </Dropdown.Item></div>))}
                     </DropdownButton>
                     <DropdownButton id="dropdown-basic-button" className="colDropDown" title="Remove Column">
                         {addFields.map(({ id, name }) => (
                             <div className="dd-item-containter"><Dropdown.Item className="dd-item" eventKey={id} onSelect={this.keyDropDownOnSelect}>{name} </Dropdown.Item></div>))}
-                    </DropdownButton>
+                    </DropdownButton>*/
+            optionsMenu = !this.props.active() ? (null) : (<Flyout
+                anchorPoint={anchorPoints.LEFT_TOP}
+                content={<div id="options-flyout-div"> <h5>Options</h5>
+                    <div className="addColumn-options">
+                        <ul style={{ overflow: "scroll" }}>
+                            {Array.from(Object.keys(allKeys)).map(item => {
+                                return (<KeyToggle checked={allKeys[item]} key={item} keyId={item} toggle={this.toggleKey} />)
+                            })}
+                        </ul>
+                    </div>
                 </div>
                 }>
                 <div id="schemaOptionsMenuBtn" />
             </Flyout>);
         }
 
-
-=======
         let colDividerDragger = this._columnsPercentage == 0 ? (null) :
             <div className="collectionSchemaView-colDividerDragger" onPointerDown={this.onColDividerDown} style={{ height: `${this.DIVIDER_WIDTH}px` }} />
->>>>>>> f70ad315167b714f11f7d68f35a46abe9e525a4d
 
         return (
             <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} ref={this._mainCont} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} >
@@ -424,19 +405,6 @@ export class CollectionSchemaView extends CollectionViewBase {
                                         getTrProps={this.getTrProps}
                                     />
                                 </div>
-                                {colDividerDragger}
-                                <div className="collectionSchemaView-addColumn" style={{ height: `${this._columnsPercentage}%` }} >
-                                    {/* <input type="checkbox" id="addColumn-toggle" />
-                                    <label htmlFor="addColumn-toggle" title="Add Column"><p>+</p></label> */}
-
-                                    <div className="addColumn-options">
-                                        <ul style={{ overflow: "scroll" }}>
-                                            {Array.from(Object.keys(allKeys)).map(item => {
-                                                return (<KeyToggle checked={allKeys[item]} key={item} keyId={item} toggle={this.toggleKey} />)
-                                            })}
-                                        </ul>
-                                    </div>
-                                </div>
                             </div>
                         }
                     </Measure>
@@ -445,14 +413,26 @@ export class CollectionSchemaView extends CollectionViewBase {
                         {content}
                     </div>
                     {previewHandle}
-<<<<<<< HEAD
-                    {optionsMenu}
-=======
-                    {columnsHandle}
->>>>>>> f70ad315167b714f11f7d68f35a46abe9e525a4d
 
+                    {columnsHandle}
+                    {optionsMenu}
                 </div>
             </div >
         )
     }
-}
\ No newline at end of file
+}
+/*
+{colDividerDragger}
+                                <div className="collectionSchemaView-addColumn" style={{ height: `${this._columnsPercentage}%` }} >
+                                    {/* <input type="checkbox" id="addColumn-toggle" />
+                                    <label htmlFor="addColumn-toggle" title="Add Column"><p>+</p></label> }
+
+                                    <div className="addColumn-options">
+                                        <ul style={{ overflow: "scroll" }}>
+                                            {Array.from(Object.keys(allKeys)).map(item => {
+                                                return (<KeyToggle checked={allKeys[item]} key={item} keyId={item} toggle={this.toggleKey} />)
+                                            })}
+                                        </ul>
+                                    </div>
+                                </div>
+                                */
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 5e416c352ebfb875482f890c6362fee5f84af1f6 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 19 Mar 2019 15:50:54 -0400
Subject: started to integrate Northstar.  changed server port to 4321.

---
 deploy/assets/env.json                             |   15 +
 package.json                                       |    3 +-
 src/client/Server.ts                               |    2 +-
 src/client/northstar/manager/Gateway.ts            |  269 +
 .../northstar/model/idea/MetricTypeMapping.ts      |   30 +
 src/client/northstar/model/idea/idea.ts            | 8551 ++++++++++++++++++++
 src/client/views/Main.tsx                          |   28 +-
 .../views/collections/CollectionSchemaView.tsx     |    4 +-
 src/server/index.ts                                |    2 +-
 webpack.config.js                                  |    2 +-
 10 files changed, 8896 insertions(+), 10 deletions(-)
 create mode 100644 deploy/assets/env.json
 create mode 100644 src/client/northstar/manager/Gateway.ts
 create mode 100644 src/client/northstar/model/idea/MetricTypeMapping.ts
 create mode 100644 src/client/northstar/model/idea/idea.ts

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

diff --git a/deploy/assets/env.json b/deploy/assets/env.json
new file mode 100644
index 000000000..80963ea0d
--- /dev/null
+++ b/deploy/assets/env.json
@@ -0,0 +1,15 @@
+{
+    "IS_DARPA": false,
+    "IS_IGT": false,
+    "IS_MENU_FIXED": true,
+    "SERVER_URL": "http://localhost:1234",
+    "SERVER_API_PATH": "api",
+    "SAMPLE_SIZE": 100000,
+    "X_BINS": 15,
+    "Y_BINS": 15,
+    "SPLASH_TIME_IN_MS": 0,
+    "SHOW_FPS_COUNTER": true,
+    "SHOW_SHUTDOWN_BUTTON": false,
+    "DEGREE_OF_PARALLISM": 1,
+    "SHOW_WARNINGS": false
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 7ed94c508..c87e31bc4 100644
--- a/package.json
+++ b/package.json
@@ -38,9 +38,9 @@
     "@fortawesome/free-solid-svg-icons": "^5.7.2",
     "@fortawesome/react-fontawesome": "^0.1.4",
     "@hig/flyout": "^1.0.3",
-    "@types/async": "^2.4.1",
     "@hig/theme-context": "^2.1.3",
     "@hig/theme-data": "^2.3.3",
+    "@types/async": "^2.4.1",
     "@types/bcrypt-nodejs": "0.0.30",
     "@types/bluebird": "^3.5.25",
     "@types/body-parser": "^1.17.0",
@@ -149,6 +149,7 @@
     "socket.io": "^2.2.0",
     "socket.io-client": "^2.2.0",
     "socketio": "^1.0.0",
+    "typescript-collections": "^1.3.2",
     "url-loader": "^1.1.2",
     "uuid": "^3.3.2",
     "xoauth2": "^1.2.0"
diff --git a/src/client/Server.ts b/src/client/Server.ts
index 3fb1ae878..bbdc27397 100644
--- a/src/client/Server.ts
+++ b/src/client/Server.ts
@@ -9,7 +9,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}:1234`);
+    static Socket: SocketIOClient.Socket = OpenSocket(`${window.location.protocol}//${window.location.hostname}:4321`);
     static GUID: string = Utils.GenerateGuid()
 
 
diff --git a/src/client/northstar/manager/Gateway.ts b/src/client/northstar/manager/Gateway.ts
new file mode 100644
index 000000000..5ae5e4f47
--- /dev/null
+++ b/src/client/northstar/manager/Gateway.ts
@@ -0,0 +1,269 @@
+import { Catalog, OperationReference, Result, CompileResults } from "../model/idea/idea"
+import { computed, observable, action } from "mobx";
+
+export class Gateway {
+
+    private static _instance: Gateway;
+
+    private constructor() {
+    }
+
+    public static get Instance() {
+        return this._instance || (this._instance = new this());
+    }
+
+    public async GetCatalog(): Promise<Catalog> {
+        try {
+            const json = await this.MakeGetRequest("catalog");
+            const cat = Catalog.fromJS(json);
+            return cat;
+        }
+        catch (error) {
+            throw new Error("can not reach northstar's backend");
+        }
+    }
+
+    public async ClearCatalog(): Promise<void> {
+        try {
+            const json = await this.MakePostJsonRequest("Datamart/ClearAllAugmentations", {});
+        }
+        catch (error) {
+            throw new Error("can not reach northstar's backend");
+        }
+    }
+
+    public async TerminateServer(): Promise<void> {
+        try {
+            const url = Gateway.ConstructUrl("terminateServer");
+            const response = await fetch(url,
+                {
+                    redirect: "follow",
+                    method: "POST",
+                    credentials: "include"
+                });
+        }
+        catch (error) {
+            throw new Error("can not reach northstar's backend");
+        }
+    }
+
+    public async Compile(data: any): Promise<CompileResults | undefined> {
+        const json = await this.MakePostJsonRequest("compile", data);
+        if (json != null) {
+            const cr = CompileResults.fromJS(json);
+            return cr;
+        }
+    }
+
+    public async SubmitResult(data: any): Promise<void> {
+        try {
+            console.log(data);
+            const url = Gateway.ConstructUrl("submitProblem");
+            const response = await fetch(url,
+                {
+                    redirect: "follow",
+                    method: "POST",
+                    credentials: "include",
+                    body: JSON.stringify(data)
+                });
+        }
+        catch (error) {
+            throw new Error("can not reach northstar's backend");
+        }
+    }
+
+    public async SpecifyProblem(data: any): Promise<void> {
+        try {
+            console.log(data);
+            const url = Gateway.ConstructUrl("specifyProblem");
+            const response = await fetch(url,
+                {
+                    redirect: "follow",
+                    method: "POST",
+                    credentials: "include",
+                    body: JSON.stringify(data)
+                });
+        }
+        catch (error) {
+            throw new Error("can not reach northstar's backend");
+        }
+    }
+
+    public async ExportToScript(solutionId: string): Promise<string> {
+        try {
+            const url = Gateway.ConstructUrl("exportsolution/script/" + solutionId);
+            const response = await fetch(url,
+                {
+                    redirect: "follow",
+                    method: "GET",
+                    credentials: "include"
+                });
+            return await response.text();
+        }
+        catch (error) {
+            throw new Error("can not reach northstar's backend");
+        }
+    }
+
+
+    public async StartOperation(data: any): Promise<OperationReference | undefined> {
+        const json = await this.MakePostJsonRequest("operation", data);
+        if (json != null) {
+            const or = OperationReference.fromJS(json);
+            return or;
+        }
+    }
+
+    public async GetResult(data: any): Promise<Result | undefined> {
+        const json = await this.MakePostJsonRequest("result", data);
+        if (json != null) {
+            const res = Result.fromJS(json);
+            return res;
+        }
+    }
+
+    public async PauseOperation(data: any): Promise<void> {
+        const url = Gateway.ConstructUrl("pause");
+        await fetch(url,
+            {
+                redirect: "follow",
+                method: "POST",
+                credentials: "include",
+                body: JSON.stringify(data)
+            });
+    }
+
+    public async MakeGetRequest(endpoint: string, signal?: AbortSignal): Promise<any> {
+        const url = Gateway.ConstructUrl(endpoint);
+        const response = await fetch(url,
+            {
+                redirect: "follow",
+                method: "GET",
+                credentials: "include",
+                signal
+            });
+        const json = await response.json();
+        return json;
+    }
+
+    public async MakePostJsonRequest(endpoint: string, data: any, signal?: AbortSignal): Promise<any> {
+        const url = Gateway.ConstructUrl(endpoint);
+        const response = await fetch(url,
+            {
+                redirect: "follow",
+                method: "POST",
+                credentials: "include",
+                body: JSON.stringify(data),
+                signal
+            });
+        const json = await response.json();
+        return json;
+    }
+
+
+    public static ConstructUrl(appendix: string): string {
+        let base = Settings.Instance.ServerUrl;
+        if (base.slice(-1) == "/") {
+            base = base.slice(0, -1);
+        }
+        let url = base + "/" + Settings.Instance.ServerApiPath + "/" + appendix;
+        return url;
+    }
+}
+
+declare var ENV: any;
+
+export class Settings {
+    private _environment: any;
+
+    @observable
+    public ServerUrl: string = document.URL;
+
+    @observable
+    public ServerApiPath?: string;
+
+    @observable
+    public SampleSize?: number;
+
+    @observable
+    public XBins?: number;
+
+    @observable
+    public YBins?: number;
+
+    @observable
+    public SplashTimeInMS?: number;
+
+    @observable
+    public ShowFpsCounter?: boolean;
+
+    @observable
+    public IsMenuFixed?: boolean;
+
+    @observable
+    public ShowShutdownButton?: boolean;
+
+    @observable
+    public IsDarpa?: boolean;
+
+    @observable
+    public IsIGT?: boolean;
+
+    @observable
+    public DegreeOfParallelism?: number;
+
+    @observable
+    public ShowWarnings?: boolean;
+
+    @computed
+    public get IsDev(): boolean {
+        return ENV.IsDev;
+    }
+
+    @computed
+    public get TestDataFolderPath(): string {
+        return this.Origin + "testdata/";
+    }
+
+    @computed
+    public get Origin(): string {
+        return window.location.origin + "/";
+    }
+
+    private static _instance: Settings;
+
+    @action
+    public Update(environment: any): void {
+        /*let serverParam = new URL(document.URL).searchParams.get("serverUrl");
+        if (serverParam) {
+            if (serverParam === "debug") {
+                this.ServerUrl = `http://${window.location.hostname}:1234`;
+            }
+            else {
+                this.ServerUrl = serverParam;
+            }
+        }
+        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"];
+    }
+
+    public static get Instance(): Settings {
+        if (!this._instance) {
+            this._instance = new Settings();
+        }
+        return this._instance;
+    }
+}
diff --git a/src/client/northstar/model/idea/MetricTypeMapping.ts b/src/client/northstar/model/idea/MetricTypeMapping.ts
new file mode 100644
index 000000000..11e0c871a
--- /dev/null
+++ b/src/client/northstar/model/idea/MetricTypeMapping.ts
@@ -0,0 +1,30 @@
+import { MetricType } from "./Idea";
+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) {
+            return MetricInterpretation.HigherIsBetter;
+        }
+        return MetricInterpretation.LowerIsBetter;
+    }
+}
+
+export enum MetricInterpretation {
+    HigherIsBetter, LowerIsBetter
+}
\ No newline at end of file
diff --git a/src/client/northstar/model/idea/idea.ts b/src/client/northstar/model/idea/idea.ts
new file mode 100644
index 000000000..9d9d60678
--- /dev/null
+++ b/src/client/northstar/model/idea/idea.ts
@@ -0,0 +1,8551 @@
+/* tslint:disable */
+//----------------------
+// <auto-generated>
+//     Generated using the NSwag toolchain v11.19.2.0 (NJsonSchema v9.10.73.0 (Newtonsoft.Json v9.0.0.0)) (http://NSwag.org)
+// </auto-generated>
+//----------------------
+// ReSharper disable InconsistentNaming
+
+
+
+export enum AggregateFunction {
+    None = "None",
+    Sum = "Sum",
+    SumE = "SumE",
+    Count = "Count",
+    Min = "Min",
+    Max = "Max",
+    Avg = "Avg",
+}
+
+export abstract class AggregateParameters implements IAggregateParameters {
+
+    protected _discriminator: string;
+
+    constructor(data?: IAggregateParameters) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+        this._discriminator = "AggregateParameters";
+    }
+
+    init(data?: any) {
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): AggregateParameters {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "AverageAggregateParameters") {
+            let result = new AverageAggregateParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "SingleDimensionAggregateParameters") {
+            throw new Error("The abstract class 'SingleDimensionAggregateParameters' cannot be instantiated.");
+        }
+        if (data["discriminator"] === "CountAggregateParameters") {
+            let result = new CountAggregateParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "KDEAggregateParameters") {
+            let result = new KDEAggregateParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "MarginAggregateParameters") {
+            let result = new MarginAggregateParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "MaxAggregateParameters") {
+            let result = new MaxAggregateParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "MinAggregateParameters") {
+            let result = new MinAggregateParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "SumAggregateParameters") {
+            let result = new SumAggregateParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "SumEstimationAggregateParameters") {
+            let result = new SumEstimationAggregateParameters();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'AggregateParameters' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["discriminator"] = this._discriminator;
+        return data;
+    }
+}
+
+export interface IAggregateParameters {
+}
+
+export abstract class SingleDimensionAggregateParameters extends AggregateParameters implements ISingleDimensionAggregateParameters {
+    attributeParameters?: AttributeParameters | undefined;
+    distinctAttributeParameters?: AttributeParameters | undefined;
+
+    constructor(data?: ISingleDimensionAggregateParameters) {
+        super(data);
+        this._discriminator = "SingleDimensionAggregateParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.attributeParameters = data["AttributeParameters"] ? AttributeParameters.fromJS(data["AttributeParameters"]) : <any>undefined;
+            this.distinctAttributeParameters = data["DistinctAttributeParameters"] ? AttributeParameters.fromJS(data["DistinctAttributeParameters"]) : <any>undefined;
+        }
+    }
+
+    static fromJS(data: any): SingleDimensionAggregateParameters {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "AverageAggregateParameters") {
+            let result = new AverageAggregateParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "CountAggregateParameters") {
+            let result = new CountAggregateParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "KDEAggregateParameters") {
+            let result = new KDEAggregateParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "MarginAggregateParameters") {
+            let result = new MarginAggregateParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "MaxAggregateParameters") {
+            let result = new MaxAggregateParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "MinAggregateParameters") {
+            let result = new MinAggregateParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "SumAggregateParameters") {
+            let result = new SumAggregateParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "SumEstimationAggregateParameters") {
+            let result = new SumEstimationAggregateParameters();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'SingleDimensionAggregateParameters' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["AttributeParameters"] = this.attributeParameters ? this.attributeParameters.toJSON() : <any>undefined;
+        data["DistinctAttributeParameters"] = this.distinctAttributeParameters ? this.distinctAttributeParameters.toJSON() : <any>undefined;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ISingleDimensionAggregateParameters extends IAggregateParameters {
+    attributeParameters?: AttributeParameters | undefined;
+    distinctAttributeParameters?: AttributeParameters | undefined;
+}
+
+export class AverageAggregateParameters extends SingleDimensionAggregateParameters implements IAverageAggregateParameters {
+
+    constructor(data?: IAverageAggregateParameters) {
+        super(data);
+        this._discriminator = "AverageAggregateParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): AverageAggregateParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new AverageAggregateParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IAverageAggregateParameters extends ISingleDimensionAggregateParameters {
+}
+
+export abstract class AttributeParameters implements IAttributeParameters {
+    visualizationHints?: VisualizationHint[] | undefined;
+    rawName?: string | undefined;
+
+    protected _discriminator: string;
+
+    constructor(data?: IAttributeParameters) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+        this._discriminator = "AttributeParameters";
+    }
+
+    init(data?: any) {
+        if (data) {
+            if (data["VisualizationHints"] && data["VisualizationHints"].constructor === Array) {
+                this.visualizationHints = [];
+                for (let item of data["VisualizationHints"])
+                    this.visualizationHints.push(item);
+            }
+            this.rawName = data["RawName"];
+        }
+    }
+
+    static fromJS(data: any): AttributeParameters {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "AttributeBackendParameters") {
+            let result = new AttributeBackendParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "AttributeCaclculatedParameters") {
+            throw new Error("The abstract class 'AttributeCaclculatedParameters' cannot be instantiated.");
+        }
+        if (data["discriminator"] === "AttributeCodeParameters") {
+            let result = new AttributeCodeParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "AttributeColumnParameters") {
+            let result = new AttributeColumnParameters();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'AttributeParameters' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["discriminator"] = this._discriminator;
+        if (this.visualizationHints && this.visualizationHints.constructor === Array) {
+            data["VisualizationHints"] = [];
+            for (let item of this.visualizationHints)
+                data["VisualizationHints"].push(item);
+        }
+        data["RawName"] = this.rawName;
+        return data;
+    }
+}
+
+export interface IAttributeParameters {
+    visualizationHints?: VisualizationHint[] | undefined;
+    rawName?: string | undefined;
+}
+
+export enum VisualizationHint {
+    TreatAsEnumeration = "TreatAsEnumeration",
+    DefaultFlipAxis = "DefaultFlipAxis",
+    Image = "Image",
+}
+
+export abstract class AttributeCaclculatedParameters extends AttributeParameters implements IAttributeCaclculatedParameters {
+
+    constructor(data?: IAttributeCaclculatedParameters) {
+        super(data);
+        this._discriminator = "AttributeCaclculatedParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): AttributeCaclculatedParameters {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "AttributeBackendParameters") {
+            let result = new AttributeBackendParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "AttributeCodeParameters") {
+            let result = new AttributeCodeParameters();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'AttributeCaclculatedParameters' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IAttributeCaclculatedParameters extends IAttributeParameters {
+}
+
+export class AttributeBackendParameters extends AttributeCaclculatedParameters implements IAttributeBackendParameters {
+    id?: string | undefined;
+
+    constructor(data?: IAttributeBackendParameters) {
+        super(data);
+        this._discriminator = "AttributeBackendParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.id = data["Id"];
+        }
+    }
+
+    static fromJS(data: any): AttributeBackendParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new AttributeBackendParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Id"] = this.id;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IAttributeBackendParameters extends IAttributeCaclculatedParameters {
+    id?: string | undefined;
+}
+
+export class AttributeCodeParameters extends AttributeCaclculatedParameters implements IAttributeCodeParameters {
+    code?: string | undefined;
+
+    constructor(data?: IAttributeCodeParameters) {
+        super(data);
+        this._discriminator = "AttributeCodeParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.code = data["Code"];
+        }
+    }
+
+    static fromJS(data: any): AttributeCodeParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new AttributeCodeParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Code"] = this.code;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IAttributeCodeParameters extends IAttributeCaclculatedParameters {
+    code?: string | undefined;
+}
+
+export class AttributeColumnParameters extends AttributeParameters implements IAttributeColumnParameters {
+
+    constructor(data?: IAttributeColumnParameters) {
+        super(data);
+        this._discriminator = "AttributeColumnParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): AttributeColumnParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new AttributeColumnParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IAttributeColumnParameters extends IAttributeParameters {
+}
+
+export class CountAggregateParameters extends SingleDimensionAggregateParameters implements ICountAggregateParameters {
+
+    constructor(data?: ICountAggregateParameters) {
+        super(data);
+        this._discriminator = "CountAggregateParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): CountAggregateParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new CountAggregateParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ICountAggregateParameters extends ISingleDimensionAggregateParameters {
+}
+
+export class KDEAggregateParameters extends SingleDimensionAggregateParameters implements IKDEAggregateParameters {
+    nrOfSamples?: number | undefined;
+
+    constructor(data?: IKDEAggregateParameters) {
+        super(data);
+        this._discriminator = "KDEAggregateParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.nrOfSamples = data["NrOfSamples"];
+        }
+    }
+
+    static fromJS(data: any): KDEAggregateParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new KDEAggregateParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["NrOfSamples"] = this.nrOfSamples;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IKDEAggregateParameters extends ISingleDimensionAggregateParameters {
+    nrOfSamples?: number | undefined;
+}
+
+export class MarginAggregateParameters extends SingleDimensionAggregateParameters implements IMarginAggregateParameters {
+    aggregateFunction?: AggregateFunction | undefined;
+
+    constructor(data?: IMarginAggregateParameters) {
+        super(data);
+        this._discriminator = "MarginAggregateParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.aggregateFunction = data["AggregateFunction"];
+        }
+    }
+
+    static fromJS(data: any): MarginAggregateParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new MarginAggregateParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["AggregateFunction"] = this.aggregateFunction;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IMarginAggregateParameters extends ISingleDimensionAggregateParameters {
+    aggregateFunction?: AggregateFunction | undefined;
+}
+
+export class MaxAggregateParameters extends SingleDimensionAggregateParameters implements IMaxAggregateParameters {
+
+    constructor(data?: IMaxAggregateParameters) {
+        super(data);
+        this._discriminator = "MaxAggregateParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): MaxAggregateParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new MaxAggregateParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IMaxAggregateParameters extends ISingleDimensionAggregateParameters {
+}
+
+export class MinAggregateParameters extends SingleDimensionAggregateParameters implements IMinAggregateParameters {
+
+    constructor(data?: IMinAggregateParameters) {
+        super(data);
+        this._discriminator = "MinAggregateParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): MinAggregateParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new MinAggregateParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IMinAggregateParameters extends ISingleDimensionAggregateParameters {
+}
+
+export class SumAggregateParameters extends SingleDimensionAggregateParameters implements ISumAggregateParameters {
+
+    constructor(data?: ISumAggregateParameters) {
+        super(data);
+        this._discriminator = "SumAggregateParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): SumAggregateParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new SumAggregateParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ISumAggregateParameters extends ISingleDimensionAggregateParameters {
+}
+
+export class SumEstimationAggregateParameters extends SingleDimensionAggregateParameters implements ISumEstimationAggregateParameters {
+
+    constructor(data?: ISumEstimationAggregateParameters) {
+        super(data);
+        this._discriminator = "SumEstimationAggregateParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): SumEstimationAggregateParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new SumEstimationAggregateParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ISumEstimationAggregateParameters extends ISingleDimensionAggregateParameters {
+}
+
+export enum OrderingFunction {
+    None = 0,
+    SortUp = 1,
+    SortDown = 2,
+}
+
+export abstract class BinningParameters implements IBinningParameters {
+    attributeParameters?: AttributeParameters | undefined;
+
+    protected _discriminator: string;
+
+    constructor(data?: IBinningParameters) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+        this._discriminator = "BinningParameters";
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.attributeParameters = data["AttributeParameters"] ? AttributeParameters.fromJS(data["AttributeParameters"]) : <any>undefined;
+        }
+    }
+
+    static fromJS(data: any): BinningParameters {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "EquiWidthBinningParameters") {
+            let result = new EquiWidthBinningParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "SingleBinBinningParameters") {
+            let result = new SingleBinBinningParameters();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'BinningParameters' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["discriminator"] = this._discriminator;
+        data["AttributeParameters"] = this.attributeParameters ? this.attributeParameters.toJSON() : <any>undefined;
+        return data;
+    }
+}
+
+export interface IBinningParameters {
+    attributeParameters?: AttributeParameters | undefined;
+}
+
+export class EquiWidthBinningParameters extends BinningParameters implements IEquiWidthBinningParameters {
+    minValue?: number | undefined;
+    maxValue?: number | undefined;
+    requestedNrOfBins?: number | undefined;
+    referenceValue?: number | undefined;
+    step?: number | undefined;
+
+    constructor(data?: IEquiWidthBinningParameters) {
+        super(data);
+        this._discriminator = "EquiWidthBinningParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.minValue = data["MinValue"];
+            this.maxValue = data["MaxValue"];
+            this.requestedNrOfBins = data["RequestedNrOfBins"];
+            this.referenceValue = data["ReferenceValue"];
+            this.step = data["Step"];
+        }
+    }
+
+    static fromJS(data: any): EquiWidthBinningParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new EquiWidthBinningParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["MinValue"] = this.minValue;
+        data["MaxValue"] = this.maxValue;
+        data["RequestedNrOfBins"] = this.requestedNrOfBins;
+        data["ReferenceValue"] = this.referenceValue;
+        data["Step"] = this.step;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IEquiWidthBinningParameters extends IBinningParameters {
+    minValue?: number | undefined;
+    maxValue?: number | undefined;
+    requestedNrOfBins?: number | undefined;
+    referenceValue?: number | undefined;
+    step?: number | undefined;
+}
+
+export class SingleBinBinningParameters extends BinningParameters implements ISingleBinBinningParameters {
+
+    constructor(data?: ISingleBinBinningParameters) {
+        super(data);
+        this._discriminator = "SingleBinBinningParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): SingleBinBinningParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new SingleBinBinningParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ISingleBinBinningParameters extends IBinningParameters {
+}
+
+export class Attribute implements IAttribute {
+    displayName?: string | undefined;
+    rawName?: string | undefined;
+    description?: string | undefined;
+    dataType?: DataType | undefined;
+    visualizationHints?: VisualizationHint[] | undefined;
+    isTarget?: boolean | undefined;
+
+    constructor(data?: IAttribute) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.displayName = data["DisplayName"];
+            this.rawName = data["RawName"];
+            this.description = data["Description"];
+            this.dataType = data["DataType"];
+            if (data["VisualizationHints"] && data["VisualizationHints"].constructor === Array) {
+                this.visualizationHints = [];
+                for (let item of data["VisualizationHints"])
+                    this.visualizationHints.push(item);
+            }
+            this.isTarget = data["IsTarget"];
+        }
+    }
+
+    static fromJS(data: any): Attribute {
+        data = typeof data === 'object' ? data : {};
+        let result = new Attribute();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["DisplayName"] = this.displayName;
+        data["RawName"] = this.rawName;
+        data["Description"] = this.description;
+        data["DataType"] = this.dataType;
+        if (this.visualizationHints && this.visualizationHints.constructor === Array) {
+            data["VisualizationHints"] = [];
+            for (let item of this.visualizationHints)
+                data["VisualizationHints"].push(item);
+        }
+        data["IsTarget"] = this.isTarget;
+        return data;
+    }
+}
+
+export interface IAttribute {
+    displayName?: string | undefined;
+    rawName?: string | undefined;
+    description?: string | undefined;
+    dataType?: DataType | undefined;
+    visualizationHints?: VisualizationHint[] | undefined;
+    isTarget?: boolean | undefined;
+}
+
+export enum DataType {
+    Int = "Int",
+    String = "String",
+    Float = "Float",
+    Double = "Double",
+    DateTime = "DateTime",
+    Object = "Object",
+    Undefined = "Undefined",
+}
+
+export class AttributeGroup implements IAttributeGroup {
+    name?: string | undefined;
+    attributeGroups?: AttributeGroup[] | undefined;
+    attributes?: Attribute[] | undefined;
+
+    constructor(data?: IAttributeGroup) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.name = data["Name"];
+            if (data["AttributeGroups"] && data["AttributeGroups"].constructor === Array) {
+                this.attributeGroups = [];
+                for (let item of data["AttributeGroups"])
+                    this.attributeGroups.push(AttributeGroup.fromJS(item));
+            }
+            if (data["Attributes"] && data["Attributes"].constructor === Array) {
+                this.attributes = [];
+                for (let item of data["Attributes"])
+                    this.attributes.push(Attribute.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): AttributeGroup {
+        data = typeof data === 'object' ? data : {};
+        let result = new AttributeGroup();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Name"] = this.name;
+        if (this.attributeGroups && this.attributeGroups.constructor === Array) {
+            data["AttributeGroups"] = [];
+            for (let item of this.attributeGroups)
+                data["AttributeGroups"].push(item.toJSON());
+        }
+        if (this.attributes && this.attributes.constructor === Array) {
+            data["Attributes"] = [];
+            for (let item of this.attributes)
+                data["Attributes"].push(item.toJSON());
+        }
+        return data;
+    }
+}
+
+export interface IAttributeGroup {
+    name?: string | undefined;
+    attributeGroups?: AttributeGroup[] | undefined;
+    attributes?: Attribute[] | undefined;
+}
+
+export class Catalog implements ICatalog {
+    supportedOperations?: string[] | undefined;
+    schemas?: Schema[] | undefined;
+
+    constructor(data?: ICatalog) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            if (data["SupportedOperations"] && data["SupportedOperations"].constructor === Array) {
+                this.supportedOperations = [];
+                for (let item of data["SupportedOperations"])
+                    this.supportedOperations.push(item);
+            }
+            if (data["Schemas"] && data["Schemas"].constructor === Array) {
+                this.schemas = [];
+                for (let item of data["Schemas"])
+                    this.schemas.push(Schema.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): Catalog {
+        data = typeof data === 'object' ? data : {};
+        let result = new Catalog();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.supportedOperations && this.supportedOperations.constructor === Array) {
+            data["SupportedOperations"] = [];
+            for (let item of this.supportedOperations)
+                data["SupportedOperations"].push(item);
+        }
+        if (this.schemas && this.schemas.constructor === Array) {
+            data["Schemas"] = [];
+            for (let item of this.schemas)
+                data["Schemas"].push(item.toJSON());
+        }
+        return data;
+    }
+}
+
+export interface ICatalog {
+    supportedOperations?: string[] | undefined;
+    schemas?: Schema[] | undefined;
+}
+
+export class Schema implements ISchema {
+    rootAttributeGroup?: AttributeGroup | undefined;
+    displayName?: string | undefined;
+    augmentedFrom?: string | undefined;
+    rawName?: string | undefined;
+    problemDescription?: string | undefined;
+    darpaProblemDoc?: DarpaProblemDoc | undefined;
+    distinctAttributeParameters?: AttributeParameters | undefined;
+    darpaDatasetDoc?: DarpaDatasetDoc | undefined;
+    darpaDatasetLocation?: string | undefined;
+    isMultiResourceData?: boolean | undefined;
+    problemFinderRows?: ProblemFinderRows[] | undefined;
+    correlationRows?: ProblemFinderRows[] | undefined;
+
+    constructor(data?: ISchema) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.rootAttributeGroup = data["RootAttributeGroup"] ? AttributeGroup.fromJS(data["RootAttributeGroup"]) : <any>undefined;
+            this.displayName = data["DisplayName"];
+            this.augmentedFrom = data["AugmentedFrom"];
+            this.rawName = data["RawName"];
+            this.problemDescription = data["ProblemDescription"];
+            this.darpaProblemDoc = data["DarpaProblemDoc"] ? DarpaProblemDoc.fromJS(data["DarpaProblemDoc"]) : <any>undefined;
+            this.distinctAttributeParameters = data["DistinctAttributeParameters"] ? AttributeParameters.fromJS(data["DistinctAttributeParameters"]) : <any>undefined;
+            this.darpaDatasetDoc = data["DarpaDatasetDoc"] ? DarpaDatasetDoc.fromJS(data["DarpaDatasetDoc"]) : <any>undefined;
+            this.darpaDatasetLocation = data["DarpaDatasetLocation"];
+            this.isMultiResourceData = data["IsMultiResourceData"];
+            if (data["ProblemFinderRows"] && data["ProblemFinderRows"].constructor === Array) {
+                this.problemFinderRows = [];
+                for (let item of data["ProblemFinderRows"])
+                    this.problemFinderRows.push(ProblemFinderRows.fromJS(item));
+            }
+            if (data["CorrelationRows"] && data["CorrelationRows"].constructor === Array) {
+                this.correlationRows = [];
+                for (let item of data["CorrelationRows"])
+                    this.correlationRows.push(ProblemFinderRows.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): Schema {
+        data = typeof data === 'object' ? data : {};
+        let result = new Schema();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["RootAttributeGroup"] = this.rootAttributeGroup ? this.rootAttributeGroup.toJSON() : <any>undefined;
+        data["DisplayName"] = this.displayName;
+        data["AugmentedFrom"] = this.augmentedFrom;
+        data["RawName"] = this.rawName;
+        data["ProblemDescription"] = this.problemDescription;
+        data["DarpaProblemDoc"] = this.darpaProblemDoc ? this.darpaProblemDoc.toJSON() : <any>undefined;
+        data["DistinctAttributeParameters"] = this.distinctAttributeParameters ? this.distinctAttributeParameters.toJSON() : <any>undefined;
+        data["DarpaDatasetDoc"] = this.darpaDatasetDoc ? this.darpaDatasetDoc.toJSON() : <any>undefined;
+        data["DarpaDatasetLocation"] = this.darpaDatasetLocation;
+        data["IsMultiResourceData"] = this.isMultiResourceData;
+        if (this.problemFinderRows && this.problemFinderRows.constructor === Array) {
+            data["ProblemFinderRows"] = [];
+            for (let item of this.problemFinderRows)
+                data["ProblemFinderRows"].push(item.toJSON());
+        }
+        if (this.correlationRows && this.correlationRows.constructor === Array) {
+            data["CorrelationRows"] = [];
+            for (let item of this.correlationRows)
+                data["CorrelationRows"].push(item.toJSON());
+        }
+        return data;
+    }
+}
+
+export interface ISchema {
+    rootAttributeGroup?: AttributeGroup | undefined;
+    displayName?: string | undefined;
+    augmentedFrom?: string | undefined;
+    rawName?: string | undefined;
+    problemDescription?: string | undefined;
+    darpaProblemDoc?: DarpaProblemDoc | undefined;
+    distinctAttributeParameters?: AttributeParameters | undefined;
+    darpaDatasetDoc?: DarpaDatasetDoc | undefined;
+    darpaDatasetLocation?: string | undefined;
+    isMultiResourceData?: boolean | undefined;
+    problemFinderRows?: ProblemFinderRows[] | undefined;
+    correlationRows?: ProblemFinderRows[] | undefined;
+}
+
+export class DarpaProblemDoc implements IDarpaProblemDoc {
+    about?: ProblemAbout | undefined;
+    inputs?: ProblemInputs | undefined;
+
+    constructor(data?: IDarpaProblemDoc) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.about = data["about"] ? ProblemAbout.fromJS(data["about"]) : <any>undefined;
+            this.inputs = data["inputs"] ? ProblemInputs.fromJS(data["inputs"]) : <any>undefined;
+        }
+    }
+
+    static fromJS(data: any): DarpaProblemDoc {
+        data = typeof data === 'object' ? data : {};
+        let result = new DarpaProblemDoc();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["about"] = this.about ? this.about.toJSON() : <any>undefined;
+        data["inputs"] = this.inputs ? this.inputs.toJSON() : <any>undefined;
+        return data;
+    }
+}
+
+export interface IDarpaProblemDoc {
+    about?: ProblemAbout | undefined;
+    inputs?: ProblemInputs | undefined;
+}
+
+export class ProblemAbout implements IProblemAbout {
+    problemID?: string | undefined;
+    problemName?: string | undefined;
+    problemDescription?: string | undefined;
+    taskType?: string | undefined;
+    taskSubType?: string | undefined;
+    problemSchemaVersion?: string | undefined;
+    problemVersion?: string | undefined;
+
+    constructor(data?: IProblemAbout) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.problemID = data["problemID"];
+            this.problemName = data["problemName"];
+            this.problemDescription = data["problemDescription"];
+            this.taskType = data["taskType"];
+            this.taskSubType = data["taskSubType"];
+            this.problemSchemaVersion = data["problemSchemaVersion"];
+            this.problemVersion = data["problemVersion"];
+        }
+    }
+
+    static fromJS(data: any): ProblemAbout {
+        data = typeof data === 'object' ? data : {};
+        let result = new ProblemAbout();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["problemID"] = this.problemID;
+        data["problemName"] = this.problemName;
+        data["problemDescription"] = this.problemDescription;
+        data["taskType"] = this.taskType;
+        data["taskSubType"] = this.taskSubType;
+        data["problemSchemaVersion"] = this.problemSchemaVersion;
+        data["problemVersion"] = this.problemVersion;
+        return data;
+    }
+}
+
+export interface IProblemAbout {
+    problemID?: string | undefined;
+    problemName?: string | undefined;
+    problemDescription?: string | undefined;
+    taskType?: string | undefined;
+    taskSubType?: string | undefined;
+    problemSchemaVersion?: string | undefined;
+    problemVersion?: string | undefined;
+}
+
+export class ProblemInputs implements IProblemInputs {
+    data?: ProblemData[] | undefined;
+    performanceMetrics?: ProblemPerformanceMetric[] | undefined;
+
+    constructor(data?: IProblemInputs) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            if (data["data"] && data["data"].constructor === Array) {
+                this.data = [];
+                for (let item of data["data"])
+                    this.data.push(ProblemData.fromJS(item));
+            }
+            if (data["performanceMetrics"] && data["performanceMetrics"].constructor === Array) {
+                this.performanceMetrics = [];
+                for (let item of data["performanceMetrics"])
+                    this.performanceMetrics.push(ProblemPerformanceMetric.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): ProblemInputs {
+        data = typeof data === 'object' ? data : {};
+        let result = new ProblemInputs();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.data && this.data.constructor === Array) {
+            data["data"] = [];
+            for (let item of this.data)
+                data["data"].push(item.toJSON());
+        }
+        if (this.performanceMetrics && this.performanceMetrics.constructor === Array) {
+            data["performanceMetrics"] = [];
+            for (let item of this.performanceMetrics)
+                data["performanceMetrics"].push(item.toJSON());
+        }
+        return data;
+    }
+}
+
+export interface IProblemInputs {
+    data?: ProblemData[] | undefined;
+    performanceMetrics?: ProblemPerformanceMetric[] | undefined;
+}
+
+export class ProblemData implements IProblemData {
+    datasetID?: string | undefined;
+    targets?: ProblemTarget[] | undefined;
+
+    constructor(data?: IProblemData) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.datasetID = data["datasetID"];
+            if (data["targets"] && data["targets"].constructor === Array) {
+                this.targets = [];
+                for (let item of data["targets"])
+                    this.targets.push(ProblemTarget.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): ProblemData {
+        data = typeof data === 'object' ? data : {};
+        let result = new ProblemData();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["datasetID"] = this.datasetID;
+        if (this.targets && this.targets.constructor === Array) {
+            data["targets"] = [];
+            for (let item of this.targets)
+                data["targets"].push(item.toJSON());
+        }
+        return data;
+    }
+}
+
+export interface IProblemData {
+    datasetID?: string | undefined;
+    targets?: ProblemTarget[] | undefined;
+}
+
+export class ProblemTarget implements IProblemTarget {
+    targetIndex?: number | undefined;
+    resID?: string | undefined;
+    colIndex?: number | undefined;
+    colName?: string | undefined;
+
+    constructor(data?: IProblemTarget) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.targetIndex = data["targetIndex"];
+            this.resID = data["resID"];
+            this.colIndex = data["colIndex"];
+            this.colName = data["colName"];
+        }
+    }
+
+    static fromJS(data: any): ProblemTarget {
+        data = typeof data === 'object' ? data : {};
+        let result = new ProblemTarget();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["targetIndex"] = this.targetIndex;
+        data["resID"] = this.resID;
+        data["colIndex"] = this.colIndex;
+        data["colName"] = this.colName;
+        return data;
+    }
+}
+
+export interface IProblemTarget {
+    targetIndex?: number | undefined;
+    resID?: string | undefined;
+    colIndex?: number | undefined;
+    colName?: string | undefined;
+}
+
+export class ProblemPerformanceMetric implements IProblemPerformanceMetric {
+    metric?: string | undefined;
+
+    constructor(data?: IProblemPerformanceMetric) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.metric = data["metric"];
+        }
+    }
+
+    static fromJS(data: any): ProblemPerformanceMetric {
+        data = typeof data === 'object' ? data : {};
+        let result = new ProblemPerformanceMetric();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["metric"] = this.metric;
+        return data;
+    }
+}
+
+export interface IProblemPerformanceMetric {
+    metric?: string | undefined;
+}
+
+export class DarpaDatasetDoc implements IDarpaDatasetDoc {
+    about?: DatasetAbout | undefined;
+    dataResources?: Resource[] | undefined;
+
+    constructor(data?: IDarpaDatasetDoc) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.about = data["about"] ? DatasetAbout.fromJS(data["about"]) : <any>undefined;
+            if (data["dataResources"] && data["dataResources"].constructor === Array) {
+                this.dataResources = [];
+                for (let item of data["dataResources"])
+                    this.dataResources.push(Resource.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): DarpaDatasetDoc {
+        data = typeof data === 'object' ? data : {};
+        let result = new DarpaDatasetDoc();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["about"] = this.about ? this.about.toJSON() : <any>undefined;
+        if (this.dataResources && this.dataResources.constructor === Array) {
+            data["dataResources"] = [];
+            for (let item of this.dataResources)
+                data["dataResources"].push(item.toJSON());
+        }
+        return data;
+    }
+}
+
+export interface IDarpaDatasetDoc {
+    about?: DatasetAbout | undefined;
+    dataResources?: Resource[] | undefined;
+}
+
+export class DatasetAbout implements IDatasetAbout {
+    datasetID?: string | undefined;
+    datasetName?: string | undefined;
+    description?: string | undefined;
+    citation?: string | undefined;
+    license?: string | undefined;
+    source?: string | undefined;
+    sourceURI?: string | undefined;
+    approximateSize?: string | undefined;
+    datasetSchemaVersion?: string | undefined;
+    redacted?: boolean | undefined;
+    datasetVersion?: string | undefined;
+
+    constructor(data?: IDatasetAbout) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.datasetID = data["datasetID"];
+            this.datasetName = data["datasetName"];
+            this.description = data["description"];
+            this.citation = data["citation"];
+            this.license = data["license"];
+            this.source = data["source"];
+            this.sourceURI = data["sourceURI"];
+            this.approximateSize = data["approximateSize"];
+            this.datasetSchemaVersion = data["datasetSchemaVersion"];
+            this.redacted = data["redacted"];
+            this.datasetVersion = data["datasetVersion"];
+        }
+    }
+
+    static fromJS(data: any): DatasetAbout {
+        data = typeof data === 'object' ? data : {};
+        let result = new DatasetAbout();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["datasetID"] = this.datasetID;
+        data["datasetName"] = this.datasetName;
+        data["description"] = this.description;
+        data["citation"] = this.citation;
+        data["license"] = this.license;
+        data["source"] = this.source;
+        data["sourceURI"] = this.sourceURI;
+        data["approximateSize"] = this.approximateSize;
+        data["datasetSchemaVersion"] = this.datasetSchemaVersion;
+        data["redacted"] = this.redacted;
+        data["datasetVersion"] = this.datasetVersion;
+        return data;
+    }
+}
+
+export interface IDatasetAbout {
+    datasetID?: string | undefined;
+    datasetName?: string | undefined;
+    description?: string | undefined;
+    citation?: string | undefined;
+    license?: string | undefined;
+    source?: string | undefined;
+    sourceURI?: string | undefined;
+    approximateSize?: string | undefined;
+    datasetSchemaVersion?: string | undefined;
+    redacted?: boolean | undefined;
+    datasetVersion?: string | undefined;
+}
+
+export class Resource implements IResource {
+    resID?: string | undefined;
+    resPath?: string | undefined;
+    resType?: string | undefined;
+    resFormat?: string[] | undefined;
+    columns?: Column[] | undefined;
+    isCollection?: boolean | undefined;
+
+    constructor(data?: IResource) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.resID = data["resID"];
+            this.resPath = data["resPath"];
+            this.resType = data["resType"];
+            if (data["resFormat"] && data["resFormat"].constructor === Array) {
+                this.resFormat = [];
+                for (let item of data["resFormat"])
+                    this.resFormat.push(item);
+            }
+            if (data["columns"] && data["columns"].constructor === Array) {
+                this.columns = [];
+                for (let item of data["columns"])
+                    this.columns.push(Column.fromJS(item));
+            }
+            this.isCollection = data["isCollection"];
+        }
+    }
+
+    static fromJS(data: any): Resource {
+        data = typeof data === 'object' ? data : {};
+        let result = new Resource();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["resID"] = this.resID;
+        data["resPath"] = this.resPath;
+        data["resType"] = this.resType;
+        if (this.resFormat && this.resFormat.constructor === Array) {
+            data["resFormat"] = [];
+            for (let item of this.resFormat)
+                data["resFormat"].push(item);
+        }
+        if (this.columns && this.columns.constructor === Array) {
+            data["columns"] = [];
+            for (let item of this.columns)
+                data["columns"].push(item.toJSON());
+        }
+        data["isCollection"] = this.isCollection;
+        return data;
+    }
+}
+
+export interface IResource {
+    resID?: string | undefined;
+    resPath?: string | undefined;
+    resType?: string | undefined;
+    resFormat?: string[] | undefined;
+    columns?: Column[] | undefined;
+    isCollection?: boolean | undefined;
+}
+
+export class Column implements IColumn {
+    colIndex?: number | undefined;
+    colDescription?: string | undefined;
+    colName?: string | undefined;
+    colType?: string | undefined;
+    role?: string[] | undefined;
+    refersTo?: Reference | undefined;
+
+    constructor(data?: IColumn) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.colIndex = data["colIndex"];
+            this.colDescription = data["colDescription"];
+            this.colName = data["colName"];
+            this.colType = data["colType"];
+            if (data["role"] && data["role"].constructor === Array) {
+                this.role = [];
+                for (let item of data["role"])
+                    this.role.push(item);
+            }
+            this.refersTo = data["refersTo"] ? Reference.fromJS(data["refersTo"]) : <any>undefined;
+        }
+    }
+
+    static fromJS(data: any): Column {
+        data = typeof data === 'object' ? data : {};
+        let result = new Column();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["colIndex"] = this.colIndex;
+        data["colDescription"] = this.colDescription;
+        data["colName"] = this.colName;
+        data["colType"] = this.colType;
+        if (this.role && this.role.constructor === Array) {
+            data["role"] = [];
+            for (let item of this.role)
+                data["role"].push(item);
+        }
+        data["refersTo"] = this.refersTo ? this.refersTo.toJSON() : <any>undefined;
+        return data;
+    }
+}
+
+export interface IColumn {
+    colIndex?: number | undefined;
+    colDescription?: string | undefined;
+    colName?: string | undefined;
+    colType?: string | undefined;
+    role?: string[] | undefined;
+    refersTo?: Reference | undefined;
+}
+
+export class Reference implements IReference {
+    resID?: string | undefined;
+
+    constructor(data?: IReference) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.resID = data["resID"];
+        }
+    }
+
+    static fromJS(data: any): Reference {
+        data = typeof data === 'object' ? data : {};
+        let result = new Reference();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["resID"] = this.resID;
+        return data;
+    }
+}
+
+export interface IReference {
+    resID?: string | undefined;
+}
+
+export class ProblemFinderRows implements IProblemFinderRows {
+    label?: string | undefined;
+    type?: string | undefined;
+    features?: Feature[] | undefined;
+
+    constructor(data?: IProblemFinderRows) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.label = data["label"];
+            this.type = data["type"];
+            if (data["features"] && data["features"].constructor === Array) {
+                this.features = [];
+                for (let item of data["features"])
+                    this.features.push(Feature.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): ProblemFinderRows {
+        data = typeof data === 'object' ? data : {};
+        let result = new ProblemFinderRows();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["label"] = this.label;
+        data["type"] = this.type;
+        if (this.features && this.features.constructor === Array) {
+            data["features"] = [];
+            for (let item of this.features)
+                data["features"].push(item.toJSON());
+        }
+        return data;
+    }
+}
+
+export interface IProblemFinderRows {
+    label?: string | undefined;
+    type?: string | undefined;
+    features?: Feature[] | undefined;
+}
+
+export class Feature implements IFeature {
+    name?: string | undefined;
+    selected?: boolean | undefined;
+    value?: number | undefined;
+
+    constructor(data?: IFeature) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.name = data["name"];
+            this.selected = data["selected"];
+            this.value = data["value"];
+        }
+    }
+
+    static fromJS(data: any): Feature {
+        data = typeof data === 'object' ? data : {};
+        let result = new Feature();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["name"] = this.name;
+        data["selected"] = this.selected;
+        data["value"] = this.value;
+        return data;
+    }
+}
+
+export interface IFeature {
+    name?: string | undefined;
+    selected?: boolean | undefined;
+    value?: number | undefined;
+}
+
+export enum DataType2 {
+    Int = 0,
+    String = 1,
+    Float = 2,
+    Double = 3,
+    DateTime = 4,
+    Object = 5,
+    Undefined = 6,
+}
+
+export abstract class DataTypeExtensions implements IDataTypeExtensions {
+
+    constructor(data?: IDataTypeExtensions) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): DataTypeExtensions {
+        data = typeof data === 'object' ? data : {};
+        throw new Error("The abstract class 'DataTypeExtensions' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        return data;
+    }
+}
+
+export interface IDataTypeExtensions {
+}
+
+export class ResObject implements IResObject {
+    columnName?: string | undefined;
+
+    constructor(data?: IResObject) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.columnName = data["columnName"];
+        }
+    }
+
+    static fromJS(data: any): ResObject {
+        data = typeof data === 'object' ? data : {};
+        let result = new ResObject();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["columnName"] = this.columnName;
+        return data;
+    }
+}
+
+export interface IResObject {
+    columnName?: string | undefined;
+}
+
+export class Exception implements IException {
+    message?: string | undefined;
+    innerException?: Exception | undefined;
+    stackTrace?: string | undefined;
+    source?: string | undefined;
+
+    constructor(data?: IException) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.message = data["Message"];
+            this.innerException = data["InnerException"] ? Exception.fromJS(data["InnerException"]) : <any>undefined;
+            this.stackTrace = data["StackTrace"];
+            this.source = data["Source"];
+        }
+    }
+
+    static fromJS(data: any): Exception {
+        data = typeof data === 'object' ? data : {};
+        let result = new Exception();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Message"] = this.message;
+        data["InnerException"] = this.innerException ? this.innerException.toJSON() : <any>undefined;
+        data["StackTrace"] = this.stackTrace;
+        data["Source"] = this.source;
+        return data;
+    }
+}
+
+export interface IException {
+    message?: string | undefined;
+    innerException?: Exception | undefined;
+    stackTrace?: string | undefined;
+    source?: string | undefined;
+}
+
+export class IDEAException extends Exception implements IIDEAException {
+
+    constructor(data?: IIDEAException) {
+        super(data);
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): IDEAException {
+        data = typeof data === 'object' ? data : {};
+        let result = new IDEAException();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IIDEAException extends IException {
+}
+
+export class CodeParameters implements ICodeParameters {
+    attributeCodeParameters?: AttributeCodeParameters[] | undefined;
+    adapterName?: string | undefined;
+
+    constructor(data?: ICodeParameters) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            if (data["AttributeCodeParameters"] && data["AttributeCodeParameters"].constructor === Array) {
+                this.attributeCodeParameters = [];
+                for (let item of data["AttributeCodeParameters"])
+                    this.attributeCodeParameters.push(AttributeCodeParameters.fromJS(item));
+            }
+            this.adapterName = data["AdapterName"];
+        }
+    }
+
+    static fromJS(data: any): CodeParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new CodeParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.attributeCodeParameters && this.attributeCodeParameters.constructor === Array) {
+            data["AttributeCodeParameters"] = [];
+            for (let item of this.attributeCodeParameters)
+                data["AttributeCodeParameters"].push(item.toJSON());
+        }
+        data["AdapterName"] = this.adapterName;
+        return data;
+    }
+}
+
+export interface ICodeParameters {
+    attributeCodeParameters?: AttributeCodeParameters[] | undefined;
+    adapterName?: string | undefined;
+}
+
+export class CompileResult implements ICompileResult {
+    compileSuccess?: boolean | undefined;
+    compileMessage?: string | undefined;
+    dataType?: DataType | undefined;
+    replaceAttributeParameters?: AttributeParameters[] | undefined;
+
+    constructor(data?: ICompileResult) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.compileSuccess = data["CompileSuccess"];
+            this.compileMessage = data["CompileMessage"];
+            this.dataType = data["DataType"];
+            if (data["ReplaceAttributeParameters"] && data["ReplaceAttributeParameters"].constructor === Array) {
+                this.replaceAttributeParameters = [];
+                for (let item of data["ReplaceAttributeParameters"])
+                    this.replaceAttributeParameters.push(AttributeParameters.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): CompileResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new CompileResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["CompileSuccess"] = this.compileSuccess;
+        data["CompileMessage"] = this.compileMessage;
+        data["DataType"] = this.dataType;
+        if (this.replaceAttributeParameters && this.replaceAttributeParameters.constructor === Array) {
+            data["ReplaceAttributeParameters"] = [];
+            for (let item of this.replaceAttributeParameters)
+                data["ReplaceAttributeParameters"].push(item.toJSON());
+        }
+        return data;
+    }
+}
+
+export interface ICompileResult {
+    compileSuccess?: boolean | undefined;
+    compileMessage?: string | undefined;
+    dataType?: DataType | undefined;
+    replaceAttributeParameters?: AttributeParameters[] | undefined;
+}
+
+export class CompileResults implements ICompileResults {
+    rawNameToCompileResult?: { [key: string]: CompileResult; } | undefined;
+
+    constructor(data?: ICompileResults) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            if (data["RawNameToCompileResult"]) {
+                this.rawNameToCompileResult = {};
+                for (let key in data["RawNameToCompileResult"]) {
+                    if (data["RawNameToCompileResult"].hasOwnProperty(key))
+                        this.rawNameToCompileResult[key] = data["RawNameToCompileResult"][key] ? CompileResult.fromJS(data["RawNameToCompileResult"][key]) : new CompileResult();
+                }
+            }
+        }
+    }
+
+    static fromJS(data: any): CompileResults {
+        data = typeof data === 'object' ? data : {};
+        let result = new CompileResults();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.rawNameToCompileResult) {
+            data["RawNameToCompileResult"] = {};
+            for (let key in this.rawNameToCompileResult) {
+                if (this.rawNameToCompileResult.hasOwnProperty(key))
+                    data["RawNameToCompileResult"][key] = this.rawNameToCompileResult[key];
+            }
+        }
+        return data;
+    }
+}
+
+export interface ICompileResults {
+    rawNameToCompileResult?: { [key: string]: CompileResult; } | undefined;
+}
+
+export abstract class UniqueJson implements IUniqueJson {
+
+    constructor(data?: IUniqueJson) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): UniqueJson {
+        data = typeof data === 'object' ? data : {};
+        throw new Error("The abstract class 'UniqueJson' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        return data;
+    }
+}
+
+export interface IUniqueJson {
+}
+
+export abstract class OperationParameters extends UniqueJson implements IOperationParameters {
+    isCachable?: boolean | undefined;
+
+    protected _discriminator: string;
+
+    constructor(data?: IOperationParameters) {
+        super(data);
+        this._discriminator = "OperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.isCachable = data["IsCachable"];
+        }
+    }
+
+    static fromJS(data: any): OperationParameters {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "DataOperationParameters") {
+            throw new Error("The abstract class 'DataOperationParameters' cannot be instantiated.");
+        }
+        if (data["discriminator"] === "ExampleOperationParameters") {
+            let result = new ExampleOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "HistogramOperationParameters") {
+            let result = new HistogramOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "DistOperationParameters") {
+            throw new Error("The abstract class 'DistOperationParameters' cannot be instantiated.");
+        }
+        if (data["discriminator"] === "OptimizerOperationParameters") {
+            let result = new OptimizerOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "RawDataOperationParameters") {
+            let result = new RawDataOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "RecommenderOperationParameters") {
+            let result = new RecommenderOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "CDFOperationParameters") {
+            let result = new CDFOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "TestDistOperationParameters") {
+            throw new Error("The abstract class 'TestDistOperationParameters' cannot be instantiated.");
+        }
+        if (data["discriminator"] === "ChiSquaredTestOperationParameters") {
+            let result = new ChiSquaredTestOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "HypothesisTestParameters") {
+            throw new Error("The abstract class 'HypothesisTestParameters' cannot be instantiated.");
+        }
+        if (data["discriminator"] === "CorrelationTestOperationParameters") {
+            let result = new CorrelationTestOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "EmpiricalDistOperationParameters") {
+            let result = new EmpiricalDistOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "KSTestOperationParameters") {
+            let result = new KSTestOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "NewModelOperationParameters") {
+            let result = new NewModelOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "ModelOperationParameters") {
+            throw new Error("The abstract class 'ModelOperationParameters' cannot be instantiated.");
+        }
+        if (data["discriminator"] === "RootMeanSquareTestOperationParameters") {
+            let result = new RootMeanSquareTestOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "TTestOperationParameters") {
+            let result = new TTestOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "FeatureImportanceOperationParameters") {
+            let result = new FeatureImportanceOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "SampleOperationParameters") {
+            let result = new SampleOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "AddComparisonParameters") {
+            let result = new AddComparisonParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "GetModelStateParameters") {
+            let result = new GetModelStateParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "FrequentItemsetOperationParameters") {
+            let result = new FrequentItemsetOperationParameters();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'OperationParameters' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["discriminator"] = this._discriminator;
+        data["IsCachable"] = this.isCachable;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IOperationParameters extends IUniqueJson {
+    isCachable?: boolean | undefined;
+}
+
+export abstract class DataOperationParameters extends OperationParameters implements IDataOperationParameters {
+    sampleStreamBlockSize?: number | undefined;
+    adapterName?: string | undefined;
+    isCachable?: boolean | undefined;
+
+    constructor(data?: IDataOperationParameters) {
+        super(data);
+        this._discriminator = "DataOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.sampleStreamBlockSize = data["SampleStreamBlockSize"];
+            this.adapterName = data["AdapterName"];
+            this.isCachable = data["IsCachable"];
+        }
+    }
+
+    static fromJS(data: any): DataOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "ExampleOperationParameters") {
+            let result = new ExampleOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "HistogramOperationParameters") {
+            let result = new HistogramOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "DistOperationParameters") {
+            throw new Error("The abstract class 'DistOperationParameters' cannot be instantiated.");
+        }
+        if (data["discriminator"] === "OptimizerOperationParameters") {
+            let result = new OptimizerOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "RawDataOperationParameters") {
+            let result = new RawDataOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "RecommenderOperationParameters") {
+            let result = new RecommenderOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "CDFOperationParameters") {
+            let result = new CDFOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "TestDistOperationParameters") {
+            throw new Error("The abstract class 'TestDistOperationParameters' cannot be instantiated.");
+        }
+        if (data["discriminator"] === "EmpiricalDistOperationParameters") {
+            let result = new EmpiricalDistOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "FeatureImportanceOperationParameters") {
+            let result = new FeatureImportanceOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "SampleOperationParameters") {
+            let result = new SampleOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "FrequentItemsetOperationParameters") {
+            let result = new FrequentItemsetOperationParameters();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'DataOperationParameters' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["SampleStreamBlockSize"] = this.sampleStreamBlockSize;
+        data["AdapterName"] = this.adapterName;
+        data["IsCachable"] = this.isCachable;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IDataOperationParameters extends IOperationParameters {
+    sampleStreamBlockSize?: number | undefined;
+    adapterName?: string | undefined;
+    isCachable?: boolean | undefined;
+}
+
+export class ExampleOperationParameters extends DataOperationParameters implements IExampleOperationParameters {
+    filter?: string | undefined;
+    attributeParameters?: AttributeParameters[] | undefined;
+    attributeCodeParameters?: AttributeCaclculatedParameters[] | undefined;
+    dummyValue?: number | undefined;
+    exampleType?: string | undefined;
+
+    constructor(data?: IExampleOperationParameters) {
+        super(data);
+        this._discriminator = "ExampleOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.filter = data["Filter"];
+            if (data["AttributeParameters"] && data["AttributeParameters"].constructor === Array) {
+                this.attributeParameters = [];
+                for (let item of data["AttributeParameters"])
+                    this.attributeParameters.push(AttributeParameters.fromJS(item));
+            }
+            if (data["AttributeCodeParameters"] && data["AttributeCodeParameters"].constructor === Array) {
+                this.attributeCodeParameters = [];
+                for (let item of data["AttributeCodeParameters"])
+                    this.attributeCodeParameters.push(AttributeCaclculatedParameters.fromJS(item));
+            }
+            this.dummyValue = data["DummyValue"];
+            this.exampleType = data["ExampleType"];
+        }
+    }
+
+    static fromJS(data: any): ExampleOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new ExampleOperationParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Filter"] = this.filter;
+        if (this.attributeParameters && this.attributeParameters.constructor === Array) {
+            data["AttributeParameters"] = [];
+            for (let item of this.attributeParameters)
+                data["AttributeParameters"].push(item.toJSON());
+        }
+        if (this.attributeCodeParameters && this.attributeCodeParameters.constructor === Array) {
+            data["AttributeCodeParameters"] = [];
+            for (let item of this.attributeCodeParameters)
+                data["AttributeCodeParameters"].push(item.toJSON());
+        }
+        data["DummyValue"] = this.dummyValue;
+        data["ExampleType"] = this.exampleType;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IExampleOperationParameters extends IDataOperationParameters {
+    filter?: string | undefined;
+    attributeParameters?: AttributeParameters[] | undefined;
+    attributeCodeParameters?: AttributeCaclculatedParameters[] | undefined;
+    dummyValue?: number | undefined;
+    exampleType?: string | undefined;
+}
+
+export abstract class DistOperationParameters extends DataOperationParameters implements IDistOperationParameters {
+    filter?: string | undefined;
+    attributeCalculatedParameters?: AttributeCaclculatedParameters[] | undefined;
+
+    constructor(data?: IDistOperationParameters) {
+        super(data);
+        this._discriminator = "DistOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.filter = data["Filter"];
+            if (data["AttributeCalculatedParameters"] && data["AttributeCalculatedParameters"].constructor === Array) {
+                this.attributeCalculatedParameters = [];
+                for (let item of data["AttributeCalculatedParameters"])
+                    this.attributeCalculatedParameters.push(AttributeCaclculatedParameters.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): DistOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "HistogramOperationParameters") {
+            let result = new HistogramOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "OptimizerOperationParameters") {
+            let result = new OptimizerOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "RawDataOperationParameters") {
+            let result = new RawDataOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "CDFOperationParameters") {
+            let result = new CDFOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "TestDistOperationParameters") {
+            throw new Error("The abstract class 'TestDistOperationParameters' cannot be instantiated.");
+        }
+        if (data["discriminator"] === "EmpiricalDistOperationParameters") {
+            let result = new EmpiricalDistOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "FeatureImportanceOperationParameters") {
+            let result = new FeatureImportanceOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "SampleOperationParameters") {
+            let result = new SampleOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "FrequentItemsetOperationParameters") {
+            let result = new FrequentItemsetOperationParameters();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'DistOperationParameters' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Filter"] = this.filter;
+        if (this.attributeCalculatedParameters && this.attributeCalculatedParameters.constructor === Array) {
+            data["AttributeCalculatedParameters"] = [];
+            for (let item of this.attributeCalculatedParameters)
+                data["AttributeCalculatedParameters"].push(item.toJSON());
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IDistOperationParameters extends IDataOperationParameters {
+    filter?: string | undefined;
+    attributeCalculatedParameters?: AttributeCaclculatedParameters[] | undefined;
+}
+
+export class HistogramOperationParameters extends DistOperationParameters implements IHistogramOperationParameters {
+    sortPerBinAggregateParameter?: AggregateParameters | undefined;
+    binningParameters?: BinningParameters[] | undefined;
+    perBinAggregateParameters?: AggregateParameters[] | undefined;
+    globalAggregateParameters?: AggregateParameters[] | undefined;
+    brushes?: string[] | undefined;
+    enableBrushComputation?: boolean | undefined;
+    degreeOfParallism?: number | undefined;
+
+    constructor(data?: IHistogramOperationParameters) {
+        super(data);
+        this._discriminator = "HistogramOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.sortPerBinAggregateParameter = data["SortPerBinAggregateParameter"] ? AggregateParameters.fromJS(data["SortPerBinAggregateParameter"]) : <any>undefined;
+            if (data["BinningParameters"] && data["BinningParameters"].constructor === Array) {
+                this.binningParameters = [];
+                for (let item of data["BinningParameters"])
+                    this.binningParameters.push(BinningParameters.fromJS(item));
+            }
+            if (data["PerBinAggregateParameters"] && data["PerBinAggregateParameters"].constructor === Array) {
+                this.perBinAggregateParameters = [];
+                for (let item of data["PerBinAggregateParameters"])
+                    this.perBinAggregateParameters.push(AggregateParameters.fromJS(item));
+            }
+            if (data["GlobalAggregateParameters"] && data["GlobalAggregateParameters"].constructor === Array) {
+                this.globalAggregateParameters = [];
+                for (let item of data["GlobalAggregateParameters"])
+                    this.globalAggregateParameters.push(AggregateParameters.fromJS(item));
+            }
+            if (data["Brushes"] && data["Brushes"].constructor === Array) {
+                this.brushes = [];
+                for (let item of data["Brushes"])
+                    this.brushes.push(item);
+            }
+            this.enableBrushComputation = data["EnableBrushComputation"];
+            this.degreeOfParallism = data["DegreeOfParallism"];
+        }
+    }
+
+    static fromJS(data: any): HistogramOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new HistogramOperationParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["SortPerBinAggregateParameter"] = this.sortPerBinAggregateParameter ? this.sortPerBinAggregateParameter.toJSON() : <any>undefined;
+        if (this.binningParameters && this.binningParameters.constructor === Array) {
+            data["BinningParameters"] = [];
+            for (let item of this.binningParameters)
+                data["BinningParameters"].push(item.toJSON());
+        }
+        if (this.perBinAggregateParameters && this.perBinAggregateParameters.constructor === Array) {
+            data["PerBinAggregateParameters"] = [];
+            for (let item of this.perBinAggregateParameters)
+                data["PerBinAggregateParameters"].push(item.toJSON());
+        }
+        if (this.globalAggregateParameters && this.globalAggregateParameters.constructor === Array) {
+            data["GlobalAggregateParameters"] = [];
+            for (let item of this.globalAggregateParameters)
+                data["GlobalAggregateParameters"].push(item.toJSON());
+        }
+        if (this.brushes && this.brushes.constructor === Array) {
+            data["Brushes"] = [];
+            for (let item of this.brushes)
+                data["Brushes"].push(item);
+        }
+        data["EnableBrushComputation"] = this.enableBrushComputation;
+        data["DegreeOfParallism"] = this.degreeOfParallism;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IHistogramOperationParameters extends IDistOperationParameters {
+    sortPerBinAggregateParameter?: AggregateParameters | undefined;
+    binningParameters?: BinningParameters[] | undefined;
+    perBinAggregateParameters?: AggregateParameters[] | undefined;
+    globalAggregateParameters?: AggregateParameters[] | undefined;
+    brushes?: string[] | undefined;
+    enableBrushComputation?: boolean | undefined;
+    degreeOfParallism?: number | undefined;
+}
+
+export class OptimizerOperationParameters extends DistOperationParameters implements IOptimizerOperationParameters {
+    taskType?: TaskType | undefined;
+    taskSubType?: TaskSubType | undefined;
+    metricType?: MetricType | undefined;
+    labelAttribute?: AttributeParameters | undefined;
+    featureAttributes?: AttributeParameters[] | undefined;
+    requiredPrimitives?: string[] | undefined;
+    pythonFilter?: string | undefined;
+    trainFilter?: string | undefined;
+    pythonTrainFilter?: string | undefined;
+    testFilter?: string | undefined;
+    pythonTestFilter?: string | undefined;
+
+    constructor(data?: IOptimizerOperationParameters) {
+        super(data);
+        this._discriminator = "OptimizerOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.taskType = data["TaskType"];
+            this.taskSubType = data["TaskSubType"];
+            this.metricType = data["MetricType"];
+            this.labelAttribute = data["LabelAttribute"] ? AttributeParameters.fromJS(data["LabelAttribute"]) : <any>undefined;
+            if (data["FeatureAttributes"] && data["FeatureAttributes"].constructor === Array) {
+                this.featureAttributes = [];
+                for (let item of data["FeatureAttributes"])
+                    this.featureAttributes.push(AttributeParameters.fromJS(item));
+            }
+            if (data["RequiredPrimitives"] && data["RequiredPrimitives"].constructor === Array) {
+                this.requiredPrimitives = [];
+                for (let item of data["RequiredPrimitives"])
+                    this.requiredPrimitives.push(item);
+            }
+            this.pythonFilter = data["PythonFilter"];
+            this.trainFilter = data["TrainFilter"];
+            this.pythonTrainFilter = data["PythonTrainFilter"];
+            this.testFilter = data["TestFilter"];
+            this.pythonTestFilter = data["PythonTestFilter"];
+        }
+    }
+
+    static fromJS(data: any): OptimizerOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new OptimizerOperationParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["TaskType"] = this.taskType;
+        data["TaskSubType"] = this.taskSubType;
+        data["MetricType"] = this.metricType;
+        data["LabelAttribute"] = this.labelAttribute ? this.labelAttribute.toJSON() : <any>undefined;
+        if (this.featureAttributes && this.featureAttributes.constructor === Array) {
+            data["FeatureAttributes"] = [];
+            for (let item of this.featureAttributes)
+                data["FeatureAttributes"].push(item.toJSON());
+        }
+        if (this.requiredPrimitives && this.requiredPrimitives.constructor === Array) {
+            data["RequiredPrimitives"] = [];
+            for (let item of this.requiredPrimitives)
+                data["RequiredPrimitives"].push(item);
+        }
+        data["PythonFilter"] = this.pythonFilter;
+        data["TrainFilter"] = this.trainFilter;
+        data["PythonTrainFilter"] = this.pythonTrainFilter;
+        data["TestFilter"] = this.testFilter;
+        data["PythonTestFilter"] = this.pythonTestFilter;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IOptimizerOperationParameters extends IDistOperationParameters {
+    taskType?: TaskType | undefined;
+    taskSubType?: TaskSubType | undefined;
+    metricType?: MetricType | undefined;
+    labelAttribute?: AttributeParameters | undefined;
+    featureAttributes?: AttributeParameters[] | undefined;
+    requiredPrimitives?: string[] | undefined;
+    pythonFilter?: string | undefined;
+    trainFilter?: string | undefined;
+    pythonTrainFilter?: string | undefined;
+    testFilter?: string | undefined;
+    pythonTestFilter?: string | undefined;
+}
+
+export enum TaskType {
+    Undefined = 0,
+    Classification = 1,
+    Regression = 2,
+    Clustering = 3,
+    LinkPrediction = 4,
+    VertexNomination = 5,
+    CommunityDetection = 6,
+    GraphClustering = 7,
+    GraphMatching = 8,
+    TimeSeriesForecasting = 9,
+    CollaborativeFiltering = 10,
+}
+
+export enum TaskSubType {
+    Undefined = 0,
+    None = 1,
+    Binary = 2,
+    Multiclass = 3,
+    Multilabel = 4,
+    Univariate = 5,
+    Multivariate = 6,
+    Overlapping = 7,
+    Nonoverlapping = 8,
+}
+
+export enum MetricType {
+    MetricUndefined = 0,
+    Accuracy = 1,
+    Precision = 2,
+    Recall = 3,
+    F1 = 4,
+    F1Micro = 5,
+    F1Macro = 6,
+    RocAuc = 7,
+    RocAucMicro = 8,
+    RocAucMacro = 9,
+    MeanSquaredError = 10,
+    RootMeanSquaredError = 11,
+    RootMeanSquaredErrorAvg = 12,
+    MeanAbsoluteError = 13,
+    RSquared = 14,
+    NormalizedMutualInformation = 15,
+    JaccardSimilarityScore = 16,
+    PrecisionAtTopK = 17,
+    ObjectDetectionAveragePrecision = 18,
+    Loss = 100,
+}
+
+export class RawDataOperationParameters extends DistOperationParameters implements IRawDataOperationParameters {
+    sortUpRawName?: string | undefined;
+    sortDownRawName?: string | undefined;
+    numRecords?: number | undefined;
+    binningParameters?: BinningParameters[] | undefined;
+    brushes?: string[] | undefined;
+    enableBrushComputation?: boolean | undefined;
+
+    constructor(data?: IRawDataOperationParameters) {
+        super(data);
+        this._discriminator = "RawDataOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.sortUpRawName = data["SortUpRawName"];
+            this.sortDownRawName = data["SortDownRawName"];
+            this.numRecords = data["NumRecords"];
+            if (data["BinningParameters"] && data["BinningParameters"].constructor === Array) {
+                this.binningParameters = [];
+                for (let item of data["BinningParameters"])
+                    this.binningParameters.push(BinningParameters.fromJS(item));
+            }
+            if (data["Brushes"] && data["Brushes"].constructor === Array) {
+                this.brushes = [];
+                for (let item of data["Brushes"])
+                    this.brushes.push(item);
+            }
+            this.enableBrushComputation = data["EnableBrushComputation"];
+        }
+    }
+
+    static fromJS(data: any): RawDataOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new RawDataOperationParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["SortUpRawName"] = this.sortUpRawName;
+        data["SortDownRawName"] = this.sortDownRawName;
+        data["NumRecords"] = this.numRecords;
+        if (this.binningParameters && this.binningParameters.constructor === Array) {
+            data["BinningParameters"] = [];
+            for (let item of this.binningParameters)
+                data["BinningParameters"].push(item.toJSON());
+        }
+        if (this.brushes && this.brushes.constructor === Array) {
+            data["Brushes"] = [];
+            for (let item of this.brushes)
+                data["Brushes"].push(item);
+        }
+        data["EnableBrushComputation"] = this.enableBrushComputation;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IRawDataOperationParameters extends IDistOperationParameters {
+    sortUpRawName?: string | undefined;
+    sortDownRawName?: string | undefined;
+    numRecords?: number | undefined;
+    binningParameters?: BinningParameters[] | undefined;
+    brushes?: string[] | undefined;
+    enableBrushComputation?: boolean | undefined;
+}
+
+export class RecommenderOperationParameters extends DataOperationParameters implements IRecommenderOperationParameters {
+    target?: HistogramOperationParameters | undefined;
+    budget?: number | undefined;
+    modelId?: ModelId | undefined;
+    includeAttributeParameters?: AttributeParameters[] | undefined;
+    excludeAttributeParameters?: AttributeParameters[] | undefined;
+    riskControlType?: RiskControlType | undefined;
+
+    constructor(data?: IRecommenderOperationParameters) {
+        super(data);
+        this._discriminator = "RecommenderOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.target = data["Target"] ? HistogramOperationParameters.fromJS(data["Target"]) : <any>undefined;
+            this.budget = data["Budget"];
+            this.modelId = data["ModelId"] ? ModelId.fromJS(data["ModelId"]) : <any>undefined;
+            if (data["IncludeAttributeParameters"] && data["IncludeAttributeParameters"].constructor === Array) {
+                this.includeAttributeParameters = [];
+                for (let item of data["IncludeAttributeParameters"])
+                    this.includeAttributeParameters.push(AttributeParameters.fromJS(item));
+            }
+            if (data["ExcludeAttributeParameters"] && data["ExcludeAttributeParameters"].constructor === Array) {
+                this.excludeAttributeParameters = [];
+                for (let item of data["ExcludeAttributeParameters"])
+                    this.excludeAttributeParameters.push(AttributeParameters.fromJS(item));
+            }
+            this.riskControlType = data["RiskControlType"];
+        }
+    }
+
+    static fromJS(data: any): RecommenderOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new RecommenderOperationParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Target"] = this.target ? this.target.toJSON() : <any>undefined;
+        data["Budget"] = this.budget;
+        data["ModelId"] = this.modelId ? this.modelId.toJSON() : <any>undefined;
+        if (this.includeAttributeParameters && this.includeAttributeParameters.constructor === Array) {
+            data["IncludeAttributeParameters"] = [];
+            for (let item of this.includeAttributeParameters)
+                data["IncludeAttributeParameters"].push(item.toJSON());
+        }
+        if (this.excludeAttributeParameters && this.excludeAttributeParameters.constructor === Array) {
+            data["ExcludeAttributeParameters"] = [];
+            for (let item of this.excludeAttributeParameters)
+                data["ExcludeAttributeParameters"].push(item.toJSON());
+        }
+        data["RiskControlType"] = this.riskControlType;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IRecommenderOperationParameters extends IDataOperationParameters {
+    target?: HistogramOperationParameters | undefined;
+    budget?: number | undefined;
+    modelId?: ModelId | undefined;
+    includeAttributeParameters?: AttributeParameters[] | undefined;
+    excludeAttributeParameters?: AttributeParameters[] | undefined;
+    riskControlType?: RiskControlType | undefined;
+}
+
+export class ModelId implements IModelId {
+    value?: string | undefined;
+
+    constructor(data?: IModelId) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.value = data["Value"];
+        }
+    }
+
+    static fromJS(data: any): ModelId {
+        data = typeof data === 'object' ? data : {};
+        let result = new ModelId();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Value"] = this.value;
+        return data;
+    }
+}
+
+export interface IModelId {
+    value?: string | undefined;
+}
+
+export enum RiskControlType {
+    PCER = 0,
+    Bonferroni = 1,
+    AdaBonferroni = 2,
+    HolmBonferroni = 3,
+    BHFDR = 4,
+    SeqFDR = 5,
+    AlphaFDR = 6,
+    BestFootForward = 7,
+    BetaFarsighted = 8,
+    BetaFarsightedWithSupport = 9,
+    GammaFixed = 10,
+    DeltaHopeful = 11,
+    EpsilonHybrid = 12,
+    EpsilonHybridWithoutSupport = 13,
+    PsiSupport = 14,
+    ZetaDynamic = 15,
+    Unknown = 16,
+}
+
+export abstract class TestDistOperationParameters extends DistOperationParameters implements ITestDistOperationParameters {
+    attributeParameters?: AttributeParameters[] | undefined;
+
+    constructor(data?: ITestDistOperationParameters) {
+        super(data);
+        this._discriminator = "TestDistOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["AttributeParameters"] && data["AttributeParameters"].constructor === Array) {
+                this.attributeParameters = [];
+                for (let item of data["AttributeParameters"])
+                    this.attributeParameters.push(AttributeParameters.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): TestDistOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "CDFOperationParameters") {
+            let result = new CDFOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "EmpiricalDistOperationParameters") {
+            let result = new EmpiricalDistOperationParameters();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'TestDistOperationParameters' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.attributeParameters && this.attributeParameters.constructor === Array) {
+            data["AttributeParameters"] = [];
+            for (let item of this.attributeParameters)
+                data["AttributeParameters"].push(item.toJSON());
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ITestDistOperationParameters extends IDistOperationParameters {
+    attributeParameters?: AttributeParameters[] | undefined;
+}
+
+export class CDFOperationParameters extends TestDistOperationParameters implements ICDFOperationParameters {
+
+    constructor(data?: ICDFOperationParameters) {
+        super(data);
+        this._discriminator = "CDFOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): CDFOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new CDFOperationParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ICDFOperationParameters extends ITestDistOperationParameters {
+}
+
+export abstract class HypothesisTestParameters extends OperationParameters implements IHypothesisTestParameters {
+    childOperationParameters?: OperationParameters[] | undefined;
+    isCachable?: boolean | undefined;
+
+    constructor(data?: IHypothesisTestParameters) {
+        super(data);
+        this._discriminator = "HypothesisTestParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["ChildOperationParameters"] && data["ChildOperationParameters"].constructor === Array) {
+                this.childOperationParameters = [];
+                for (let item of data["ChildOperationParameters"])
+                    this.childOperationParameters.push(OperationParameters.fromJS(item));
+            }
+            this.isCachable = data["IsCachable"];
+        }
+    }
+
+    static fromJS(data: any): HypothesisTestParameters {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "ChiSquaredTestOperationParameters") {
+            let result = new ChiSquaredTestOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "CorrelationTestOperationParameters") {
+            let result = new CorrelationTestOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "KSTestOperationParameters") {
+            let result = new KSTestOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "RootMeanSquareTestOperationParameters") {
+            let result = new RootMeanSquareTestOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "TTestOperationParameters") {
+            let result = new TTestOperationParameters();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'HypothesisTestParameters' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.childOperationParameters && this.childOperationParameters.constructor === Array) {
+            data["ChildOperationParameters"] = [];
+            for (let item of this.childOperationParameters)
+                data["ChildOperationParameters"].push(item.toJSON());
+        }
+        data["IsCachable"] = this.isCachable;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IHypothesisTestParameters extends IOperationParameters {
+    childOperationParameters?: OperationParameters[] | undefined;
+    isCachable?: boolean | undefined;
+}
+
+export class ChiSquaredTestOperationParameters extends HypothesisTestParameters implements IChiSquaredTestOperationParameters {
+
+    constructor(data?: IChiSquaredTestOperationParameters) {
+        super(data);
+        this._discriminator = "ChiSquaredTestOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): ChiSquaredTestOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new ChiSquaredTestOperationParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IChiSquaredTestOperationParameters extends IHypothesisTestParameters {
+}
+
+export class CorrelationTestOperationParameters extends HypothesisTestParameters implements ICorrelationTestOperationParameters {
+
+    constructor(data?: ICorrelationTestOperationParameters) {
+        super(data);
+        this._discriminator = "CorrelationTestOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): CorrelationTestOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new CorrelationTestOperationParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ICorrelationTestOperationParameters extends IHypothesisTestParameters {
+}
+
+export class SubmitProblemParameters implements ISubmitProblemParameters {
+    id?: string | undefined;
+
+    constructor(data?: ISubmitProblemParameters) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.id = data["Id"];
+        }
+    }
+
+    static fromJS(data: any): SubmitProblemParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new SubmitProblemParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Id"] = this.id;
+        return data;
+    }
+}
+
+export interface ISubmitProblemParameters {
+    id?: string | undefined;
+}
+
+export class SpecifyProblemParameters implements ISpecifyProblemParameters {
+    id?: string | undefined;
+    userComment?: string | undefined;
+    optimizerOperationParameters?: OptimizerOperationParameters | undefined;
+
+    constructor(data?: ISpecifyProblemParameters) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.id = data["Id"];
+            this.userComment = data["UserComment"];
+            this.optimizerOperationParameters = data["OptimizerOperationParameters"] ? OptimizerOperationParameters.fromJS(data["OptimizerOperationParameters"]) : <any>undefined;
+        }
+    }
+
+    static fromJS(data: any): SpecifyProblemParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new SpecifyProblemParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Id"] = this.id;
+        data["UserComment"] = this.userComment;
+        data["OptimizerOperationParameters"] = this.optimizerOperationParameters ? this.optimizerOperationParameters.toJSON() : <any>undefined;
+        return data;
+    }
+}
+
+export interface ISpecifyProblemParameters {
+    id?: string | undefined;
+    userComment?: string | undefined;
+    optimizerOperationParameters?: OptimizerOperationParameters | undefined;
+}
+
+export class EmpiricalDistOperationParameters extends TestDistOperationParameters implements IEmpiricalDistOperationParameters {
+    keepSamples?: boolean | undefined;
+
+    constructor(data?: IEmpiricalDistOperationParameters) {
+        super(data);
+        this._discriminator = "EmpiricalDistOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.keepSamples = data["KeepSamples"];
+        }
+    }
+
+    static fromJS(data: any): EmpiricalDistOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new EmpiricalDistOperationParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["KeepSamples"] = this.keepSamples;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IEmpiricalDistOperationParameters extends ITestDistOperationParameters {
+    keepSamples?: boolean | undefined;
+}
+
+export class KSTestOperationParameters extends HypothesisTestParameters implements IKSTestOperationParameters {
+
+    constructor(data?: IKSTestOperationParameters) {
+        super(data);
+        this._discriminator = "KSTestOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): KSTestOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new KSTestOperationParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IKSTestOperationParameters extends IHypothesisTestParameters {
+}
+
+export abstract class ModelOperationParameters extends OperationParameters implements IModelOperationParameters {
+
+    constructor(data?: IModelOperationParameters) {
+        super(data);
+        this._discriminator = "ModelOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): ModelOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "NewModelOperationParameters") {
+            let result = new NewModelOperationParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "AddComparisonParameters") {
+            let result = new AddComparisonParameters();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "GetModelStateParameters") {
+            let result = new GetModelStateParameters();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'ModelOperationParameters' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IModelOperationParameters extends IOperationParameters {
+}
+
+export class NewModelOperationParameters extends ModelOperationParameters implements INewModelOperationParameters {
+    riskControlTypes?: RiskControlType[] | undefined;
+    alpha?: number | undefined;
+    alphaInvestParameter?: AlphaInvestParameter | undefined;
+
+    constructor(data?: INewModelOperationParameters) {
+        super(data);
+        this._discriminator = "NewModelOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["RiskControlTypes"] && data["RiskControlTypes"].constructor === Array) {
+                this.riskControlTypes = [];
+                for (let item of data["RiskControlTypes"])
+                    this.riskControlTypes.push(item);
+            }
+            this.alpha = data["Alpha"];
+            this.alphaInvestParameter = data["AlphaInvestParameter"] ? AlphaInvestParameter.fromJS(data["AlphaInvestParameter"]) : <any>undefined;
+        }
+    }
+
+    static fromJS(data: any): NewModelOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new NewModelOperationParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.riskControlTypes && this.riskControlTypes.constructor === Array) {
+            data["RiskControlTypes"] = [];
+            for (let item of this.riskControlTypes)
+                data["RiskControlTypes"].push(item);
+        }
+        data["Alpha"] = this.alpha;
+        data["AlphaInvestParameter"] = this.alphaInvestParameter ? this.alphaInvestParameter.toJSON() : <any>undefined;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface INewModelOperationParameters extends IModelOperationParameters {
+    riskControlTypes?: RiskControlType[] | undefined;
+    alpha?: number | undefined;
+    alphaInvestParameter?: AlphaInvestParameter | undefined;
+}
+
+export class AlphaInvestParameter extends UniqueJson implements IAlphaInvestParameter {
+    beta?: number | undefined;
+    gamma?: number | undefined;
+    delta?: number | undefined;
+    epsilon?: number | undefined;
+    windowSize?: number | undefined;
+    psi?: number | undefined;
+
+    constructor(data?: IAlphaInvestParameter) {
+        super(data);
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.beta = data["Beta"];
+            this.gamma = data["Gamma"];
+            this.delta = data["Delta"];
+            this.epsilon = data["Epsilon"];
+            this.windowSize = data["WindowSize"];
+            this.psi = data["Psi"];
+        }
+    }
+
+    static fromJS(data: any): AlphaInvestParameter {
+        data = typeof data === 'object' ? data : {};
+        let result = new AlphaInvestParameter();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Beta"] = this.beta;
+        data["Gamma"] = this.gamma;
+        data["Delta"] = this.delta;
+        data["Epsilon"] = this.epsilon;
+        data["WindowSize"] = this.windowSize;
+        data["Psi"] = this.psi;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IAlphaInvestParameter extends IUniqueJson {
+    beta?: number | undefined;
+    gamma?: number | undefined;
+    delta?: number | undefined;
+    epsilon?: number | undefined;
+    windowSize?: number | undefined;
+    psi?: number | undefined;
+}
+
+export class RootMeanSquareTestOperationParameters extends HypothesisTestParameters implements IRootMeanSquareTestOperationParameters {
+    seeded?: boolean | undefined;
+    pValueConvergenceThreshold?: number | undefined;
+    maxSimulationBatchCount?: number | undefined;
+    perBatchSimulationCount?: number | undefined;
+
+    constructor(data?: IRootMeanSquareTestOperationParameters) {
+        super(data);
+        this._discriminator = "RootMeanSquareTestOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.seeded = data["Seeded"];
+            this.pValueConvergenceThreshold = data["PValueConvergenceThreshold"];
+            this.maxSimulationBatchCount = data["MaxSimulationBatchCount"];
+            this.perBatchSimulationCount = data["PerBatchSimulationCount"];
+        }
+    }
+
+    static fromJS(data: any): RootMeanSquareTestOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new RootMeanSquareTestOperationParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Seeded"] = this.seeded;
+        data["PValueConvergenceThreshold"] = this.pValueConvergenceThreshold;
+        data["MaxSimulationBatchCount"] = this.maxSimulationBatchCount;
+        data["PerBatchSimulationCount"] = this.perBatchSimulationCount;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IRootMeanSquareTestOperationParameters extends IHypothesisTestParameters {
+    seeded?: boolean | undefined;
+    pValueConvergenceThreshold?: number | undefined;
+    maxSimulationBatchCount?: number | undefined;
+    perBatchSimulationCount?: number | undefined;
+}
+
+export class TTestOperationParameters extends HypothesisTestParameters implements ITTestOperationParameters {
+
+    constructor(data?: ITTestOperationParameters) {
+        super(data);
+        this._discriminator = "TTestOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): TTestOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new TTestOperationParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ITTestOperationParameters extends IHypothesisTestParameters {
+}
+
+export enum EffectSize {
+    Small = 1,
+    Meduim = 2,
+    Large = 4,
+}
+
+export abstract class Result extends UniqueJson implements IResult {
+    progress?: number | undefined;
+
+    protected _discriminator: string;
+
+    constructor(data?: IResult) {
+        super(data);
+        this._discriminator = "Result";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.progress = data["Progress"];
+        }
+    }
+
+    static fromJS(data: any): Result {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "ErrorResult") {
+            let result = new ErrorResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "HistogramResult") {
+            let result = new HistogramResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "DistResult") {
+            throw new Error("The abstract class 'DistResult' cannot be instantiated.");
+        }
+        if (data["discriminator"] === "ModelWealthResult") {
+            let result = new ModelWealthResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "HypothesisTestResult") {
+            throw new Error("The abstract class 'HypothesisTestResult' cannot be instantiated.");
+        }
+        if (data["discriminator"] === "ModelOperationResult") {
+            throw new Error("The abstract class 'ModelOperationResult' cannot be instantiated.");
+        }
+        if (data["discriminator"] === "RecommenderResult") {
+            let result = new RecommenderResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "Decision") {
+            let result = new Decision();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "OptimizerResult") {
+            let result = new OptimizerResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "ExampleResult") {
+            let result = new ExampleResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "NewModelOperationResult") {
+            let result = new NewModelOperationResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "AddComparisonResult") {
+            let result = new AddComparisonResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "GetModelStateResult") {
+            let result = new GetModelStateResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "FeatureImportanceResult") {
+            let result = new FeatureImportanceResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "RawDataResult") {
+            let result = new RawDataResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "SampleResult") {
+            let result = new SampleResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "CDFResult") {
+            let result = new CDFResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "ChiSquaredTestResult") {
+            let result = new ChiSquaredTestResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "CorrelationTestResult") {
+            let result = new CorrelationTestResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "EmpiricalDistResult") {
+            let result = new EmpiricalDistResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "KSTestResult") {
+            let result = new KSTestResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "RootMeanSquareTestResult") {
+            let result = new RootMeanSquareTestResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "TTestResult") {
+            let result = new TTestResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "FrequentItemsetResult") {
+            let result = new FrequentItemsetResult();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'Result' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["discriminator"] = this._discriminator;
+        data["Progress"] = this.progress;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IResult extends IUniqueJson {
+    progress?: number | undefined;
+}
+
+export class ErrorResult extends Result implements IErrorResult {
+    message?: string | undefined;
+
+    constructor(data?: IErrorResult) {
+        super(data);
+        this._discriminator = "ErrorResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.message = data["Message"];
+        }
+    }
+
+    static fromJS(data: any): ErrorResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new ErrorResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Message"] = this.message;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IErrorResult extends IResult {
+    message?: string | undefined;
+}
+
+export abstract class DistResult extends Result implements IDistResult {
+    sampleSize?: number | undefined;
+    populationSize?: number | undefined;
+
+    constructor(data?: IDistResult) {
+        super(data);
+        this._discriminator = "DistResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.sampleSize = data["SampleSize"];
+            this.populationSize = data["PopulationSize"];
+        }
+    }
+
+    static fromJS(data: any): DistResult {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "HistogramResult") {
+            let result = new HistogramResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "RawDataResult") {
+            let result = new RawDataResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "SampleResult") {
+            let result = new SampleResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "CDFResult") {
+            let result = new CDFResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "EmpiricalDistResult") {
+            let result = new EmpiricalDistResult();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'DistResult' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["SampleSize"] = this.sampleSize;
+        data["PopulationSize"] = this.populationSize;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IDistResult extends IResult {
+    sampleSize?: number | undefined;
+    populationSize?: number | undefined;
+}
+
+export class HistogramResult extends DistResult implements IHistogramResult {
+    aggregateResults?: AggregateResult[][] | undefined;
+    isEmpty?: boolean | undefined;
+    brushes?: Brush[] | undefined;
+    binRanges?: BinRange[] | undefined;
+    aggregateParameters?: AggregateParameters[] | undefined;
+    nullValueCount?: number | undefined;
+    bins?: { [key: string]: Bin; } | undefined;
+
+    constructor(data?: IHistogramResult) {
+        super(data);
+        this._discriminator = "HistogramResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["AggregateResults"] && data["AggregateResults"].constructor === Array) {
+                this.aggregateResults = [];
+                for (let item of data["AggregateResults"])
+                    this.aggregateResults.push(item);
+            }
+            this.isEmpty = data["IsEmpty"];
+            if (data["Brushes"] && data["Brushes"].constructor === Array) {
+                this.brushes = [];
+                for (let item of data["Brushes"])
+                    this.brushes.push(Brush.fromJS(item));
+            }
+            if (data["BinRanges"] && data["BinRanges"].constructor === Array) {
+                this.binRanges = [];
+                for (let item of data["BinRanges"])
+                    this.binRanges.push(BinRange.fromJS(item));
+            }
+            if (data["AggregateParameters"] && data["AggregateParameters"].constructor === Array) {
+                this.aggregateParameters = [];
+                for (let item of data["AggregateParameters"])
+                    this.aggregateParameters.push(AggregateParameters.fromJS(item));
+            }
+            this.nullValueCount = data["NullValueCount"];
+            if (data["Bins"]) {
+                this.bins = {};
+                for (let key in data["Bins"]) {
+                    if (data["Bins"].hasOwnProperty(key))
+                        this.bins[key] = data["Bins"][key] ? Bin.fromJS(data["Bins"][key]) : new Bin();
+                }
+            }
+        }
+    }
+
+    static fromJS(data: any): HistogramResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new HistogramResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.aggregateResults && this.aggregateResults.constructor === Array) {
+            data["AggregateResults"] = [];
+            for (let item of this.aggregateResults)
+                data["AggregateResults"].push(item);
+        }
+        data["IsEmpty"] = this.isEmpty;
+        if (this.brushes && this.brushes.constructor === Array) {
+            data["Brushes"] = [];
+            for (let item of this.brushes)
+                data["Brushes"].push(item.toJSON());
+        }
+        if (this.binRanges && this.binRanges.constructor === Array) {
+            data["BinRanges"] = [];
+            for (let item of this.binRanges)
+                data["BinRanges"].push(item.toJSON());
+        }
+        if (this.aggregateParameters && this.aggregateParameters.constructor === Array) {
+            data["AggregateParameters"] = [];
+            for (let item of this.aggregateParameters)
+                data["AggregateParameters"].push(item.toJSON());
+        }
+        data["NullValueCount"] = this.nullValueCount;
+        if (this.bins) {
+            data["Bins"] = {};
+            for (let key in this.bins) {
+                if (this.bins.hasOwnProperty(key))
+                    data["Bins"][key] = this.bins[key];
+            }
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IHistogramResult extends IDistResult {
+    aggregateResults?: AggregateResult[][] | undefined;
+    isEmpty?: boolean | undefined;
+    brushes?: Brush[] | undefined;
+    binRanges?: BinRange[] | undefined;
+    aggregateParameters?: AggregateParameters[] | undefined;
+    nullValueCount?: number | undefined;
+    bins?: { [key: string]: Bin; } | undefined;
+}
+
+export abstract class AggregateResult implements IAggregateResult {
+    hasResult?: boolean | undefined;
+    n?: number | undefined;
+
+    protected _discriminator: string;
+
+    constructor(data?: IAggregateResult) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+        this._discriminator = "AggregateResult";
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.hasResult = data["HasResult"];
+            this.n = data["N"];
+        }
+    }
+
+    static fromJS(data: any): AggregateResult | undefined {
+        if (data === null || data === undefined) {
+            return undefined;
+        }
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "MarginAggregateResult") {
+            let result = new MarginAggregateResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "DoubleValueAggregateResult") {
+            let result = new DoubleValueAggregateResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "PointsAggregateResult") {
+            let result = new PointsAggregateResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "SumEstimationAggregateResult") {
+            let result = new SumEstimationAggregateResult();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'AggregateResult' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["discriminator"] = this._discriminator;
+        data["HasResult"] = this.hasResult;
+        data["N"] = this.n;
+        return data;
+    }
+}
+
+export interface IAggregateResult {
+    hasResult?: boolean | undefined;
+    n?: number | undefined;
+}
+
+export class MarginAggregateResult extends AggregateResult implements IMarginAggregateResult {
+    margin?: number | undefined;
+    absolutMargin?: number | undefined;
+    sumOfSquare?: number | undefined;
+    sampleStandardDeviation?: number | undefined;
+    mean?: number | undefined;
+    ex?: number | undefined;
+    ex2?: number | undefined;
+    variance?: number | undefined;
+
+    constructor(data?: IMarginAggregateResult) {
+        super(data);
+        this._discriminator = "MarginAggregateResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.margin = data["Margin"];
+            this.absolutMargin = data["AbsolutMargin"];
+            this.sumOfSquare = data["SumOfSquare"];
+            this.sampleStandardDeviation = data["SampleStandardDeviation"];
+            this.mean = data["Mean"];
+            this.ex = data["Ex"];
+            this.ex2 = data["Ex2"];
+            this.variance = data["Variance"];
+        }
+    }
+
+    static fromJS(data: any): MarginAggregateResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new MarginAggregateResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Margin"] = this.margin;
+        data["AbsolutMargin"] = this.absolutMargin;
+        data["SumOfSquare"] = this.sumOfSquare;
+        data["SampleStandardDeviation"] = this.sampleStandardDeviation;
+        data["Mean"] = this.mean;
+        data["Ex"] = this.ex;
+        data["Ex2"] = this.ex2;
+        data["Variance"] = this.variance;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IMarginAggregateResult extends IAggregateResult {
+    margin?: number | undefined;
+    absolutMargin?: number | undefined;
+    sumOfSquare?: number | undefined;
+    sampleStandardDeviation?: number | undefined;
+    mean?: number | undefined;
+    ex?: number | undefined;
+    ex2?: number | undefined;
+    variance?: number | undefined;
+}
+
+export class DoubleValueAggregateResult extends AggregateResult implements IDoubleValueAggregateResult {
+    result?: number | undefined;
+    temporaryResult?: number | undefined;
+
+    constructor(data?: IDoubleValueAggregateResult) {
+        super(data);
+        this._discriminator = "DoubleValueAggregateResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.result = data["Result"];
+            this.temporaryResult = data["TemporaryResult"];
+        }
+    }
+
+    static fromJS(data: any): DoubleValueAggregateResult {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "SumEstimationAggregateResult") {
+            let result = new SumEstimationAggregateResult();
+            result.init(data);
+            return result;
+        }
+        let result = new DoubleValueAggregateResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Result"] = this.result;
+        data["TemporaryResult"] = this.temporaryResult;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IDoubleValueAggregateResult extends IAggregateResult {
+    result?: number | undefined;
+    temporaryResult?: number | undefined;
+}
+
+export class PointsAggregateResult extends AggregateResult implements IPointsAggregateResult {
+    points?: Point[] | undefined;
+
+    constructor(data?: IPointsAggregateResult) {
+        super(data);
+        this._discriminator = "PointsAggregateResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["Points"] && data["Points"].constructor === Array) {
+                this.points = [];
+                for (let item of data["Points"])
+                    this.points.push(Point.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): PointsAggregateResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new PointsAggregateResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.points && this.points.constructor === Array) {
+            data["Points"] = [];
+            for (let item of this.points)
+                data["Points"].push(item.toJSON());
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IPointsAggregateResult extends IAggregateResult {
+    points?: Point[] | undefined;
+}
+
+export class Point implements IPoint {
+    x?: number | undefined;
+    y?: number | undefined;
+
+    constructor(data?: IPoint) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.x = data["X"];
+            this.y = data["Y"];
+        }
+    }
+
+    static fromJS(data: any): Point {
+        data = typeof data === 'object' ? data : {};
+        let result = new Point();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["X"] = this.x;
+        data["Y"] = this.y;
+        return data;
+    }
+}
+
+export interface IPoint {
+    x?: number | undefined;
+    y?: number | undefined;
+}
+
+export class SumEstimationAggregateResult extends DoubleValueAggregateResult implements ISumEstimationAggregateResult {
+    sum?: number | undefined;
+    sumEstimation?: number | undefined;
+
+    constructor(data?: ISumEstimationAggregateResult) {
+        super(data);
+        this._discriminator = "SumEstimationAggregateResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.sum = data["Sum"];
+            this.sumEstimation = data["SumEstimation"];
+        }
+    }
+
+    static fromJS(data: any): SumEstimationAggregateResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new SumEstimationAggregateResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Sum"] = this.sum;
+        data["SumEstimation"] = this.sumEstimation;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ISumEstimationAggregateResult extends IDoubleValueAggregateResult {
+    sum?: number | undefined;
+    sumEstimation?: number | undefined;
+}
+
+export class Brush implements IBrush {
+    brushIndex?: number | undefined;
+    brushEnum?: BrushEnum | undefined;
+
+    constructor(data?: IBrush) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.brushIndex = data["BrushIndex"];
+            this.brushEnum = data["BrushEnum"];
+        }
+    }
+
+    static fromJS(data: any): Brush {
+        data = typeof data === 'object' ? data : {};
+        let result = new Brush();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["BrushIndex"] = this.brushIndex;
+        data["BrushEnum"] = this.brushEnum;
+        return data;
+    }
+}
+
+export interface IBrush {
+    brushIndex?: number | undefined;
+    brushEnum?: BrushEnum | undefined;
+}
+
+export enum BrushEnum {
+    Overlap = 0,
+    Rest = 1,
+    All = 2,
+    UserSpecified = 3,
+}
+
+export abstract class BinRange implements IBinRange {
+    minValue?: number | undefined;
+    maxValue?: number | undefined;
+    targetBinNumber?: number | undefined;
+
+    protected _discriminator: string;
+
+    constructor(data?: IBinRange) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+        this._discriminator = "BinRange";
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.minValue = data["MinValue"];
+            this.maxValue = data["MaxValue"];
+            this.targetBinNumber = data["TargetBinNumber"];
+        }
+    }
+
+    static fromJS(data: any): BinRange {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "NominalBinRange") {
+            let result = new NominalBinRange();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "QuantitativeBinRange") {
+            let result = new QuantitativeBinRange();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "AggregateBinRange") {
+            let result = new AggregateBinRange();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "AlphabeticBinRange") {
+            let result = new AlphabeticBinRange();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "DateTimeBinRange") {
+            let result = new DateTimeBinRange();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'BinRange' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["discriminator"] = this._discriminator;
+        data["MinValue"] = this.minValue;
+        data["MaxValue"] = this.maxValue;
+        data["TargetBinNumber"] = this.targetBinNumber;
+        return data;
+    }
+}
+
+export interface IBinRange {
+    minValue?: number | undefined;
+    maxValue?: number | undefined;
+    targetBinNumber?: number | undefined;
+}
+
+export class NominalBinRange extends BinRange implements INominalBinRange {
+    labelsValue?: { [key: string]: number; } | undefined;
+    valuesLabel?: { [key: string]: string; } | undefined;
+
+    constructor(data?: INominalBinRange) {
+        super(data);
+        this._discriminator = "NominalBinRange";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["LabelsValue"]) {
+                this.labelsValue = {};
+                for (let key in data["LabelsValue"]) {
+                    if (data["LabelsValue"].hasOwnProperty(key))
+                        this.labelsValue[key] = data["LabelsValue"][key];
+                }
+            }
+            if (data["ValuesLabel"]) {
+                this.valuesLabel = {};
+                for (let key in data["ValuesLabel"]) {
+                    if (data["ValuesLabel"].hasOwnProperty(key))
+                        this.valuesLabel[key] = data["ValuesLabel"][key];
+                }
+            }
+        }
+    }
+
+    static fromJS(data: any): NominalBinRange {
+        data = typeof data === 'object' ? data : {};
+        let result = new NominalBinRange();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.labelsValue) {
+            data["LabelsValue"] = {};
+            for (let key in this.labelsValue) {
+                if (this.labelsValue.hasOwnProperty(key))
+                    data["LabelsValue"][key] = this.labelsValue[key];
+            }
+        }
+        if (this.valuesLabel) {
+            data["ValuesLabel"] = {};
+            for (let key in this.valuesLabel) {
+                if (this.valuesLabel.hasOwnProperty(key))
+                    data["ValuesLabel"][key] = this.valuesLabel[key];
+            }
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface INominalBinRange extends IBinRange {
+    labelsValue?: { [key: string]: number; } | undefined;
+    valuesLabel?: { [key: string]: string; } | undefined;
+}
+
+export class QuantitativeBinRange extends BinRange implements IQuantitativeBinRange {
+    isIntegerRange?: boolean | undefined;
+    step?: number | undefined;
+
+    constructor(data?: IQuantitativeBinRange) {
+        super(data);
+        this._discriminator = "QuantitativeBinRange";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.isIntegerRange = data["IsIntegerRange"];
+            this.step = data["Step"];
+        }
+    }
+
+    static fromJS(data: any): QuantitativeBinRange {
+        data = typeof data === 'object' ? data : {};
+        let result = new QuantitativeBinRange();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["IsIntegerRange"] = this.isIntegerRange;
+        data["Step"] = this.step;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IQuantitativeBinRange extends IBinRange {
+    isIntegerRange?: boolean | undefined;
+    step?: number | undefined;
+}
+
+export class AggregateBinRange extends BinRange implements IAggregateBinRange {
+
+    constructor(data?: IAggregateBinRange) {
+        super(data);
+        this._discriminator = "AggregateBinRange";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): AggregateBinRange {
+        data = typeof data === 'object' ? data : {};
+        let result = new AggregateBinRange();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IAggregateBinRange extends IBinRange {
+}
+
+export class AlphabeticBinRange extends BinRange implements IAlphabeticBinRange {
+    prefix?: string | undefined;
+    labelsValue?: { [key: string]: number; } | undefined;
+    valuesLabel?: { [key: string]: string; } | undefined;
+
+    constructor(data?: IAlphabeticBinRange) {
+        super(data);
+        this._discriminator = "AlphabeticBinRange";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.prefix = data["Prefix"];
+            if (data["LabelsValue"]) {
+                this.labelsValue = {};
+                for (let key in data["LabelsValue"]) {
+                    if (data["LabelsValue"].hasOwnProperty(key))
+                        this.labelsValue[key] = data["LabelsValue"][key];
+                }
+            }
+            if (data["ValuesLabel"]) {
+                this.valuesLabel = {};
+                for (let key in data["ValuesLabel"]) {
+                    if (data["ValuesLabel"].hasOwnProperty(key))
+                        this.valuesLabel[key] = data["ValuesLabel"][key];
+                }
+            }
+        }
+    }
+
+    static fromJS(data: any): AlphabeticBinRange {
+        data = typeof data === 'object' ? data : {};
+        let result = new AlphabeticBinRange();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Prefix"] = this.prefix;
+        if (this.labelsValue) {
+            data["LabelsValue"] = {};
+            for (let key in this.labelsValue) {
+                if (this.labelsValue.hasOwnProperty(key))
+                    data["LabelsValue"][key] = this.labelsValue[key];
+            }
+        }
+        if (this.valuesLabel) {
+            data["ValuesLabel"] = {};
+            for (let key in this.valuesLabel) {
+                if (this.valuesLabel.hasOwnProperty(key))
+                    data["ValuesLabel"][key] = this.valuesLabel[key];
+            }
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IAlphabeticBinRange extends IBinRange {
+    prefix?: string | undefined;
+    labelsValue?: { [key: string]: number; } | undefined;
+    valuesLabel?: { [key: string]: string; } | undefined;
+}
+
+export class DateTimeBinRange extends BinRange implements IDateTimeBinRange {
+    step?: DateTimeStep | undefined;
+
+    constructor(data?: IDateTimeBinRange) {
+        super(data);
+        this._discriminator = "DateTimeBinRange";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.step = data["Step"] ? DateTimeStep.fromJS(data["Step"]) : <any>undefined;
+        }
+    }
+
+    static fromJS(data: any): DateTimeBinRange {
+        data = typeof data === 'object' ? data : {};
+        let result = new DateTimeBinRange();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Step"] = this.step ? this.step.toJSON() : <any>undefined;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IDateTimeBinRange extends IBinRange {
+    step?: DateTimeStep | undefined;
+}
+
+export class DateTimeStep implements IDateTimeStep {
+    dateTimeStepGranularity?: DateTimeStepGranularity | undefined;
+    dateTimeStepValue?: number | undefined;
+    dateTimeStepMaxValue?: number | undefined;
+
+    constructor(data?: IDateTimeStep) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.dateTimeStepGranularity = data["DateTimeStepGranularity"];
+            this.dateTimeStepValue = data["DateTimeStepValue"];
+            this.dateTimeStepMaxValue = data["DateTimeStepMaxValue"];
+        }
+    }
+
+    static fromJS(data: any): DateTimeStep {
+        data = typeof data === 'object' ? data : {};
+        let result = new DateTimeStep();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["DateTimeStepGranularity"] = this.dateTimeStepGranularity;
+        data["DateTimeStepValue"] = this.dateTimeStepValue;
+        data["DateTimeStepMaxValue"] = this.dateTimeStepMaxValue;
+        return data;
+    }
+}
+
+export interface IDateTimeStep {
+    dateTimeStepGranularity?: DateTimeStepGranularity | undefined;
+    dateTimeStepValue?: number | undefined;
+    dateTimeStepMaxValue?: number | undefined;
+}
+
+export enum DateTimeStepGranularity {
+    Second = 0,
+    Minute = 1,
+    Hour = 2,
+    Day = 3,
+    Month = 4,
+    Year = 5,
+}
+
+export class Bin implements IBin {
+    aggregateResults?: AggregateResult[] | undefined;
+    count?: number | undefined;
+    binIndex?: BinIndex | undefined;
+    spans?: Span[] | undefined;
+    xSize?: number | undefined;
+    ySize?: number | undefined;
+
+    constructor(data?: IBin) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            if (data["AggregateResults"] && data["AggregateResults"].constructor === Array) {
+                this.aggregateResults = [];
+                for (let item of data["AggregateResults"]) {
+                    let fromJs = AggregateResult.fromJS(item);
+                    if (fromJs)
+                        this.aggregateResults.push(fromJs);
+                }
+            }
+            this.count = data["Count"];
+            this.binIndex = data["BinIndex"] ? BinIndex.fromJS(data["BinIndex"]) : <any>undefined;
+            if (data["Spans"] && data["Spans"].constructor === Array) {
+                this.spans = [];
+                for (let item of data["Spans"])
+                    this.spans.push(Span.fromJS(item));
+            }
+            this.xSize = data["XSize"];
+            this.ySize = data["YSize"];
+        }
+    }
+
+    static fromJS(data: any): Bin {
+        data = typeof data === 'object' ? data : {};
+        let result = new Bin();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.aggregateResults && this.aggregateResults.constructor === Array) {
+            data["AggregateResults"] = [];
+            for (let item of this.aggregateResults)
+                data["AggregateResults"].push(item.toJSON());
+        }
+        data["Count"] = this.count;
+        data["BinIndex"] = this.binIndex ? this.binIndex.toJSON() : <any>undefined;
+        if (this.spans && this.spans.constructor === Array) {
+            data["Spans"] = [];
+            for (let item of this.spans)
+                data["Spans"].push(item.toJSON());
+        }
+        data["XSize"] = this.xSize;
+        data["YSize"] = this.ySize;
+        return data;
+    }
+}
+
+export interface IBin {
+    aggregateResults?: AggregateResult[] | undefined;
+    count?: number | undefined;
+    binIndex?: BinIndex | undefined;
+    spans?: Span[] | undefined;
+    xSize?: number | undefined;
+    ySize?: number | undefined;
+}
+
+export class BinIndex implements IBinIndex {
+    indices?: number[] | undefined;
+    flatIndex?: number | undefined;
+
+    constructor(data?: IBinIndex) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            if (data["Indices"] && data["Indices"].constructor === Array) {
+                this.indices = [];
+                for (let item of data["Indices"])
+                    this.indices.push(item);
+            }
+            this.flatIndex = data["FlatIndex"];
+        }
+    }
+
+    static fromJS(data: any): BinIndex {
+        data = typeof data === 'object' ? data : {};
+        let result = new BinIndex();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.indices && this.indices.constructor === Array) {
+            data["Indices"] = [];
+            for (let item of this.indices)
+                data["Indices"].push(item);
+        }
+        data["FlatIndex"] = this.flatIndex;
+        return data;
+    }
+}
+
+export interface IBinIndex {
+    indices?: number[] | undefined;
+    flatIndex?: number | undefined;
+}
+
+export class Span implements ISpan {
+    min?: number | undefined;
+    max?: number | undefined;
+    index?: number | undefined;
+
+    constructor(data?: ISpan) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.min = data["Min"];
+            this.max = data["Max"];
+            this.index = data["Index"];
+        }
+    }
+
+    static fromJS(data: any): Span {
+        data = typeof data === 'object' ? data : {};
+        let result = new Span();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Min"] = this.min;
+        data["Max"] = this.max;
+        data["Index"] = this.index;
+        return data;
+    }
+}
+
+export interface ISpan {
+    min?: number | undefined;
+    max?: number | undefined;
+    index?: number | undefined;
+}
+
+export class ModelWealthResult extends Result implements IModelWealthResult {
+    wealth?: number | undefined;
+    startWealth?: number | undefined;
+
+    constructor(data?: IModelWealthResult) {
+        super(data);
+        this._discriminator = "ModelWealthResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.wealth = data["Wealth"];
+            this.startWealth = data["StartWealth"];
+        }
+    }
+
+    static fromJS(data: any): ModelWealthResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new ModelWealthResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Wealth"] = this.wealth;
+        data["StartWealth"] = this.startWealth;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IModelWealthResult extends IResult {
+    wealth?: number | undefined;
+    startWealth?: number | undefined;
+}
+
+export abstract class HypothesisTestResult extends Result implements IHypothesisTestResult {
+    pValue?: number | undefined;
+    statistic?: number | undefined;
+    support?: number | undefined;
+    sampleSizes?: number[] | undefined;
+    errorMessage?: string | undefined;
+
+    constructor(data?: IHypothesisTestResult) {
+        super(data);
+        this._discriminator = "HypothesisTestResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.pValue = data["PValue"];
+            this.statistic = data["Statistic"];
+            this.support = data["Support"];
+            if (data["SampleSizes"] && data["SampleSizes"].constructor === Array) {
+                this.sampleSizes = [];
+                for (let item of data["SampleSizes"])
+                    this.sampleSizes.push(item);
+            }
+            this.errorMessage = data["ErrorMessage"];
+        }
+    }
+
+    static fromJS(data: any): HypothesisTestResult {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "ChiSquaredTestResult") {
+            let result = new ChiSquaredTestResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "CorrelationTestResult") {
+            let result = new CorrelationTestResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "KSTestResult") {
+            let result = new KSTestResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "RootMeanSquareTestResult") {
+            let result = new RootMeanSquareTestResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "TTestResult") {
+            let result = new TTestResult();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'HypothesisTestResult' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["PValue"] = this.pValue;
+        data["Statistic"] = this.statistic;
+        data["Support"] = this.support;
+        if (this.sampleSizes && this.sampleSizes.constructor === Array) {
+            data["SampleSizes"] = [];
+            for (let item of this.sampleSizes)
+                data["SampleSizes"].push(item);
+        }
+        data["ErrorMessage"] = this.errorMessage;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IHypothesisTestResult extends IResult {
+    pValue?: number | undefined;
+    statistic?: number | undefined;
+    support?: number | undefined;
+    sampleSizes?: number[] | undefined;
+    errorMessage?: string | undefined;
+}
+
+export abstract class ModelOperationResult extends Result implements IModelOperationResult {
+    modelId?: ModelId | undefined;
+
+    constructor(data?: IModelOperationResult) {
+        super(data);
+        this._discriminator = "ModelOperationResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.modelId = data["ModelId"] ? ModelId.fromJS(data["ModelId"]) : <any>undefined;
+        }
+    }
+
+    static fromJS(data: any): ModelOperationResult {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "NewModelOperationResult") {
+            let result = new NewModelOperationResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "AddComparisonResult") {
+            let result = new AddComparisonResult();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "GetModelStateResult") {
+            let result = new GetModelStateResult();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'ModelOperationResult' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["ModelId"] = this.modelId ? this.modelId.toJSON() : <any>undefined;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IModelOperationResult extends IResult {
+    modelId?: ModelId | undefined;
+}
+
+export class RecommenderResult extends Result implements IRecommenderResult {
+    recommendedHistograms?: RecommendedHistogram[] | undefined;
+    totalCount?: number | undefined;
+
+    constructor(data?: IRecommenderResult) {
+        super(data);
+        this._discriminator = "RecommenderResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["RecommendedHistograms"] && data["RecommendedHistograms"].constructor === Array) {
+                this.recommendedHistograms = [];
+                for (let item of data["RecommendedHistograms"])
+                    this.recommendedHistograms.push(RecommendedHistogram.fromJS(item));
+            }
+            this.totalCount = data["TotalCount"];
+        }
+    }
+
+    static fromJS(data: any): RecommenderResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new RecommenderResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.recommendedHistograms && this.recommendedHistograms.constructor === Array) {
+            data["RecommendedHistograms"] = [];
+            for (let item of this.recommendedHistograms)
+                data["RecommendedHistograms"].push(item.toJSON());
+        }
+        data["TotalCount"] = this.totalCount;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IRecommenderResult extends IResult {
+    recommendedHistograms?: RecommendedHistogram[] | undefined;
+    totalCount?: number | undefined;
+}
+
+export class RecommendedHistogram implements IRecommendedHistogram {
+    histogramResult?: HistogramResult | undefined;
+    selectedBinIndices?: BinIndex[] | undefined;
+    selections?: Selection[] | undefined;
+    pValue?: number | undefined;
+    significance?: boolean | undefined;
+    decision?: Decision | undefined;
+    effectSize?: number | undefined;
+    hypothesisTestResult?: HypothesisTestResult | undefined;
+    id?: string | undefined;
+    xAttribute?: Attribute | undefined;
+    yAttribute?: Attribute | undefined;
+
+    constructor(data?: IRecommendedHistogram) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.histogramResult = data["HistogramResult"] ? HistogramResult.fromJS(data["HistogramResult"]) : <any>undefined;
+            if (data["SelectedBinIndices"] && data["SelectedBinIndices"].constructor === Array) {
+                this.selectedBinIndices = [];
+                for (let item of data["SelectedBinIndices"])
+                    this.selectedBinIndices.push(BinIndex.fromJS(item));
+            }
+            if (data["Selections"] && data["Selections"].constructor === Array) {
+                this.selections = [];
+                for (let item of data["Selections"])
+                    this.selections.push(Selection.fromJS(item));
+            }
+            this.pValue = data["PValue"];
+            this.significance = data["Significance"];
+            this.decision = data["Decision"] ? Decision.fromJS(data["Decision"]) : <any>undefined;
+            this.effectSize = data["EffectSize"];
+            this.hypothesisTestResult = data["HypothesisTestResult"] ? HypothesisTestResult.fromJS(data["HypothesisTestResult"]) : <any>undefined;
+            this.id = data["Id"];
+            this.xAttribute = data["XAttribute"] ? Attribute.fromJS(data["XAttribute"]) : <any>undefined;
+            this.yAttribute = data["YAttribute"] ? Attribute.fromJS(data["YAttribute"]) : <any>undefined;
+        }
+    }
+
+    static fromJS(data: any): RecommendedHistogram {
+        data = typeof data === 'object' ? data : {};
+        let result = new RecommendedHistogram();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["HistogramResult"] = this.histogramResult ? this.histogramResult.toJSON() : <any>undefined;
+        if (this.selectedBinIndices && this.selectedBinIndices.constructor === Array) {
+            data["SelectedBinIndices"] = [];
+            for (let item of this.selectedBinIndices)
+                data["SelectedBinIndices"].push(item.toJSON());
+        }
+        if (this.selections && this.selections.constructor === Array) {
+            data["Selections"] = [];
+            for (let item of this.selections)
+                data["Selections"].push(item.toJSON());
+        }
+        data["PValue"] = this.pValue;
+        data["Significance"] = this.significance;
+        data["Decision"] = this.decision ? this.decision.toJSON() : <any>undefined;
+        data["EffectSize"] = this.effectSize;
+        data["HypothesisTestResult"] = this.hypothesisTestResult ? this.hypothesisTestResult.toJSON() : <any>undefined;
+        data["Id"] = this.id;
+        data["XAttribute"] = this.xAttribute ? this.xAttribute.toJSON() : <any>undefined;
+        data["YAttribute"] = this.yAttribute ? this.yAttribute.toJSON() : <any>undefined;
+        return data;
+    }
+}
+
+export interface IRecommendedHistogram {
+    histogramResult?: HistogramResult | undefined;
+    selectedBinIndices?: BinIndex[] | undefined;
+    selections?: Selection[] | undefined;
+    pValue?: number | undefined;
+    significance?: boolean | undefined;
+    decision?: Decision | undefined;
+    effectSize?: number | undefined;
+    hypothesisTestResult?: HypothesisTestResult | undefined;
+    id?: string | undefined;
+    xAttribute?: Attribute | undefined;
+    yAttribute?: Attribute | undefined;
+}
+
+export class Selection implements ISelection {
+    statements?: Statement[] | undefined;
+    filterHistogramOperationReference?: IOperationReference | undefined;
+
+    constructor(data?: ISelection) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            if (data["Statements"] && data["Statements"].constructor === Array) {
+                this.statements = [];
+                for (let item of data["Statements"])
+                    this.statements.push(Statement.fromJS(item));
+            }
+            this.filterHistogramOperationReference = data["FilterHistogramOperationReference"] ? IOperationReference.fromJS(data["FilterHistogramOperationReference"]) : <any>undefined;
+        }
+    }
+
+    static fromJS(data: any): Selection {
+        data = typeof data === 'object' ? data : {};
+        let result = new Selection();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.statements && this.statements.constructor === Array) {
+            data["Statements"] = [];
+            for (let item of this.statements)
+                data["Statements"].push(item.toJSON());
+        }
+        data["FilterHistogramOperationReference"] = this.filterHistogramOperationReference ? this.filterHistogramOperationReference.toJSON() : <any>undefined;
+        return data;
+    }
+}
+
+export interface ISelection {
+    statements?: Statement[] | undefined;
+    filterHistogramOperationReference?: IOperationReference | undefined;
+}
+
+export class Statement implements IStatement {
+    attribute?: Attribute | undefined;
+    predicate?: Predicate | undefined;
+    value?: any | undefined;
+
+    constructor(data?: IStatement) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.attribute = data["Attribute"] ? Attribute.fromJS(data["Attribute"]) : <any>undefined;
+            this.predicate = data["Predicate"];
+            this.value = data["Value"];
+        }
+    }
+
+    static fromJS(data: any): Statement {
+        data = typeof data === 'object' ? data : {};
+        let result = new Statement();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Attribute"] = this.attribute ? this.attribute.toJSON() : <any>undefined;
+        data["Predicate"] = this.predicate;
+        data["Value"] = this.value;
+        return data;
+    }
+}
+
+export interface IStatement {
+    attribute?: Attribute | undefined;
+    predicate?: Predicate | undefined;
+    value?: any | undefined;
+}
+
+export enum Predicate {
+    EQUALS = 0,
+    LIKE = 1,
+    GREATER_THAN = 2,
+    LESS_THAN = 3,
+    GREATER_THAN_EQUAL = 4,
+    LESS_THAN_EQUAL = 5,
+    STARTS_WITH = 6,
+    ENDS_WITH = 7,
+    CONTAINS = 8,
+}
+
+export abstract class IOperationReference implements IIOperationReference {
+    id?: string | undefined;
+
+    protected _discriminator: string;
+
+    constructor(data?: IIOperationReference) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+        this._discriminator = "IOperationReference";
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.id = data["Id"];
+        }
+    }
+
+    static fromJS(data: any): IOperationReference {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "OperationReference") {
+            let result = new OperationReference();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'IOperationReference' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["discriminator"] = this._discriminator;
+        data["Id"] = this.id;
+        return data;
+    }
+}
+
+export interface IIOperationReference {
+    id?: string | undefined;
+}
+
+export class OperationReference extends IOperationReference implements IOperationReference {
+    id?: string | undefined;
+
+    constructor(data?: IOperationReference) {
+        super(data);
+        this._discriminator = "OperationReference";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.id = data["Id"];
+        }
+    }
+
+    static fromJS(data: any): OperationReference {
+        data = typeof data === 'object' ? data : {};
+        let result = new OperationReference();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Id"] = this.id;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IOperationReference extends IIOperationReference {
+    id?: string | undefined;
+}
+
+export class Decision extends Result implements IDecision {
+    comparisonId?: ComparisonId | undefined;
+    riskControlType?: RiskControlType | undefined;
+    significance?: boolean | undefined;
+    pValue?: number | undefined;
+    lhs?: number | undefined;
+    significanceLevel?: number | undefined;
+    sampleSizeEstimate?: number | undefined;
+
+    constructor(data?: IDecision) {
+        super(data);
+        this._discriminator = "Decision";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.comparisonId = data["ComparisonId"] ? ComparisonId.fromJS(data["ComparisonId"]) : <any>undefined;
+            this.riskControlType = data["RiskControlType"];
+            this.significance = data["Significance"];
+            this.pValue = data["PValue"];
+            this.lhs = data["Lhs"];
+            this.significanceLevel = data["SignificanceLevel"];
+            this.sampleSizeEstimate = data["SampleSizeEstimate"];
+        }
+    }
+
+    static fromJS(data: any): Decision {
+        data = typeof data === 'object' ? data : {};
+        let result = new Decision();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["ComparisonId"] = this.comparisonId ? this.comparisonId.toJSON() : <any>undefined;
+        data["RiskControlType"] = this.riskControlType;
+        data["Significance"] = this.significance;
+        data["PValue"] = this.pValue;
+        data["Lhs"] = this.lhs;
+        data["SignificanceLevel"] = this.significanceLevel;
+        data["SampleSizeEstimate"] = this.sampleSizeEstimate;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IDecision extends IResult {
+    comparisonId?: ComparisonId | undefined;
+    riskControlType?: RiskControlType | undefined;
+    significance?: boolean | undefined;
+    pValue?: number | undefined;
+    lhs?: number | undefined;
+    significanceLevel?: number | undefined;
+    sampleSizeEstimate?: number | undefined;
+}
+
+export class ComparisonId implements IComparisonId {
+    value?: string | undefined;
+
+    constructor(data?: IComparisonId) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.value = data["Value"];
+        }
+    }
+
+    static fromJS(data: any): ComparisonId {
+        data = typeof data === 'object' ? data : {};
+        let result = new ComparisonId();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Value"] = this.value;
+        return data;
+    }
+}
+
+export interface IComparisonId {
+    value?: string | undefined;
+}
+
+export class OptimizerResult extends Result implements IOptimizerResult {
+    topKSolutions?: Solution[] | undefined;
+
+    constructor(data?: IOptimizerResult) {
+        super(data);
+        this._discriminator = "OptimizerResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["TopKSolutions"] && data["TopKSolutions"].constructor === Array) {
+                this.topKSolutions = [];
+                for (let item of data["TopKSolutions"])
+                    this.topKSolutions.push(Solution.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): OptimizerResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new OptimizerResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.topKSolutions && this.topKSolutions.constructor === Array) {
+            data["TopKSolutions"] = [];
+            for (let item of this.topKSolutions)
+                data["TopKSolutions"].push(item.toJSON());
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IOptimizerResult extends IResult {
+    topKSolutions?: Solution[] | undefined;
+}
+
+export class Solution implements ISolution {
+    solutionId?: string | undefined;
+    stepDescriptions?: StepDescription[] | undefined;
+    pipelineDescription?: PipelineDescription | undefined;
+    score?: Score | undefined;
+    naiveScore?: Score | undefined;
+
+    constructor(data?: ISolution) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.solutionId = data["SolutionId"];
+            if (data["StepDescriptions"] && data["StepDescriptions"].constructor === Array) {
+                this.stepDescriptions = [];
+                for (let item of data["StepDescriptions"])
+                    this.stepDescriptions.push(StepDescription.fromJS(item));
+            }
+            this.pipelineDescription = data["PipelineDescription"] ? PipelineDescription.fromJS(data["PipelineDescription"]) : <any>undefined;
+            this.score = data["Score"] ? Score.fromJS(data["Score"]) : <any>undefined;
+            this.naiveScore = data["NaiveScore"] ? Score.fromJS(data["NaiveScore"]) : <any>undefined;
+        }
+    }
+
+    static fromJS(data: any): Solution {
+        data = typeof data === 'object' ? data : {};
+        let result = new Solution();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["SolutionId"] = this.solutionId;
+        if (this.stepDescriptions && this.stepDescriptions.constructor === Array) {
+            data["StepDescriptions"] = [];
+            for (let item of this.stepDescriptions)
+                data["StepDescriptions"].push(item.toJSON());
+        }
+        data["PipelineDescription"] = this.pipelineDescription ? this.pipelineDescription.toJSON() : <any>undefined;
+        data["Score"] = this.score ? this.score.toJSON() : <any>undefined;
+        data["NaiveScore"] = this.naiveScore ? this.naiveScore.toJSON() : <any>undefined;
+        return data;
+    }
+}
+
+export interface ISolution {
+    solutionId?: string | undefined;
+    stepDescriptions?: StepDescription[] | undefined;
+    pipelineDescription?: PipelineDescription | undefined;
+    score?: Score | undefined;
+    naiveScore?: Score | undefined;
+}
+
+export class StepDescription implements IStepDescription {
+
+    protected _discriminator: string;
+
+    constructor(data?: IStepDescription) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+        this._discriminator = "StepDescription";
+    }
+
+    init(data?: any) {
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): StepDescription {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "SubpipelineStepDescription") {
+            let result = new SubpipelineStepDescription();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "PrimitiveStepDescription") {
+            let result = new PrimitiveStepDescription();
+            result.init(data);
+            return result;
+        }
+        let result = new StepDescription();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["discriminator"] = this._discriminator;
+        return data;
+    }
+}
+
+export interface IStepDescription {
+}
+
+export class SubpipelineStepDescription extends StepDescription implements ISubpipelineStepDescription {
+    steps?: StepDescription[] | undefined;
+
+    constructor(data?: ISubpipelineStepDescription) {
+        super(data);
+        this._discriminator = "SubpipelineStepDescription";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["Steps"] && data["Steps"].constructor === Array) {
+                this.steps = [];
+                for (let item of data["Steps"])
+                    this.steps.push(StepDescription.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): SubpipelineStepDescription {
+        data = typeof data === 'object' ? data : {};
+        let result = new SubpipelineStepDescription();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.steps && this.steps.constructor === Array) {
+            data["Steps"] = [];
+            for (let item of this.steps)
+                data["Steps"].push(item.toJSON());
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ISubpipelineStepDescription extends IStepDescription {
+    steps?: StepDescription[] | undefined;
+}
+
+export class PipelineDescription implements IPipelineDescription {
+    id?: string | undefined;
+    name?: string | undefined;
+    description?: string | undefined;
+    inputs?: PipelineDescriptionInput[] | undefined;
+    outputs?: PipelineDescriptionOutput[] | undefined;
+    steps?: PipelineDescriptionStep[] | undefined;
+
+    constructor(data?: IPipelineDescription) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.id = data["Id"];
+            this.name = data["Name"];
+            this.description = data["Description"];
+            if (data["Inputs"] && data["Inputs"].constructor === Array) {
+                this.inputs = [];
+                for (let item of data["Inputs"])
+                    this.inputs.push(PipelineDescriptionInput.fromJS(item));
+            }
+            if (data["Outputs"] && data["Outputs"].constructor === Array) {
+                this.outputs = [];
+                for (let item of data["Outputs"])
+                    this.outputs.push(PipelineDescriptionOutput.fromJS(item));
+            }
+            if (data["Steps"] && data["Steps"].constructor === Array) {
+                this.steps = [];
+                for (let item of data["Steps"])
+                    this.steps.push(PipelineDescriptionStep.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): PipelineDescription {
+        data = typeof data === 'object' ? data : {};
+        let result = new PipelineDescription();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Id"] = this.id;
+        data["Name"] = this.name;
+        data["Description"] = this.description;
+        if (this.inputs && this.inputs.constructor === Array) {
+            data["Inputs"] = [];
+            for (let item of this.inputs)
+                data["Inputs"].push(item.toJSON());
+        }
+        if (this.outputs && this.outputs.constructor === Array) {
+            data["Outputs"] = [];
+            for (let item of this.outputs)
+                data["Outputs"].push(item.toJSON());
+        }
+        if (this.steps && this.steps.constructor === Array) {
+            data["Steps"] = [];
+            for (let item of this.steps)
+                data["Steps"].push(item.toJSON());
+        }
+        return data;
+    }
+}
+
+export interface IPipelineDescription {
+    id?: string | undefined;
+    name?: string | undefined;
+    description?: string | undefined;
+    inputs?: PipelineDescriptionInput[] | undefined;
+    outputs?: PipelineDescriptionOutput[] | undefined;
+    steps?: PipelineDescriptionStep[] | undefined;
+}
+
+export class PipelineDescriptionInput implements IPipelineDescriptionInput {
+    name?: string | undefined;
+
+    constructor(data?: IPipelineDescriptionInput) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.name = data["Name"];
+        }
+    }
+
+    static fromJS(data: any): PipelineDescriptionInput {
+        data = typeof data === 'object' ? data : {};
+        let result = new PipelineDescriptionInput();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Name"] = this.name;
+        return data;
+    }
+}
+
+export interface IPipelineDescriptionInput {
+    name?: string | undefined;
+}
+
+export class PipelineDescriptionOutput implements IPipelineDescriptionOutput {
+    name?: string | undefined;
+    data?: string | undefined;
+
+    constructor(data?: IPipelineDescriptionOutput) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.name = data["Name"];
+            this.data = data["Data"];
+        }
+    }
+
+    static fromJS(data: any): PipelineDescriptionOutput {
+        data = typeof data === 'object' ? data : {};
+        let result = new PipelineDescriptionOutput();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Name"] = this.name;
+        data["Data"] = this.data;
+        return data;
+    }
+}
+
+export interface IPipelineDescriptionOutput {
+    name?: string | undefined;
+    data?: string | undefined;
+}
+
+export class PipelineDescriptionStep implements IPipelineDescriptionStep {
+    outputs?: StepOutput[] | undefined;
+
+    protected _discriminator: string;
+
+    constructor(data?: IPipelineDescriptionStep) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+        this._discriminator = "PipelineDescriptionStep";
+    }
+
+    init(data?: any) {
+        if (data) {
+            if (data["Outputs"] && data["Outputs"].constructor === Array) {
+                this.outputs = [];
+                for (let item of data["Outputs"])
+                    this.outputs.push(StepOutput.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): PipelineDescriptionStep {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "PlaceholderPipelineDescriptionStep") {
+            let result = new PlaceholderPipelineDescriptionStep();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "SubpipelinePipelineDescriptionStep") {
+            let result = new SubpipelinePipelineDescriptionStep();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "PrimitivePipelineDescriptionStep") {
+            let result = new PrimitivePipelineDescriptionStep();
+            result.init(data);
+            return result;
+        }
+        let result = new PipelineDescriptionStep();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["discriminator"] = this._discriminator;
+        if (this.outputs && this.outputs.constructor === Array) {
+            data["Outputs"] = [];
+            for (let item of this.outputs)
+                data["Outputs"].push(item.toJSON());
+        }
+        return data;
+    }
+}
+
+export interface IPipelineDescriptionStep {
+    outputs?: StepOutput[] | undefined;
+}
+
+export class StepOutput implements IStepOutput {
+    id?: string | undefined;
+
+    constructor(data?: IStepOutput) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.id = data["Id"];
+        }
+    }
+
+    static fromJS(data: any): StepOutput {
+        data = typeof data === 'object' ? data : {};
+        let result = new StepOutput();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Id"] = this.id;
+        return data;
+    }
+}
+
+export interface IStepOutput {
+    id?: string | undefined;
+}
+
+export class PlaceholderPipelineDescriptionStep extends PipelineDescriptionStep implements IPlaceholderPipelineDescriptionStep {
+    inputs?: StepInput[] | undefined;
+
+    constructor(data?: IPlaceholderPipelineDescriptionStep) {
+        super(data);
+        this._discriminator = "PlaceholderPipelineDescriptionStep";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["Inputs"] && data["Inputs"].constructor === Array) {
+                this.inputs = [];
+                for (let item of data["Inputs"])
+                    this.inputs.push(StepInput.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): PlaceholderPipelineDescriptionStep {
+        data = typeof data === 'object' ? data : {};
+        let result = new PlaceholderPipelineDescriptionStep();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.inputs && this.inputs.constructor === Array) {
+            data["Inputs"] = [];
+            for (let item of this.inputs)
+                data["Inputs"].push(item.toJSON());
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IPlaceholderPipelineDescriptionStep extends IPipelineDescriptionStep {
+    inputs?: StepInput[] | undefined;
+}
+
+export class StepInput implements IStepInput {
+    data?: string | undefined;
+
+    constructor(data?: IStepInput) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.data = data["Data"];
+        }
+    }
+
+    static fromJS(data: any): StepInput {
+        data = typeof data === 'object' ? data : {};
+        let result = new StepInput();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Data"] = this.data;
+        return data;
+    }
+}
+
+export interface IStepInput {
+    data?: string | undefined;
+}
+
+export class SubpipelinePipelineDescriptionStep extends PipelineDescriptionStep implements ISubpipelinePipelineDescriptionStep {
+    pipelineDescription?: PipelineDescription | undefined;
+    inputs?: StepInput[] | undefined;
+
+    constructor(data?: ISubpipelinePipelineDescriptionStep) {
+        super(data);
+        this._discriminator = "SubpipelinePipelineDescriptionStep";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.pipelineDescription = data["PipelineDescription"] ? PipelineDescription.fromJS(data["PipelineDescription"]) : <any>undefined;
+            if (data["Inputs"] && data["Inputs"].constructor === Array) {
+                this.inputs = [];
+                for (let item of data["Inputs"])
+                    this.inputs.push(StepInput.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): SubpipelinePipelineDescriptionStep {
+        data = typeof data === 'object' ? data : {};
+        let result = new SubpipelinePipelineDescriptionStep();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["PipelineDescription"] = this.pipelineDescription ? this.pipelineDescription.toJSON() : <any>undefined;
+        if (this.inputs && this.inputs.constructor === Array) {
+            data["Inputs"] = [];
+            for (let item of this.inputs)
+                data["Inputs"].push(item.toJSON());
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ISubpipelinePipelineDescriptionStep extends IPipelineDescriptionStep {
+    pipelineDescription?: PipelineDescription | undefined;
+    inputs?: StepInput[] | undefined;
+}
+
+export class PrimitivePipelineDescriptionStep extends PipelineDescriptionStep implements IPrimitivePipelineDescriptionStep {
+    primitive?: Primitive | undefined;
+    arguments?: { [key: string]: PrimitiveStepArgument; } | undefined;
+    hyperparams?: { [key: string]: PrimitiveStepHyperparameter; } | undefined;
+
+    constructor(data?: IPrimitivePipelineDescriptionStep) {
+        super(data);
+        this._discriminator = "PrimitivePipelineDescriptionStep";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.primitive = data["Primitive"] ? Primitive.fromJS(data["Primitive"]) : <any>undefined;
+            if (data["Arguments"]) {
+                this.arguments = {};
+                for (let key in data["Arguments"]) {
+                    if (data["Arguments"].hasOwnProperty(key))
+                        this.arguments[key] = data["Arguments"][key] ? PrimitiveStepArgument.fromJS(data["Arguments"][key]) : new PrimitiveStepArgument();
+                }
+            }
+            if (data["Hyperparams"]) {
+                this.hyperparams = {};
+                for (let key in data["Hyperparams"]) {
+                    if (data["Hyperparams"].hasOwnProperty(key))
+                        this.hyperparams[key] = data["Hyperparams"][key] ? PrimitiveStepHyperparameter.fromJS(data["Hyperparams"][key]) : new PrimitiveStepHyperparameter();
+                }
+            }
+        }
+    }
+
+    static fromJS(data: any): PrimitivePipelineDescriptionStep {
+        data = typeof data === 'object' ? data : {};
+        let result = new PrimitivePipelineDescriptionStep();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Primitive"] = this.primitive ? this.primitive.toJSON() : <any>undefined;
+        if (this.arguments) {
+            data["Arguments"] = {};
+            for (let key in this.arguments) {
+                if (this.arguments.hasOwnProperty(key))
+                    data["Arguments"][key] = this.arguments[key];
+            }
+        }
+        if (this.hyperparams) {
+            data["Hyperparams"] = {};
+            for (let key in this.hyperparams) {
+                if (this.hyperparams.hasOwnProperty(key))
+                    data["Hyperparams"][key] = this.hyperparams[key];
+            }
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IPrimitivePipelineDescriptionStep extends IPipelineDescriptionStep {
+    primitive?: Primitive | undefined;
+    arguments?: { [key: string]: PrimitiveStepArgument; } | undefined;
+    hyperparams?: { [key: string]: PrimitiveStepHyperparameter; } | undefined;
+}
+
+export class Primitive implements IPrimitive {
+    id?: string | undefined;
+    version?: string | undefined;
+    pythonPath?: string | undefined;
+    name?: string | undefined;
+    digest?: string | undefined;
+
+    constructor(data?: IPrimitive) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.id = data["Id"];
+            this.version = data["Version"];
+            this.pythonPath = data["PythonPath"];
+            this.name = data["Name"];
+            this.digest = data["Digest"];
+        }
+    }
+
+    static fromJS(data: any): Primitive {
+        data = typeof data === 'object' ? data : {};
+        let result = new Primitive();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Id"] = this.id;
+        data["Version"] = this.version;
+        data["PythonPath"] = this.pythonPath;
+        data["Name"] = this.name;
+        data["Digest"] = this.digest;
+        return data;
+    }
+}
+
+export interface IPrimitive {
+    id?: string | undefined;
+    version?: string | undefined;
+    pythonPath?: string | undefined;
+    name?: string | undefined;
+    digest?: string | undefined;
+}
+
+export class PrimitiveStepHyperparameter implements IPrimitiveStepHyperparameter {
+
+    protected _discriminator: string;
+
+    constructor(data?: IPrimitiveStepHyperparameter) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+        this._discriminator = "PrimitiveStepHyperparameter";
+    }
+
+    init(data?: any) {
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): PrimitiveStepHyperparameter {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "PrimitiveStepArgument") {
+            let result = new PrimitiveStepArgument();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "DataArguments") {
+            let result = new DataArguments();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "PrimitiveArgument") {
+            let result = new PrimitiveArgument();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "PrimitiveArguments") {
+            let result = new PrimitiveArguments();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "ValueArgument") {
+            let result = new ValueArgument();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "ContainerArgument") {
+            let result = new ContainerArgument();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "DataArgument") {
+            let result = new DataArgument();
+            result.init(data);
+            return result;
+        }
+        let result = new PrimitiveStepHyperparameter();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["discriminator"] = this._discriminator;
+        return data;
+    }
+}
+
+export interface IPrimitiveStepHyperparameter {
+}
+
+export class PrimitiveStepArgument extends PrimitiveStepHyperparameter implements IPrimitiveStepArgument {
+
+    protected _discriminator: string;
+
+    constructor(data?: IPrimitiveStepArgument) {
+        super(data);
+        this._discriminator = "PrimitiveStepArgument";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): PrimitiveStepArgument {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "ContainerArgument") {
+            let result = new ContainerArgument();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "DataArgument") {
+            let result = new DataArgument();
+            result.init(data);
+            return result;
+        }
+        let result = new PrimitiveStepArgument();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["discriminator"] = this._discriminator;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IPrimitiveStepArgument extends IPrimitiveStepHyperparameter {
+}
+
+export class DataArguments extends PrimitiveStepHyperparameter implements IDataArguments {
+    data?: string[] | undefined;
+
+    constructor(data?: IDataArguments) {
+        super(data);
+        this._discriminator = "DataArguments";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["Data"] && data["Data"].constructor === Array) {
+                this.data = [];
+                for (let item of data["Data"])
+                    this.data.push(item);
+            }
+        }
+    }
+
+    static fromJS(data: any): DataArguments {
+        data = typeof data === 'object' ? data : {};
+        let result = new DataArguments();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.data && this.data.constructor === Array) {
+            data["Data"] = [];
+            for (let item of this.data)
+                data["Data"].push(item);
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IDataArguments extends IPrimitiveStepHyperparameter {
+    data?: string[] | undefined;
+}
+
+export class PrimitiveArgument extends PrimitiveStepHyperparameter implements IPrimitiveArgument {
+    data?: number | undefined;
+
+    constructor(data?: IPrimitiveArgument) {
+        super(data);
+        this._discriminator = "PrimitiveArgument";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.data = data["Data"];
+        }
+    }
+
+    static fromJS(data: any): PrimitiveArgument {
+        data = typeof data === 'object' ? data : {};
+        let result = new PrimitiveArgument();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Data"] = this.data;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IPrimitiveArgument extends IPrimitiveStepHyperparameter {
+    data?: number | undefined;
+}
+
+export class PrimitiveArguments extends PrimitiveStepHyperparameter implements IPrimitiveArguments {
+    data?: number[] | undefined;
+
+    constructor(data?: IPrimitiveArguments) {
+        super(data);
+        this._discriminator = "PrimitiveArguments";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["Data"] && data["Data"].constructor === Array) {
+                this.data = [];
+                for (let item of data["Data"])
+                    this.data.push(item);
+            }
+        }
+    }
+
+    static fromJS(data: any): PrimitiveArguments {
+        data = typeof data === 'object' ? data : {};
+        let result = new PrimitiveArguments();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.data && this.data.constructor === Array) {
+            data["Data"] = [];
+            for (let item of this.data)
+                data["Data"].push(item);
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IPrimitiveArguments extends IPrimitiveStepHyperparameter {
+    data?: number[] | undefined;
+}
+
+export class ValueArgument extends PrimitiveStepHyperparameter implements IValueArgument {
+    data?: Value | undefined;
+
+    constructor(data?: IValueArgument) {
+        super(data);
+        this._discriminator = "ValueArgument";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.data = data["Data"] ? Value.fromJS(data["Data"]) : <any>undefined;
+        }
+    }
+
+    static fromJS(data: any): ValueArgument {
+        data = typeof data === 'object' ? data : {};
+        let result = new ValueArgument();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Data"] = this.data ? this.data.toJSON() : <any>undefined;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IValueArgument extends IPrimitiveStepHyperparameter {
+    data?: Value | undefined;
+}
+
+export abstract class Value implements IValue {
+
+    protected _discriminator: string;
+
+    constructor(data?: IValue) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+        this._discriminator = "Value";
+    }
+
+    init(data?: any) {
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): Value {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "ErrorValue") {
+            let result = new ErrorValue();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "DoubleValue") {
+            let result = new DoubleValue();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "LongValue") {
+            let result = new LongValue();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "BoolValue") {
+            let result = new BoolValue();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "StringValue") {
+            let result = new StringValue();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "DatasetUriValue") {
+            let result = new DatasetUriValue();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "CsvUriValue") {
+            let result = new CsvUriValue();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "PickleUriValue") {
+            let result = new PickleUriValue();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "PickleBlobValue") {
+            let result = new PickleBlobValue();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "PlasmaIdValue") {
+            let result = new PlasmaIdValue();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "BytesValue") {
+            let result = new BytesValue();
+            result.init(data);
+            return result;
+        }
+        if (data["discriminator"] === "ListValue") {
+            let result = new ListValue();
+            result.init(data);
+            return result;
+        }
+        throw new Error("The abstract class 'Value' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["discriminator"] = this._discriminator;
+        return data;
+    }
+}
+
+export interface IValue {
+}
+
+export class ErrorValue extends Value implements IErrorValue {
+    message?: string | undefined;
+
+    constructor(data?: IErrorValue) {
+        super(data);
+        this._discriminator = "ErrorValue";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.message = data["Message"];
+        }
+    }
+
+    static fromJS(data: any): ErrorValue {
+        data = typeof data === 'object' ? data : {};
+        let result = new ErrorValue();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Message"] = this.message;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IErrorValue extends IValue {
+    message?: string | undefined;
+}
+
+export class DoubleValue extends Value implements IDoubleValue {
+    value?: number | undefined;
+
+    constructor(data?: IDoubleValue) {
+        super(data);
+        this._discriminator = "DoubleValue";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.value = data["Value"];
+        }
+    }
+
+    static fromJS(data: any): DoubleValue {
+        data = typeof data === 'object' ? data : {};
+        let result = new DoubleValue();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Value"] = this.value;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IDoubleValue extends IValue {
+    value?: number | undefined;
+}
+
+export class LongValue extends Value implements ILongValue {
+    value?: number | undefined;
+
+    constructor(data?: ILongValue) {
+        super(data);
+        this._discriminator = "LongValue";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.value = data["Value"];
+        }
+    }
+
+    static fromJS(data: any): LongValue {
+        data = typeof data === 'object' ? data : {};
+        let result = new LongValue();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Value"] = this.value;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ILongValue extends IValue {
+    value?: number | undefined;
+}
+
+export class BoolValue extends Value implements IBoolValue {
+    value?: boolean | undefined;
+
+    constructor(data?: IBoolValue) {
+        super(data);
+        this._discriminator = "BoolValue";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.value = data["Value"];
+        }
+    }
+
+    static fromJS(data: any): BoolValue {
+        data = typeof data === 'object' ? data : {};
+        let result = new BoolValue();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Value"] = this.value;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IBoolValue extends IValue {
+    value?: boolean | undefined;
+}
+
+export class StringValue extends Value implements IStringValue {
+    value?: string | undefined;
+
+    constructor(data?: IStringValue) {
+        super(data);
+        this._discriminator = "StringValue";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.value = data["Value"];
+        }
+    }
+
+    static fromJS(data: any): StringValue {
+        data = typeof data === 'object' ? data : {};
+        let result = new StringValue();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Value"] = this.value;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IStringValue extends IValue {
+    value?: string | undefined;
+}
+
+export class DatasetUriValue extends Value implements IDatasetUriValue {
+    value?: string | undefined;
+
+    constructor(data?: IDatasetUriValue) {
+        super(data);
+        this._discriminator = "DatasetUriValue";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.value = data["Value"];
+        }
+    }
+
+    static fromJS(data: any): DatasetUriValue {
+        data = typeof data === 'object' ? data : {};
+        let result = new DatasetUriValue();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Value"] = this.value;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IDatasetUriValue extends IValue {
+    value?: string | undefined;
+}
+
+export class CsvUriValue extends Value implements ICsvUriValue {
+    value?: string | undefined;
+
+    constructor(data?: ICsvUriValue) {
+        super(data);
+        this._discriminator = "CsvUriValue";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.value = data["Value"];
+        }
+    }
+
+    static fromJS(data: any): CsvUriValue {
+        data = typeof data === 'object' ? data : {};
+        let result = new CsvUriValue();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Value"] = this.value;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ICsvUriValue extends IValue {
+    value?: string | undefined;
+}
+
+export class PickleUriValue extends Value implements IPickleUriValue {
+    value?: string | undefined;
+
+    constructor(data?: IPickleUriValue) {
+        super(data);
+        this._discriminator = "PickleUriValue";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.value = data["Value"];
+        }
+    }
+
+    static fromJS(data: any): PickleUriValue {
+        data = typeof data === 'object' ? data : {};
+        let result = new PickleUriValue();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Value"] = this.value;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IPickleUriValue extends IValue {
+    value?: string | undefined;
+}
+
+export class PickleBlobValue extends Value implements IPickleBlobValue {
+    value?: string | undefined;
+
+    constructor(data?: IPickleBlobValue) {
+        super(data);
+        this._discriminator = "PickleBlobValue";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.value = data["Value"];
+        }
+    }
+
+    static fromJS(data: any): PickleBlobValue {
+        data = typeof data === 'object' ? data : {};
+        let result = new PickleBlobValue();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Value"] = this.value;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IPickleBlobValue extends IValue {
+    value?: string | undefined;
+}
+
+export class PlasmaIdValue extends Value implements IPlasmaIdValue {
+    value?: string | undefined;
+
+    constructor(data?: IPlasmaIdValue) {
+        super(data);
+        this._discriminator = "PlasmaIdValue";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.value = data["Value"];
+        }
+    }
+
+    static fromJS(data: any): PlasmaIdValue {
+        data = typeof data === 'object' ? data : {};
+        let result = new PlasmaIdValue();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Value"] = this.value;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IPlasmaIdValue extends IValue {
+    value?: string | undefined;
+}
+
+export class BytesValue extends Value implements IBytesValue {
+    value?: string | undefined;
+
+    constructor(data?: IBytesValue) {
+        super(data);
+        this._discriminator = "BytesValue";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.value = data["Value"];
+        }
+    }
+
+    static fromJS(data: any): BytesValue {
+        data = typeof data === 'object' ? data : {};
+        let result = new BytesValue();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Value"] = this.value;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IBytesValue extends IValue {
+    value?: string | undefined;
+}
+
+export class ContainerArgument extends PrimitiveStepArgument implements IContainerArgument {
+    data?: string | undefined;
+
+    constructor(data?: IContainerArgument) {
+        super(data);
+        this._discriminator = "ContainerArgument";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.data = data["Data"];
+        }
+    }
+
+    static fromJS(data: any): ContainerArgument {
+        data = typeof data === 'object' ? data : {};
+        let result = new ContainerArgument();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Data"] = this.data;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IContainerArgument extends IPrimitiveStepArgument {
+    data?: string | undefined;
+}
+
+export class Score implements IScore {
+    metricType?: MetricType | undefined;
+    value?: number | undefined;
+
+    constructor(data?: IScore) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.metricType = data["MetricType"];
+            this.value = data["Value"];
+        }
+    }
+
+    static fromJS(data: any): Score {
+        data = typeof data === 'object' ? data : {};
+        let result = new Score();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["MetricType"] = this.metricType;
+        data["Value"] = this.value;
+        return data;
+    }
+}
+
+export interface IScore {
+    metricType?: MetricType | undefined;
+    value?: number | undefined;
+}
+
+export class ExampleResult extends Result implements IExampleResult {
+    resultValues?: { [key: string]: string; } | undefined;
+    message?: string | undefined;
+
+    constructor(data?: IExampleResult) {
+        super(data);
+        this._discriminator = "ExampleResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["ResultValues"]) {
+                this.resultValues = {};
+                for (let key in data["ResultValues"]) {
+                    if (data["ResultValues"].hasOwnProperty(key))
+                        this.resultValues[key] = data["ResultValues"][key];
+                }
+            }
+            this.message = data["Message"];
+        }
+    }
+
+    static fromJS(data: any): ExampleResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new ExampleResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.resultValues) {
+            data["ResultValues"] = {};
+            for (let key in this.resultValues) {
+                if (this.resultValues.hasOwnProperty(key))
+                    data["ResultValues"][key] = this.resultValues[key];
+            }
+        }
+        data["Message"] = this.message;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IExampleResult extends IResult {
+    resultValues?: { [key: string]: string; } | undefined;
+    message?: string | undefined;
+}
+
+export class NewModelOperationResult extends ModelOperationResult implements INewModelOperationResult {
+
+    constructor(data?: INewModelOperationResult) {
+        super(data);
+        this._discriminator = "NewModelOperationResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): NewModelOperationResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new NewModelOperationResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface INewModelOperationResult extends IModelOperationResult {
+}
+
+export class AddComparisonResult extends ModelOperationResult implements IAddComparisonResult {
+    comparisonId?: ComparisonId | undefined;
+
+    constructor(data?: IAddComparisonResult) {
+        super(data);
+        this._discriminator = "AddComparisonResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.comparisonId = data["ComparisonId"] ? ComparisonId.fromJS(data["ComparisonId"]) : <any>undefined;
+        }
+    }
+
+    static fromJS(data: any): AddComparisonResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new AddComparisonResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["ComparisonId"] = this.comparisonId ? this.comparisonId.toJSON() : <any>undefined;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IAddComparisonResult extends IModelOperationResult {
+    comparisonId?: ComparisonId | undefined;
+}
+
+export class GetModelStateResult extends ModelOperationResult implements IGetModelStateResult {
+    decisions?: Decision[] | undefined;
+    startingWealth?: number | undefined;
+    currentWealth?: number | undefined;
+
+    constructor(data?: IGetModelStateResult) {
+        super(data);
+        this._discriminator = "GetModelStateResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["Decisions"] && data["Decisions"].constructor === Array) {
+                this.decisions = [];
+                for (let item of data["Decisions"])
+                    this.decisions.push(Decision.fromJS(item));
+            }
+            this.startingWealth = data["StartingWealth"];
+            this.currentWealth = data["CurrentWealth"];
+        }
+    }
+
+    static fromJS(data: any): GetModelStateResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new GetModelStateResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.decisions && this.decisions.constructor === Array) {
+            data["Decisions"] = [];
+            for (let item of this.decisions)
+                data["Decisions"].push(item.toJSON());
+        }
+        data["StartingWealth"] = this.startingWealth;
+        data["CurrentWealth"] = this.currentWealth;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IGetModelStateResult extends IModelOperationResult {
+    decisions?: Decision[] | undefined;
+    startingWealth?: number | undefined;
+    currentWealth?: number | undefined;
+}
+
+export class AggregateKey implements IAggregateKey {
+    aggregateParameterIndex?: number | undefined;
+    brushIndex?: number | undefined;
+
+    constructor(data?: IAggregateKey) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.aggregateParameterIndex = data["AggregateParameterIndex"];
+            this.brushIndex = data["BrushIndex"];
+        }
+    }
+
+    static fromJS(data: any): AggregateKey {
+        data = typeof data === 'object' ? data : {};
+        let result = new AggregateKey();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["AggregateParameterIndex"] = this.aggregateParameterIndex;
+        data["BrushIndex"] = this.brushIndex;
+        return data;
+    }
+}
+
+export interface IAggregateKey {
+    aggregateParameterIndex?: number | undefined;
+    brushIndex?: number | undefined;
+}
+
+export abstract class IResult implements IIResult {
+
+    constructor(data?: IIResult) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): IResult {
+        data = typeof data === 'object' ? data : {};
+        throw new Error("The abstract class 'IResult' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        return data;
+    }
+}
+
+export interface IIResult {
+}
+
+export class DataArgument extends PrimitiveStepArgument implements IDataArgument {
+    data?: string | undefined;
+
+    constructor(data?: IDataArgument) {
+        super(data);
+        this._discriminator = "DataArgument";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.data = data["Data"];
+        }
+    }
+
+    static fromJS(data: any): DataArgument {
+        data = typeof data === 'object' ? data : {};
+        let result = new DataArgument();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Data"] = this.data;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IDataArgument extends IPrimitiveStepArgument {
+    data?: string | undefined;
+}
+
+export class ListValue extends Value implements IListValue {
+    items?: Value[] | undefined;
+
+    constructor(data?: IListValue) {
+        super(data);
+        this._discriminator = "ListValue";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["Items"] && data["Items"].constructor === Array) {
+                this.items = [];
+                for (let item of data["Items"])
+                    this.items.push(Value.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): ListValue {
+        data = typeof data === 'object' ? data : {};
+        let result = new ListValue();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.items && this.items.constructor === Array) {
+            data["Items"] = [];
+            for (let item of this.items)
+                data["Items"].push(item.toJSON());
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IListValue extends IValue {
+    items?: Value[] | undefined;
+}
+
+export class Metrics implements IMetrics {
+    averageAccuracy?: number | undefined;
+    averageRSquared?: number | undefined;
+    f1Macro?: number | undefined;
+
+    constructor(data?: IMetrics) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.averageAccuracy = data["AverageAccuracy"];
+            this.averageRSquared = data["AverageRSquared"];
+            this.f1Macro = data["F1Macro"];
+        }
+    }
+
+    static fromJS(data: any): Metrics {
+        data = typeof data === 'object' ? data : {};
+        let result = new Metrics();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["AverageAccuracy"] = this.averageAccuracy;
+        data["AverageRSquared"] = this.averageRSquared;
+        data["F1Macro"] = this.f1Macro;
+        return data;
+    }
+}
+
+export interface IMetrics {
+    averageAccuracy?: number | undefined;
+    averageRSquared?: number | undefined;
+    f1Macro?: number | undefined;
+}
+
+export class FeatureImportanceOperationParameters extends DistOperationParameters implements IFeatureImportanceOperationParameters {
+    solutionId?: string | undefined;
+
+    constructor(data?: IFeatureImportanceOperationParameters) {
+        super(data);
+        this._discriminator = "FeatureImportanceOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.solutionId = data["SolutionId"];
+        }
+    }
+
+    static fromJS(data: any): FeatureImportanceOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new FeatureImportanceOperationParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["SolutionId"] = this.solutionId;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IFeatureImportanceOperationParameters extends IDistOperationParameters {
+    solutionId?: string | undefined;
+}
+
+export class FeatureImportanceResult extends Result implements IFeatureImportanceResult {
+    featureImportances?: { [key: string]: TupleOfDoubleAndDouble; } | undefined;
+
+    constructor(data?: IFeatureImportanceResult) {
+        super(data);
+        this._discriminator = "FeatureImportanceResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["FeatureImportances"]) {
+                this.featureImportances = {};
+                for (let key in data["FeatureImportances"]) {
+                    if (data["FeatureImportances"].hasOwnProperty(key))
+                        this.featureImportances[key] = data["FeatureImportances"][key] ? TupleOfDoubleAndDouble.fromJS(data["FeatureImportances"][key]) : new TupleOfDoubleAndDouble();
+                }
+            }
+        }
+    }
+
+    static fromJS(data: any): FeatureImportanceResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new FeatureImportanceResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.featureImportances) {
+            data["FeatureImportances"] = {};
+            for (let key in this.featureImportances) {
+                if (this.featureImportances.hasOwnProperty(key))
+                    data["FeatureImportances"][key] = this.featureImportances[key];
+            }
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IFeatureImportanceResult extends IResult {
+    featureImportances?: { [key: string]: TupleOfDoubleAndDouble; } | undefined;
+}
+
+export class TupleOfDoubleAndDouble implements ITupleOfDoubleAndDouble {
+    item1?: number | undefined;
+    item2?: number | undefined;
+
+    constructor(data?: ITupleOfDoubleAndDouble) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.item1 = data["Item1"];
+            this.item2 = data["Item2"];
+        }
+    }
+
+    static fromJS(data: any): TupleOfDoubleAndDouble {
+        data = typeof data === 'object' ? data : {};
+        let result = new TupleOfDoubleAndDouble();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Item1"] = this.item1;
+        data["Item2"] = this.item2;
+        return data;
+    }
+}
+
+export interface ITupleOfDoubleAndDouble {
+    item1?: number | undefined;
+    item2?: number | undefined;
+}
+
+export class PrimitiveStepDescription extends StepDescription implements IPrimitiveStepDescription {
+    hyperparams?: { [key: string]: Value; } | undefined;
+
+    constructor(data?: IPrimitiveStepDescription) {
+        super(data);
+        this._discriminator = "PrimitiveStepDescription";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["Hyperparams"]) {
+                this.hyperparams = {};
+                for (let key in data["Hyperparams"]) {
+                    if (data["Hyperparams"].hasOwnProperty(key))
+                        this.hyperparams[key] = data["Hyperparams"][key] ? Value.fromJS(data["Hyperparams"][key]) : <any>undefined;
+                }
+            }
+        }
+    }
+
+    static fromJS(data: any): PrimitiveStepDescription {
+        data = typeof data === 'object' ? data : {};
+        let result = new PrimitiveStepDescription();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.hyperparams) {
+            data["Hyperparams"] = {};
+            for (let key in this.hyperparams) {
+                if (this.hyperparams.hasOwnProperty(key))
+                    data["Hyperparams"][key] = this.hyperparams[key];
+            }
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IPrimitiveStepDescription extends IStepDescription {
+    hyperparams?: { [key: string]: Value; } | undefined;
+}
+
+export enum ValueType {
+    VALUE_TYPE_UNDEFINED = 0,
+    RAW = 1,
+    DATASET_URI = 2,
+    CSV_URI = 3,
+    PICKLE_URI = 4,
+    PICKLE_BLOB = 5,
+    PLASMA_ID = 6,
+}
+
+export class DatamartSearchParameters implements IDatamartSearchParameters {
+    adapterName?: string | undefined;
+    queryJson?: string | undefined;
+
+    constructor(data?: IDatamartSearchParameters) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.adapterName = data["AdapterName"];
+            this.queryJson = data["QueryJson"];
+        }
+    }
+
+    static fromJS(data: any): DatamartSearchParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new DatamartSearchParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["AdapterName"] = this.adapterName;
+        data["QueryJson"] = this.queryJson;
+        return data;
+    }
+}
+
+export interface IDatamartSearchParameters {
+    adapterName?: string | undefined;
+    queryJson?: string | undefined;
+}
+
+export class DatamartAugmentParameters implements IDatamartAugmentParameters {
+    adapterName?: string | undefined;
+    augmentationJson?: string | undefined;
+    numberOfSamples?: number | undefined;
+    augmentedAdapterName?: string | undefined;
+
+    constructor(data?: IDatamartAugmentParameters) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.adapterName = data["AdapterName"];
+            this.augmentationJson = data["AugmentationJson"];
+            this.numberOfSamples = data["NumberOfSamples"];
+            this.augmentedAdapterName = data["AugmentedAdapterName"];
+        }
+    }
+
+    static fromJS(data: any): DatamartAugmentParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new DatamartAugmentParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["AdapterName"] = this.adapterName;
+        data["AugmentationJson"] = this.augmentationJson;
+        data["NumberOfSamples"] = this.numberOfSamples;
+        data["AugmentedAdapterName"] = this.augmentedAdapterName;
+        return data;
+    }
+}
+
+export interface IDatamartAugmentParameters {
+    adapterName?: string | undefined;
+    augmentationJson?: string | undefined;
+    numberOfSamples?: number | undefined;
+    augmentedAdapterName?: string | undefined;
+}
+
+export class RawDataResult extends DistResult implements IRawDataResult {
+    samples?: { [key: string]: any[]; } | undefined;
+    weightedWords?: { [key: string]: Word[]; } | undefined;
+
+    constructor(data?: IRawDataResult) {
+        super(data);
+        this._discriminator = "RawDataResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["Samples"]) {
+                this.samples = {};
+                for (let key in data["Samples"]) {
+                    if (data["Samples"].hasOwnProperty(key))
+                        this.samples[key] = data["Samples"][key] !== undefined ? data["Samples"][key] : [];
+                }
+            }
+            if (data["WeightedWords"]) {
+                this.weightedWords = {};
+                for (let key in data["WeightedWords"]) {
+                    if (data["WeightedWords"].hasOwnProperty(key))
+                        this.weightedWords[key] = data["WeightedWords"][key] ? data["WeightedWords"][key].map((i: any) => Word.fromJS(i)) : [];
+                }
+            }
+        }
+    }
+
+    static fromJS(data: any): RawDataResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new RawDataResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.samples) {
+            data["Samples"] = {};
+            for (let key in this.samples) {
+                if (this.samples.hasOwnProperty(key))
+                    data["Samples"][key] = this.samples[key];
+            }
+        }
+        if (this.weightedWords) {
+            data["WeightedWords"] = {};
+            for (let key in this.weightedWords) {
+                if (this.weightedWords.hasOwnProperty(key))
+                    data["WeightedWords"][key] = this.weightedWords[key];
+            }
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IRawDataResult extends IDistResult {
+    samples?: { [key: string]: any[]; } | undefined;
+    weightedWords?: { [key: string]: Word[]; } | undefined;
+}
+
+export class Word implements IWord {
+    text?: string | undefined;
+    occurrences?: number | undefined;
+    stem?: string | undefined;
+    isWordGroup?: boolean | undefined;
+
+    constructor(data?: IWord) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.text = data["Text"];
+            this.occurrences = data["Occurrences"];
+            this.stem = data["Stem"];
+            this.isWordGroup = data["IsWordGroup"];
+        }
+    }
+
+    static fromJS(data: any): Word {
+        data = typeof data === 'object' ? data : {};
+        let result = new Word();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Text"] = this.text;
+        data["Occurrences"] = this.occurrences;
+        data["Stem"] = this.stem;
+        data["IsWordGroup"] = this.isWordGroup;
+        return data;
+    }
+}
+
+export interface IWord {
+    text?: string | undefined;
+    occurrences?: number | undefined;
+    stem?: string | undefined;
+    isWordGroup?: boolean | undefined;
+}
+
+export class SampleOperationParameters extends DistOperationParameters implements ISampleOperationParameters {
+    numSamples?: number | undefined;
+    attributeParameters?: AttributeParameters[] | undefined;
+    brushes?: string[] | undefined;
+
+    constructor(data?: ISampleOperationParameters) {
+        super(data);
+        this._discriminator = "SampleOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.numSamples = data["NumSamples"];
+            if (data["AttributeParameters"] && data["AttributeParameters"].constructor === Array) {
+                this.attributeParameters = [];
+                for (let item of data["AttributeParameters"])
+                    this.attributeParameters.push(AttributeParameters.fromJS(item));
+            }
+            if (data["Brushes"] && data["Brushes"].constructor === Array) {
+                this.brushes = [];
+                for (let item of data["Brushes"])
+                    this.brushes.push(item);
+            }
+        }
+    }
+
+    static fromJS(data: any): SampleOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new SampleOperationParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["NumSamples"] = this.numSamples;
+        if (this.attributeParameters && this.attributeParameters.constructor === Array) {
+            data["AttributeParameters"] = [];
+            for (let item of this.attributeParameters)
+                data["AttributeParameters"].push(item.toJSON());
+        }
+        if (this.brushes && this.brushes.constructor === Array) {
+            data["Brushes"] = [];
+            for (let item of this.brushes)
+                data["Brushes"].push(item);
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ISampleOperationParameters extends IDistOperationParameters {
+    numSamples?: number | undefined;
+    attributeParameters?: AttributeParameters[] | undefined;
+    brushes?: string[] | undefined;
+}
+
+export class SampleResult extends DistResult implements ISampleResult {
+    samples?: { [key: string]: { [key: string]: number; }; } | undefined;
+    isTruncated?: boolean | undefined;
+
+    constructor(data?: ISampleResult) {
+        super(data);
+        this._discriminator = "SampleResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["Samples"]) {
+                this.samples = {};
+                for (let key in data["Samples"]) {
+                    if (data["Samples"].hasOwnProperty(key))
+                        this.samples[key] = data["Samples"][key] !== undefined ? data["Samples"][key] : {};
+                }
+            }
+            this.isTruncated = data["IsTruncated"];
+        }
+    }
+
+    static fromJS(data: any): SampleResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new SampleResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.samples) {
+            data["Samples"] = {};
+            for (let key in this.samples) {
+                if (this.samples.hasOwnProperty(key))
+                    data["Samples"][key] = this.samples[key];
+            }
+        }
+        data["IsTruncated"] = this.isTruncated;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ISampleResult extends IDistResult {
+    samples?: { [key: string]: { [key: string]: number; }; } | undefined;
+    isTruncated?: boolean | undefined;
+}
+
+export class ResultParameters extends UniqueJson implements IResultParameters {
+    operationReference?: IOperationReference | undefined;
+    stopOperation?: boolean | undefined;
+
+    protected _discriminator: string;
+
+    constructor(data?: IResultParameters) {
+        super(data);
+        this._discriminator = "ResultParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.operationReference = data["OperationReference"] ? IOperationReference.fromJS(data["OperationReference"]) : <any>undefined;
+            this.stopOperation = data["StopOperation"];
+        }
+    }
+
+    static fromJS(data: any): ResultParameters {
+        data = typeof data === 'object' ? data : {};
+        if (data["discriminator"] === "RecommenderResultParameters") {
+            let result = new RecommenderResultParameters();
+            result.init(data);
+            return result;
+        }
+        let result = new ResultParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["discriminator"] = this._discriminator;
+        data["OperationReference"] = this.operationReference ? this.operationReference.toJSON() : <any>undefined;
+        data["StopOperation"] = this.stopOperation;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IResultParameters extends IUniqueJson {
+    operationReference?: IOperationReference | undefined;
+    stopOperation?: boolean | undefined;
+}
+
+export class RecommenderResultParameters extends ResultParameters implements IRecommenderResultParameters {
+    from?: number | undefined;
+    to?: number | undefined;
+    pValueSorting?: Sorting | undefined;
+    effectSizeFilter?: EffectSize | undefined;
+
+    constructor(data?: IRecommenderResultParameters) {
+        super(data);
+        this._discriminator = "RecommenderResultParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.from = data["From"];
+            this.to = data["To"];
+            this.pValueSorting = data["PValueSorting"];
+            this.effectSizeFilter = data["EffectSizeFilter"];
+        }
+    }
+
+    static fromJS(data: any): RecommenderResultParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new RecommenderResultParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["From"] = this.from;
+        data["To"] = this.to;
+        data["PValueSorting"] = this.pValueSorting;
+        data["EffectSizeFilter"] = this.effectSizeFilter;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IRecommenderResultParameters extends IResultParameters {
+    from?: number | undefined;
+    to?: number | undefined;
+    pValueSorting?: Sorting | undefined;
+    effectSizeFilter?: EffectSize | undefined;
+}
+
+export enum Sorting {
+    Ascending = "Ascending",
+    Descending = "Descending",
+}
+
+export class AddComparisonParameters extends ModelOperationParameters implements IAddComparisonParameters {
+    modelId?: ModelId | undefined;
+    comparisonOrder?: number | undefined;
+    childOperationParameters?: OperationParameters[] | undefined;
+    isCachable?: boolean | undefined;
+
+    constructor(data?: IAddComparisonParameters) {
+        super(data);
+        this._discriminator = "AddComparisonParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.modelId = data["ModelId"] ? ModelId.fromJS(data["ModelId"]) : <any>undefined;
+            this.comparisonOrder = data["ComparisonOrder"];
+            if (data["ChildOperationParameters"] && data["ChildOperationParameters"].constructor === Array) {
+                this.childOperationParameters = [];
+                for (let item of data["ChildOperationParameters"])
+                    this.childOperationParameters.push(OperationParameters.fromJS(item));
+            }
+            this.isCachable = data["IsCachable"];
+        }
+    }
+
+    static fromJS(data: any): AddComparisonParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new AddComparisonParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["ModelId"] = this.modelId ? this.modelId.toJSON() : <any>undefined;
+        data["ComparisonOrder"] = this.comparisonOrder;
+        if (this.childOperationParameters && this.childOperationParameters.constructor === Array) {
+            data["ChildOperationParameters"] = [];
+            for (let item of this.childOperationParameters)
+                data["ChildOperationParameters"].push(item.toJSON());
+        }
+        data["IsCachable"] = this.isCachable;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IAddComparisonParameters extends IModelOperationParameters {
+    modelId?: ModelId | undefined;
+    comparisonOrder?: number | undefined;
+    childOperationParameters?: OperationParameters[] | undefined;
+    isCachable?: boolean | undefined;
+}
+
+export class CDFResult extends DistResult implements ICDFResult {
+    cDF?: { [key: string]: number; } | undefined;
+
+    constructor(data?: ICDFResult) {
+        super(data);
+        this._discriminator = "CDFResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["CDF"]) {
+                this.cDF = {};
+                for (let key in data["CDF"]) {
+                    if (data["CDF"].hasOwnProperty(key))
+                        this.cDF[key] = data["CDF"][key];
+                }
+            }
+        }
+    }
+
+    static fromJS(data: any): CDFResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new CDFResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.cDF) {
+            data["CDF"] = {};
+            for (let key in this.cDF) {
+                if (this.cDF.hasOwnProperty(key))
+                    data["CDF"][key] = this.cDF[key];
+            }
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ICDFResult extends IDistResult {
+    cDF?: { [key: string]: number; } | undefined;
+}
+
+export class ChiSquaredTestResult extends HypothesisTestResult implements IChiSquaredTestResult {
+    hs_aligned?: TupleOfDoubleAndDouble[] | undefined;
+
+    constructor(data?: IChiSquaredTestResult) {
+        super(data);
+        this._discriminator = "ChiSquaredTestResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["hs_aligned"] && data["hs_aligned"].constructor === Array) {
+                this.hs_aligned = [];
+                for (let item of data["hs_aligned"])
+                    this.hs_aligned.push(TupleOfDoubleAndDouble.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): ChiSquaredTestResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new ChiSquaredTestResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.hs_aligned && this.hs_aligned.constructor === Array) {
+            data["hs_aligned"] = [];
+            for (let item of this.hs_aligned)
+                data["hs_aligned"].push(item.toJSON());
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IChiSquaredTestResult extends IHypothesisTestResult {
+    hs_aligned?: TupleOfDoubleAndDouble[] | undefined;
+}
+
+export class CorrelationTestResult extends HypothesisTestResult implements ICorrelationTestResult {
+    degreeOfFreedom?: number | undefined;
+    sampleCorrelationCoefficient?: number | undefined;
+    distResult?: EmpiricalDistResult | undefined;
+
+    constructor(data?: ICorrelationTestResult) {
+        super(data);
+        this._discriminator = "CorrelationTestResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.degreeOfFreedom = data["DegreeOfFreedom"];
+            this.sampleCorrelationCoefficient = data["SampleCorrelationCoefficient"];
+            this.distResult = data["DistResult"] ? EmpiricalDistResult.fromJS(data["DistResult"]) : <any>undefined;
+        }
+    }
+
+    static fromJS(data: any): CorrelationTestResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new CorrelationTestResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["DegreeOfFreedom"] = this.degreeOfFreedom;
+        data["SampleCorrelationCoefficient"] = this.sampleCorrelationCoefficient;
+        data["DistResult"] = this.distResult ? this.distResult.toJSON() : <any>undefined;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ICorrelationTestResult extends IHypothesisTestResult {
+    degreeOfFreedom?: number | undefined;
+    sampleCorrelationCoefficient?: number | undefined;
+    distResult?: EmpiricalDistResult | undefined;
+}
+
+export class EmpiricalDistResult extends DistResult implements IEmpiricalDistResult {
+    marginals?: AttributeParameters[] | undefined;
+    marginalDistParameters?: { [key: string]: DistParameter; } | undefined;
+    jointDistParameter?: JointDistParameter | undefined;
+
+    constructor(data?: IEmpiricalDistResult) {
+        super(data);
+        this._discriminator = "EmpiricalDistResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["Marginals"] && data["Marginals"].constructor === Array) {
+                this.marginals = [];
+                for (let item of data["Marginals"])
+                    this.marginals.push(AttributeParameters.fromJS(item));
+            }
+            if (data["MarginalDistParameters"]) {
+                this.marginalDistParameters = {};
+                for (let key in data["MarginalDistParameters"]) {
+                    if (data["MarginalDistParameters"].hasOwnProperty(key))
+                        this.marginalDistParameters[key] = data["MarginalDistParameters"][key] ? DistParameter.fromJS(data["MarginalDistParameters"][key]) : new DistParameter();
+                }
+            }
+            this.jointDistParameter = data["JointDistParameter"] ? JointDistParameter.fromJS(data["JointDistParameter"]) : <any>undefined;
+        }
+    }
+
+    static fromJS(data: any): EmpiricalDistResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new EmpiricalDistResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.marginals && this.marginals.constructor === Array) {
+            data["Marginals"] = [];
+            for (let item of this.marginals)
+                data["Marginals"].push(item.toJSON());
+        }
+        if (this.marginalDistParameters) {
+            data["MarginalDistParameters"] = {};
+            for (let key in this.marginalDistParameters) {
+                if (this.marginalDistParameters.hasOwnProperty(key))
+                    data["MarginalDistParameters"][key] = this.marginalDistParameters[key];
+            }
+        }
+        data["JointDistParameter"] = this.jointDistParameter ? this.jointDistParameter.toJSON() : <any>undefined;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IEmpiricalDistResult extends IDistResult {
+    marginals?: AttributeParameters[] | undefined;
+    marginalDistParameters?: { [key: string]: DistParameter; } | undefined;
+    jointDistParameter?: JointDistParameter | undefined;
+}
+
+export class DistParameter implements IDistParameter {
+    mean?: number | undefined;
+    moment2?: number | undefined;
+    variance?: number | undefined;
+    varianceEstimate?: number | undefined;
+    min?: number | undefined;
+    max?: number | undefined;
+
+    constructor(data?: IDistParameter) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.mean = data["Mean"];
+            this.moment2 = data["Moment2"];
+            this.variance = data["Variance"];
+            this.varianceEstimate = data["VarianceEstimate"];
+            this.min = data["Min"];
+            this.max = data["Max"];
+        }
+    }
+
+    static fromJS(data: any): DistParameter {
+        data = typeof data === 'object' ? data : {};
+        let result = new DistParameter();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Mean"] = this.mean;
+        data["Moment2"] = this.moment2;
+        data["Variance"] = this.variance;
+        data["VarianceEstimate"] = this.varianceEstimate;
+        data["Min"] = this.min;
+        data["Max"] = this.max;
+        return data;
+    }
+}
+
+export interface IDistParameter {
+    mean?: number | undefined;
+    moment2?: number | undefined;
+    variance?: number | undefined;
+    varianceEstimate?: number | undefined;
+    min?: number | undefined;
+    max?: number | undefined;
+}
+
+export class JointDistParameter implements IJointDistParameter {
+    jointDist?: DistParameter | undefined;
+    covariance?: number | undefined;
+
+    constructor(data?: IJointDistParameter) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.jointDist = data["JointDist"] ? DistParameter.fromJS(data["JointDist"]) : <any>undefined;
+            this.covariance = data["Covariance"];
+        }
+    }
+
+    static fromJS(data: any): JointDistParameter {
+        data = typeof data === 'object' ? data : {};
+        let result = new JointDistParameter();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["JointDist"] = this.jointDist ? this.jointDist.toJSON() : <any>undefined;
+        data["Covariance"] = this.covariance;
+        return data;
+    }
+}
+
+export interface IJointDistParameter {
+    jointDist?: DistParameter | undefined;
+    covariance?: number | undefined;
+}
+
+export enum DistributionType {
+    Continuous = 0,
+    Discrete = 1,
+}
+
+export abstract class DistributionTypeExtension implements IDistributionTypeExtension {
+
+    constructor(data?: IDistributionTypeExtension) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): DistributionTypeExtension {
+        data = typeof data === 'object' ? data : {};
+        throw new Error("The abstract class 'DistributionTypeExtension' cannot be instantiated.");
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        return data;
+    }
+}
+
+export interface IDistributionTypeExtension {
+}
+
+export class GetModelStateParameters extends ModelOperationParameters implements IGetModelStateParameters {
+    modelId?: ModelId | undefined;
+    comparisonIds?: ComparisonId[] | undefined;
+    riskControlType?: RiskControlType | undefined;
+
+    constructor(data?: IGetModelStateParameters) {
+        super(data);
+        this._discriminator = "GetModelStateParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.modelId = data["ModelId"] ? ModelId.fromJS(data["ModelId"]) : <any>undefined;
+            if (data["ComparisonIds"] && data["ComparisonIds"].constructor === Array) {
+                this.comparisonIds = [];
+                for (let item of data["ComparisonIds"])
+                    this.comparisonIds.push(ComparisonId.fromJS(item));
+            }
+            this.riskControlType = data["RiskControlType"];
+        }
+    }
+
+    static fromJS(data: any): GetModelStateParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new GetModelStateParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["ModelId"] = this.modelId ? this.modelId.toJSON() : <any>undefined;
+        if (this.comparisonIds && this.comparisonIds.constructor === Array) {
+            data["ComparisonIds"] = [];
+            for (let item of this.comparisonIds)
+                data["ComparisonIds"].push(item.toJSON());
+        }
+        data["RiskControlType"] = this.riskControlType;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IGetModelStateParameters extends IModelOperationParameters {
+    modelId?: ModelId | undefined;
+    comparisonIds?: ComparisonId[] | undefined;
+    riskControlType?: RiskControlType | undefined;
+}
+
+export class KSTestResult extends HypothesisTestResult implements IKSTestResult {
+
+    constructor(data?: IKSTestResult) {
+        super(data);
+        this._discriminator = "KSTestResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): KSTestResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new KSTestResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IKSTestResult extends IHypothesisTestResult {
+}
+
+export class ModelWealthParameters extends UniqueJson implements IModelWealthParameters {
+    modelId?: ModelId | undefined;
+    riskControlType?: RiskControlType | undefined;
+
+    constructor(data?: IModelWealthParameters) {
+        super(data);
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.modelId = data["ModelId"] ? ModelId.fromJS(data["ModelId"]) : <any>undefined;
+            this.riskControlType = data["RiskControlType"];
+        }
+    }
+
+    static fromJS(data: any): ModelWealthParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new ModelWealthParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["ModelId"] = this.modelId ? this.modelId.toJSON() : <any>undefined;
+        data["RiskControlType"] = this.riskControlType;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IModelWealthParameters extends IUniqueJson {
+    modelId?: ModelId | undefined;
+    riskControlType?: RiskControlType | undefined;
+}
+
+export class RootMeanSquareTestResult extends HypothesisTestResult implements IRootMeanSquareTestResult {
+    simulationCount?: number | undefined;
+    extremeSimulationCount?: number | undefined;
+
+    constructor(data?: IRootMeanSquareTestResult) {
+        super(data);
+        this._discriminator = "RootMeanSquareTestResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.simulationCount = data["SimulationCount"];
+            this.extremeSimulationCount = data["ExtremeSimulationCount"];
+        }
+    }
+
+    static fromJS(data: any): RootMeanSquareTestResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new RootMeanSquareTestResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["SimulationCount"] = this.simulationCount;
+        data["ExtremeSimulationCount"] = this.extremeSimulationCount;
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IRootMeanSquareTestResult extends IHypothesisTestResult {
+    simulationCount?: number | undefined;
+    extremeSimulationCount?: number | undefined;
+}
+
+export class TTestResult extends HypothesisTestResult implements ITTestResult {
+    degreeOfFreedom?: number | undefined;
+    distResults?: EmpiricalDistResult[] | undefined;
+
+    constructor(data?: ITTestResult) {
+        super(data);
+        this._discriminator = "TTestResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.degreeOfFreedom = data["DegreeOfFreedom"];
+            if (data["DistResults"] && data["DistResults"].constructor === Array) {
+                this.distResults = [];
+                for (let item of data["DistResults"])
+                    this.distResults.push(EmpiricalDistResult.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): TTestResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new TTestResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["DegreeOfFreedom"] = this.degreeOfFreedom;
+        if (this.distResults && this.distResults.constructor === Array) {
+            data["DistResults"] = [];
+            for (let item of this.distResults)
+                data["DistResults"].push(item.toJSON());
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface ITTestResult extends IHypothesisTestResult {
+    degreeOfFreedom?: number | undefined;
+    distResults?: EmpiricalDistResult[] | undefined;
+}
+
+export enum Sorting2 {
+    Ascending = 0,
+    Descending = 1,
+}
+
+export class BinLabel implements IBinLabel {
+    value?: number | undefined;
+    minValue?: number | undefined;
+    maxValue?: number | undefined;
+    label?: string | undefined;
+
+    constructor(data?: IBinLabel) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.value = data["Value"];
+            this.minValue = data["MinValue"];
+            this.maxValue = data["MaxValue"];
+            this.label = data["Label"];
+        }
+    }
+
+    static fromJS(data: any): BinLabel {
+        data = typeof data === 'object' ? data : {};
+        let result = new BinLabel();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Value"] = this.value;
+        data["MinValue"] = this.minValue;
+        data["MaxValue"] = this.maxValue;
+        data["Label"] = this.label;
+        return data;
+    }
+}
+
+export interface IBinLabel {
+    value?: number | undefined;
+    minValue?: number | undefined;
+    maxValue?: number | undefined;
+    label?: string | undefined;
+}
+
+export class PreProcessedString implements IPreProcessedString {
+    value?: string | undefined;
+    id?: number | undefined;
+    stringLookup?: { [key: string]: number; } | undefined;
+    indexLookup?: { [key: string]: string; } | undefined;
+
+    constructor(data?: IPreProcessedString) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.value = data["Value"];
+            this.id = data["Id"];
+            if (data["StringLookup"]) {
+                this.stringLookup = {};
+                for (let key in data["StringLookup"]) {
+                    if (data["StringLookup"].hasOwnProperty(key))
+                        this.stringLookup[key] = data["StringLookup"][key];
+                }
+            }
+            if (data["IndexLookup"]) {
+                this.indexLookup = {};
+                for (let key in data["IndexLookup"]) {
+                    if (data["IndexLookup"].hasOwnProperty(key))
+                        this.indexLookup[key] = data["IndexLookup"][key];
+                }
+            }
+        }
+    }
+
+    static fromJS(data: any): PreProcessedString {
+        data = typeof data === 'object' ? data : {};
+        let result = new PreProcessedString();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Value"] = this.value;
+        data["Id"] = this.id;
+        if (this.stringLookup) {
+            data["StringLookup"] = {};
+            for (let key in this.stringLookup) {
+                if (this.stringLookup.hasOwnProperty(key))
+                    data["StringLookup"][key] = this.stringLookup[key];
+            }
+        }
+        if (this.indexLookup) {
+            data["IndexLookup"] = {};
+            for (let key in this.indexLookup) {
+                if (this.indexLookup.hasOwnProperty(key))
+                    data["IndexLookup"][key] = this.indexLookup[key];
+            }
+        }
+        return data;
+    }
+}
+
+export interface IPreProcessedString {
+    value?: string | undefined;
+    id?: number | undefined;
+    stringLookup?: { [key: string]: number; } | undefined;
+    indexLookup?: { [key: string]: string; } | undefined;
+}
+
+export class BitSet implements IBitSet {
+    length?: number | undefined;
+    size?: number | undefined;
+
+    constructor(data?: IBitSet) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+            this.length = data["Length"];
+            this.size = data["Size"];
+        }
+    }
+
+    static fromJS(data: any): BitSet {
+        data = typeof data === 'object' ? data : {};
+        let result = new BitSet();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Length"] = this.length;
+        data["Size"] = this.size;
+        return data;
+    }
+}
+
+export interface IBitSet {
+    length?: number | undefined;
+    size?: number | undefined;
+}
+
+export class DateTimeUtil implements IDateTimeUtil {
+
+    constructor(data?: IDateTimeUtil) {
+        if (data) {
+            for (var property in data) {
+                if (data.hasOwnProperty(property))
+                    (<any>this)[property] = (<any>data)[property];
+            }
+        }
+    }
+
+    init(data?: any) {
+        if (data) {
+        }
+    }
+
+    static fromJS(data: any): DateTimeUtil {
+        data = typeof data === 'object' ? data : {};
+        let result = new DateTimeUtil();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        return data;
+    }
+}
+
+export interface IDateTimeUtil {
+}
+
+export class FrequentItemsetOperationParameters extends DistOperationParameters implements IFrequentItemsetOperationParameters {
+    filter?: string | undefined;
+    attributeParameters?: AttributeParameters[] | undefined;
+    attributeCodeParameters?: AttributeCaclculatedParameters[] | undefined;
+
+    constructor(data?: IFrequentItemsetOperationParameters) {
+        super(data);
+        this._discriminator = "FrequentItemsetOperationParameters";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            this.filter = data["Filter"];
+            if (data["AttributeParameters"] && data["AttributeParameters"].constructor === Array) {
+                this.attributeParameters = [];
+                for (let item of data["AttributeParameters"])
+                    this.attributeParameters.push(AttributeParameters.fromJS(item));
+            }
+            if (data["AttributeCodeParameters"] && data["AttributeCodeParameters"].constructor === Array) {
+                this.attributeCodeParameters = [];
+                for (let item of data["AttributeCodeParameters"])
+                    this.attributeCodeParameters.push(AttributeCaclculatedParameters.fromJS(item));
+            }
+        }
+    }
+
+    static fromJS(data: any): FrequentItemsetOperationParameters {
+        data = typeof data === 'object' ? data : {};
+        let result = new FrequentItemsetOperationParameters();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        data["Filter"] = this.filter;
+        if (this.attributeParameters && this.attributeParameters.constructor === Array) {
+            data["AttributeParameters"] = [];
+            for (let item of this.attributeParameters)
+                data["AttributeParameters"].push(item.toJSON());
+        }
+        if (this.attributeCodeParameters && this.attributeCodeParameters.constructor === Array) {
+            data["AttributeCodeParameters"] = [];
+            for (let item of this.attributeCodeParameters)
+                data["AttributeCodeParameters"].push(item.toJSON());
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IFrequentItemsetOperationParameters extends IDistOperationParameters {
+    filter?: string | undefined;
+    attributeParameters?: AttributeParameters[] | undefined;
+    attributeCodeParameters?: AttributeCaclculatedParameters[] | undefined;
+}
+
+export class FrequentItemsetResult extends Result implements IFrequentItemsetResult {
+    frequentItems?: { [key: string]: number; } | undefined;
+
+    constructor(data?: IFrequentItemsetResult) {
+        super(data);
+        this._discriminator = "FrequentItemsetResult";
+    }
+
+    init(data?: any) {
+        super.init(data);
+        if (data) {
+            if (data["FrequentItems"]) {
+                this.frequentItems = {};
+                for (let key in data["FrequentItems"]) {
+                    if (data["FrequentItems"].hasOwnProperty(key))
+                        this.frequentItems[key] = data["FrequentItems"][key];
+                }
+            }
+        }
+    }
+
+    static fromJS(data: any): FrequentItemsetResult {
+        data = typeof data === 'object' ? data : {};
+        let result = new FrequentItemsetResult();
+        result.init(data);
+        return result;
+    }
+
+    toJSON(data?: any) {
+        data = typeof data === 'object' ? data : {};
+        if (this.frequentItems) {
+            data["FrequentItems"] = {};
+            for (let key in this.frequentItems) {
+                if (this.frequentItems.hasOwnProperty(key))
+                    data["FrequentItems"][key] = this.frequentItems[key];
+            }
+        }
+        super.toJSON(data);
+        return data;
+    }
+}
+
+export interface IFrequentItemsetResult extends IResult {
+    frequentItems?: { [key: string]: number; } | undefined;
+}
+
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 8a52a501c..ac51a7d87 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -43,6 +43,8 @@ import { CurrentUserUtils } from '../../server/authentication/models/current_use
 import { Field, Opt } from '../../fields/Field';
 import { ListField } from '../../fields/ListField';
 import { map } from 'bluebird';
+import { Gateway, Settings } from '../northstar/manager/Gateway';
+import { Catalog } from '../northstar/model/idea/idea';
 
 @observer
 export class Main extends React.Component {
@@ -52,6 +54,7 @@ export class Main extends React.Component {
     @observable private userWorkspaces: Document[] = [];
     @observable public pwidth: number = 0;
     @observable public pheight: number = 0;
+    @observable private _northstarCatalog: Catalog | undefined = undefined;
 
     public mainDocId: string | undefined;
     private currentUser?: DashUserModel;
@@ -65,7 +68,9 @@ export class Main extends React.Component {
         if (window.location.pathname !== RouteStore.home) {
             let pathname = window.location.pathname.split("/");
             this.mainDocId = pathname[pathname.length - 1];
-        }
+        };
+
+        this.initializeNorthstar();
 
         CurrentUserUtils.loadCurrentUser();
 
@@ -82,9 +87,26 @@ export class Main extends React.Component {
         library.add(faMusic);
 
         this.initEventListeners();
-        Documents.initProtos(() => {
-            this.initAuthenticationRouters();
+        Documents.initProtos(() => this.initAuthenticationRouters());
+    }
+
+    @action SetNorthstarCatalog(ctlog: Catalog) {
+        this._northstarCatalog = ctlog;
+        if (this._northstarCatalog) {
+            console.log("CATALOG " + this._northstarCatalog.schemas);
+        }
+    }
+    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.SetNorthstarCatalog(await Gateway.Instance.GetCatalog()));
     }
 
     onHistory = () => {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 50f5e9618..062babe58 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -9,7 +9,6 @@ import { Field, Opt } from "../../../fields/Field";
 import { KeyStore } from "../../../fields/KeyStore";
 import { CompileScript, ToField } from "../../util/Scripting";
 import { Transform } from "../../util/Transform";
-import { ContextMenu } from "../ContextMenu";
 import { EditableView } from "../EditableView";
 import { DocumentView } from "../nodes/DocumentView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
@@ -27,8 +26,7 @@ import { ListField } from "../../../fields/ListField";
 
 @observer
 class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggle: (key: Key) => void }> {
-    @observable
-    key: Key | undefined;
+    @observable key: Key | undefined;
 
     componentWillReceiveProps() {
         Server.GetField(this.props.keyId, action((field: Opt<Field>) => {
diff --git a/src/server/index.ts b/src/server/index.ts
index 6226dbfe2..d1eb6847d 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -18,7 +18,7 @@ import { getLogin, postLogin, getSignup, postSignup, getLogout, postReset, getFo
 const config = require('../../webpack.config');
 const compiler = webpack(config);
 const port = 1050; // default port to listen
-const serverPort = 1234;
+const serverPort = 4321;
 import * as expressValidator from 'express-validator';
 import expressFlash = require('express-flash');
 import flash = require('connect-flash');
diff --git a/webpack.config.js b/webpack.config.js
index ff03181c9..5ba9dd4b5 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -77,7 +77,7 @@ module.exports = {
     compress: false,
     host: "localhost",
     contentBase: path.join(__dirname, 'deploy'),
-    port: 1234,
+    port: 4321,
     hot: true,
     https: false,
     overlay: {
-- 
cgit v1.2.3-70-g09d2


From f2fa6d6d28f2011a13f9034f5685b08301c291e4 Mon Sep 17 00:00:00 2001
From: laurawilsonri <laura_wilson@brown.edu>
Date: Tue, 19 Mar 2019 16:48:10 -0400
Subject: ui much better

---
 src/client/views/Main.tsx                          |   1 +
 .../views/collections/CollectionSchemaView.scss    |  87 ++++++-------
 .../views/collections/CollectionSchemaView.tsx     | 143 ++++++---------------
 3 files changed, 77 insertions(+), 154 deletions(-)

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

diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 26a07fdfe..a6b893eca 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -78,6 +78,7 @@ export class Main extends React.Component {
         library.add(faFilm);
         library.add(faMusic);
 
+
         this.initEventListeners();
         Documents.initProtos(() => {
             this.initAuthenticationRouters();
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index d16f0edf9..8c2f62d43 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -1,66 +1,57 @@
+@import "../global_variables";
+
 //options menu styling
 #schemaOptionsMenuBtn {
     position: absolute;
-    height: 15px;
-    width: 15px;
+    height: 20px;
+    width: 20px;
     border-radius: 50%;
     z-index: 21; 
-    right: 3px;
-    top: 3px; 
+    right: 4px;
+    top: 4px; 
     pointer-events: auto;
-    background-color:red;
-}
-
-.dd-item-containter:hover {
-  background-color: rgb(204, 204, 204);
+    background-color:black;
+    display:inline-block;
+    padding: 0px;
+    font-size: 100%;
 }
-
-.dd-item {
-    color: black;
-    padding-left: 6px;
-    padding-bottom: 0px;
-    padding-top: 4px;
-    font-size: 12px;
+#schema-options-header {
+    text-align: center;
+    padding: 0px;
     margin: 0px;
-    display: block;
-    width: 100%;
-    height: 25px;
-    z-index: 100;
 }
-.dd-item:hover {
-    color: black;
-    font-weight: 500;
+.schema-options-subHeader {
+    color: $intermediate-color;
+    margin-bottom: 5px;
+}
+#schemaOptionsMenuBtn:hover {
+    transform: scale(1.15);
 }
 
-.dropdown-menu {
-    height: 100px;
-    overflow-y: scroll;
-  }
-
-#dropdown-basic-button {
-    background-color: #fff;
-    border: .75px solid #bbbbbb;
-    border-radius: 0;
-    -webkit-box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
-    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-    margin:10px;
-    margin-top: 8px;
-    color: black;
-    display: inline-block;
-    width: 160px;
-    height: 25px;
-    font-size: 15px;
-    padding: 0px;
-    border-radius: 3px;
-    font-size: 12px;
-  }
+#preview-schema-checkbox-div {
+    margin-left: 20px;
+    font-size: 11px;
+}
 
   #options-flyout-div {
-    text-align: center;
+    text-align: left;
     padding:0px;
+    z-index: 100;
+    font-family: $sans-serif;
+    padding-left: 5px;
+  }
+
+  #schema-col-checklist {
+    overflow: scroll;
+    text-align: left;
+    //background-color: $light-color-secondary;
+    line-height: 25px;
+    max-height: 175px;
+    font-family: $sans-serif;
+    font-size: 12px;
   }
   
-@import "../global_variables";
+
 .collectionSchemaView-container {
     border: 1px solid $intermediate-color;
     border-radius: $border-radius;
@@ -68,7 +59,7 @@
     position: absolute;
     width: 100%;
     height: 100%;
-    overflow: hidden;
+    
     .collectionSchemaView-content {
         position: absolute;
         height: 100%;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index da38d54c4..07b4d150a 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -19,10 +19,12 @@ import { CollectionViewBase } from "./CollectionViewBase";
 import { setupDrag } from "../../util/DragManager";
 import '../DocumentDecorations.scss';
 import { Flyout, anchorPoints } from "../DocumentDecorations";
-import { DropdownButton, Dropdown } from 'react-bootstrap';
 import { ListField } from "../../../fields/ListField";
 import { Key } from "../../../fields/Key";
 import { Server } from "../../Server";
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faCog } from '@fortawesome/free-solid-svg-icons';
 
 
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
@@ -44,7 +46,7 @@ class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggl
     render() {
         if (this.key) {
             return (<div key={this.key.Id}>
-                <input type="checkbox" checked={this.props.checked} onChange={() => this.key && this.props.toggle(this.key)} />{this.key.Name}
+                <input type="checkbox" checked={this.props.checked} onChange={() => this.key && this.props.toggle(this.key)} />  {this.key.Name}
             </div>)
         } else {
             return <div></div>
@@ -66,6 +68,7 @@ export class CollectionSchemaView extends CollectionViewBase {
     @observable _columnsPercentage = 0;
     @computed get splitPercentage() { return this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0); }
 
+
     renderCell = (rowProps: CellInfo) => {
         let props: FieldViewProps = {
             doc: rowProps.value[0],
@@ -156,6 +159,15 @@ export class CollectionSchemaView extends CollectionViewBase {
             })
     }
 
+    //toggles preview side-panel of schema
+    @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);
+        }
+    }
+
     @observable keys: Key[] = [];
 
     findAllDocumentKeys = (): { [id: string]: boolean } => {
@@ -220,37 +232,6 @@ export class CollectionSchemaView extends CollectionViewBase {
         e.stopPropagation();
         e.preventDefault();
     }
-    @action
-    onExpanderUp = (e: PointerEvent): void => {
-        e.stopPropagation();
-        e.preventDefault();
-        document.removeEventListener("pointermove", this.onExpanderMove);
-        document.removeEventListener('pointerup', this.onExpanderUp);
-        if (this._startSplitPercent == this.splitPercentage) {
-            this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage == 0 ? 33 : 0);
-        }
-    }
-    onExpanderDown = (e: React.PointerEvent) => {
-        this._startSplitPercent = this.splitPercentage;
-        e.stopPropagation();
-        e.preventDefault();
-        document.addEventListener("pointermove", this.onExpanderMove);
-        document.addEventListener('pointerup', this.onExpanderUp);
-    }
-
-    onPointerDown = (e: React.PointerEvent) => {
-        // if (e.button === 2 && this.active) {
-        //     e.stopPropagation();
-        //     e.preventDefault();
-        // } else 
-        {
-            // if (e.buttons === 1) {
-            //     if (this.props.isSelected()) {
-            //         e.stopPropagation();
-            //     }
-            // }
-        }
-    }
 
     @action
     onColumnsMove = (e: PointerEvent): void => {
@@ -291,23 +272,16 @@ export class CollectionSchemaView extends CollectionViewBase {
 
     focusDocument = (doc: Document) => { }
 
-    keyDropDownOnSelect = (keyId: string) => {
-        const columns = this.props.Document.GetList(KeyStore.ColumnsKey, [KeyStore.Title, KeyStore.Data, KeyStore.Author]);
-        const listOfFields = this.props.Document.GetList<Document>(this.props.fieldKey, [])[1].fields;
-        if (listOfFields.has(keyId)) {
-            let index = columns.push(listOfFields!.get(keyId)!.key);
-            this.props.Document.SetData(KeyStore.ColumnsKey, columns, ListField);
-        }
-    }
-
-    dropdownItemOnPointerDown = (e: React.PointerEvent): void => {
-        //(e.currentTarget.children[0] as Dropdown.Item ).onSelect();
+    onPointerDown = (e: React.PointerEvent): void => {
+        //
     }
 
     render() {
+        library.add(faCog);
         const columns = this.columns;
         const children = this.props.Document.GetList<Document>(this.props.fieldKey, []);
         const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
+        //all the keys/columns that will be displayed in the schema
         const allKeys = this.findAllDocumentKeys();
         let content = this._selectedIndex == -1 || !selected ? (null) : (
             <Measure onResize={this.setScaling}>
@@ -328,53 +302,28 @@ export class CollectionSchemaView extends CollectionViewBase {
                 }
             </Measure>
         )
-        let previewHandle = !this.props.active() ? (null) : (
-            <div className="collectionSchemaView-previewHandle" onPointerDown={this.onExpanderDown} />);
-        let columnsHandle = !this.props.active() ? (null) : (
-            <div className="collectionSchemaView-columnsHandle" onPointerDown={this.onColumnsDown} />);
         let dividerDragger = this.splitPercentage == 0 ? (null) :
             <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />
 
-        //get the union of all childrens' keys
-        let addFields: { id: string, name: string }[] = [];
-        let removeFields: { id: string, name: string }[] = [];
-        let optionsMenu = null;
-        if (this.props.active()) {
-            let protos = this.props.Document.GetAllPrototypes();
-            for (const proto of protos) {
-                proto._proxies.forEach((val, key) => {
-                    if (!(key in addFields.keys)) {
-                        let this_name: string = proto.fields!.get(key)!.key.Name;
-                        addFields.push({ id: key, name: this_name });
-                    }
-                })
-            }
-            /*<DropdownButton id="dropdown-basic-button" className="colDropDown" title="Add Column">
-                        {addFields.map(({ id, name }) => (
-                            <div className="dd-item-containter"><Dropdown.Item className="dd-item" eventKey={id} onSelect={this.keyDropDownOnSelect}>{name} </Dropdown.Item></div>))}
-                    </DropdownButton>
-                    <DropdownButton id="dropdown-basic-button" className="colDropDown" title="Remove Column">
-                        {addFields.map(({ id, name }) => (
-                            <div className="dd-item-containter"><Dropdown.Item className="dd-item" eventKey={id} onSelect={this.keyDropDownOnSelect}>{name} </Dropdown.Item></div>))}
-                    </DropdownButton>*/
-            optionsMenu = !this.props.active() ? (null) : (<Flyout
-                anchorPoint={anchorPoints.LEFT_TOP}
-                content={<div id="options-flyout-div"> <h5>Options</h5>
-                    <div className="addColumn-options">
-                        <ul style={{ overflow: "scroll" }}>
-                            {Array.from(Object.keys(allKeys)).map(item => {
-                                return (<KeyToggle checked={allKeys[item]} key={item} keyId={item} toggle={this.toggleKey} />)
-                            })}
-                        </ul>
-                    </div>
+        //options button and menu
+        let optionsMenu = !this.props.active() ? (null) : (<Flyout
+            anchorPoint={anchorPoints.LEFT_TOP}
+            content={<div>
+                <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>
+                    <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} />)
+                        })}
+                    </ul>
                 </div>
-                }>
-                <div id="schemaOptionsMenuBtn" />
-            </Flyout>);
-        }
-
-        let colDividerDragger = this._columnsPercentage == 0 ? (null) :
-            <div className="collectionSchemaView-colDividerDragger" onPointerDown={this.onColDividerDown} style={{ height: `${this.DIVIDER_WIDTH}px` }} />
+            </div>
+            }>
+            <button id="schemaOptionsMenuBtn"><FontAwesomeIcon style={{ color: "white" }} icon="cog" size="sm" /></button>
+        </Flyout>);
 
         return (
             <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} ref={this._mainCont} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} >
@@ -412,27 +361,9 @@ export class CollectionSchemaView extends CollectionViewBase {
                     <div className="collectionSchemaView-previewRegion" style={{ width: `calc(${this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0)}% - ${this.DIVIDER_WIDTH}px)` }}>
                         {content}
                     </div>
-                    {previewHandle}
-
-                    {columnsHandle}
                     {optionsMenu}
                 </div>
             </div >
         )
     }
-}
-/*
-{colDividerDragger}
-                                <div className="collectionSchemaView-addColumn" style={{ height: `${this._columnsPercentage}%` }} >
-                                    {/* <input type="checkbox" id="addColumn-toggle" />
-                                    <label htmlFor="addColumn-toggle" title="Add Column"><p>+</p></label> }
-
-                                    <div className="addColumn-options">
-                                        <ul style={{ overflow: "scroll" }}>
-                                            {Array.from(Object.keys(allKeys)).map(item => {
-                                                return (<KeyToggle checked={allKeys[item]} key={item} keyId={item} toggle={this.toggleKey} />)
-                                            })}
-                                        </ul>
-                                    </div>
-                                </div>
-                                */
\ No newline at end of file
+}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 79846f5e85c04435082106724d49b03eea31657f Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 21 Mar 2019 00:57:02 -0400
Subject: minor fix to schema dragger and treeview

---
 src/client/views/collections/CollectionSchemaView.tsx | 2 +-
 src/client/views/collections/CollectionTreeView.tsx   | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index c2645ea73..d3ade7ebb 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -189,7 +189,7 @@ export class CollectionSchemaView extends CollectionViewBase {
     @action
     onDividerMove = (e: PointerEvent): void => {
         let nativeWidth = this._mainCont.current!.getBoundingClientRect();
-        this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100));
+        this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100)));
     }
     @action
     onDividerUp = (e: PointerEvent): void => {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 6edd90215..ec1bf5d0e 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,7 +1,7 @@
 import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
 import { faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, observable } from "mobx";
+import { action, observable, trace } from "mobx";
 import { observer } from "mobx-react";
 import { Document } from "../../../fields/Document";
 import { FieldWaiting } from "../../../fields/Field";
@@ -90,14 +90,14 @@ class TreeView extends React.Component<TreeViewProps> {
         if (children && children !== FieldWaiting) { // add children for a collection
             if (!this._collapsed) {
                 bulletType = BulletType.Collapsible;
-                childElements = <ul key={this.props.document.Id}>
+                childElements = <ul>
                     {children.Data.map(value => <TreeView key={value.Id} document={value} deleteDoc={this.remove} />)}
                 </ul>
             }
             else bulletType = BulletType.Collapsed;
         }
         return <div className="treeViewItem-container" >
-            <li className="collection-child" key={this.props.document.Id}>
+            <li className="collection-child">
                 {this.renderBullet(bulletType)}
                 {this.renderTitle()}
                 {childElements ? childElements : (null)}
-- 
cgit v1.2.3-70-g09d2


From 18ef731f66340c759f6818c923e1032bd5b84c7a Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 21 Mar 2019 01:50:48 -0400
Subject: fixed selections within schema.  fixed image double-tap.  fixed Open
 Full Screen

---
 .../views/collections/CollectionDockingView.tsx    |   2 +
 .../views/collections/CollectionSchemaView.tsx     | 151 +++++++--------------
 src/client/views/nodes/ImageBox.tsx                |   4 +-
 3 files changed, 53 insertions(+), 104 deletions(-)

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

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 0f0276929..fd0810242 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -61,6 +61,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         docconfig.callDownwards('_$init');
         this._goldenLayout._$maximiseItem(docconfig);
         this._fullScreen = docconfig;
+        this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
         this.stateChanged();
     }
     @action
@@ -69,6 +70,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             this._goldenLayout._$minimiseItem(this._fullScreen);
             this._goldenLayout.root.contentItems[0].removeChild(this._fullScreen);
             this._fullScreen = null;
+            this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
             this.stateChanged();
         }
     }
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index d3ade7ebb..b986f394c 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -1,29 +1,29 @@
 import React = require("react")
-import { action, observable, ObservableMap, computed } from "mobx";
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faCog } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable } 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 } from "../../../fields/Field";
+import { Key } from "../../../fields/Key";
 import { KeyStore } from "../../../fields/KeyStore";
+import { ListField } from "../../../fields/ListField";
+import { Server } from "../../Server";
+import { setupDrag } from "../../util/DragManager";
 import { CompileScript, ToField } from "../../util/Scripting";
 import { Transform } from "../../util/Transform";
+import { anchorPoints, Flyout } from "../DocumentDecorations";
+import '../DocumentDecorations.scss';
 import { EditableView } from "../EditableView";
 import { DocumentView } from "../nodes/DocumentView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
-import { COLLECTION_BORDER_WIDTH, CollectionView } from "./CollectionView";
+import { CollectionView, COLLECTION_BORDER_WIDTH } from "./CollectionView";
 import { CollectionViewBase } from "./CollectionViewBase";
-import { setupDrag } from "../../util/DragManager";
-import '../DocumentDecorations.scss';
-import { Flyout, anchorPoints } from "../DocumentDecorations";
-import { ListField } from "../../../fields/ListField";
-import { Key } from "../../../fields/Key";
-import { Server } from "../../Server";
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faCog } from '@fortawesome/free-solid-svg-icons';
 
 
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
@@ -44,17 +44,18 @@ class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggl
     render() {
         if (this.key) {
             return (<div key={this.key.Id}>
-                <input type="checkbox" checked={this.props.checked} onChange={() => this.key && this.props.toggle(this.key)} />  {this.key.Name}
+                <input type="checkbox" checked={this.props.checked} onChange={() => this.key && this.props.toggle(this.key)} />
+                {this.key.Name}
             </div>)
-        } else {
-            return <div></div>
         }
+        return (null);
     }
 }
 
 @observer
 export class CollectionSchemaView extends CollectionViewBase {
     private _mainCont = React.createRef<HTMLDivElement>();
+    private _startSplitPercent = 0;
     private DIVIDER_WIDTH = 4;
 
     @observable _columns: Array<Key> = [KeyStore.Title, KeyStore.Data, KeyStore.Author];
@@ -64,6 +65,8 @@ export class CollectionSchemaView extends CollectionViewBase {
     @observable _panelHeight = 0;
     @observable _selectedIndex = 0;
     @observable _columnsPercentage = 0;
+    @observable _keys: Key[] = [];
+
     @computed get splitPercentage() { return this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0); }
 
 
@@ -124,9 +127,8 @@ export class CollectionSchemaView extends CollectionViewBase {
         }
         return {
             onClick: action((e: React.MouseEvent, handleOriginal: Function) => {
+                that.props.select(e.ctrlKey);
                 that._selectedIndex = rowInfo.index;
-                // bcz - ugh - needed to force Measure to do its thing and call onResize
-                this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage - 0.05)
 
                 if (handleOriginal) {
                     handleOriginal()
@@ -165,27 +167,14 @@ export class CollectionSchemaView extends CollectionViewBase {
             this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage == 0 ? 33 : 0);
         }
     }
-
-    @observable keys: Key[] = [];
-
     findAllDocumentKeys = (): { [id: string]: boolean } => {
         const docs = this.props.Document.GetList<Document>(this.props.fieldKey, []);
         let keys: { [id: string]: boolean } = {}
-        docs.forEach(doc => {
-            let protos = doc.GetAllPrototypes();
-            for (const proto of protos) {
-                proto._proxies.forEach((val: any, key: string) => {
-                    keys[key] = false
-                })
-            }
-        })
-        this.columns.forEach(key => {
-            keys[key.Id] = true;
-        })
+        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)
         return keys;
     }
 
-    _startSplitPercent = 0;
     @action
     onDividerMove = (e: PointerEvent): void => {
         let nativeWidth = this._mainCont.current!.getBoundingClientRect();
@@ -207,51 +196,11 @@ export class CollectionSchemaView extends CollectionViewBase {
         document.addEventListener('pointerup', this.onDividerUp);
     }
 
-
+    @observable _tableWidth = 0;
     @action
-    onColDividerMove = (e: PointerEvent): void => {
-        let nativeWidth = this._mainCont.current!.getBoundingClientRect();
-        this._columnsPercentage = 100 - (e.clientY - nativeWidth.top) / nativeWidth.height * 100;
+    setTableDimensions = (r: any) => {
+        this._tableWidth = r.entry.width;
     }
-    @action
-    onColDividerUp = (e: PointerEvent): void => {
-        document.removeEventListener("pointermove", this.onColDividerMove);
-        document.removeEventListener('pointerup', this.onColDividerUp);
-    }
-    onColDividerDown = (e: React.PointerEvent) => {
-        e.stopPropagation();
-        e.preventDefault();
-        document.addEventListener("pointermove", this.onColDividerMove);
-        document.addEventListener('pointerup', this.onColDividerUp);
-    }
-
-    @action
-    onExpanderMove = (e: PointerEvent): void => {
-        e.stopPropagation();
-        e.preventDefault();
-    }
-
-    @action
-    onColumnsMove = (e: PointerEvent): void => {
-        e.stopPropagation();
-        e.preventDefault();
-    }
-    @action
-    onColumnsUp = (e: PointerEvent): void => {
-        e.stopPropagation();
-        e.preventDefault();
-        document.removeEventListener("pointermove", this.onColumnsMove);
-        document.removeEventListener('pointerup', this.onColumnsUp);
-        this._columnsPercentage = this._columnsPercentage ? 0 : 50;
-    }
-    onColumnsDown = (e: React.PointerEvent) => {
-        this._startSplitPercent = this.splitPercentage;
-        e.stopPropagation();
-        e.preventDefault();
-        document.addEventListener("pointermove", this.onColumnsMove);
-        document.addEventListener('pointerup', this.onColumnsUp);
-    }
-
     @action
     setScaling = (r: any) => {
         const children = this.props.Document.GetList<Document>(this.props.fieldKey, []);
@@ -267,11 +216,16 @@ export class CollectionSchemaView extends CollectionViewBase {
     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);
+    }
 
     focusDocument = (doc: Document) => { }
 
     onPointerDown = (e: React.PointerEvent): void => {
-        //
+        if (this.props.isSelected()) {
+            e.stopPropagation();
+        }
     }
 
     render() {
@@ -289,7 +243,7 @@ export class CollectionSchemaView extends CollectionViewBase {
                             AddDocument={this.props.addDocument} RemoveDocument={this.props.removeDocument}
                             isTopMost={false}
                             SelectOnLoad={false}
-                            ScreenToLocalTransform={this.getTransform}
+                            ScreenToLocalTransform={this.getPreviewTransform}
                             ContentScaling={this.getContentScaling}
                             PanelWidth={this.getPanelWidth}
                             PanelHeight={this.getPanelHeight}
@@ -326,34 +280,27 @@ export class CollectionSchemaView extends CollectionViewBase {
         return (
             <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} ref={this._mainCont} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} >
                 <div className="collectionSchemaView-dropTarget" onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
-                    <Measure onResize={action((r: any) => {
-                        this._dividerX = r.entry.width;
-                        this._panelHeight = r.entry.height;
-                    })}>
+                    <Measure onResize={this.setTableDimensions}>
                         {({ measureRef }) =>
-                            <div ref={measureRef} className="collectionSchemaView-tableContainer"
-                                style={{ width: `calc(100% - ${this.splitPercentage}%)` }}>
-                                <div className="collectionSchemaView-reactContainer" style={{ height: `calc(100% - ${this._columnsPercentage}%)` }}>
-                                    <ReactTable
-                                        data={children}
-                                        pageSize={children.length}
-                                        page={0}
-                                        showPagination={false}
-                                        columns={columns.map(col => ({
-                                            Header: col.Name,
-                                            accessor: (doc: Document) => [doc, col],
-                                            id: col.Id
-                                        }))}
-                                        column={{
-                                            ...ReactTableDefaults.column,
-                                            Cell: this.renderCell,
+                            <div className="collectionSchemaView-tableContainer" ref={measureRef} style={{ width: `calc(100% - ${this.splitPercentage}%)` }}>
+                                <ReactTable
+                                    data={children}
+                                    pageSize={children.length}
+                                    page={0}
+                                    showPagination={false}
+                                    columns={columns.map(col => ({
+                                        Header: col.Name,
+                                        accessor: (doc: Document) => [doc, col],
+                                        id: col.Id
+                                    }))}
+                                    column={{
+                                        ...ReactTableDefaults.column,
+                                        Cell: this.renderCell,
 
-                                        }}
-                                        getTrProps={this.getTrProps}
-                                    />
-                                </div>
-                            </div>
-                        }
+                                    }}
+                                    getTrProps={this.getTrProps}
+                                />
+                            </div>}
                     </Measure>
                     {dividerDragger}
                     <div className="collectionSchemaView-previewRegion" style={{ width: `calc(${this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0)}% - ${this.DIVIDER_WIDTH}px)` }}>
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 2db0cc4e2..60d1f7214 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -50,7 +50,7 @@ export class ImageBox extends React.Component<FieldViewProps> {
 
     onPointerDown = (e: React.PointerEvent): void => {
         if (Date.now() - this._lastTap < 300) {
-            if (e.buttons === 1 && this.props.isSelected()) {
+            if (e.buttons === 1) {
                 e.stopPropagation();
                 this._downX = e.clientX;
                 this._downY = e.clientY;
@@ -72,7 +72,7 @@ export class ImageBox extends React.Component<FieldViewProps> {
 
     lightbox = (path: string) => {
         const images = [path];
-        if (this._isOpen && this.props.isSelected()) {
+        if (this._isOpen) {
             return (<Lightbox
                 mainSrc={images[this._photoIndex]}
                 nextSrc={images[(this._photoIndex + 1) % images.length]}
-- 
cgit v1.2.3-70-g09d2


From 08c35582dc71623c2b7300458238b6ba25ff98d7 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 21 Mar 2019 15:30:15 -0400
Subject: fixed performance problem with loading schema with a lot of
 documents.

---
 .../views/collections/CollectionSchemaView.tsx     | 25 +++++++++++++++++-----
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 2 files changed, 21 insertions(+), 6 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index b986f394c..34b019244 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -2,7 +2,7 @@ import React = require("react")
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faCog } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from "mobx";
+import { action, computed, observable, trace, untracked } from "mobx";
 import { observer } from "mobx-react";
 import Measure from "react-measure";
 import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
@@ -141,6 +141,7 @@ export class CollectionSchemaView extends CollectionViewBase {
         };
     }
 
+    @computed
     get columns() {
         return this.props.Document.GetList<Key>(KeyStore.ColumnsKey, []);
     }
@@ -167,10 +168,19 @@ export class CollectionSchemaView extends CollectionViewBase {
             this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage == 0 ? 33 : 0);
         }
     }
-    findAllDocumentKeys = (): { [id: string]: boolean } => {
+
+    @computed
+    get findAllDocumentKeys(): { [id: string]: boolean } {
         const docs = this.props.Document.GetList<Document>(this.props.fieldKey, []);
         let keys: { [id: string]: boolean } = {}
-        docs.map(doc => doc.GetAllPrototypes().map(proto => proto._proxies.forEach((val: any, key: string) => keys[key] = false)));
+        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
+            //  invalidated and re-rendered.   This workaround will inquire all of the document fields before the options button is clicked.
+            //  then by the time the options button is clicked, all of the fields should be in place.  If a new field is added while this menu
+            //  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)
         return keys;
     }
@@ -228,13 +238,18 @@ export class CollectionSchemaView extends CollectionViewBase {
         }
     }
 
+    @observable _optionsActivated: number = 0;
+    @action
+    OptionsMenuDown = (e: React.PointerEvent) => {
+        this._optionsActivated++;
+    }
     render() {
         library.add(faCog);
         const columns = this.columns;
         const children = this.props.Document.GetList<Document>(this.props.fieldKey, []);
         const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
         //all the keys/columns that will be displayed in the schema
-        const allKeys = this.findAllDocumentKeys();
+        const allKeys = this.findAllDocumentKeys;
         let content = this._selectedIndex == -1 || !selected ? (null) : (
             <Measure onResize={this.setScaling}>
                 {({ measureRef }) =>
@@ -274,7 +289,7 @@ export class CollectionSchemaView extends CollectionViewBase {
                 </div>
             </div>
             }>
-            <button id="schemaOptionsMenuBtn"><FontAwesomeIcon style={{ color: "white" }} icon="cog" size="sm" /></button>
+            <button id="schemaOptionsMenuBtn" onPointerDown={this.OptionsMenuDown}><FontAwesomeIcon style={{ color: "white" }} icon="cog" size="sm" /></button>
         </Flyout>);
 
         return (
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ec1bf5d0e..6cc14ebcb 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -76,7 +76,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 }}
             />);
         return (
-            <div key={this.props.document.Id} className="docContainer" ref={reference} onPointerDown={onItemDown}>
+            <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 >)
-- 
cgit v1.2.3-70-g09d2


From 619c01891713b0ceb889ab45659b35f4b9fbc7e3 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Fri, 22 Mar 2019 07:15:22 -0400
Subject: Added basic fill down to schema view

---
 src/client/util/Scripting.ts                       |  2 +-
 src/client/util/type_decls.d                       |  7 ++++
 src/client/views/EditableView.tsx                  | 11 +++++-
 .../views/collections/CollectionSchemaView.tsx     | 43 ++++++++++++++--------
 src/fields/Document.ts                             |  8 +++-
 5 files changed, 51 insertions(+), 20 deletions(-)

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

diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 46bd1a206..4cf98472f 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -110,7 +110,7 @@ export function CompileScript(script: string, scope?: { [name: string]: any }, a
     let host = new ScriptingCompilerHost;
     let funcScript = `(function() {
         ${addReturn ? `return ${script};` : script}
-    })()`
+    }).apply(this)`
     host.writeFile("file.ts", funcScript);
     host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib);
     let program = ts.createProgram(["file.ts"], {}, host);
diff --git a/src/client/util/type_decls.d b/src/client/util/type_decls.d
index 679f73f42..dcf79285d 100644
--- a/src/client/util/type_decls.d
+++ b/src/client/util/type_decls.d
@@ -213,3 +213,10 @@ declare class Document extends Field {
     GetAllPrototypes(): Document[];
     MakeDelegate(): Document;
 }
+
+declare const KeyStore: {
+    [name: string]: Key;
+}
+
+// @ts-ignore
+declare const console: any;
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 579d6e6ad..29bf6add7 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -16,6 +16,8 @@ export interface EditableProps {
      *  */
     SetValue(value: string): boolean;
 
+    OnFillDown?(value: string): void;
+
     /**
      * The contents to render when not editing
      */
@@ -36,8 +38,13 @@ export class EditableView extends React.Component<EditableProps> {
 
     @action
     onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
-        if (e.key == "Enter" && !e.ctrlKey) {
-            if (this.props.SetValue(e.currentTarget.value)) {
+        if (e.key == "Enter") {
+            if (!e.ctrlKey) {
+                if (this.props.SetValue(e.currentTarget.value)) {
+                    this.editing = false;
+                }
+            } else if (this.props.OnFillDown) {
+                this.props.OnFillDown(e.currentTarget.value);
                 this.editing = false;
             }
         } else if (e.key == "Escape") {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 34b019244..8e0a38f05 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -85,12 +85,31 @@ export class CollectionSchemaView extends CollectionViewBase {
         )
         let reference = React.createRef<HTMLDivElement>();
         let onItemDown = setupDrag(reference, () => props.doc, (containingCollection: CollectionView) => this.props.removeDocument(props.doc));
+        let applyToDoc = (doc: Document, value: string) => {
+            let script = CompileScript(value, { this: doc }, true);
+            if (!script.compiled) {
+                return false;
+            }
+            let field = script();
+            if (field instanceof Field) {
+                doc.Set(props.fieldKey, field);
+                return true;
+            } else {
+                let dataField = ToField(field);
+                if (dataField) {
+                    doc.Set(props.fieldKey, dataField);
+                    return true;
+                }
+            }
+            return false;
+        }
         return (
             <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "36px" }} key={props.doc.Id} ref={reference}>
                 <EditableView
                     display={"inline"}
                     contents={contents}
-                    height={36} GetValue={() => {
+                    height={36}
+                    GetValue={() => {
                         let field = props.doc.Get(props.fieldKey);
                         if (field && field instanceof Field) {
                             return field.ToScriptString();
@@ -98,22 +117,14 @@ export class CollectionSchemaView extends CollectionViewBase {
                         return field || "";
                     }}
                     SetValue={(value: string) => {
-                        let script = CompileScript(value);
-                        if (!script.compiled) {
-                            return false;
-                        }
-                        let field = script();
-                        if (field instanceof Field) {
-                            props.doc.Set(props.fieldKey, field);
-                            return true;
-                        } else {
-                            let dataField = ToField(field);
-                            if (dataField) {
-                                props.doc.Set(props.fieldKey, dataField);
-                                return true;
+                        return applyToDoc(props.doc, value);
+                    }}
+                    OnFillDown={(value: string) => {
+                        this.props.Document.GetTAsync<ListField<Document>>(this.props.fieldKey, ListField).then((val) => {
+                            if (val) {
+                                val.Data.forEach(doc => applyToDoc(doc, value));
                             }
-                        }
-                        return false;
+                        })
                     }}>
                 </EditableView>
             </div>
diff --git a/src/fields/Document.ts b/src/fields/Document.ts
index fc4906d17..d098cc96d 100644
--- a/src/fields/Document.ts
+++ b/src/fields/Document.ts
@@ -132,7 +132,13 @@ export class Document extends Field {
         } else if (this._proxies.has(key.Id)) {
             Server.GetDocumentField(this, key, callback);
         } else {
-            callback(undefined);
+            this.GetTAsync(KeyStore.Prototype, Document, proto => {
+                if (proto) {
+                    proto.GetAsync(key, callback);
+                } else {
+                    callback(undefined);
+                }
+            })
         }
     }
 
-- 
cgit v1.2.3-70-g09d2


From 1238c172a2ac9fb7dfdee2588f141f2ae0c22b8e Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 22 Mar 2019 08:54:37 -0400
Subject: fixed schema scrolling

---
 src/client/views/collections/CollectionSchemaView.tsx | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 34b019244..696527049 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -238,6 +238,11 @@ export class CollectionSchemaView extends CollectionViewBase {
         }
     }
 
+    onWheel = (e: React.WheelEvent): void => {
+        if (this.props.active())
+            e.stopPropagation();
+    }
+
     @observable _optionsActivated: number = 0;
     @action
     OptionsMenuDown = (e: React.PointerEvent) => {
@@ -293,7 +298,7 @@ export class CollectionSchemaView extends CollectionViewBase {
         </Flyout>);
 
         return (
-            <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} ref={this._mainCont} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} >
+            <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel} ref={this._mainCont} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} >
                 <div className="collectionSchemaView-dropTarget" onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
                     <Measure onResize={this.setTableDimensions}>
                         {({ measureRef }) =>
-- 
cgit v1.2.3-70-g09d2


From 356991c6100a44ef45b4574b43c815383d9be751 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sat, 23 Mar 2019 00:22:52 -0400
Subject: Various fixes for the demo

---
 src/client/documents/Documents.ts                  | 17 ++++++---
 src/client/util/Scripting.ts                       |  8 +++--
 src/client/util/type_decls.d                       |  3 ++
 .../views/collections/CollectionFreeFormView.tsx   |  5 +++
 .../views/collections/CollectionSchemaView.tsx     | 42 ++++++++++++++++++----
 .../views/collections/CollectionViewBase.tsx       | 39 ++++++++++++++------
 src/client/views/nodes/DocumentView.tsx            |  7 ++++
 src/client/views/nodes/FieldView.tsx               | 18 +++++++++-
 src/client/views/nodes/FormattedTextBox.tsx        |  2 ++
 9 files changed, 117 insertions(+), 24 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 96a7332aa..a2444db06 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,6 +1,6 @@
 import { AudioField } from "../../fields/AudioField";
 import { Document } from "../../fields/Document";
-import { Field } from "../../fields/Field";
+import { Field, FieldWaiting } from "../../fields/Field";
 import { HtmlField } from "../../fields/HtmlField";
 import { ImageField } from "../../fields/ImageField";
 import { InkField, StrokeData } from "../../fields/InkField";
@@ -23,6 +23,7 @@ import { PDFBox } from "../views/nodes/PDFBox";
 import { VideoBox } from "../views/nodes/VideoBox";
 import { WebBox } from "../views/nodes/WebBox";
 import { HistogramBox } from "../views/nodes/HistogramBox";
+import { FieldView } from "../views/nodes/FieldView";
 
 export interface DocumentOptions {
     x?: number;
@@ -112,7 +113,7 @@ export namespace Documents {
     function GetImagePrototype(): Document {
         if (!imageProto) {
             imageProto = setupPrototypeOptions(imageProtoId, "IMAGE_PROTO", CollectionView.LayoutString("AnnotationsKey"),
-                { x: 0, y: 0, nativeWidth: 300, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations] });
+                { x: 0, y: 0, nativeWidth: 300, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] });
             imageProto.SetText(KeyStore.BackgroundLayout, ImageBox.LayoutString());
         }
         return imageProto;
@@ -155,7 +156,7 @@ export namespace Documents {
     function GetVideoPrototype(): Document {
         if (!videoProto) {
             videoProto = setupPrototypeOptions(videoProtoId, "VIDEO_PROTO", CollectionVideoView.LayoutString("AnnotationsKey"),
-                { x: 0, y: 0, nativeWidth: 600, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations] });
+                { x: 0, y: 0, nativeWidth: 600, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] });
             videoProto.SetNumber(KeyStore.CurPage, 0);
             videoProto.SetText(KeyStore.BackgroundLayout, VideoBox.LayoutString());
         }
@@ -218,6 +219,14 @@ export namespace Documents {
         return assignToDelegate(SetInstanceOptions(GetCollectionPrototype(), { ...options, viewType: CollectionViewType.Docking }, [config, TextField], id), options)
     }
 
+    export function CaptionDocument(doc: Document) {
+        const captionDoc = doc.CreateAlias();
+        captionDoc.SetText(KeyStore.OverlayLayout, FixedCaption());
+        captionDoc.SetNumber(KeyStore.Width, doc.GetNumber(KeyStore.Width, 0));
+        captionDoc.SetNumber(KeyStore.Height, doc.GetNumber(KeyStore.Height, 0));
+        return captionDoc;
+    }
+
     // example of custom display string for an image that shows a caption.
     function EmbeddedCaption() {
         return `<div style="height:100%">
@@ -228,7 +237,7 @@ export namespace Documents {
             + FormattedTextBox.LayoutString("CaptionKey") +
             `</div> 
         </div>` };
-    function FixedCaption(fieldName: string = "Caption") {
+    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") +
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 4cf98472f..4e97b9401 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -15,6 +15,8 @@ import { ListField } from "../../fields/ListField";
 
 // @ts-ignore
 import * as typescriptlib from '!!raw-loader!./type_decls.d'
+import { Documents } from "../documents/Documents";
+import { Key } from "../../fields/Key";
 
 
 export interface ExecutableScript {
@@ -28,9 +30,9 @@ function Compile(script: string | undefined, diagnostics: Opt<any[]>, scope: { [
 
     let func: () => Opt<Field>;
     if (compiled && script) {
-        let fieldTypes = [Document, NumberField, TextField, ImageField, RichTextField, ListField];
-        let paramNames = ["KeyStore", ...fieldTypes.map(fn => fn.name)];
-        let params: any[] = [KeyStore, ...fieldTypes]
+        let fieldTypes = [Document, NumberField, TextField, ImageField, RichTextField, ListField, Key];
+        let paramNames = ["KeyStore", "Documents", ...fieldTypes.map(fn => fn.name)];
+        let params: any[] = [KeyStore, Documents, ...fieldTypes]
         for (let prop in scope) {
             if (prop === "this") {
                 continue;
diff --git a/src/client/util/type_decls.d b/src/client/util/type_decls.d
index dcf79285d..4f69053b1 100644
--- a/src/client/util/type_decls.d
+++ b/src/client/util/type_decls.d
@@ -174,6 +174,7 @@ declare class ListField<T> extends BasicField<T[]>{
     Copy(): Field;
 }
 declare class Key extends Field {
+    constructor(name:string);
     Name: string;
     TrySetValue(value: any): boolean;
     GetValue(): any;
@@ -220,3 +221,5 @@ declare const KeyStore: {
 
 // @ts-ignore
 declare const console: any;
+
+declare const Documents: any;
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index da9f7b392..8f7b4cfe7 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -83,6 +83,10 @@ export class CollectionFreeFormView extends CollectionViewBase {
             const [x, y] = this.getTransform().transformPoint(screenX, screenY);
             de.data.droppedDocument.SetNumber(KeyStore.X, x);
             de.data.droppedDocument.SetNumber(KeyStore.Y, y);
+            if (!de.data.droppedDocument.GetNumber(KeyStore.Width, 0)) {
+                de.data.droppedDocument.SetNumber(KeyStore.Width, 300);
+                de.data.droppedDocument.SetNumber(KeyStore.Height, 300);
+            }
             this.bringToFront(de.data.droppedDocument);
         }
     }
@@ -259,6 +263,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
         const lvalue = this.props.Document.GetT<ListField<Document>>(this.props.fieldKey, ListField);
         if (lvalue && lvalue != FieldWaiting) {
             return lvalue.Data.map(doc => {
+                if (!doc) return null;
                 var page = doc.GetNumber(KeyStore.Page, 0);
                 return (page != curPage && page != 0) ? (null) :
                     (<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 8e0a38f05..7dd364449 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -1,6 +1,6 @@
 import React = require("react")
 import { library } from '@fortawesome/fontawesome-svg-core';
-import { faCog } from '@fortawesome/free-solid-svg-icons';
+import { faCog, faPlus } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable, trace, untracked } from "mobx";
 import { observer } from "mobx-react";
@@ -8,7 +8,7 @@ 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 } from "../../../fields/Field";
+import { Field, Opt, FieldWaiting } from "../../../fields/Field";
 import { Key } from "../../../fields/Key";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ListField } from "../../../fields/ListField";
@@ -24,6 +24,7 @@ import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
 import { CollectionView, COLLECTION_BORDER_WIDTH } from "./CollectionView";
 import { CollectionViewBase } from "./CollectionViewBase";
+import { TextField } from "../../../fields/TextField";
 
 
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
@@ -104,11 +105,11 @@ export class CollectionSchemaView extends CollectionViewBase {
             return false;
         }
         return (
-            <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "36px" }} key={props.doc.Id} ref={reference}>
+            <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "56px" }} key={props.doc.Id} ref={reference}>
                 <EditableView
                     display={"inline"}
                     contents={contents}
-                    height={36}
+                    height={56}
                     GetValue={() => {
                         let field = props.doc.Get(props.fieldKey);
                         if (field && field instanceof Field) {
@@ -249,23 +250,48 @@ export class CollectionSchemaView extends CollectionViewBase {
         }
     }
 
+    @action
+    addColumn = () => {
+        this.columns.push(new Key(this.newKeyName));
+        this.newKeyName = "";
+    }
+
+    @observable
+    newKeyName: string = "";
+
+    @action
+    newKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this.newKeyName = e.currentTarget.value;
+    }
+
     @observable _optionsActivated: number = 0;
     @action
     OptionsMenuDown = (e: React.PointerEvent) => {
         this._optionsActivated++;
     }
+
+    @observable previewScript: string = "this";
+    @action
+    onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this.previewScript = e.currentTarget.value;
+    }
+
     render() {
         library.add(faCog);
+        library.add(faPlus);
         const columns = this.columns;
         const children = this.props.Document.GetList<Document>(this.props.fieldKey, []);
         const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
         //all the keys/columns that will be displayed in the schema
         const allKeys = this.findAllDocumentKeys;
+        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) : (
             <Measure onResize={this.setScaling}>
                 {({ measureRef }) =>
                     <div className="collectionSchemaView-content" ref={measureRef}>
-                        <DocumentView Document={selected}
+                        {doc instanceof Document ? <DocumentView Document={doc}
                             AddDocument={this.props.addDocument} RemoveDocument={this.props.removeDocument}
                             isTopMost={false}
                             SelectOnLoad={false}
@@ -275,7 +301,9 @@ export class CollectionSchemaView extends CollectionViewBase {
                             PanelHeight={this.getPanelHeight}
                             ContainingCollectionView={this.props.CollectionView}
                             focus={this.focusDocument}
-                        />
+                        /> : null}
+                        <input value={this.previewScript} onChange={this.onPreviewScriptChange}
+                            style={{ position: 'absolute', bottom: '0px' }} />
                     </div>
                 }
             </Measure>
@@ -297,6 +325,8 @@ export class CollectionSchemaView extends CollectionViewBase {
                             return (<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>
                 </div>
             </div>
             }>
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index f33007196..7d903899d 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -17,6 +17,7 @@ import { NumberField } from "../../../fields/NumberField";
 import { DocumentManager } from "../../util/DocumentManager";
 import request = require("request");
 import { ServerUtils } from "../../../server/ServerUtil";
+import { Server } from "../../Server";
 
 export interface CollectionViewProps {
     fieldKey: Key;
@@ -59,17 +60,20 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
         let email = CurrentUserUtils.email;
         if (id && email) {
             let textInfo: [string, string] = [id, email];
-            doc.GetOrCreateAsync<ListField<CursorEntry>>(KeyStore.Cursors, ListField, field => {
-                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);
+            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);
+                    }
+                }))
             })
-
-
         }
     }
 
@@ -111,6 +115,21 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
             ctor = Documents.PdfDocument;
         }
         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, };
         }
@@ -130,7 +149,7 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
         e.stopPropagation()
         e.preventDefault()
 
-        if (html && html.indexOf("<img") != 0) {
+        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);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index cfe67c8fe..55aa1ccab 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -18,6 +18,7 @@ import { ContextMenu } from "../ContextMenu";
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
 import React = require("react");
+import { ServerUtils } from "../../../server/ServerUtil";
 
 
 export interface DocumentViewProps {
@@ -283,6 +284,12 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         ContextMenu.Instance.addItem({ description: "Fields", event: this.fieldsClicked })
         ContextMenu.Instance.addItem({ description: "Center", event: () => this.props.focus(this.props.Document) })
         ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document) })
+        ContextMenu.Instance.addItem({
+            description: "Copy URL",
+            event: () => {
+                Utils.CopyText(ServerUtils.prepend("/doc/" + this.props.Document.Id));
+            }
+        });
         ContextMenu.Instance.addItem({
             description: "Copy ID",
             event: () => {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index f6343c631..4e83ec7b9 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -16,6 +16,9 @@ import { VideoBox } from "./VideoBox";
 import { AudioBox } from "./AudioBox";
 import { AudioField } from "../../../fields/AudioField";
 import { ListField } from "../../../fields/ListField";
+import { DocumentContentsView } from "./DocumentContentsView";
+import { Transform } from "../../util/Transform";
+import { KeyStore } from "../../../fields/KeyStore";
 
 
 //
@@ -65,7 +68,20 @@ export class FieldView extends React.Component<FieldViewProps> {
             return <AudioBox {...this.props} />
         }
         else if (field instanceof Document) {
-            return <div>{field.Title}</div>
+            return (<DocumentContentsView Document={field}
+                AddDocument={undefined}
+                RemoveDocument={undefined}
+                ScreenToLocalTransform={() => Transform.Identity}
+                ContentScaling={() => 1}
+                PanelWidth={() => 100}
+                PanelHeight={() => 100}
+                isTopMost={true}
+                SelectOnLoad={false}
+                focus={() => { }}
+                isSelected={() => false}
+                select={() => false}
+                layoutKey={KeyStore.Layout}
+                ContainingCollectionView={undefined} />)
         }
         else if (field instanceof ListField) {
             return (<div>
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index ba9bd9566..30fa1342e 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -167,6 +167,8 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
             onKeyPress={this.onKeyPress}
             onPointerDown={this.onPointerDown}
             onContextMenu={this.specificContextMenu}
+            // tfs: do we need this event handler
+            onKeyDown={this.onKeyPress}
             onWheel={this.onPointerWheel}
             ref={this._ref} />)
     }
-- 
cgit v1.2.3-70-g09d2


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

---
 src/client/views/Main.tsx                          |  2 +-
 .../views/collections/CollectionDockingView.tsx    |  2 +-
 src/client/views/collections/CollectionPDFView.tsx | 17 ++++++------
 .../views/collections/CollectionSchemaView.tsx     | 13 +++++-----
 .../views/collections/CollectionVideoView.tsx      |  7 ++---
 src/client/views/collections/CollectionView.tsx    | 15 ++++++-----
 .../views/collections/CollectionViewBase.tsx       |  9 ++-----
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/nodes/AudioBox.tsx                |  2 +-
 src/client/views/nodes/DocumentContentsView.tsx    | 23 ++++++++++++++---
 src/client/views/nodes/DocumentView.tsx            | 11 +-------
 src/client/views/nodes/FieldView.tsx               | 11 ++++----
 src/client/views/nodes/FormattedTextBox.tsx        | 10 ++++----
 src/client/views/nodes/ImageBox.tsx                |  8 +++---
 src/client/views/nodes/KeyValueBox.tsx             |  4 +--
 src/client/views/nodes/KeyValuePair.tsx            | 13 +++++-----
 src/client/views/nodes/PDFBox.tsx                  | 30 +++++++++++-----------
 src/client/views/nodes/VideoBox.tsx                | 12 ++++-----
 src/client/views/nodes/WebBox.tsx                  |  4 +--
 19 files changed, 98 insertions(+), 97 deletions(-)

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

diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 75855c3d1..82fd1e7c9 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -228,7 +228,7 @@ export class Main extends React.Component {
                 PanelWidth={this.pwidthFunc}
                 PanelHeight={this.pheightFunc}
                 isTopMost={true}
-                SelectOnLoad={false}
+                selectOnLoad={false}
                 focus={this.focusDocument}
                 ContainingCollectionView={undefined} />
     }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index f123149dd..c67354139 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -317,7 +317,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                     PanelHeight={this._nativeHeight}
                     ScreenToLocalTransform={this.ScreenToLocalTransform}
                     isTopMost={true}
-                    SelectOnLoad={false}
+                    selectOnLoad={false}
                     focus={(doc: Document) => { }}
                     ContainingCollectionView={undefined} />
             </div>
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 4d2daf149..6bb3bacc7 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -8,6 +8,7 @@ import { CollectionViewProps } from "./CollectionViewBase";
 import "./CollectionPDFView.scss"
 import React = require("react");
 import { FieldId } from "../../../fields/Field";
+import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
 
 
 @observer
@@ -30,7 +31,8 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
             <div className="collectionPdfView-buttonTray" key="tray" style={{ transform: `scale(${scaling}, ${scaling})` }}>
                 <button className="collectionPdfView-backward" onClick={this.onPageBack}>{"<"}</button>
                 <button className="collectionPdfView-forward" onClick={this.onPageForward}>{">"}</button>
-            </div>);
+            </div>
+        );
     }
 
     // "inherited" CollectionView API starts here...
@@ -47,13 +49,12 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
         }
     }
 
-    get collectionViewType(): CollectionViewType { return CollectionViewType.Freeform; }
-    get subView(): any { return CollectionView.SubView(this); }
-
     render() {
-        return (<div className="collectionPdfView-cont" onContextMenu={this.specificContextMenu}>
-            {this.subView}
-            {this.props.isSelected() ? this.uIButtons : (null)}
-        </div>)
+        return (
+            <div className="collectionPdfView-cont" onContextMenu={this.specificContextMenu}>
+                <CollectionFreeFormView {...CollectionView.SubViewProps(this)} />
+                {this.props.isSelected() ? this.uIButtons : (null)}
+            </div>
+        )
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 0ff6c3b40..0c7e28691 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -73,19 +73,18 @@ export class CollectionSchemaView extends CollectionViewBase {
 
     renderCell = (rowProps: CellInfo) => {
         let props: FieldViewProps = {
-            doc: rowProps.value[0],
+            Document: rowProps.value[0],
             fieldKey: rowProps.value[1],
             isSelected: () => false,
             select: () => { },
             isTopMost: false,
-            bindings: {},
             selectOnLoad: false,
         }
         let contents = (
             <FieldView {...props} />
         )
         let reference = React.createRef<HTMLDivElement>();
-        let onItemDown = setupDrag(reference, () => props.doc, (containingCollection: CollectionView) => this.props.removeDocument(props.doc));
+        let onItemDown = setupDrag(reference, () => props.Document, (containingCollection: CollectionView) => this.props.removeDocument(props.Document));
         let applyToDoc = (doc: Document, value: string) => {
             let script = CompileScript(value, { this: doc }, true);
             if (!script.compiled) {
@@ -105,20 +104,20 @@ export class CollectionSchemaView extends CollectionViewBase {
             return false;
         }
         return (
-            <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "56px" }} key={props.doc.Id} ref={reference}>
+            <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "56px" }} key={props.Document.Id} ref={reference}>
                 <EditableView
                     display={"inline"}
                     contents={contents}
                     height={56}
                     GetValue={() => {
-                        let field = props.doc.Get(props.fieldKey);
+                        let field = props.Document.Get(props.fieldKey);
                         if (field && field instanceof Field) {
                             return field.ToScriptString();
                         }
                         return field || "";
                     }}
                     SetValue={(value: string) => {
-                        return applyToDoc(props.doc, value);
+                        return applyToDoc(props.Document, value);
                     }}
                     OnFillDown={(value: string) => {
                         this.props.Document.GetTAsync<ListField<Document>>(this.props.fieldKey, ListField).then((val) => {
@@ -298,7 +297,7 @@ export class CollectionSchemaView extends CollectionViewBase {
                         {doc instanceof Document ? <DocumentView Document={doc}
                             AddDocument={this.props.addDocument} RemoveDocument={this.props.removeDocument}
                             isTopMost={false}
-                            SelectOnLoad={false}
+                            selectOnLoad={false}
                             ScreenToLocalTransform={this.getPreviewTransform}
                             ContentScaling={this.getContentScaling}
                             PanelWidth={this.getPanelWidth}
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 470a853e3..1f0143d87 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -8,6 +8,7 @@ import { CollectionViewProps } from "./CollectionViewBase";
 import React = require("react");
 import { FieldId } from "../../../fields/Field";
 import "./CollectionVideoView.scss"
+import { CollectionFreeFormView } from "./CollectionFreeFormView";
 
 
 @observer
@@ -116,14 +117,10 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
         }
     }
 
-    get collectionViewType(): CollectionViewType { return CollectionViewType.Freeform; }
-    get subView(): any { return CollectionView.SubView(this); }
-
-
     render() {
         trace();
         return (<div className="collectionVideoView-cont" ref={this.mainCont} onContextMenu={this.specificContextMenu}>
-            {this.subView}
+            <CollectionFreeFormView {...CollectionView.SubViewProps(this)} />
             {this.props.isSelected() ? this.uIButtons : (null)}
         </div>)
     }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index a1498e0ae..e6351618c 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -10,7 +10,7 @@ import { NumberField } from "../../../fields/NumberField";
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
 import { CollectionDockingView } from "./CollectionDockingView";
 import { CollectionSchemaView } from "./CollectionSchemaView";
-import { CollectionViewProps } from "./CollectionViewBase";
+import { CollectionViewProps, SubCollectionViewProps } from "./CollectionViewBase";
 import { CollectionTreeView } from "./CollectionTreeView";
 import { Field, FieldId, FieldWaiting } from "../../../fields/Field";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
@@ -39,7 +39,6 @@ export class CollectionView extends React.Component<CollectionViewProps> {
     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); }
-    get subView() { return CollectionView.SubView(this); }
 
     public static Active(self: CollectionView): boolean {
         var isSelected = self.props.isSelected();
@@ -140,9 +139,13 @@ export class CollectionView extends React.Component<CollectionViewProps> {
         }
     }
 
-    public static SubView(self: CollectionView) {
-        let subProps = { ...self.props, addDocument: self.addDocument, removeDocument: self.removeDocument, active: self.active, CollectionView: self }
-        switch (self.collectionViewType) {
+    public static SubViewProps(self: CollectionView): SubCollectionViewProps {
+        return { ...self.props, addDocument: self.addDocument, removeDocument: self.removeDocument, active: self.active, CollectionView: self }
+    }
+
+    private get SubView() {
+        let subProps = CollectionView.SubViewProps(this);
+        switch (this.collectionViewType) {
             case CollectionViewType.Freeform: return (<CollectionFreeFormView {...subProps} />)
             case CollectionViewType.Schema: return (<CollectionSchemaView {...subProps} />)
             case CollectionViewType.Docking: return (<CollectionDockingView {...subProps} />)
@@ -153,7 +156,7 @@ export class CollectionView extends React.Component<CollectionViewProps> {
 
     render() {
         return (<div className="collectionView-cont" onContextMenu={this.specificContextMenu}>
-            {this.subView}
+            {this.SubView}
         </div>)
     }
 }
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index 987f3cb6c..c5a6c53b3 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -17,15 +17,10 @@ import { NumberField } from "../../../fields/NumberField";
 import request = require("request");
 import { ServerUtils } from "../../../server/ServerUtil";
 import { Server } from "../../Server";
+import { FieldViewProps } from "../nodes/FieldView";
 
-export interface CollectionViewProps {
-    fieldKey: Key;
-    Document: Document;
+export interface CollectionViewProps extends FieldViewProps {
     ScreenToLocalTransform: () => Transform;
-    isSelected: () => boolean;
-    isTopMost: boolean;
-    select: (ctrlPressed: boolean) => void;
-    bindings: any;
     panelWidth: () => number;
     panelHeight: () => number;
     focus: (doc: Document) => void;
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,
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 6daf15f5f..0ce991485 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -28,7 +28,7 @@ export class AudioBox extends React.Component<FieldViewProps> {
 
 
     render() {
-        let field = this.props.doc.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";
 
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 77551649c..a5e4b1a17 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,6 +1,6 @@
 import { computed } from "mobx";
 import { observer } from "mobx-react";
-import { FieldWaiting } from "../../../fields/Field";
+import { FieldWaiting, Field } from "../../../fields/Field";
 import { Key } from "../../../fields/Key";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ListField } from "../../../fields/ListField";
@@ -11,7 +11,7 @@ import { CollectionSchemaView } from "../collections/CollectionSchemaView";
 import { CollectionVideoView } from "../collections/CollectionVideoView";
 import { CollectionView } from "../collections/CollectionView";
 import { AudioBox } from "./AudioBox";
-import { DocumentViewProps, JsxBindings } from "./DocumentView";
+import { DocumentViewProps } from "./DocumentView";
 import "./DocumentView.scss";
 import { FormattedTextBox } from "./FormattedTextBox";
 import { ImageBox } from "./ImageBox";
@@ -21,8 +21,16 @@ import { VideoBox } from "./VideoBox";
 import { WebBox } from "./WebBox";
 import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox";
 import React = require("react");
+import { Document } from "../../../fields/Document";
+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'>
+export interface JsxBindings {
+    props: BindingProps;
+    [keyName: string]: BindingProps | Field;
+}
 
 @observer
 export class DocumentContentsView extends React.Component<DocumentViewProps & {
@@ -36,7 +44,16 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
 
 
     CreateBindings(): JsxBindings {
-        let bindings: JsxBindings = { ...this.props, };
+        let { Document, isSelected, select, isTopMost, selectOnLoad } = this.props;
+        let bindings: JsxBindings = {
+            props: {
+                Document,
+                isSelected,
+                select,
+                isTopMost,
+                selectOnLoad,
+            }
+        };
         for (const key of this.layoutKeys) {
             bindings[key.Name + "Key"] = key; // this maps string values of the form <keyname>Key to an actual key Kestore.keyname  e.g,   "DataKey" => KeyStore.Data
         }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 71613ca4f..6530dafc6 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -32,7 +32,7 @@ export interface DocumentViewProps {
     PanelWidth: () => number;
     PanelHeight: () => number;
     focus: (doc: Document) => void;
-    SelectOnLoad: boolean;
+    selectOnLoad: boolean;
 }
 export interface JsxArgs extends DocumentViewProps {
     Keys: { [name: string]: Key }
@@ -76,15 +76,6 @@ export function FakeJsxArgs(keys: string[], fields: string[] = []): JsxArgs {
     return args;
 }
 
-export interface JsxBindings {
-    Document: Document;
-    isSelected: () => boolean;
-    select: (isCtrlPressed: boolean) => void;
-    isTopMost: boolean;
-    SelectOnLoad: boolean;
-    [prop: string]: any;
-}
-
 
 
 @observer
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 4e83ec7b9..43688989b 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -28,23 +28,22 @@ import { KeyStore } from "../../../fields/KeyStore";
 //
 export interface FieldViewProps {
     fieldKey: Key;
-    doc: Document;
+    Document: Document;
     isSelected: () => boolean;
-    select: () => void;
+    select: (isCtrlPressed: boolean) => void;
     isTopMost: boolean;
     selectOnLoad: boolean;
-    bindings: any;
 }
 
 @observer
 export class FieldView extends React.Component<FieldViewProps> {
     public static LayoutString(fieldType: { name: string }, fieldStr: string = "DataKey") {
-        return `<${fieldType.name} doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={${fieldStr}} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost} />`;
+        return `<${fieldType.name} {...props} fieldKey={${fieldStr}} />`;
     }
 
     @computed
     get field(): FieldValue<Field> {
-        const { doc, fieldKey } = this.props;
+        const { Document: doc, fieldKey } = this.props;
         return doc.Get(fieldKey);
     }
     render() {
@@ -76,7 +75,7 @@ export class FieldView extends React.Component<FieldViewProps> {
                 PanelWidth={() => 100}
                 PanelHeight={() => 100}
                 isTopMost={true}
-                SelectOnLoad={false}
+                selectOnLoad={false}
                 focus={() => { }}
                 isSelected={() => false}
                 select={() => false}
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 512ad7d70..99ba9addc 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -55,7 +55,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         if (this._editorView) {
             const state = this._editorView.state.apply(tx);
             this._editorView.updateState(state);
-            const { doc, fieldKey } = this.props;
+            const { Document: doc, fieldKey } = this.props;
             doc.SetDataOnPrototype(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
             // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
         }
@@ -74,7 +74,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
             ]
         };
 
-        let field = this.props.doc.GetT(this.props.fieldKey, RichTextField);
+        let field = this.props.Document.GetT(this.props.fieldKey, RichTextField);
         if (field && field != FieldWaiting && field.Data) {
             state = EditorState.fromJSON(config, JSON.parse(field.Data));
         } else {
@@ -88,7 +88,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         }
 
         this._reactionDisposer = reaction(() => {
-            const field = this.props.doc.GetT(this.props.fieldKey, RichTextField);
+            const field = this.props.Document.GetT(this.props.fieldKey, RichTextField);
             return field && field != FieldWaiting ? field.Data : undefined;
         }, (field) => {
             if (field && this._editorView) {
@@ -96,7 +96,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
             }
         })
         if (this.props.selectOnLoad) {
-            this.props.select();
+            this.props.select(false);
             this._editorView!.focus();
         }
     }
@@ -116,7 +116,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
 
     @action
     onChange(e: React.ChangeEvent<HTMLInputElement>) {
-        const { fieldKey, doc } = this.props;
+        const { fieldKey, Document: doc } = this.props;
         doc.SetOnPrototype(fieldKey, new RichTextField(e.target.value))
         // doc.SetData(fieldKey, e.target.value, RichTextField);
     }
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 60d1f7214..9b9dfe645 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -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.doc.SetNumber(KeyStore.NativeHeight, this.props.doc.GetNumber(KeyStore.NativeWidth, 0) * h / w)
+        this.props.Document.SetNumber(KeyStore.NativeHeight, this.props.Document.GetNumber(KeyStore.NativeWidth, 0) * h / w)
     }
 
     componentDidMount() {
@@ -91,7 +91,7 @@ export class ImageBox extends React.Component<FieldViewProps> {
     }
 
     specificContextMenu = (e: React.MouseEvent): void => {
-        let field = this.props.doc.GetT(this.props.fieldKey, ImageField);
+        let field = this.props.Document.GetT(this.props.fieldKey, ImageField);
         if (field && field !== FieldWaiting) {
             let url = field.Data.href;
             ContextMenu.Instance.addItem({
@@ -103,10 +103,10 @@ export class ImageBox extends React.Component<FieldViewProps> {
     }
 
     render() {
-        let field = this.props.doc.Get(this.props.fieldKey);
+        let field = this.props.Document.Get(this.props.fieldKey);
         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.doc.GetNumber(KeyStore.NativeWidth, 1);
+        let nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 1);
         return (
             <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} />
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 283c1f732..a3478143d 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -34,7 +34,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     onEnterKey = (e: React.KeyboardEvent): void => {
         if (e.key == 'Enter') {
             if (this._keyInput && this._valueInput) {
-                let doc = this.props.doc.GetT(KeyStore.Data, Document);
+                let doc = this.props.Document.GetT(KeyStore.Data, Document);
                 if (!doc || doc == FieldWaiting) {
                     return
                 }
@@ -69,7 +69,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     }
 
     createTable = () => {
-        let doc = this.props.doc.GetT(KeyStore.Data, Document);
+        let doc = this.props.Document.GetT(KeyStore.Data, Document);
         if (!doc || doc == FieldWaiting) {
             return <tr><td>Loading...</td></tr>
         }
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 7ed5ee272..2fc11328f 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -43,12 +43,11 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
 
         }
         let props: FieldViewProps = {
-            doc: this.props.doc,
+            Document: this.props.doc,
             fieldKey: this.key,
             isSelected: () => false,
             select: () => { },
             isTopMost: false,
-            bindings: {},
             selectOnLoad: false,
         }
         let contents = (
@@ -61,15 +60,15 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
                     <div className="container">
                         <div>{this.key.Name}</div>
                         <button className="delete" onClick={() => {
-                            let field = props.doc.Get(props.fieldKey);
+                            let field = props.Document.Get(props.fieldKey);
                             if (field && field instanceof Field) {
-                                props.doc.Set(props.fieldKey, undefined);
+                                props.Document.Set(props.fieldKey, undefined);
                             }
                         }}>X</button>
                     </div>
                 </td>
                 <td><EditableView contents={contents} height={36} GetValue={() => {
-                    let field = props.doc.Get(props.fieldKey);
+                    let field = props.Document.Get(props.fieldKey);
                     if (field && field instanceof Field) {
                         return field.ToScriptString();
                     }
@@ -82,12 +81,12 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
                         }
                         let field = script();
                         if (field instanceof Field) {
-                            props.doc.Set(props.fieldKey, field);
+                            props.Document.Set(props.fieldKey, field);
                             return true;
                         } else {
                             let dataField = ToField(field);
                             if (dataField) {
-                                props.doc.Set(props.fieldKey, dataField);
+                                props.Document.Set(props.fieldKey, dataField);
                                 return true;
                             }
                         }
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 28a1f9757..3b5e3a570 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -87,8 +87,8 @@ export class PDFBox extends React.Component<FieldViewProps> {
     @observable private _interactive: boolean = false;
     @observable private _loaded: boolean = false;
 
-    @computed private get curPage() { return this.props.doc.GetNumber(KeyStore.CurPage, 1); }
-    @computed private get thumbnailPage() { return this.props.doc.GetNumber(KeyStore.ThumbnailPage, -1); }
+    @computed private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, 1); }
+    @computed private get thumbnailPage() { return this.props.Document.GetNumber(KeyStore.ThumbnailPage, -1); }
 
     componentDidMount() {
         this._reactionDisposer = reaction(
@@ -379,10 +379,10 @@ export class PDFBox extends React.Component<FieldViewProps> {
         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 })
+                { width: me.props.Document.GetNumber(KeyStore.NativeWidth, 0), height: me.props.Document.GetNumber(KeyStore.NativeHeight, 0), quality: 0.5 })
                 .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));
+                    me.props.Document.SetData(KeyStore.Thumbnail, new URL(dataUrl), ImageField);
+                    me.props.Document.SetNumber(KeyStore.ThumbnailPage, me.props.Document.GetNumber(KeyStore.CurPage, -1));
                 })
                 .catch(function (error: any) {
                     console.error('oops, something went wrong!', error);
@@ -409,7 +409,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
         }
 
         // bcz: the number of pages should really be set when the document is imported.
-        this.props.doc.SetNumber(KeyStore.NumPages, page._transport.numPages);
+        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)]
         }
@@ -421,13 +421,13 @@ export class PDFBox extends React.Component<FieldViewProps> {
         // bcz: the nativeHeight should really be set when the document is imported.
         //      also, the native dimensions could be different for different pages of the PDF
         //      so this design is flawed.
-        var nativeWidth = this.props.doc.GetNumber(KeyStore.NativeWidth, 0);
-        if (!this.props.doc.GetNumber(KeyStore.NativeHeight, 0)) {
+        var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
+        if (!this.props.Document.GetNumber(KeyStore.NativeHeight, 0)) {
             var nativeHeight = nativeWidth * r.entry.height / r.entry.width;
-            this.props.doc.SetNumber(KeyStore.Height, nativeHeight / nativeWidth * this.props.doc.GetNumber(KeyStore.Width, 0));
-            this.props.doc.SetNumber(KeyStore.NativeHeight, nativeHeight);
+            this.props.Document.SetNumber(KeyStore.Height, nativeHeight / nativeWidth * this.props.Document.GetNumber(KeyStore.Width, 0));
+            this.props.Document.SetNumber(KeyStore.NativeHeight, nativeHeight);
         }
-        if (!this.props.doc.GetT(KeyStore.Thumbnail, ImageField)) {
+        if (!this.props.Document.GetT(KeyStore.Thumbnail, ImageField)) {
             this.saveThumbnail();
         }
     }
@@ -436,8 +436,8 @@ export class PDFBox extends React.Component<FieldViewProps> {
     get pdfContent() {
         let page = this.curPage;
         const renderHeight = 2400;
-        let pdfUrl = this.props.doc.GetT(this.props.fieldKey, PDFField);
-        let xf = this.props.doc.GetNumber(KeyStore.NativeHeight, 0) / renderHeight;
+        let pdfUrl = this.props.Document.GetT(this.props.fieldKey, PDFField);
+        let xf = this.props.Document.GetNumber(KeyStore.NativeHeight, 0) / renderHeight;
         return <div className="pdfBox-contentContainer" key="container" style={{ transform: `scale(${xf}, ${xf})` }}>
             <Document file={window.origin + RouteStore.corsProxy + `/${pdfUrl}`}>
                 <Measure onResize={this.setScaling}>
@@ -454,7 +454,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
     @computed
     get pdfRenderer() {
         let proxy = this._loaded ? (null) : this.imageProxyRenderer;
-        let pdfUrl = this.props.doc.GetT(this.props.fieldKey, PDFField);
+        let pdfUrl = this.props.Document.GetT(this.props.fieldKey, PDFField);
         if ((!this._interactive && proxy) || !pdfUrl || pdfUrl == FieldWaiting) {
             return proxy;
         }
@@ -468,7 +468,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
 
     @computed
     get imageProxyRenderer() {
-        let thumbField = this.props.doc.Get(KeyStore.Thumbnail);
+        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" :
                 thumbField instanceof ImageField ? thumbField.Data.href : "http://cs.brown.edu/people/bcz/prairie.jpg";
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 7c0db83a8..72495a964 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -20,7 +20,7 @@ export class VideoBox extends React.Component<FieldViewProps> {
         super(props);
     }
 
-    @computed private get curPage() { return this.props.doc.GetNumber(KeyStore.CurPage, -1); }
+    @computed private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, -1); }
 
 
     _loaded: boolean = false;
@@ -31,12 +31,12 @@ export class VideoBox extends React.Component<FieldViewProps> {
             // bcz: the nativeHeight should really be set when the document is imported.
             //      also, the native dimensions could be different for different pages of the PDF
             //      so this design is flawed.
-            var nativeWidth = this.props.doc.GetNumber(KeyStore.NativeWidth, 0);
-            var nativeHeight = this.props.doc.GetNumber(KeyStore.NativeHeight, 0);
+            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)) {
-                this.props.doc.SetNumber(KeyStore.Height, newNativeHeight / nativeWidth * this.props.doc.GetNumber(KeyStore.Width, 0));
-                this.props.doc.SetNumber(KeyStore.NativeHeight, newNativeHeight);
+                this.props.Document.SetNumber(KeyStore.Height, newNativeHeight / nativeWidth * this.props.Document.GetNumber(KeyStore.Width, 0));
+                this.props.Document.SetNumber(KeyStore.NativeHeight, newNativeHeight);
             }
         } else {
             this._loaded = true;
@@ -56,7 +56,7 @@ export class VideoBox extends React.Component<FieldViewProps> {
     }
 
     render() {
-        let field = this.props.doc.GetT(this.props.fieldKey, VideoField);
+        let field = this.props.Document.GetT(this.props.fieldKey, VideoField);
         if (!field || field === FieldWaiting) {
             return <div>Loading</div>
         }
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 2ca8d49ce..92e2fabd4 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -16,10 +16,10 @@ export class WebBox extends React.Component<FieldViewProps> {
         super(props);
     }
 
-    @computed get html(): string { return this.props.doc.GetHtml(KeyStore.Data, ""); }
+    @computed get html(): string { return this.props.Document.GetHtml(KeyStore.Data, ""); }
 
     render() {
-        let field = this.props.doc.Get(this.props.fieldKey);
+        let field = this.props.Document.Get(this.props.fieldKey);
         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";
 
-- 
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/CollectionSchemaView.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/CollectionSchemaView.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 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/CollectionSchemaView.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/CollectionSchemaView.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 44aa6fe49de97998fd84a6d6331501c8a1d7a775 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sun, 7 Apr 2019 01:14:31 -0400
Subject: Refactored scripting to seperate compilation and execution

---
 src/client/util/Scripting.ts                       | 97 +++++++++++++++-------
 src/client/util/Transform.ts                       | 14 ++--
 .../views/collections/CollectionSchemaView.tsx     |  7 +-
 src/client/views/nodes/KeyValueBox.tsx             |  6 +-
 src/client/views/nodes/KeyValuePair.tsx            |  6 +-
 src/fields/NumberField.ts                          |  2 +-
 6 files changed, 87 insertions(+), 45 deletions(-)

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

diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 4e97b9401..7c0649a6a 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -18,41 +18,57 @@ import * as typescriptlib from '!!raw-loader!./type_decls.d'
 import { Documents } from "../documents/Documents";
 import { Key } from "../../fields/Key";
 
+export interface ScriptSucccess {
+    success: true;
+    result: any;
+}
+
+export interface ScriptError {
+    success: false;
+    error: any;
+}
+
+export type ScriptResult = ScriptSucccess | ScriptError;
 
-export interface ExecutableScript {
-    (): any;
+export interface CompileSuccess {
+    compiled: true;
+    run(args?: { [name: string]: any }): ScriptResult;
+}
 
-    compiled: boolean;
+export interface CompileError {
+    compiled: false;
+    errors: any[];
 }
 
-function Compile(script: string | undefined, diagnostics: Opt<any[]>, scope: { [name: string]: any }): ExecutableScript {
-    const compiled = !(diagnostics && diagnostics.some(diag => diag.category == ts.DiagnosticCategory.Error));
+export type CompiledScript = CompileSuccess | CompileError;
+
+function Run(script: string | undefined, customParams: string[], diagnostics: any[]): CompiledScript {
+    const errors = diagnostics.some(diag => diag.category == ts.DiagnosticCategory.Error);
+    if (errors || !script) {
+        return { compiled: false, errors: diagnostics };
+    }
 
-    let func: () => Opt<Field>;
-    if (compiled && script) {
-        let fieldTypes = [Document, NumberField, TextField, ImageField, RichTextField, ListField, Key];
-        let paramNames = ["KeyStore", "Documents", ...fieldTypes.map(fn => fn.name)];
-        let params: any[] = [KeyStore, Documents, ...fieldTypes]
-        for (let prop in scope) {
-            if (prop === "this") {
+    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 compiledFunction = new Function(...paramNames, `return ${script}`);
+    let run = (args: { [name: string]: any } = {}): ScriptResult => {
+        let argsArray: any[] = [];
+        for (let name of customParams) {
+            if (name === "this") {
                 continue;
             }
-            paramNames.push(prop);
-            params.push(scope[prop]);
+            argsArray.push(args[name]);
+        }
+        let thisParam = args["this"];
+        try {
+            const result = compiledFunction.apply(thisParam, params).apply(thisParam, argsArray);
+            return { success: true, result };
+        } catch (error) {
+            return { success: false, error };
         }
-        let thisParam = scope["this"];
-        let compiledFunction = new Function(...paramNames, script);
-        func = function (): Opt<Field> {
-            return compiledFunction.apply(thisParam, params)
-        };
-    } else {
-        func = () => undefined;
     }
-
-    return Object.assign(func,
-        {
-            compiled
-        });
+    return { compiled: true, run };
 }
 
 interface File {
@@ -108,20 +124,39 @@ class ScriptingCompilerHost {
     }
 }
 
-export function CompileScript(script: string, scope?: { [name: string]: any }, addReturn: boolean = false): ExecutableScript {
+export interface ScriptOptions {
+    requiredType?: string;
+    addReturn?: boolean;
+    params?: { [name: string]: string };
+}
+
+export function CompileScript(script: string, { requiredType = "", addReturn = false, params = {} }: ScriptOptions = {}): CompiledScript {
     let host = new ScriptingCompilerHost;
-    let funcScript = `(function() {
+    let paramArray: string[] = [];
+    if ("this" in params) {
+        paramArray.push("this");
+    }
+    for (const key in params) {
+        if (key === "this") continue;
+        paramArray.push(key);
+    }
+    let paramString = paramArray.map(key => `${key}: ${params[key]}`).join(", ");
+    let funcScript = `(function(${paramString})${requiredType ? `: ${requiredType}` : ''} {
         ${addReturn ? `return ${script};` : script}
-    }).apply(this)`
+    })`;
     host.writeFile("file.ts", funcScript);
     host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib);
     let program = ts.createProgram(["file.ts"], {}, host);
     let testResult = program.emit();
-    let outputText = "return " + host.readFile("file.js");
+    let outputText = host.readFile("file.js");
 
     let diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics);
 
-    return Compile(outputText, diagnostics, scope || {});
+    return Run(outputText, paramArray, diagnostics);
+}
+
+export function OrLiteralType(returnType: string): string {
+    return `${returnType} | string | number`;
 }
 
 export function ToField(data: any): Opt<Field> {
diff --git a/src/client/util/Transform.ts b/src/client/util/Transform.ts
index 3e1039166..060c5da82 100644
--- a/src/client/util/Transform.ts
+++ b/src/client/util/Transform.ts
@@ -17,45 +17,45 @@ export class Transform {
         this._scale = scale;
     }
 
-    translate = (x: number, y: number): Transform => {
+    translate = (x: number, y: number): this => {
         this._translateX += x;
         this._translateY += y;
         return this;
     }
 
-    scale = (scale: number): Transform => {
+    scale = (scale: number): this => {
         this._scale *= scale;
         this._translateX *= scale;
         this._translateY *= scale;
         return this;
     }
 
-    scaleAbout = (scale: number, x: number, y: number): Transform => {
+    scaleAbout = (scale: number, x: number, y: number): this => {
         this._translateX += x * this._scale - x * this._scale * scale;
         this._translateY += y * this._scale - y * this._scale * scale;
         this._scale *= scale;
         return this;
     }
 
-    transform = (transform: Transform): Transform => {
+    transform = (transform: Transform): this => {
         this._translateX = transform._translateX + transform._scale * this._translateX;
         this._translateY = transform._translateY + transform._scale * this._translateY;
         this._scale *= transform._scale;
         return this;
     }
 
-    preTranslate = (x: number, y: number): Transform => {
+    preTranslate = (x: number, y: number): this => {
         this._translateX += this._scale * x;
         this._translateY += this._scale * y;
         return this;
     }
 
-    preScale = (scale: number): Transform => {
+    preScale = (scale: number): this => {
         this._scale *= scale;
         return this;
     }
 
-    preTransform = (transform: Transform): Transform => {
+    preTransform = (transform: Transform): this => {
         this._translateX += transform._translateX * this._scale;
         this._translateY += transform._translateY * this._scale;
         this._scale *= transform._scale;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 0ff6c3b40..afe530c1a 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -87,11 +87,13 @@ export class CollectionSchemaView extends CollectionViewBase {
         let reference = React.createRef<HTMLDivElement>();
         let onItemDown = setupDrag(reference, () => props.doc, (containingCollection: CollectionView) => this.props.removeDocument(props.doc));
         let applyToDoc = (doc: Document, value: string) => {
-            let script = CompileScript(value, { this: doc }, true);
+            let script = CompileScript(value, { addReturn: true, params: { this: "Document" } });
             if (!script.compiled) {
                 return false;
             }
-            let field = script();
+            const res = script.run({ this: doc });
+            if (!res.success) return false;
+            const field = res.result;
             if (field instanceof Field) {
                 doc.Set(props.fieldKey, field);
                 return true;
@@ -121,6 +123,7 @@ export class CollectionSchemaView extends CollectionViewBase {
                         return applyToDoc(props.doc, value);
                     }}
                     OnFillDown={(value: string) => {
+                        //TODO This should be able to be refactored to compile the script once
                         this.props.Document.GetTAsync<ListField<Document>>(this.props.fieldKey, ListField).then((val) => {
                             if (val) {
                                 val.Data.forEach(doc => applyToDoc(doc, value));
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 283c1f732..9bd6c1052 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -40,11 +40,13 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
                 }
                 let realDoc = doc;
 
-                let script = CompileScript(this._valueInput, undefined, true);
+                let script = CompileScript(this._valueInput, { addReturn: true });
                 if (!script.compiled) {
                     return;
                 }
-                let field = script();
+                let res = script.run();
+                if (!res.success) return;
+                const field = res.result;
                 if (field instanceof Field) {
                     realDoc.Set(new Key(this._keyInput), field);
                 } else {
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 7ed5ee272..5647f45bf 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -76,11 +76,13 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
                     return field || "";
                 }}
                     SetValue={(value: string) => {
-                        let script = CompileScript(value, undefined, true);
+                        let script = CompileScript(value, { addReturn: true });
                         if (!script.compiled) {
                             return false;
                         }
-                        let field = script();
+                        let res = script.run();
+                        if (!res.success) return false;
+                        const field = res.result;
                         if (field instanceof Field) {
                             props.doc.Set(props.fieldKey, field);
                             return true;
diff --git a/src/fields/NumberField.ts b/src/fields/NumberField.ts
index 47dfc74cb..e0c8648de 100644
--- a/src/fields/NumberField.ts
+++ b/src/fields/NumberField.ts
@@ -8,7 +8,7 @@ export class NumberField extends BasicField<number> {
     }
 
     ToScriptString(): string {
-        return "new NumberField(this.Data)";
+        return `new NumberField(${this.Data})`;
     }
 
     Copy() {
-- 
cgit v1.2.3-70-g09d2


From bf05eac7677997075bc58d2b70c3e101e2e87560 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sun, 7 Apr 2019 17:39:23 -0400
Subject: Refactored schema scripting Changed tree view style to not be global

---
 .../views/collections/CollectionSchemaView.tsx     | 21 ++++--
 .../views/collections/CollectionTreeView.scss      | 86 +++++++++++-----------
 2 files changed, 55 insertions(+), 52 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index afe530c1a..b10aaba98 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -86,12 +86,8 @@ export class CollectionSchemaView extends CollectionViewBase {
         )
         let reference = React.createRef<HTMLDivElement>();
         let onItemDown = setupDrag(reference, () => props.doc, (containingCollection: CollectionView) => this.props.removeDocument(props.doc));
-        let applyToDoc = (doc: Document, value: string) => {
-            let script = CompileScript(value, { addReturn: true, params: { this: "Document" } });
-            if (!script.compiled) {
-                return false;
-            }
-            const res = script.run({ this: doc });
+        let applyToDoc = (doc: Document, run: (args?: { [name: string]: any }) => any) => {
+            const res = run({ this: doc });
             if (!res.success) return false;
             const field = res.result;
             if (field instanceof Field) {
@@ -120,13 +116,22 @@ export class CollectionSchemaView extends CollectionViewBase {
                         return field || "";
                     }}
                     SetValue={(value: string) => {
-                        return applyToDoc(props.doc, value);
+                        let script = CompileScript(value, { addReturn: true, params: { this: "Document" } });
+                        if (!script.compiled) {
+                            return false;
+                        }
+                        return applyToDoc(props.doc, script.run);
                     }}
                     OnFillDown={(value: string) => {
+                        let script = CompileScript(value, { addReturn: true, params: { this: "Document" } });
+                        if (!script.compiled) {
+                            return;
+                        }
+                        const run = script.run;
                         //TODO This should be able to be refactored to compile the script once
                         this.props.Document.GetTAsync<ListField<Document>>(this.props.fieldKey, ListField).then((val) => {
                             if (val) {
-                                val.Data.forEach(doc => applyToDoc(doc, value));
+                                val.Data.forEach(doc => applyToDoc(doc, run));
                             }
                         })
                     }}>
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 5a14aa54d..f2affbf55 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -1,64 +1,62 @@
 @import "../global_variables";
-#body {
+
+.collectionTreeView-dropTarget {
+    border: 0px solid transparent;
+    border-radius: $border-radius;
+    box-sizing: border-box;
+    height: 100%;
     padding: 20px;
     padding-left: 10px;
     padding-right: 0px;
     background: $light-color-secondary;
     font-size: 13px;
     overflow: scroll;
-}
 
-ul {
-    list-style: none;
-    padding-left: 20px;
-}
+    ul {
+        list-style: none;
+        padding-left: 20px;
+    }
 
-li {
-    margin: 5px 0;
-}
+    li {
+        margin: 5px 0;
+    }
 
-.collection-child {
-    margin-top: 10px;
-    margin-bottom: 10px;
-}
+    .collection-child {
+        margin-top: 10px;
+        margin-bottom: 10px;
+    }
 
-.no-indent {
-    padding-left: 0;
-}
+    .no-indent {
+        padding-left: 0;
+    }
 
-.bullet {
-    width: 1.5em;
-    display: inline-block;
-    color: $intermediate-color;
-}
+    .bullet {
+        width: 1.5em;
+        display: inline-block;
+        color: $intermediate-color;
+    }
 
-.coll-title {
-    font-size: 24px;
-    margin-bottom: 20px;
-}
+    .coll-title {
+        font-size: 24px;
+        margin-bottom: 20px;
+    }
 
-.collectionTreeView-dropTarget {
-    border: 0px solid transparent;
-    border-radius: $border-radius;
-    box-sizing: border-box;
-    height: 100%;
-}
+    .docContainer {
+        display: inline-table;
+    }
 
-.docContainer {
-    display: inline-table;
-}
+    .docContainer:hover {
+        .delete-button {
+            display: inline;
+            width: auto;
+        }
+    }
 
-.docContainer:hover {
     .delete-button {
+        color: $intermediate-color;
+        float: right;
+        margin-left: 15px;
+        margin-top: 3px;
         display: inline;
-        width: auto;
     }
-}
-
-.delete-button {
-    color: $intermediate-color;
-    float: right;
-    margin-left: 15px;
-    margin-top: 3px;
-    display: inline;
 }
\ No newline at end of file
-- 
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/CollectionSchemaView.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/CollectionSchemaView.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 cf86d1b7917f0317af550293344f784a341bd7b9 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Tue, 9 Apr 2019 02:45:06 -0400
Subject: Added script field and added list of scripts to list field

---
 src/client/util/Scripting.ts                       | 21 +++--
 .../views/collections/CollectionBaseView.tsx       | 21 ++++-
 .../views/collections/CollectionSchemaView.tsx     |  4 +-
 src/fields/Document.ts                             |  7 +-
 src/fields/ListField.ts                            | 96 ++++++++++++++++++----
 src/fields/ScriptField.ts                          | 64 +++++++++++++++
 src/server/Message.ts                              |  3 +-
 src/server/ServerUtil.ts                           |  3 +
 8 files changed, 187 insertions(+), 32 deletions(-)
 create mode 100644 src/fields/ScriptField.ts

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

diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 468484928..9015f21cf 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -30,8 +30,10 @@ export interface ScriptError {
 
 export type ScriptResult = ScriptSucccess | ScriptError;
 
-export interface CompileSuccess {
-    compiled: true;
+export interface CompiledScript {
+    readonly compiled: true;
+    readonly originalScript: string;
+    readonly options: Readonly<ScriptOptions>;
     run(args?: { [name: string]: any }): ScriptResult;
 }
 
@@ -40,9 +42,9 @@ export interface CompileError {
     errors: any[];
 }
 
-export type CompiledScript = CompileSuccess | CompileError;
+export type CompileResult = CompiledScript | CompileError;
 
-function Run(script: string | undefined, customParams: string[], diagnostics: any[]): CompiledScript {
+function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult {
     const errors = diagnostics.some(diag => diag.category === ts.DiagnosticCategory.Error);
     if (errors || !script) {
         return { compiled: false, errors: diagnostics };
@@ -68,7 +70,7 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
             return { success: false, error };
         }
     };
-    return { compiled: true, run };
+    return { compiled: true, run, originalScript, options };
 }
 
 interface File {
@@ -130,7 +132,7 @@ export interface ScriptOptions {
     params?: { [name: string]: string };
 }
 
-export function CompileScript(script: string, { requiredType = "", addReturn = false, params = {} }: ScriptOptions = {}): CompiledScript {
+export function CompileScript(script: string, { requiredType = "", addReturn = false, params = {} }: ScriptOptions = {}): CompileResult {
     let host = new ScriptingCompilerHost;
     let paramArray: string[] = [];
     if ("this" in params) {
@@ -140,7 +142,10 @@ export function CompileScript(script: string, { requiredType = "", addReturn = f
         if (key === "this") continue;
         paramArray.push(key);
     }
-    let paramString = paramArray.map(key => `${key}: ${params[key]}`).join(", ");
+    let paramString = paramArray.map(key => {
+        const val = params[key];
+        return `${key}: ${val}`;
+    }).join(", ");
     let funcScript = `(function(${paramString})${requiredType ? `: ${requiredType}` : ''} {
         ${addReturn ? `return ${script};` : script}
     })`;
@@ -152,7 +157,7 @@ export function CompileScript(script: string, { requiredType = "", addReturn = f
 
     let diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics);
 
-    return Run(outputText, paramArray, diagnostics);
+    return Run(outputText, paramArray, diagnostics, script, { requiredType, addReturn, params });
 }
 
 export function OrLiteralType(returnType: string): string {
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 32462a4c1..4afa7cbf6 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -10,6 +10,8 @@ import { ListField } from '../../../fields/ListField';
 import { action } from 'mobx';
 import { Transform } from '../../util/Transform';
 import { observer } from 'mobx-react';
+import { CompileScript } from '../../util/Scripting';
+import { ScriptField } from '../../../fields/ScriptField';
 
 export enum CollectionViewType {
     Invalid,
@@ -106,7 +108,24 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         } else {
             let proto = props.Document.GetPrototype();
             if (!proto || proto === FieldWaiting || !this.createsCycle(proto, doc)) {
-                props.Document.SetOnPrototype(props.fieldKey, new ListField([doc]));
+                const field = new ListField([doc]);
+                // const script = CompileScript(`
+                //     if(added) {
+                //         console.log("added " + field.Title);
+                //     } else {
+                //         console.log("removed " + field.Title);
+                //     }
+                // `, {
+                //         addReturn: false,
+                //         params: {
+                //             field: Document.name,
+                //             added: "boolean"
+                //         }
+                //     });
+                // if (script.compiled) {
+                //     field.addScript(new ScriptField(script));
+                // }
+                props.Document.SetOnPrototype(props.fieldKey, field);
             }
             else {
                 return false;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 52933818f..587f60b3d 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -121,14 +121,14 @@ export class CollectionSchemaView extends CollectionSubView {
                         return field || "";
                     }}
                     SetValue={(value: string) => {
-                        let script = CompileScript(value, { addReturn: true, params: { this: "Document" } });
+                        let script = CompileScript(value, { addReturn: true, params: { this: Document.name } });
                         if (!script.compiled) {
                             return false;
                         }
                         return applyToDoc(props.Document, script.run);
                     }}
                     OnFillDown={(value: string) => {
-                        let script = CompileScript(value, { addReturn: true, params: { this: "Document" } });
+                        let script = CompileScript(value, { addReturn: true, params: { this: Document.name } });
                         if (!script.compiled) {
                             return;
                         }
diff --git a/src/fields/Document.ts b/src/fields/Document.ts
index 4584660fb..60eaf5b51 100644
--- a/src/fields/Document.ts
+++ b/src/fields/Document.ts
@@ -16,10 +16,7 @@ import { HistogramField } from "../client/northstar/dash-fields/HistogramField";
 
 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 fields: ObservableMap<string, { key: Key; field: Field }> = new ObservableMap();
     public _proxies: ObservableMap<string, FieldId> = new ObservableMap();
 
     constructor(id?: string, save: boolean = true) {
@@ -406,7 +403,7 @@ export class Document extends Field {
                         }
                         else if (field) {
                             copy.Set(key!, field.Copy());
- }
+                        }
                 });
             }
         });
diff --git a/src/fields/ListField.ts b/src/fields/ListField.ts
index b6eab5f86..8311e737b 100644
--- a/src/fields/ListField.ts
+++ b/src/fields/ListField.ts
@@ -5,12 +5,18 @@ import { Types } from "../server/Message";
 import { BasicField } from "./BasicField";
 import { Field, FieldId } from "./Field";
 import { FieldMap } from "../client/SocketStub";
+import { ScriptField } from "./ScriptField";
 
 export class ListField<T extends Field> extends BasicField<T[]> {
     private _proxies: string[] = [];
-    constructor(data: T[] = [], id?: FieldId, save: boolean = true) {
+    private _scriptIds: string[] = [];
+    private scripts: ScriptField[] = [];
+
+    constructor(data: T[] = [], scripts: ScriptField[] = [], id?: FieldId, save: boolean = true) {
         super(data, save, id);
+        this.scripts = scripts;
         this.updateProxies();
+        this._scriptIds = this.scripts.map(script => script.Id);
         if (save) {
             Server.UpdateField(this);
         }
@@ -25,17 +31,22 @@ export class ListField<T extends Field> extends BasicField<T[]> {
             this.observeDisposer();
         }
         this.observeDisposer = observe(this.Data as IObservableArray<T>, (change: IArrayChange<T> | IArraySplice<T>) => {
+            const target = change.object;
             this.updateProxies();
             if (change.type === "splice") {
+                this.runScripts(change.removed, false);
                 UndoManager.AddEvent({
-                    undo: () => this.Data.splice(change.index, change.addedCount, ...change.removed),
-                    redo: () => this.Data.splice(change.index, change.removedCount, ...change.added)
+                    undo: () => target.splice(change.index, change.addedCount, ...change.removed),
+                    redo: () => target.splice(change.index, change.removedCount, ...change.added)
                 });
+                this.runScripts(change.added, true);
             } else {
+                this.runScripts([change.oldValue], false);
                 UndoManager.AddEvent({
-                    undo: () => this.Data[change.index] = change.oldValue,
-                    redo: () => this.Data[change.index] = change.newValue
+                    undo: () => target[change.index] = change.oldValue,
+                    redo: () => target[change.index] = change.newValue
                 });
+                this.runScripts([change.newValue], true);
             }
             if (!this._processingServerUpdate) {
                 Server.UpdateField(this);
@@ -43,19 +54,60 @@ export class ListField<T extends Field> extends BasicField<T[]> {
         });
     }
 
+    private runScripts(fields: T[], added: boolean) {
+        for (const script of this.scripts) {
+            this.runScript(fields, script, added);
+        }
+    }
+
+    private runScript(fields: T[], script: ScriptField, added: boolean) {
+        if (!this._processingServerUpdate) {
+            for (const field of fields) {
+                script.script.run({ field, added });
+            }
+        }
+    }
+
+    addScript(script: ScriptField) {
+        this.scripts.push(script);
+        this._scriptIds.push(script.Id);
+
+        this.runScript(this.Data, script, true);
+        UndoManager.AddEvent({
+            undo: () => this.removeScript(script),
+            redo: () => this.addScript(script),
+        });
+        Server.UpdateField(this);
+    }
+
+    removeScript(script: ScriptField) {
+        const index = this.scripts.indexOf(script);
+        if (index === -1) {
+            return;
+        }
+        this.scripts.splice(index, 1);
+        this._scriptIds.splice(index, 1);
+        UndoManager.AddEvent({
+            undo: () => this.addScript(script),
+            redo: () => this.removeScript(script),
+        });
+        this.runScript(this.Data, script, false);
+        Server.UpdateField(this);
+    }
+
     protected setData(value: T[]) {
+        this.runScripts(this.data, false);
+
         this.data = observable(value);
         this.updateProxies();
         this.observeList();
+        this.runScripts(this.data, true);
     }
 
     private updateProxies() {
         this._proxies = this.Data.map(field => field.Id);
     }
 
-    UpdateFromServer(fields: string[]) {
-        this._proxies = fields;
-    }
     private arraysEqual(a: any[], b: any[]) {
         if (a === b) return true;
         if (a === null || b === null) return false;
@@ -73,7 +125,7 @@ export class ListField<T extends Field> extends BasicField<T[]> {
     }
 
     init(callback: (field: Field) => any) {
-        Server.GetFields(this._proxies, action((fields: FieldMap) => {
+        const fieldsPromise = Server.GetFields(this._proxies).then(action((fields: FieldMap) => {
             if (!this.arraysEqual(this._proxies, this.data.map(field => field.Id))) {
                 var dataids = this.data.map(d => d.Id);
                 var proxies = this._proxies.map(p => p);
@@ -102,8 +154,13 @@ export class ListField<T extends Field> extends BasicField<T[]> {
                 }
                 this._processingServerUpdate = false;
             }
-            callback(this);
         }));
+
+        const scriptsPromise = Server.GetFields(this._scriptIds).then((fields: FieldMap) => {
+            this.scripts = this._scriptIds.map(id => fields[id] as ScriptField);
+        });
+
+        Promise.all([fieldsPromise, scriptsPromise]).then(() => callback(this));
     }
 
     ToScriptString(): string {
@@ -114,17 +171,26 @@ export class ListField<T extends Field> extends BasicField<T[]> {
         return new ListField<T>(this.Data);
     }
 
-    ToJson(): { type: Types, data: string[], _id: string } {
+
+    UpdateFromServer(data: { fields: string[], scripts: string[] }) {
+        this._proxies = data.fields;
+        this._scriptIds = data.scripts;
+    }
+    ToJson(): { type: Types, data: { fields: string[], scripts: string[] }, _id: string } {
         return {
             type: Types.List,
-            data: this._proxies || [],
+            data: {
+                fields: this._proxies,
+                scripts: this._scriptIds,
+            },
             _id: this.Id
         };
     }
 
-    static FromJson(id: string, ids: string[]): ListField<Field> {
-        let list = new ListField([], id, false);
-        list._proxies = ids;
+    static FromJson(id: string, data: { fields: string[], scripts: string[] }): ListField<Field> {
+        let list = new ListField([], [], id, false);
+        list._proxies = data.fields;
+        list._scriptIds = data.scripts;
         return list;
     }
 }
\ No newline at end of file
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
new file mode 100644
index 000000000..24c1d9b3a
--- /dev/null
+++ b/src/fields/ScriptField.ts
@@ -0,0 +1,64 @@
+import { Field, FieldId } from "./Field";
+import { Types } from "../server/Message";
+import { CompileScript, ScriptOptions, CompiledScript } from "../client/util/Scripting";
+import { Server } from "../client/Server";
+
+export interface ScriptData {
+    script: string;
+    options: ScriptOptions;
+}
+
+export class ScriptField extends Field {
+    readonly script: CompiledScript;
+
+    constructor(script: CompiledScript, id?: FieldId, save: boolean = true) {
+        super(id);
+
+        this.script = script;
+
+        if (save) {
+            Server.UpdateField(this);
+        }
+    }
+
+    static FromJson(id: string, data: ScriptData): ScriptField {
+        const script = CompileScript(data.script, data.options);
+        if (!script.compiled) {
+            throw new Error("Can't compile script");
+        }
+        return new ScriptField(script, id, false);
+    }
+
+    ToScriptString() {
+        return "new ScriptField(...)";
+    }
+
+    GetValue() {
+        return this.script;
+    }
+
+    TrySetValue(): boolean {
+        throw new Error("Script fields currently can't be modified");
+    }
+
+    UpdateFromServer() {
+        throw new Error("Script fields currently can't be updated");
+    }
+
+    ToJson(): { _id: string, type: Types, data: ScriptData } {
+        const { options, originalScript } = this.script;
+        return {
+            _id: this.Id,
+            type: Types.Script,
+            data: {
+                script: originalScript,
+                options
+            },
+        };
+    }
+
+    Copy(): Field {
+        //Script fields are currently immutable, so we can fake copy them
+        return this;
+    }
+}
\ No newline at end of file
diff --git a/src/server/Message.ts b/src/server/Message.ts
index 0274609bb..d22e5c17c 100644
--- a/src/server/Message.ts
+++ b/src/server/Message.ts
@@ -61,7 +61,8 @@ export enum Types {
     PDF,
     Tuple,
     HistogramOp,
-    Boolean
+    Boolean,
+    Script,
 }
 
 export class DocumentTransfer implements Transferable {
diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts
index 2c2bfd0c9..0973f82b1 100644
--- a/src/server/ServerUtil.ts
+++ b/src/server/ServerUtil.ts
@@ -18,6 +18,7 @@ import { PDFField } from "../fields/PDFField";
 import { TupleField } from "../fields/TupleField";
 import { BooleanField } from "../fields/BooleanField";
 import { HistogramField } from "../client/northstar/dash-fields/HistogramField";
+import { ScriptField } from "../fields/ScriptField";
 
 export class ServerUtils {
     public static prepend(extension: string): string {
@@ -60,6 +61,8 @@ export class ServerUtils {
                 return new PDFField(new URL(data), id, false);
             case Types.List:
                 return ListField.FromJson(id, data);
+            case Types.Script:
+                return ScriptField.FromJson(id, data);
             case Types.Audio:
                 return new AudioField(new URL(data), id, false);
             case Types.Video:
-- 
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/CollectionSchemaView.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 354d65953cc6e29386b465ab2a12e85b443aad56 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 11 Apr 2019 08:57:23 -0400
Subject: fixed schema view context menu

---
 src/client/views/collections/CollectionSchemaView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index c67f8cd97..f1b3e1b8f 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -254,7 +254,7 @@ export class CollectionSchemaView extends CollectionSubView {
     focusDocument = (doc: Document) => { };
 
     onPointerDown = (e: React.PointerEvent): void => {
-        if (this.props.isSelected()) {
+        if (e.button === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
             e.stopPropagation();
         }
     }
-- 
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/CollectionSchemaView.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 0c9d3637bde9825efe93c55afce8270b2a2e979c Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 12 Apr 2019 11:18:23 -0400
Subject: cleaned up and enabled linking to a top-level document

---
 src/client/util/DragManager.ts                     | 29 ++++++++++++++++++++-
 src/client/views/DocumentDecorations.tsx           | 27 ++-----------------
 src/client/views/Main.tsx                          |  4 +--
 .../views/collections/CollectionDockingView.tsx    | 30 +++-------------------
 .../views/collections/CollectionSchemaView.tsx     |  4 +--
 .../views/collections/CollectionTreeView.tsx       |  4 +--
 src/client/views/nodes/DocumentView.tsx            |  3 ---
 7 files changed, 39 insertions(+), 62 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 2562ba18a..6a7047725 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -6,8 +6,10 @@ 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";
 
-export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) {
+export function SetupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) {
     let onRowMove = action((e: PointerEvent): void => {
         e.stopPropagation();
         e.preventDefault();
@@ -39,6 +41,31 @@ export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc:
     return onItemDown;
 }
 
+export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: 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 moddrag = [] as Document[];
+        for (const draggedDoc of draggedDocs) {
+            let doc = await draggedDoc.GetTAsync(KeyStore.AnnotationOn, Document);
+            if (doc) moddrag.push(doc);
+        }
+        let dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs);
+        DragManager.StartDocumentDrag([dragEle], dragData, x, y, {
+            handlers: {
+                dragComplete: action(emptyFunction),
+            },
+            hideSource: false
+        });
+    }
+}
+
 export namespace DragManager {
     export function Root() {
         const root = document.getElementById("root");
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 69623b8f0..03b16fb0b 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -7,7 +7,7 @@ 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 { DragManager, DragLinksAsDocuments } from "../util/DragManager";
 import { SelectionManager } from "../util/SelectionManager";
 import { CollectionView } from "./collections/CollectionView";
 import './DocumentDecorations.scss';
@@ -246,34 +246,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             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 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 moddrag = [] as Document[];
-                for (const draggedDoc of draggedDocs) {
-                    let doc = await draggedDoc.GetTAsync(KeyStore.AnnotationOn, Document);
-                    if (doc) moddrag.push(doc);
-                }
-                let dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs);
-                DragManager.StartDocumentDrag([this._linkButton.current], dragData, e.x, e.y, {
-                    handlers: {
-                        dragComplete: action(emptyFunction),
-                    },
-                    hideSource: false
-                });
-            }
+            DragLinksAsDocuments(this._linkButton.current, e.x, e.y, SelectionManager.SelectedDocuments()[0].props.Document);
         }
         e.stopPropagation();
     }
 
-
     onPointerMove = (e: PointerEvent): void => {
         e.stopPropagation();
         e.preventDefault();
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index c4c4a6bf9..60f7098fb 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -28,7 +28,7 @@ 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, DragManager } from '../util/DragManager';
 import { Transform } from '../util/Transform';
 import { UndoManager } from '../util/UndoManager';
 import { CollectionDockingView } from './collections/CollectionDockingView';
@@ -344,7 +344,7 @@ export class Main extends React.Component {
                 <ul id="add-options-list">
                     {btns.map(btn =>
                         <li key={btn[1]} ><div ref={btn[0]}>
-                            <button className="round-button add-button" title={btn[2]} onPointerDown={setupDrag(btn[0], btn[3])}>
+                            <button className="round-button add-button" title={btn[2]} onPointerDown={SetupDrag(btn[0], btn[3])}>
                                 <FontAwesomeIcon icon={btn[1]} size="sm" />
                             </button>
                         </div></li>)}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index f2f41e821..e54c25474 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -16,7 +16,7 @@ import "./CollectionDockingView.scss";
 import React = require("react");
 import { SubCollectionViewProps } from "./CollectionSubView";
 import { ServerUtils } from "../../../server/ServerUtil";
-import { DragManager } from "../../util/DragManager";
+import { DragManager, DragLinksAsDocuments } from "../../util/DragManager";
 import { TextField } from "../../../fields/TextField";
 import { ListField } from "../../../fields/ListField";
 
@@ -201,32 +201,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             let y = e.clientY;
             let docid = (e.target as any).DashDocId;
             let tab = (e.target as any).parentElement as HTMLElement;
-            Server.GetField(docid, action(async (sourceDoc: Opt<Field>) => {
-                if (sourceDoc instanceof 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 moddrag = [] as Document[];
-                        for (const draggedDoc of draggedDocs) {
-                            let doc = await draggedDoc.GetTAsync(KeyStore.AnnotationOn, Document);
-                            if (doc) moddrag.push(doc);
-                        }
-                        let dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs);
-                        DragManager.StartDocumentDrag([tab], dragData, x, y, {
-                            handlers: {
-                                dragComplete: action(emptyFunction),
-                            },
-                            hideSource: false
-                        });
-                    }
-                }
-            }));
+            Server.GetField(docid, action(async (sourceDoc: Opt<Field>) =>
+                (sourceDoc instanceof Document) && DragLinksAsDocuments(tab, x, y, sourceDoc)));
         } else
             if ((className === "lm_title" || className === "lm_tab lm_active") && !e.shiftKey) {
                 e.stopPropagation();
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 0cb88eab0..f53b2a76e 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -13,7 +13,7 @@ import { Key } from "../../../fields/Key";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ListField } from "../../../fields/ListField";
 import { Server } from "../../Server";
-import { setupDrag } from "../../util/DragManager";
+import { SetupDrag } from "../../util/DragManager";
 import { CompileScript, ToField } from "../../util/Scripting";
 import { Transform } from "../../util/Transform";
 import { anchorPoints, Flyout } from "../DocumentDecorations";
@@ -90,7 +90,7 @@ export class CollectionSchemaView extends CollectionSubView {
             <FieldView {...props} />
         );
         let reference = React.createRef<HTMLDivElement>();
-        let onItemDown = setupDrag(reference, () => props.Document, this.props.moveDocument);
+        let onItemDown = SetupDrag(reference, () => props.Document, this.props.moveDocument);
         let applyToDoc = (doc: Document, run: (args?: { [name: string]: any }) => any) => {
             const res = run({ this: doc });
             if (!res.success) return false;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 20ec16822..51a02fc25 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, DragManager } from "../../util/DragManager";
+import { SetupDrag, DragManager } from "../../util/DragManager";
 import { EditableView } from "../EditableView";
 import "./CollectionTreeView.scss";
 import { CollectionView } from "./CollectionView";
@@ -77,7 +77,7 @@ class TreeView extends React.Component<TreeViewProps> {
      */
     renderTitle() {
         let reference = React.createRef<HTMLDivElement>();
-        let onItemDown = setupDrag(reference, () => this.props.document, this.props.moveDocument, this.props.copyOnDrag);
+        let onItemDown = SetupDrag(reference, () => this.props.document, this.props.moveDocument, this.props.copyOnDrag);
         let editableView = (titleString: string) =>
             (<EditableView
                 display={"inline"}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 649104d6c..06182d1a6 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -243,9 +243,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         if (de.data instanceof DragManager.LinkDragData) {
             let sourceDoc: Document = de.data.linkSourceDocument;
             let destDoc: Document = this.props.Document;
-            if (this.props.isTopMost) {
-                return;
-            }
             let linkDoc: Document = new Document();
 
             destDoc.GetTAsync(KeyStore.Prototype, Document).then(protoDest =>
-- 
cgit v1.2.3-70-g09d2


From 0afa18bfc219c597ad55a52ffc5a2086c3d110d2 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 12 Apr 2019 14:15:05 -0400
Subject: changed signatures of focus functions.

---
 src/Utils.ts                                       |  3 +++
 src/client/views/DocumentDecorations.tsx           |  2 +-
 src/client/views/Main.tsx                          |  8 ++++----
 .../views/collections/CollectionDockingView.tsx    |  4 ++--
 .../views/collections/CollectionSchemaView.tsx     |  8 +++-----
 src/client/views/nodes/FieldView.tsx               |  4 ++--
 src/client/views/nodes/KeyValuePair.tsx            |  4 ++--
 src/client/views/nodes/LinkBox.tsx                 | 24 ++++++++--------------
 8 files changed, 25 insertions(+), 32 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index b0e66787e..ae194bcc7 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -2,6 +2,7 @@ import v4 = require('uuid/v4');
 import v5 = require("uuid/v5");
 import { Socket } from 'socket.io';
 import { Message, Types } from './server/Message';
+import { Document } from './fields/Document';
 
 export class Utils {
 
@@ -96,4 +97,6 @@ export function returnFalse() {
 
 export function emptyFunction() { }
 
+export function emptyDocFunction(doc: Document) { console.log("focus " + doc.Title) }
+
 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/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 15ac0a8fc..c172e870e 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -244,7 +244,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     }
 
     onLinkButtonMoved = async (e: PointerEvent) => {
-        if (this._linkButton.current !== null) {
+        if (this._linkButton.current !== null && (e.movementX > 1 || e.movementY > 1)) {
             document.removeEventListener("pointermove", this.onLinkButtonMoved);
             document.removeEventListener("pointerup", this.onLinkButtonUp);
 
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 60f7098fb..419de6fc1 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -15,7 +15,7 @@ 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 } from '../../Utils';
+import { Utils, returnTrue, emptyFunction, emptyDocFunction } from '../../Utils';
 import * as rp from 'request-promise';
 import { RouteStore } from '../../server/RouteStore';
 import { ServerUtils } from '../../server/ServerUtil';
@@ -202,7 +202,6 @@ export class Main extends React.Component {
 
     pwidthFunc = () => this.pwidth;
     pheightFunc = () => this.pheight;
-    focusDocument = (doc: Document) => { };
     noScaling = () => 1;
 
     @observable _textDoc?: Document = undefined;
@@ -280,7 +279,8 @@ 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={this._textFieldKey!} isOverlay={true} 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={emptyDocFunction} />
                 </div>
             </ div>;
         }
@@ -299,7 +299,7 @@ export class Main extends React.Component {
                 PanelHeight={this.pheightFunc}
                 isTopMost={true}
                 selectOnLoad={false}
-                focus={this.focusDocument}
+                focus={emptyDocFunction}
                 parentActive={returnTrue}
                 onActiveChanged={emptyFunction}
                 ContainingCollectionView={undefined} />;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index e54c25474..77ffb4157 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -8,7 +8,7 @@ import { Document } from "../../../fields/Document";
 import { KeyStore } from "../../../fields/KeyStore";
 import Measure from "react-measure";
 import { FieldId, Opt, Field, FieldWaiting } from "../../../fields/Field";
-import { Utils, returnTrue, emptyFunction } from "../../../Utils";
+import { Utils, returnTrue, emptyFunction, emptyDocFunction } from "../../../Utils";
 import { Server } from "../../Server";
 import { undoBatch } from "../../util/UndoManager";
 import { DocumentView } from "../nodes/DocumentView";
@@ -350,7 +350,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                     selectOnLoad={false}
                     parentActive={returnTrue}
                     onActiveChanged={emptyFunction}
-                    focus={(doc: Document) => { }}
+                    focus={emptyDocFunction}
                     ContainingCollectionView={undefined} />
             </div>;
 
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index f53b2a76e..4cf187963 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -26,7 +26,7 @@ import { CollectionView } from "./CollectionView";
 import { CollectionSubView } from "./CollectionSubView";
 import { TextField } from "../../../fields/TextField";
 import * as globalCssVariables from "../../views/globalCssVariables.scss";
-import { emptyFunction, returnFalse } from "../../../Utils";
+import { emptyFunction, returnFalse, emptyDocFunction } 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
@@ -82,7 +82,7 @@ export class CollectionSchemaView extends CollectionSubView {
             isTopMost: false,
             selectOnLoad: false,
             ScreenToLocalTransform: Transform.Identity,
-            focus: emptyFunction,
+            focus: emptyDocFunction,
             active: returnFalse,
             onActiveChanged: emptyFunction,
         };
@@ -253,8 +253,6 @@ export class CollectionSchemaView extends CollectionSubView {
     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) => { };
-
     onPointerDown = (e: React.PointerEvent): void => {
         if (e.button === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
             e.stopPropagation();
@@ -317,7 +315,7 @@ export class CollectionSchemaView extends CollectionSubView {
                                 PanelWidth={this.getPanelWidth}
                                 PanelHeight={this.getPanelHeight}
                                 ContainingCollectionView={undefined}
-                                focus={this.focusDocument}
+                                focus={emptyDocFunction}
                                 parentActive={this.props.active}
                                 onActiveChanged={this.props.onActiveChanged} /> : null}
                         <input value={this.previewScript} onChange={this.onPreviewScriptChange}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 40b44aae5..0037d7b28 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, emptyFunction } from "../../../Utils";
+import { returnFalse, emptyDocFunction } from "../../../Utils";
 
 
 //
@@ -85,7 +85,7 @@ export class FieldView extends React.Component<FieldViewProps> {
                     PanelHeight={() => 100}
                     isTopMost={true} //TODO Why is this top most?
                     selectOnLoad={false}
-                    focus={emptyFunction}
+                    focus={emptyDocFunction}
                     isSelected={returnFalse}
                     select={returnFalse}
                     layoutKey={KeyStore.Layout}
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index a1050dc6e..7f43d8fdf 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -12,7 +12,7 @@ import { Server } from "../../Server";
 import { EditableView } from "../EditableView";
 import { CompileScript, ToField } from "../../util/Scripting";
 import { Transform } from '../../util/Transform';
-import { returnFalse, emptyFunction } from '../../../Utils';
+import { returnFalse, emptyFunction, emptyDocFunction } from '../../../Utils';
 
 // Represents one row in a key value plane
 
@@ -54,7 +54,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             active: returnFalse,
             onActiveChanged: emptyFunction,
             ScreenToLocalTransform: Transform.Identity,
-            focus: emptyFunction,
+            focus: emptyDocFunction,
         };
         let contents = (
             <FieldView {...props} />
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index b016a3d48..1c0e316e8 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -1,24 +1,16 @@
-import { observable, computed, action } from "mobx";
-import React = require("react");
-import { SelectionManager } from "../../util/SelectionManager";
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faEdit, faEye, faTimes } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { observer } from "mobx-react";
-import './LinkBox.scss';
-import { KeyStore } from '../../../fields/KeyStore';
-import { props } from "bluebird";
-import { DocumentView } from "./DocumentView";
 import { Document } from "../../../fields/Document";
+import { KeyStore } from '../../../fields/KeyStore';
 import { ListField } from "../../../fields/ListField";
+import { NumberField } from "../../../fields/NumberField";
 import { DocumentManager } from "../../util/DocumentManager";
-import { LinkEditor } from "./LinkEditor";
-import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faEye } from '@fortawesome/free-solid-svg-icons';
-import { faEdit } from '@fortawesome/free-solid-svg-icons';
-import { faTimes } from '@fortawesome/free-solid-svg-icons';
 import { undoBatch } from "../../util/UndoManager";
-import { FieldWaiting } from "../../../fields/Field";
-import { NumberField } from "../../../fields/NumberField";
+import { CollectionDockingView } from "../collections/CollectionDockingView";
+import './LinkBox.scss';
+import React = require("react");
 
 
 library.add(faEye);
-- 
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/CollectionSchemaView.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 0737a14e2c436ab9613c638bcebdc5480df1d4d4 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 13 Apr 2019 00:23:44 -0400
Subject: repairs to keyvaluebox

---
 .../views/collections/CollectionSchemaView.tsx     |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.scss | 10 +--
 .../collectionFreeForm/MarqueeView.scss            |  2 +-
 src/client/views/nodes/DocumentView.scss           |  2 +-
 src/client/views/nodes/KeyValueBox.scss            | 91 ++++++++++++++++++----
 src/client/views/nodes/KeyValueBox.tsx             | 53 ++++++++++---
 src/client/views/nodes/KeyValuePair.scss           | 20 ++++-
 src/client/views/nodes/KeyValuePair.tsx            | 56 ++++++-------
 8 files changed, 175 insertions(+), 61 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 162a39170..fdb82690a 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -17,6 +17,7 @@ import { Server } from "../../Server";
 import { SetupDrag } from "../../util/DragManager";
 import { CompileScript, ToField } from "../../util/Scripting";
 import { Transform } from "../../util/Transform";
+import { COLLECTION_BORDER_WIDTH } from "../../views/globalCssVariables.scss";
 import { anchorPoints, Flyout } from "../DocumentDecorations";
 import '../DocumentDecorations.scss';
 import { EditableView } from "../EditableView";
@@ -24,7 +25,6 @@ import { DocumentView } from "../nodes/DocumentView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
 import { CollectionSubView } from "./CollectionSubView";
-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
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index f3ac4a383..26c794e91 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -1,13 +1,13 @@
 @import "../../globalCssVariables";
 .collectionfreeformview-measure {
-    position: absolute;
+    position: inherit;
     top: 0;
     left: 0;
     width: 100%;
     height: 100%;
   }
 .collectionfreeformview {
-  position: absolute;
+  position: inherit;
   top: 0;
   left: 0;
   width: 100%;
@@ -16,7 +16,7 @@
 }
 .collectionfreeformview-container {
   .collectionfreeformview > .jsx-parser {
-    position: absolute;
+    position: inherit;
     height: 100%;
     width: 100%;
   }
@@ -43,7 +43,7 @@
 }
 .collectionfreeformview-overlay {
   .collectionfreeformview > .jsx-parser {
-    position: absolute;
+    position: inherit;
     height: 100%;
   }
   .formattedTextBox-cont {
@@ -56,7 +56,7 @@
   border-style: solid; 
   border-radius: $border-radius;
   box-sizing: border-box;
-  position:absolute;
+  position: absolute;
   overflow: hidden;
   top: 0;
   left: 0;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
index 0b406e722..e5ffcec76 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
@@ -1,6 +1,6 @@
 
 .marqueeView {
-    position: absolute;
+    position: inherit;
     top:0;
     left:0;
     width:100%;
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 039d1bcc6..a946ac1a8 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -1,7 +1,7 @@
 @import "../globalCssVariables";
 
 .documentView-node {
-  position: absolute;
+  position: inherit;
   top: 0;
   left:0;
   background: $light-color; //overflow: hidden;
diff --git a/src/client/views/nodes/KeyValueBox.scss b/src/client/views/nodes/KeyValueBox.scss
index f60edc675..6ebd73f2c 100644
--- a/src/client/views/nodes/KeyValueBox.scss
+++ b/src/client/views/nodes/KeyValueBox.scss
@@ -1,6 +1,7 @@
 @import "../globalCssVariables";
 .keyValueBox-cont {
     overflow-y: scroll;
+    width:100%;
     height: 100%;
     background-color: $light-color;
     border: 1px solid $intermediate-color;
@@ -8,31 +9,58 @@
     box-sizing: border-box;
     display: inline-block;
     .imageBox-cont img {
-        max-height: 45px;
-        height: auto;
-    }
-    td {
-        padding: 6px 8px;
-        border-right: 1px solid $intermediate-color;
-        border-top: 1px solid $intermediate-color;
-        &:last-child {
-            border-right: none;
-        }
+        width: auto;
     }
 }
+$header-height: 30px;
+.keyValueBox-tbody {
+    width:100%;
+    height:100%;
+    position: absolute;
+    overflow-y: scroll;
+}
+.keyValueBox-key {
+    display: inline-block;
+    height:100%;
+    width:50%;
+    text-align: center;
+}
+.keyValueBox-fields {
+    display: inline-block;
+    height:100%;
+    width:50%;
+    text-align: center;
+}
 
 .keyValueBox-table {
-    position: relative;
+    position: absolute;
+    width:100%;
+    height:100%;
     border-collapse: collapse;
 }
-
+.keyValueBox-td-key {
+    display:inline-block;
+    height:30px;
+}
+.keyValueBox-td-value {
+    display:inline-block;
+    height:30px;
+}
+.keyValueBox-valueRow {
+    width:100%;
+    height:30px;
+    display: inline-block;
+}
 .keyValueBox-header {
+    width:100%;
+    position: relative;
+    display: inline-block;
     background: $intermediate-color;
     color: $light-color;
     text-transform: uppercase;
     letter-spacing: 2px;
     font-size: 12px;
-    height: 30px;
+    height: $header-height;
     padding-top: 4px;
     th {
         font-weight: normal;
@@ -43,13 +71,50 @@
 }
 
 .keyValueBox-evenRow {
+    position: relative;
+    display: inline-block;
+    width:100%;
+    height:$header-height;
     background: $light-color;
     .formattedTextBox-cont {
         background: $light-color;
     }
 }
+.keyValueBox-cont {
+    .collectionfreeformview-overlay {
+        position: relative;
+    }
+}
+.keyValueBox-dividerDraggerThumb{
+    position: relative;
+    width: 4px;
+    float: left; 
+    height: 30px;
+    width: 10px;
+    z-index: 20;
+    right: 0;
+    top: 0;
+    border-radius: 10px;
+    background: gray;
+    pointer-events: all;
+}
+.keyValueBox-dividerDragger{
+    position: relative; 
+    width: 100%;
+    float: left; 
+    height: 37px;
+    z-index: 20;
+    right: 0;
+    top: 0;
+    background: transparent;
+    pointer-events: none;
+}
 
 .keyValueBox-oddRow {
+    position: relative;
+    display: inline-block;
+    width:100%;
+    height:30px;
     background: $light-color-secondary;
     .formattedTextBox-cont {
         background: $light-color-secondary;
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index bcac113f0..29e4af160 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -1,23 +1,25 @@
 
+import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
 import { Document } from '../../../fields/Document';
-import { FieldWaiting, Field } from '../../../fields/Field';
+import { Field, FieldWaiting } from '../../../fields/Field';
+import { Key } from '../../../fields/Key';
 import { KeyStore } from '../../../fields/KeyStore';
+import { CompileScript, ToField } from "../../util/Scripting";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./KeyValueBox.scss";
 import { KeyValuePair } from "./KeyValuePair";
 import React = require("react");
-import { CompileScript, ToField } from "../../util/Scripting";
-import { Key } from '../../../fields/Key';
-import { observable, action } from "mobx";
 
 @observer
 export class KeyValueBox extends React.Component<FieldViewProps> {
+    private _mainCont = React.createRef<HTMLDivElement>();
 
     public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(KeyValueBox, fieldStr); }
     @observable private _keyInput: string = "";
     @observable private _valueInput: string = "";
+    @computed get splitPercentage() { return this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 50); }
 
 
     constructor(props: FieldViewProps) {
@@ -90,7 +92,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         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} keyWidth={100 - this.splitPercentage} rowStyle={"keyValueBox-" + (i++ % 2 ? "oddRow" : "evenRow")} fieldId={key} key={key} />);
         }
         return rows;
     }
@@ -107,24 +109,51 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
 
     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 className="keyValueBox-valueRow">
+                <td className="keyValueBox-td-key" style={{ width: `${100 - this.splitPercentage}%` }}>
+                    <input style={{ width: "100%" }} type="text" value={this._keyInput} placeholder="Key" onChange={this.keyChanged} />
+                </td>
+                <td className="keyValueBox-td-value" style={{ width: `${this.splitPercentage}%` }}>
+                    <input style={{ width: "100%" }} type="text" value={this._valueInput} placeholder="Value" onChange={this.valueChanged} onKeyPress={this.onEnterKey} />
+                </td>
             </tr>
         )
 
+    @action
+    onDividerMove = (e: PointerEvent): void => {
+        let nativeWidth = this._mainCont.current!.getBoundingClientRect();
+        this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100)));
+    }
+    @action
+    onDividerUp = (e: PointerEvent): void => {
+        document.removeEventListener("pointermove", this.onDividerMove);
+        document.removeEventListener('pointerup', this.onDividerUp);
+    }
+    onDividerDown = (e: React.PointerEvent) => {
+        e.stopPropagation();
+        e.preventDefault();
+        document.addEventListener("pointermove", this.onDividerMove);
+        document.addEventListener('pointerup', this.onDividerUp);
+    }
+
     render() {
-        return (<div className="keyValueBox-cont" onWheel={this.onPointerWheel}>
+        let dividerDragger = this.splitPercentage === 0 ? (null) :
+            <div className="keyValueBox-dividerDragger" style={{ transform: `translate(calc(${100 - this.splitPercentage}% - 5px), 0px)` }}>
+                <div className="keyValueBox-dividerDraggerThumb" onPointerDown={this.onDividerDown} />
+            </div>;
+
+        return (<div className="keyValueBox-cont" onWheel={this.onPointerWheel} ref={this._mainCont}>
             <table className="keyValueBox-table">
-                <tbody>
+                <tbody className="keyValueBox-tbody">
                     <tr className="keyValueBox-header">
-                        <th>Key</th>
-                        <th>Fields</th>
+                        <th className="keyValueBox-key" style={{ width: `${100 - this.splitPercentage}%` }}>Key</th>
+                        <th className="keyValueBox-fields" style={{ width: `${this.splitPercentage}%` }}>Fields</th>
                     </tr>
                     {this.createTable()}
                     {this.newKeyValue()}
                 </tbody>
             </table>
+            {dividerDragger}
         </div>);
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss
index 0bb92e5a3..04d002c7b 100644
--- a/src/client/views/nodes/KeyValuePair.scss
+++ b/src/client/views/nodes/KeyValuePair.scss
@@ -1,12 +1,30 @@
 @import "../globalCssVariables";
 
 .container{
+    width:100%;
+    height:100%;
     display: flex;                 
     flex-direction: row;           
     flex-wrap: nowrap;              
     justify-content: space-between; 
 }
 
+.keyValuePair-td-key {
+    display:inline-block;
+    width: 50%;
+}
+.keyValuePair-td-value {
+    display:inline-block;
+    width: 50%;
+}
+.keyValuePair-keyField {
+    width:100%;
+    text-align: center;
+    position: relative;
+    overflow: auto;
+}
 .delete{
-    color: red;
+    position: relative;
+    background-color: transparent;
+    color:red;
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 7f43d8fdf..3e0b61c3d 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -20,6 +20,7 @@ export interface KeyValuePairProps {
     rowStyle: string;
     fieldId: string;
     doc: Document;
+    keyWidth: number;
 }
 @observer
 export class KeyValuePair extends React.Component<KeyValuePairProps> {
@@ -61,45 +62,46 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
         );
         return (
             <tr className={this.props.rowStyle}>
-                {/* <button>X</button> */}
-                <td>
+                <td className="keyValuePair-td-key" style={{ width: `${this.props.keyWidth}%` }}>
                     <div className="container">
-                        <div>{this.key.Name}</div>
                         <button className="delete" onClick={() => {
                             let field = props.Document.Get(props.fieldKey);
                             if (field && field instanceof Field) {
                                 props.Document.Set(props.fieldKey, undefined);
                             }
                         }}>X</button>
+                        <div className="keyValuePair-keyField">{this.key.Name}</div>
                     </div>
                 </td>
-                <td><EditableView contents={contents} height={36} GetValue={() => {
-                    let field = props.Document.Get(props.fieldKey);
-                    if (field && field instanceof Field) {
-                        return field.ToScriptString();
-                    }
-                    return field || "";
-                }}
-                    SetValue={(value: string) => {
-                        let script = CompileScript(value, { addReturn: true });
-                        if (!script.compiled) {
-                            return false;
+                <td className="keyValuePair-td-value" style={{ width: `${100 - this.props.keyWidth}%` }}>
+                    <EditableView contents={contents} height={36} GetValue={() => {
+                        let field = props.Document.Get(props.fieldKey);
+                        if (field && field instanceof Field) {
+                            return field.ToScriptString();
                         }
-                        let res = script.run();
-                        if (!res.success) return false;
-                        const field = res.result;
-                        if (field instanceof Field) {
-                            props.Document.Set(props.fieldKey, field);
-                            return true;
-                        } else {
-                            let dataField = ToField(field);
-                            if (dataField) {
-                                props.Document.Set(props.fieldKey, dataField);
+                        return field || "";
+                    }}
+                        SetValue={(value: string) => {
+                            let script = CompileScript(value, { addReturn: true });
+                            if (!script.compiled) {
+                                return false;
+                            }
+                            let res = script.run();
+                            if (!res.success) return false;
+                            const field = res.result;
+                            if (field instanceof Field) {
+                                props.Document.Set(props.fieldKey, field);
                                 return true;
+                            } else {
+                                let dataField = ToField(field);
+                                if (dataField) {
+                                    props.Document.Set(props.fieldKey, dataField);
+                                    return true;
+                                }
                             }
-                        }
-                        return false;
-                    }}></EditableView></td>
+                            return false;
+                        }}>
+                    </EditableView></td>
             </tr>
         );
     }
-- 
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/CollectionSchemaView.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/CollectionSchemaView.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 2cb379ebe0fd8f2dc9ffa64c2b303bef648cfa33 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 14 Apr 2019 16:59:21 -0400
Subject: cleaned up ContainingCollectionView stuff a bit

---
 src/client/util/DocumentManager.ts                 | 22 ++++------
 src/client/views/Main.tsx                          |  2 +-
 src/client/views/collections/CollectionPDFView.tsx |  4 +-
 .../views/collections/CollectionSchemaView.tsx     |  1 -
 src/client/views/collections/CollectionSubView.tsx |  4 +-
 .../views/collections/CollectionVideoView.tsx      |  2 +-
 .../CollectionFreeFormLinksView.tsx                | 21 +++++-----
 src/client/views/nodes/DocumentContentsView.tsx    |  1 -
 src/client/views/nodes/DocumentView.tsx            | 12 +++---
 src/client/views/nodes/FieldView.tsx               |  5 ++-
 src/client/views/nodes/KeyValuePair.scss           | 40 +++++++++---------
 src/client/views/nodes/KeyValuePair.tsx            | 47 +++++++++-------------
 12 files changed, 71 insertions(+), 90 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 3e093c8dc..3b5a5b470 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,11 +1,9 @@
-import React = require('react');
-import { observer } from 'mobx-react';
-import { observable, action, computed } from 'mobx';
+import { computed, observable } from 'mobx';
 import { Document } from "../../fields/Document";
-import { DocumentView } from '../views/nodes/DocumentView';
-import { KeyStore } from '../../fields/KeyStore';
 import { FieldWaiting } from '../../fields/Field';
+import { KeyStore } from '../../fields/KeyStore';
 import { ListField } from '../../fields/ListField';
+import { DocumentView } from '../views/nodes/DocumentView';
 
 
 export class DocumentManager {
@@ -27,10 +25,6 @@ export class DocumentManager {
         // this.DocumentViews = new Array<DocumentView>();
     }
 
-    public getAllDocumentViews(collection: Document) {
-        return this.DocumentViews.filter(dv => dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document === collection);
-    }
-
     public getDocumentView(toFind: Document): DocumentView | null {
 
         let toReturn: DocumentView | null;
@@ -39,7 +33,6 @@ export class DocumentManager {
         //gets document view that is in a freeform canvas collection
         DocumentManager.Instance.DocumentViews.map(view => {
             let doc = view.props.Document;
-            // if (view.props.ContainingCollectionView instanceof CollectionFreeFormView) {
 
             if (doc === toFind) {
                 toReturn = view;
@@ -51,7 +44,7 @@ export class DocumentManager {
             }
         });
 
-        return (toReturn);
+        return toReturn;
     }
     public getDocumentViews(toFind: Document): DocumentView[] {
 
@@ -72,7 +65,7 @@ export class DocumentManager {
             }
         });
 
-        return (toReturn);
+        return toReturn;
     }
 
     @computed
@@ -84,9 +77,8 @@ export class DocumentManager {
                     if (link instanceof Document) {
                         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 });
-                            });
+                            DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 =>
+                                pairs.push({ a: dv, b: docView1, l: link }));
                         }
                     }
                     return pairs;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 03cfbe9da..f8c6ec0e2 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} ContainingCollectionView={undefined} CollectionView={undefined} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={emptyDocFunction} />
+                        selectOnLoad={true} ContainingCollectionView={undefined} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={emptyDocFunction} />
                 </div>
             </ div>;
         }
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 6cbe59012..229bc4059 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable } from "mobx";
+import { action } from "mobx";
 import { observer } from "mobx-react";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ContextMenu } from "../ContextMenu";
@@ -42,7 +42,7 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
         let props = { ...this.props, ...renderProps };
         return (
             <>
-                <CollectionFreeFormView {...props} />
+                <CollectionFreeFormView {...props} CollectionView={this} />
                 {this.props.isSelected() ? this.uIButtons : (null)}
             </>
         );
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 63346a4c6..2c99c9c67 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -75,7 +75,6 @@ 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,
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 61b7787e7..1c927c78d 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -17,6 +17,8 @@ import { FieldViewProps } from "../nodes/FieldView";
 import * as rp from 'request-promise';
 import { emptyFunction } from "../../../Utils";
 import { CollectionView } from "./CollectionView";
+import { CollectionPDFView } from "./CollectionPDFView";
+import { CollectionVideoView } from "./CollectionVideoView";
 
 export interface CollectionViewProps extends FieldViewProps {
     addDocument: (document: Document, allowDuplicates?: boolean) => boolean;
@@ -25,7 +27,7 @@ export interface CollectionViewProps extends FieldViewProps {
 }
 
 export interface SubCollectionViewProps extends CollectionViewProps {
-    CollectionView: Opt<CollectionView>;
+    CollectionView: CollectionView | CollectionPDFView | CollectionVideoView;
 }
 
 export type CursorEntry = TupleField<[string, string], [number, number]>;
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 6c9780adb..29fb342c6 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -109,7 +109,7 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
         let props = { ...this.props, ...renderProps };
         return (
             <>
-                <CollectionFreeFormView {...props} />
+                <CollectionFreeFormView {...props} CollectionView={this} />
                 {this.props.isSelected() ? this.uIButtons : (null)}
             </>
         );
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 647c83d4d..2f684a54e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,7 +1,6 @@
-import { computed, reaction, trace, IReactionDisposer } from "mobx";
+import { computed, IReactionDisposer, reaction } from "mobx";
 import { observer } from "mobx-react";
 import { Document } from "../../../../fields/Document";
-import { FieldWaiting } from "../../../../fields/Field";
 import { KeyStore } from "../../../../fields/KeyStore";
 import { ListField } from "../../../../fields/ListField";
 import { Utils } from "../../../../Utils";
@@ -85,19 +84,17 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
             let targetViews = this.documentAnchors(connection.b);
             let possiblePairs: { a: Document, b: Document, }[] = [];
             srcViews.map(sv => targetViews.map(tv => possiblePairs.push({ a: sv.props.Document, b: tv.props.Document })));
-            possiblePairs.map(possiblePair => {
-                if (!drawnPairs.reduce((found, drawnPair) => {
+            possiblePairs.map(possiblePair =>
+                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)) {
-                            drawnPair.l.push(connection.l);
-                        }
+                    if (match && !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] });
-                }
-            });
+                }, false)
+                ||
+                drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] })
+            );
             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/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index a967db000..88900c4b1 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -65,7 +65,6 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
                 isTopMost,
                 selectOnLoad,
                 ScreenToLocalTransform,
-                CollectionView: undefined,
                 ContainingCollectionView,
                 active,
                 onActiveChanged,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f0f9af89f..935540af1 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,30 +1,32 @@
-import { action, computed, IReactionDisposer, reaction, runInAction, trace } from "mobx";
+import { action, computed, runInAction } from "mobx";
 import { observer } from "mobx-react";
+import { BooleanField } from "../../../fields/BooleanField";
 import { Document } from "../../../fields/Document";
 import { Field, FieldWaiting, Opt } from "../../../fields/Field";
 import { Key } from "../../../fields/Key";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ListField } from "../../../fields/ListField";
-import { BooleanField } from "../../../fields/BooleanField";
 import { TextField } from "../../../fields/TextField";
 import { ServerUtils } from "../../../server/ServerUtil";
-import { Utils, emptyFunction } from "../../../Utils";
+import { emptyFunction, Utils } from "../../../Utils";
 import { Documents } from "../../documents/Documents";
 import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager } from "../../util/DragManager";
 import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
+import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { CollectionPDFView } from "../collections/CollectionPDFView";
+import { CollectionVideoView } from "../collections/CollectionVideoView";
 import { CollectionView } from "../collections/CollectionView";
 import { ContextMenu } from "../ContextMenu";
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
 import React = require("react");
-import { undoBatch, UndoManager } from "../../util/UndoManager";
 
 
 export interface DocumentViewProps {
-    ContainingCollectionView: Opt<CollectionView>;
+    ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
     Document: Document;
     opacity: number;
     addDocument?: (doc: Document, allowDuplicates?: boolean) => boolean;
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 755367d23..562de4827 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -21,6 +21,8 @@ import { Transform } from "../../util/Transform";
 import { KeyStore } from "../../../fields/KeyStore";
 import { returnFalse, emptyDocFunction } from "../../../Utils";
 import { CollectionView } from "../collections/CollectionView";
+import { CollectionPDFView } from "../collections/CollectionPDFView";
+import { CollectionVideoView } from "../collections/CollectionVideoView";
 
 
 //
@@ -30,8 +32,7 @@ import { CollectionView } from "../collections/CollectionView";
 //
 export interface FieldViewProps {
     fieldKey: Key;
-    ContainingCollectionView: Opt<CollectionView>;
-    CollectionView: Opt<CollectionView>;
+    ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
     Document: Document;
     isSelected: () => boolean;
     select: (isCtrlPressed: boolean) => void;
diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss
index 04d002c7b..01701e02c 100644
--- a/src/client/views/nodes/KeyValuePair.scss
+++ b/src/client/views/nodes/KeyValuePair.scss
@@ -1,30 +1,28 @@
 @import "../globalCssVariables";
 
-.container{
-    width:100%;
-    height:100%;
-    display: flex;                 
-    flex-direction: row;           
-    flex-wrap: nowrap;              
-    justify-content: space-between; 
-}
 
 .keyValuePair-td-key {
     display:inline-block;
-    width: 50%;
+    .keyValuePair-td-key-container{
+        width:100%;
+        height:100%;
+        display: flex;                 
+        flex-direction: row;           
+        flex-wrap: nowrap;              
+        justify-content: space-between; 
+        .keyValuePair-td-key-delete{
+            position: relative;
+            background-color: transparent;
+            color:red;
+        }
+        .keyValuePair-keyField {
+            width:100%;
+            text-align: center;
+            position: relative;
+            overflow: auto;
+        }
+    }
 }
 .keyValuePair-td-value {
     display:inline-block;
-    width: 50%;
-}
-.keyValuePair-keyField {
-    width:100%;
-    text-align: center;
-    position: relative;
-    overflow: auto;
-}
-.delete{
-    position: relative;
-    background-color: transparent;
-    color:red;
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 1b9dd26bd..5d69f23b2 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -1,18 +1,18 @@
-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 { FieldViewProps, FieldView } from './FieldView';
-import { Opt, Field } from '../../../fields/Field';
+import { action, observable } from 'mobx';
 import { observer } from "mobx-react";
-import { observable, action } from 'mobx';
+import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
 import { Document } from '../../../fields/Document';
+import { Field, Opt } from '../../../fields/Field';
 import { Key } from '../../../fields/Key';
+import { emptyDocFunction, emptyFunction, returnFalse } from '../../../Utils';
 import { Server } from "../../Server";
-import { EditableView } from "../EditableView";
 import { CompileScript, ToField } from "../../util/Scripting";
 import { Transform } from '../../util/Transform';
-import { returnFalse, emptyFunction, emptyDocFunction } from '../../../Utils';
+import { EditableView } from "../EditableView";
+import { FieldView, FieldViewProps } from './FieldView';
+import "./KeyValueBox.scss";
+import "./KeyValuePair.scss";
+import React = require("react");
 
 // Represents one row in a key value plane
 
@@ -25,29 +25,22 @@ export interface KeyValuePairProps {
 @observer
 export class KeyValuePair extends React.Component<KeyValuePairProps> {
 
-    @observable
-    private key: Opt<Key>;
+    @observable private key: Opt<Key>;
 
     constructor(props: KeyValuePairProps) {
         super(props);
         Server.GetField(this.props.fieldId,
-            action((field: Opt<Field>) => {
-                if (field) {
-                    this.key = field as Key;
-                }
-            }));
+            action((field: Opt<Field>) => field instanceof Key && (this.key = field)));
 
     }
 
 
     render() {
         if (!this.key) {
-            return <tr><td>error</td><td></td></tr>;
-
+            return <tr><td>error</td><td /></tr>;
         }
         let props: FieldViewProps = {
             Document: this.props.doc,
-            CollectionView: undefined,
             ContainingCollectionView: undefined,
             fieldKey: this.key,
             isSelected: returnFalse,
@@ -59,19 +52,17 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             ScreenToLocalTransform: Transform.Identity,
             focus: emptyDocFunction,
         };
-        let contents = (
-            <FieldView {...props} />
-        );
+        let contents = <FieldView {...props} />;
         return (
             <tr className={this.props.rowStyle}>
                 <td className="keyValuePair-td-key" style={{ width: `${this.props.keyWidth}%` }}>
-                    <div className="container">
-                        <button className="delete" onClick={() => {
+                    <div className="keyValuePair-td-key-container">
+                        <button className="keyValuePair-td-key-delete" onClick={() => {
                             let field = props.Document.Get(props.fieldKey);
-                            if (field && field instanceof Field) {
-                                props.Document.Set(props.fieldKey, undefined);
-                            }
-                        }}>X</button>
+                            field && field instanceof Field && props.Document.Set(props.fieldKey, undefined);
+                        }}>
+                            X
+                        </button>
                         <div className="keyValuePair-keyField">{this.key.Name}</div>
                     </div>
                 </td>
-- 
cgit v1.2.3-70-g09d2


From 6c0b421db6aa3204bbc6e42139d240f503000b5d Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 15 Apr 2019 11:59:50 -0400
Subject: fixed zoom fading somewhat.

---
 src/Utils.ts                                       | 15 ++++++++++
 src/client/northstar/dash-fields/HistogramField.ts | 12 ++------
 src/client/views/Main.tsx                          |  3 +-
 src/client/views/MainOverlayTextBox.tsx            |  3 +-
 .../views/collections/CollectionDockingView.tsx    |  3 +-
 .../views/collections/CollectionSchemaView.tsx     |  3 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 19 ++++++-------
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 18 ++++++++----
 src/client/views/nodes/DocumentContentsView.tsx    | 33 ++--------------------
 src/client/views/nodes/DocumentView.tsx            |  1 -
 src/client/views/nodes/FieldView.tsx               |  3 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  4 ++-
 12 files changed, 51 insertions(+), 66 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index e07d4e82d..dec6245ef 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -87,10 +87,25 @@ export class Utils {
     }
 }
 
+export function OmitKeys(obj: any, keys: any, addKeyFunc?: (dup: any) => void) {
+    var dup: any = {};
+    for (var key in obj) {
+        if (keys.indexOf(key) === -1) {
+            dup[key] = obj[key];
+        }
+    }
+    addKeyFunc && addKeyFunc(dup);
+    return dup;
+}
+
 export function returnTrue() { return true; }
 
 export function returnFalse() { return false; }
 
+export function returnOne() { return 1; }
+
+export function returnZero() { return 0; }
+
 export function emptyFunction() { }
 
 export function emptyDocFunction(doc: Document) { }
diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts
index 82de51d56..c699691a4 100644
--- a/src/client/northstar/dash-fields/HistogramField.ts
+++ b/src/client/northstar/dash-fields/HistogramField.ts
@@ -6,6 +6,7 @@ import { BasicField } from "../../../fields/BasicField";
 import { Field, FieldId } from "../../../fields/Field";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { Types } from "../../../server/Message";
+import { OmitKeys } from "../../../Utils";
 
 
 export class HistogramField extends BasicField<HistogramOperation> {
@@ -13,17 +14,8 @@ export class HistogramField extends BasicField<HistogramOperation> {
         super(data ? data : HistogramOperation.Empty, save, id);
     }
 
-    omitKeys(obj: any, keys: any) {
-        var dup: any = {};
-        for (var key in obj) {
-            if (keys.indexOf(key) === -1) {
-                dup[key] = obj[key];
-            }
-        }
-        return dup;
-    }
     toString(): string {
-        return JSON.stringify(this.omitKeys(this.Data, ['Links', 'BrushLinks', 'Result', 'BrushColors', 'FilterModels', 'FilterOperand']));
+        return JSON.stringify(OmitKeys(this.Data, ['Links', 'BrushLinks', 'Result', 'BrushColors', 'FilterModels', 'FilterOperand']));
     }
 
     Copy(): Field {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index d26586216..98ef329c7 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -17,7 +17,7 @@ import { CurrentUserUtils } from '../../server/authentication/models/current_use
 import { MessageStore } from '../../server/Message';
 import { RouteStore } from '../../server/RouteStore';
 import { ServerUtils } from '../../server/ServerUtil';
-import { emptyDocFunction, emptyFunction, returnTrue, Utils } from '../../Utils';
+import { emptyDocFunction, emptyFunction, returnTrue, Utils, returnOne } from '../../Utils';
 import { Documents } from '../documents/Documents';
 import { ColumnAttributeModel } from '../northstar/core/attribute/AttributeModel';
 import { AttributeTransformationModel } from '../northstar/core/attribute/AttributeTransformationModel';
@@ -178,7 +178,6 @@ export class Main extends React.Component {
                         <DocumentView Document={mainCont}
                             addDocument={undefined}
                             removeDocument={undefined}
-                            opacity={1}
                             ScreenToLocalTransform={Transform.Identity}
                             ContentScaling={noScaling}
                             PanelWidth={pwidthFunc}
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index df1addbc7..6d43d88f0 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -56,7 +56,8 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
     @action
     textScroll = (e: React.UIEvent) => {
         if (this._textProxyDiv.current && this._textTargetDiv) {
-            this._textTargetDiv.scrollTop = this.TextScroll = this._textProxyDiv.current.children[0].scrollTop;
+            this.TextScroll = (e as any)._targetInst.stateNode.scrollTop;//  this._textProxyDiv.current.children[0].scrollTop;
+            this._textTargetDiv.scrollTop = this.TextScroll;
         }
     }
 
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 4f7d4fc0d..2b96e7678 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -8,7 +8,7 @@ import { Document } from "../../../fields/Document";
 import { KeyStore } from "../../../fields/KeyStore";
 import Measure from "react-measure";
 import { FieldId, Opt, Field, FieldWaiting } from "../../../fields/Field";
-import { Utils, returnTrue, emptyFunction, emptyDocFunction } from "../../../Utils";
+import { Utils, returnTrue, emptyFunction, emptyDocFunction, returnOne } from "../../../Utils";
 import { Server } from "../../Server";
 import { undoBatch } from "../../util/UndoManager";
 import { DocumentView } from "../nodes/DocumentView";
@@ -343,7 +343,6 @@ 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 2c99c9c67..4f4068e7a 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -12,7 +12,7 @@ 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 { emptyDocFunction, emptyFunction, returnFalse, returnOne } from "../../../Utils";
 import { Server } from "../../Server";
 import { SetupDrag } from "../../util/DragManager";
 import { CompileScript, ToField } from "../../util/Scripting";
@@ -308,7 +308,6 @@ 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 cb4e8fb2e..4db3bf81b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -5,7 +5,7 @@ import { Document } from "../../../../fields/Document";
 import { FieldWaiting } from "../../../../fields/Field";
 import { KeyStore } from "../../../../fields/KeyStore";
 import { TextField } from "../../../../fields/TextField";
-import { emptyFunction, returnFalse } from "../../../../Utils";
+import { emptyFunction, returnFalse, returnOne } from "../../../../Utils";
 import { DocumentManager } from "../../../util/DocumentManager";
 import { DragManager } from "../../../util/DragManager";
 import { SelectionManager } from "../../../util/SelectionManager";
@@ -14,7 +14,7 @@ import { undoBatch } from "../../../util/UndoManager";
 import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss";
 import { InkingCanvas } from "../../InkingCanvas";
 import { Main } from "../../Main";
-import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
+import { CollectionFreeFormDocumentView, CollectionFreeFormDocumentViewProps } from "../../nodes/CollectionFreeFormDocumentView";
 import { DocumentContentsView } from "../../nodes/DocumentContentsView";
 import { DocumentViewProps } from "../../nodes/DocumentView";
 import { CollectionSubView, SubCollectionViewProps } from "../CollectionSubView";
@@ -200,7 +200,6 @@ 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();
         }
     }
 
@@ -261,10 +260,9 @@ export class CollectionFreeFormView extends CollectionSubView {
         this.props.focus(this.props.Document);
     }
 
-    getDocumentViewProps(document: Document, opacity: number): DocumentViewProps {
+    getDocumentViewProps(document: Document): DocumentViewProps {
         return {
             Document: document,
-            opacity: opacity,
             addDocument: this.props.addDocument,
             removeDocument: this.props.removeDocument,
             moveDocument: this.props.moveDocument,
@@ -288,9 +286,10 @@ export class CollectionFreeFormView extends CollectionSubView {
             var page = doc.GetNumber(KeyStore.Page, -1);
             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)} />);
+            let zoomFade = !this.isAnnotationOverlay || (dv && SelectionManager.IsSelected(dv)) ? 1 :
+                Math.max(0, 2 - (zoom < this.scale ? this.scale / zoom : zoom / this.scale));
+            if (page === curPage || page === -1) {
+                prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} zoomFade={zoomFade} />);
             }
             return prev;
         }, [] as JSX.Element[]);
@@ -305,13 +304,13 @@ export class CollectionFreeFormView extends CollectionSubView {
     @computed
     get backgroundView() {
         return !this.backgroundLayout ? (null) :
-            (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document, 1)}
+            (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document)}
                 layoutKey={KeyStore.BackgroundLayout} isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
     }
     @computed
     get overlayView() {
         return !this.overlayLayout ? (null) :
-            (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document, 1)}
+            (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document)}
                 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 0aa152209..b00cefbf6 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,4 +1,4 @@
-import { computed } from "mobx";
+import { computed, trace } from "mobx";
 import { observer } from "mobx-react";
 import { KeyStore } from "../../../fields/KeyStore";
 import { NumberField } from "../../../fields/NumberField";
@@ -6,13 +6,17 @@ import { Transform } from "../../util/Transform";
 import { DocumentView, DocumentViewProps } from "./DocumentView";
 import "./DocumentView.scss";
 import React = require("react");
+import { OmitKeys } from "../../../Utils";
 
+export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
+    zoomFade: number;
+}
 
 @observer
-export class CollectionFreeFormDocumentView extends React.Component<DocumentViewProps> {
+export class CollectionFreeFormDocumentView extends React.Component<CollectionFreeFormDocumentViewProps> {
     private _mainCont = React.createRef<HTMLDivElement>();
 
-    constructor(props: DocumentViewProps) {
+    constructor(props: CollectionFreeFormDocumentViewProps) {
         super(props);
     }
 
@@ -55,20 +59,24 @@ export class CollectionFreeFormDocumentView extends React.Component<DocumentView
 
     @computed
     get docView() {
-        return <DocumentView {...this.props}
+        return <DocumentView {...this.docViewProps}
             ContentScaling={this.contentScaling}
             ScreenToLocalTransform={this.getTransform}
             PanelWidth={this.panelWidth}
             PanelHeight={this.panelHeight}
         />;
     }
+    @computed
+    get docViewProps(): DocumentViewProps {
+        return (OmitKeys(this.props, ['zoomFade']));
+    }
     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 (
             <div className="collectionFreeFormDocumentView-container" ref={this._mainCont} style={{
-                opacity: this.props.opacity,
+                opacity: this.props.zoomFade,
                 transformOrigin: "left top",
                 transform: this.transform,
                 pointerEvents: "all",
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 88900c4b1..76f852601 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -23,7 +23,7 @@ import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox";
 import React = require("react");
 import { Document } from "../../../fields/Document";
 import { FieldViewProps } from "./FieldView";
-import { Without } from "../../../Utils";
+import { Without, OmitKeys } from "../../../Utils";
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 type BindingProps = Without<FieldViewProps, 'fieldKey'>;
@@ -44,35 +44,8 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
 
 
     CreateBindings(): JsxBindings {
-        let {
-            Document,
-            isSelected,
-            select,
-            isTopMost,
-            selectOnLoad,
-            ScreenToLocalTransform,
-            ContainingCollectionView,
-            addDocument,
-            removeDocument,
-            onActiveChanged,
-            parentActive: active,
-        } = this.props;
-        let bindings: JsxBindings = {
-            props: {
-                Document,
-                isSelected,
-                select,
-                isTopMost,
-                selectOnLoad,
-                ScreenToLocalTransform,
-                ContainingCollectionView,
-                active,
-                onActiveChanged,
-                addDocument,
-                removeDocument,
-                focus,
-            }
-        };
+        let bindings: JsxBindings = { props: OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive) };
+
         for (const key of this.layoutKeys) {
             bindings[key.Name + "Key"] = key; // this maps string values of the form <keyname>Key to an actual key Kestore.keyname  e.g,   "DataKey" => KeyStore.Data
         }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 935540af1..bcd746024 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -28,7 +28,6 @@ import React = require("react");
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
     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 562de4827..ebd25f937 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, emptyDocFunction } from "../../../Utils";
+import { returnFalse, emptyDocFunction, emptyFunction, returnOne } from "../../../Utils";
 import { CollectionView } from "../collections/CollectionView";
 import { CollectionPDFView } from "../collections/CollectionPDFView";
 import { CollectionVideoView } from "../collections/CollectionVideoView";
@@ -83,7 +83,6 @@ 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/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 87c1bcb1e..ad1ed5df0 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1,4 +1,4 @@
-import { action, IReactionDisposer, reaction } from "mobx";
+import { action, IReactionDisposer, reaction, trace, computed } from "mobx";
 import { baseKeymap } from "prosemirror-commands";
 import { history, redo, undo } from "prosemirror-history";
 import { keymap } from "prosemirror-keymap";
@@ -17,6 +17,7 @@ import React = require("react");
 import { TextField } from "../../../fields/TextField";
 import { KeyStore } from "../../../fields/KeyStore";
 import { MainOverlayTextBox } from "../MainOverlayTextBox";
+import { observer } from "mobx-react";
 const { buildMenuItems } = require("prosemirror-example-setup");
 const { menuBar } = require("prosemirror-menu");
 
@@ -41,6 +42,7 @@ export interface FormattedTextBoxOverlay {
     isOverlay?: boolean;
 }
 
+@observer
 export class FormattedTextBox extends React.Component<(FieldViewProps & FormattedTextBoxOverlay)> {
     public static LayoutString(fieldStr: string = "DataKey") {
         return FieldView.LayoutString(FormattedTextBox, fieldStr);
-- 
cgit v1.2.3-70-g09d2


From 21b1e20106ff49c02657c88f503ef46bc7e4fb07 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 16 Apr 2019 23:09:03 -0400
Subject: various bug fixes

---
 src/client/northstar/dash-nodes/HistogramBox.tsx   |  2 +-
 .../views/collections/CollectionBaseView.tsx       |  9 ++++--
 .../views/collections/CollectionDockingView.scss   |  5 ++++
 .../views/collections/CollectionDockingView.tsx    |  7 ++++-
 .../views/collections/CollectionSchemaView.tsx     | 14 ++++++----
 src/client/views/collections/CollectionView.tsx    |  4 ++-
 .../CollectionFreeFormLinksView.tsx                |  4 +--
 .../CollectionFreeFormRemoteCursors.tsx            |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |  1 -
 src/client/views/nodes/DocumentView.scss           |  4 +--
 src/client/views/nodes/ImageBox.tsx                | 32 ++++++----------------
 12 files changed, 44 insertions(+), 42 deletions(-)

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

diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index 3e94fed81..0e84ace50 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -118,7 +118,7 @@ export class HistogramBox extends React.Component<FieldViewProps> {
             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.Document.GetList(KeyStore.LinkedFromDocs, []), (docs: Document[]) => this.HistoOp.Links.splice(0, this.HistoOp.Links.length, ...docs), { fireImmediately: true });
+                    reaction(() => this.props.Document.GetList(KeyStore.LinkedFromDocs, [] as Document[]), (docs) => this.HistoOp.Links.splice(0, this.HistoOp.Links.length, ...docs), { fireImmediately: true });
                     reaction(() => this.props.Document.GetList(KeyStore.BrushingDocs, []).length,
                         () => {
                             let brushingDocs = this.props.Document.GetList(KeyStore.BrushingDocs, [] as Document[]);
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 444f6fc26..4fda38a26 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -61,13 +61,16 @@ 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 (!(documentToAdd instanceof Document)) {
+            return false;
+        }
+        let data = documentToAdd.GetList(KeyStore.Data, [] as Document[]);
+        for (const doc of data.filter(d => d instanceof Document)) {
             if (this.createsCycle(doc, containerDocument)) {
                 return true;
             }
         }
-        let annots = documentToAdd.GetList<Document>(KeyStore.Annotations, []);
+        let annots = documentToAdd.GetList(KeyStore.Annotations, [] as Document[]);
         for (const annot of annots) {
             if (this.createsCycle(annot, containerDocument)) {
                 return true;
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 0e7e0afa7..5ed4567cf 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -2,6 +2,11 @@
 
 .collectiondockingview-content {
     height: 100%;
+    text-align:center;
+    .documentView-node-topmost {
+        transform-origin: center top;
+        display: inline-block;
+    }
 }
 .lm_active .messageCounter{
     color:white;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 4ea21b2f5..b998362f4 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -327,7 +327,12 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
 
     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 _contentScaling = () => {
+        let wscale = this._panelWidth / (this._nativeWidth() ? this._nativeWidth() : this._panelWidth);
+        if (wscale * this._nativeHeight() > this._panelHeight)
+            return this._panelHeight / (this._nativeHeight() ? this._nativeHeight() : this._panelHeight);
+        return wscale;
+    }
 
     ScreenToLocalTransform = () => {
         let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current!);
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 4f4068e7a..1defdba7e 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -167,7 +167,7 @@ export class CollectionSchemaView extends CollectionSubView {
 
     @computed
     get columns() {
-        return this.props.Document.GetList<Key>(KeyStore.ColumnsKey, []);
+        return this.props.Document.GetList(KeyStore.ColumnsKey, [] as Key[]);
     }
 
     @action
@@ -195,7 +195,7 @@ export class CollectionSchemaView extends CollectionSubView {
 
     @computed
     get findAllDocumentKeys(): { [id: string]: boolean } {
-        const docs = this.props.Document.GetList<Document>(this.props.fieldKey, []);
+        const docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
         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.
@@ -237,7 +237,7 @@ export class CollectionSchemaView extends CollectionSubView {
     }
     @action
     setScaling = (r: any) => {
-        const children = this.props.Document.GetList<Document>(this.props.fieldKey, []);
+        const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
         const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
         this._panelWidth = r.entry.width;
         this._panelHeight = r.entry.height ? r.entry.height : this._panelHeight;
@@ -253,8 +253,10 @@ export class CollectionSchemaView extends CollectionSubView {
     getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- this.borderWidth - this.DIVIDER_WIDTH - this._dividerX - this._tableWidth, - this.borderWidth).scale(1 / this._contentScaling);
 
     onPointerDown = (e: React.PointerEvent): void => {
-        if (e.button === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
-            e.stopPropagation();
+        if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) {
+            if (this.props.isSelected())
+                e.stopPropagation();
+            else e.preventDefault();
         }
     }
 
@@ -293,7 +295,7 @@ export class CollectionSchemaView extends CollectionSubView {
         library.add(faCog);
         library.add(faPlus);
         const columns = this.columns;
-        const children = this.props.Document.GetList<Document>(this.props.fieldKey, []);
+        const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
         const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
         //all the keys/columns that will be displayed in the schema
         const allKeys = this.findAllDocumentKeys;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index bd7a5635f..675e720e2 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -29,8 +29,10 @@ export class CollectionView extends React.Component<FieldViewProps> {
         return (null);
     }
 
+    get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey.Id === KeyStore.Annotations.Id; } // bcz: ? Why do we need to compare Id's?
+
     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 (!this.isAnnotationOverlay && !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)) });
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 2f684a54e..cd74d3a84 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -16,9 +16,9 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
 
     _brushReactionDisposer?: IReactionDisposer;
     componentDidMount() {
-        this._brushReactionDisposer = reaction(() => this.props.Document.GetList<Document>(this.props.fieldKey, []).map(doc => doc.GetNumber(KeyStore.X, 0)),
+        this._brushReactionDisposer = reaction(() => this.props.Document.GetList(this.props.fieldKey, [] as Document[]).map(doc => doc.GetNumber(KeyStore.X, 0)),
             () => {
-                let views = this.props.Document.GetList<Document>(this.props.fieldKey, []);
+                let views = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
                 for (let i = 0; i < views.length; i++) {
                     for (let j = 0; j < views.length; j++) {
                         let srcDoc = views[j];
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index 751ea8190..cf0a6de00 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -12,7 +12,7 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
     protected getCursors(): CursorEntry[] {
         let doc = this.props.Document;
         let id = CurrentUserUtils.id;
-        let cursors = doc.GetList<CursorEntry>(KeyStore.Cursors, []);
+        let cursors = doc.GetList(KeyStore.Cursors, [] as CursorEntry[]);
         let notMe = cursors.filter(entry => entry.Data[0][0] !== id);
         return id ? notMe : [];
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 3ae64d2c6..97708ce19 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -218,7 +218,7 @@ export class CollectionFreeFormView extends CollectionSubView {
 
     @action
     bringToFront(doc: Document) {
-        this.props.Document.GetList<Document>(this.props.fieldKey, []).slice().sort((doc1, doc2) => {
+        this.props.Document.GetList(this.props.fieldKey, [] as Document[]).slice().sort((doc1, doc2) => {
             if (doc1 === doc) return 1;
             if (doc2 === doc) return -1;
             return doc1.GetNumber(KeyStore.ZIndex, 0) - doc2.GetNumber(KeyStore.ZIndex, 0);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 96d59c831..65f461b27 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -147,7 +147,6 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 d.SetNumber(KeyStore.X, d.GetNumber(KeyStore.X, 0) - bounds.left - bounds.width / 2);
                 d.SetNumber(KeyStore.Y, d.GetNumber(KeyStore.Y, 0) - bounds.top - bounds.height / 2);
                 d.SetNumber(KeyStore.Page, -1);
-                d.SetText(KeyStore.Title, "" + d.Width() + " " + d.Height());
                 return d;
             });
             let ink = this.props.container.props.Document.GetT(KeyStore.Ink, InkField);
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 6383ac3f6..690ee50e8 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -1,6 +1,6 @@
 @import "../globalCssVariables";
 
-.documentView-node, .documentView-node-topMost {
+.documentView-node, .documentView-node-topmost {
   position: inherit;
   top: 0;
   left:0;
@@ -29,7 +29,7 @@
     height: calc(100% - 20px);
   }
 }
-.documentView-node-topMost {
+.documentView-node-topmost {
     background: white;
 }
 
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index ac54321d5..fe0b07bc0 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,24 +1,20 @@
 
-import { action, observable, trace } from 'mobx';
+import { action, observable } from 'mobx';
 import { observer } from "mobx-react";
 import Lightbox from 'react-image-lightbox';
 import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
+import { Document } from '../../../fields/Document';
 import { FieldWaiting } from '../../../fields/Field';
 import { ImageField } from '../../../fields/ImageField';
 import { KeyStore } from '../../../fields/KeyStore';
+import { ListField } from '../../../fields/ListField';
+import { Utils } from '../../../Utils';
+import { DragManager } from '../../util/DragManager';
+import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from "../../views/ContextMenu";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./ImageBox.scss";
 import React = require("react");
-import { Utils } from '../../../Utils';
-import { ListField } from '../../../fields/ListField';
-import { DragManager } from '../../util/DragManager';
-import { undoBatch, UndoManager } from '../../util/UndoManager';
-import { TextField } from '../../../fields/TextField';
-import { Document } from '../../../fields/Document';
-import { RouteStore } from '../../../server/RouteStore';
-import { ServerUtils } from '../../../server/ServerUtil';
-import { CollectionSubView } from '../collections/CollectionSubView';
 
 @observer
 export class ImageBox extends React.Component<FieldViewProps> {
@@ -36,10 +32,6 @@ export class ImageBox extends React.Component<FieldViewProps> {
         super(props);
 
         this._imgRef = React.createRef();
-        this.state = {
-            photoIndex: 0,
-            isOpen: false,
-        };
     }
 
     @action
@@ -49,8 +41,6 @@ export class ImageBox extends React.Component<FieldViewProps> {
         if (this._photoIndex == 0) this.props.Document.SetNumber(KeyStore.NativeHeight, this.props.Document.GetNumber(KeyStore.NativeWidth, 0) * h / w);
     }
 
-    componentDidMount() {
-    }
 
     protected createDropTarget = (ele: HTMLDivElement) => {
         if (this.dropDisposer) {
@@ -60,10 +50,6 @@ export class ImageBox extends React.Component<FieldViewProps> {
             this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
         }
     }
-
-    componentWillUnmount() {
-    }
-
     onDrop = (e: React.DragEvent) => {
         e.stopPropagation();
         e.preventDefault();
@@ -158,8 +144,8 @@ export class ImageBox extends React.Component<FieldViewProps> {
         let dist = Math.min(nativeWidth / paths.length, 40);
         let left = (nativeWidth - paths.length * dist) / 2;
         return paths.map((p, i) =>
-            <div className="imageBox-placer"  >
-                <div className="imageBox-dot" style={{ transform: `translate(${i * dist + left}px, 0px)` }} key={`i`} onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); this.onDotDown(i); }} />
+            <div className="imageBox-placer" key={i} >
+                <div className="imageBox-dot" style={{ background: (i == this._photoIndex ? "black" : "gray"), transform: `translate(${i * dist + left}px, 0px)` }} onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); this.onDotDown(i); }} />
             </div>
         )
     }
@@ -174,7 +160,7 @@ export class ImageBox extends React.Component<FieldViewProps> {
         return (
             <div className="imageBox-cont" onPointerDown={this.onPointerDown} onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
                 <img src={paths[Math.min(paths.length, this._photoIndex)]} style={{ objectFit: (this._photoIndex == 0 ? undefined : "contain") }} width={nativeWidth} alt="Image not found" ref={this._imgRef} onLoad={this.onLoad} />
-                {this.dots(paths)}
+                {paths.length > 1 ? this.dots(paths) : (null)}
                 {this.lightbox(paths)}
             </div>);
     }
-- 
cgit v1.2.3-70-g09d2


From 1319573481868f2647259fce37cd15b35c3cafad Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 17 Apr 2019 12:07:55 -0400
Subject: fixed onactivechanged exception.  restored preview region

---
 src/client/util/SelectionManager.ts                           |  4 ++--
 src/client/views/Main.tsx                                     |  2 +-
 src/client/views/MainOverlayTextBox.tsx                       |  2 +-
 src/client/views/collections/CollectionBaseView.tsx           |  8 ++++----
 src/client/views/collections/CollectionDockingView.tsx        |  2 +-
 src/client/views/collections/CollectionSchemaView.tsx         | 11 +++++------
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx |  2 +-
 src/client/views/nodes/DocumentView.tsx                       |  2 +-
 src/client/views/nodes/FieldView.tsx                          |  4 ++--
 src/client/views/nodes/KeyValuePair.tsx                       |  2 +-
 10 files changed, 19 insertions(+), 20 deletions(-)

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

diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 320553952..b15a93d9f 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -18,13 +18,13 @@ export namespace SelectionManager {
 
             if (manager.SelectedDocuments.indexOf(doc) === -1) {
                 manager.SelectedDocuments.push(doc);
-                doc.props.onActiveChanged(true);
+                doc.props.whenActiveChanged(true);
             }
         }
 
         @action
         DeselectAll(): void {
-            manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false));
+            manager.SelectedDocuments.map(dv => dv.props.whenActiveChanged(false));
             manager.SelectedDocuments = [];
             MainOverlayTextBox.Instance.SetTextDoc();
         }
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 0469211fa..84fdeab92 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -190,7 +190,7 @@ export class Main extends React.Component {
                             selectOnLoad={false}
                             focus={emptyDocFunction}
                             parentActive={returnTrue}
-                            onActiveChanged={emptyFunction}
+                            whenActiveChanged={emptyFunction}
                             ContainingCollectionView={undefined} />}
                 </div>
             }
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 141b3ad74..8cb01117c 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -101,7 +101,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
             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} />
+                        selectOnLoad={true} ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={emptyDocFunction} />
                 </div>
             </ div>;
         }
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 4fda38a26..a8b061b04 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -22,7 +22,7 @@ export interface CollectionRenderProps {
     removeDocument: (document: Document) => boolean;
     moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
     active: () => boolean;
-    onActiveChanged: (isActive: boolean) => void;
+    whenActiveChanged: (isActive: boolean) => void;
 }
 
 export interface CollectionViewProps extends FieldViewProps {
@@ -55,9 +55,9 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
 
     //TODO should this be observable?
     private _isChildActive = false;
-    onActiveChanged = (isActive: boolean) => {
+    whenActiveChanged = (isActive: boolean) => {
         this._isChildActive = isActive;
-        this.props.onActiveChanged(isActive);
+        this.props.whenActiveChanged(isActive);
     }
 
     createsCycle(documentToAdd: Document, containerDocument: Document): boolean {
@@ -184,7 +184,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
             removeDocument: this.removeDocument,
             moveDocument: this.moveDocument,
             active: this.active,
-            onActiveChanged: this.onActiveChanged,
+            whenActiveChanged: this.whenActiveChanged,
         };
         const viewtype = this.collectionViewType;
         return (
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 2b886adb6..9ec0b3aeb 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -363,7 +363,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                     isTopMost={true}
                     selectOnLoad={false}
                     parentActive={returnTrue}
-                    onActiveChanged={emptyFunction}
+                    whenActiveChanged={emptyFunction}
                     focus={emptyDocFunction}
                     ContainingCollectionView={undefined} />
             </div>;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 1defdba7e..15b7617ae 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -24,7 +24,7 @@ import { EditableView } from "../EditableView";
 import { DocumentView } from "../nodes/DocumentView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
-import { CollectionSubView } from "./CollectionSubView";
+import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView";
 
 
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
@@ -70,7 +70,6 @@ export class CollectionSchemaView extends CollectionSubView {
 
     @computed get splitPercentage() { return this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0); }
 
-
     renderCell = (rowProps: CellInfo) => {
         let props: FieldViewProps = {
             Document: rowProps.value[0],
@@ -83,7 +82,7 @@ export class CollectionSchemaView extends CollectionSubView {
             ScreenToLocalTransform: Transform.Identity,
             focus: emptyDocFunction,
             active: returnFalse,
-            onActiveChanged: emptyFunction,
+            whenActiveChanged: emptyFunction,
         };
         let contents = (
             <FieldView {...props} />
@@ -285,7 +284,7 @@ export class CollectionSchemaView extends CollectionSubView {
         this._optionsActivated++;
     }
 
-    @observable previewScript: string = "this";
+    @observable previewScript: string = "";
     @action
     onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         this.previewScript = e.currentTarget.value;
@@ -299,7 +298,7 @@ export class CollectionSchemaView extends CollectionSubView {
         const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
         //all the keys/columns that will be displayed in the schema
         const allKeys = this.findAllDocumentKeys;
-        let doc: any = selected ? selected.Get(new Key(this.previewScript)) : undefined;
+        let doc: any = selected ? (this.previewScript ? selected.Get(new Key(this.previewScript)) : selected) : undefined;
 
         // let doc = CompileScript(this.previewScript, { this: selected }, true)();
         let content = this._selectedIndex === -1 || !selected ? (null) : (
@@ -318,7 +317,7 @@ export class CollectionSchemaView extends CollectionSubView {
                                 ContainingCollectionView={this.props.CollectionView}
                                 focus={emptyDocFunction}
                                 parentActive={this.props.active}
-                                onActiveChanged={this.props.onActiveChanged} /> : null}
+                                whenActiveChanged={this.props.whenActiveChanged} /> : null}
                         <input value={this.previewScript} onChange={this.onPreviewScriptChange}
                             style={{ position: 'absolute', bottom: '0px' }} />
                     </div>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 97708ce19..80322c900 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -248,7 +248,7 @@ export class CollectionFreeFormView extends CollectionSubView {
             ContainingCollectionView: this.props.CollectionView,
             focus: this.focusDocument,
             parentActive: this.props.active,
-            onActiveChanged: this.props.active,
+            whenActiveChanged: this.props.active,
         };
     }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b99e449be..d74f9fc57 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -39,7 +39,7 @@ export interface DocumentViewProps {
     focus: (doc: Document) => void;
     selectOnLoad: boolean;
     parentActive: () => boolean;
-    onActiveChanged: (isActive: boolean) => void;
+    whenActiveChanged: (isActive: boolean) => void;
 }
 export interface JsxArgs extends DocumentViewProps {
     Keys: { [name: string]: Key };
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index ebd25f937..e9f6950ff 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -43,7 +43,7 @@ export interface FieldViewProps {
     moveDocument?: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
     ScreenToLocalTransform: () => Transform;
     active: () => boolean;
-    onActiveChanged: (isActive: boolean) => void;
+    whenActiveChanged: (isActive: boolean) => void;
     focus: (doc: Document) => void;
 }
 
@@ -95,7 +95,7 @@ export class FieldView extends React.Component<FieldViewProps> {
                     layoutKey={KeyStore.Layout}
                     ContainingCollectionView={this.props.ContainingCollectionView}
                     parentActive={this.props.active}
-                    onActiveChanged={this.props.onActiveChanged} />
+                    whenActiveChanged={this.props.whenActiveChanged} />
             );
         }
         else if (field instanceof ListField) {
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 5d69f23b2..d480eb5af 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -48,7 +48,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             isTopMost: false,
             selectOnLoad: false,
             active: returnFalse,
-            onActiveChanged: emptyFunction,
+            whenActiveChanged: emptyFunction,
             ScreenToLocalTransform: Transform.Identity,
             focus: emptyDocFunction,
         };
-- 
cgit v1.2.3-70-g09d2


From 8d7d4d4bee15f250eb93f4fb850075e99bebedd3 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 17 Apr 2019 14:17:29 -0400
Subject: schemaviews

---
 .../views/collections/CollectionSchemaView.scss    | 10 ++-
 .../views/collections/CollectionSchemaView.tsx     | 89 +++++++++++++---------
 2 files changed, 60 insertions(+), 39 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index c8bfedff4..8c51ed752 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -71,7 +71,10 @@ ul {
         position: absolute;
         height: 100%;
         width: 100%;
-        overflow: auto;
+        .documentView-node:first-child {
+            position: relative;
+            background: $light-color;
+        }
     }
     .collectionSchemaView-previewRegion {
         position: relative;
@@ -193,6 +196,11 @@ ul {
         border-bottom-style: solid;
         border-bottom-width: 1;
     }
+    .documentView-node-topmost {
+        text-align:left;
+        transform-origin: center top;
+        display: inline-block;
+    }
     .documentView-node:first-child {
         background: $light-color;
         .imageBox-cont img {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 15b7617ae..9e740eb00 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -12,7 +12,7 @@ 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, returnOne } from "../../../Utils";
+import { emptyDocFunction, emptyFunction, returnFalse, returnOne, Utils } from "../../../Utils";
 import { Server } from "../../Server";
 import { SetupDrag } from "../../util/DragManager";
 import { CompileScript, ToField } from "../../util/Scripting";
@@ -25,6 +25,7 @@ import { DocumentView } from "../nodes/DocumentView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
 import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView";
+import { CollectionDockingView } from "./CollectionDockingView";
 
 
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
@@ -60,7 +61,6 @@ export class CollectionSchemaView extends CollectionSubView {
     private DIVIDER_WIDTH = 4;
 
     @observable _columns: Array<Key> = [KeyStore.Title, KeyStore.Data, KeyStore.Author];
-    @observable _contentScaling = 1; // used to transfer the dimensions of the content pane in the DOM to the ContentScaling prop of the DocumentView
     @observable _dividerX = 0;
     @observable _panelWidth = 0;
     @observable _panelHeight = 0;
@@ -234,22 +234,9 @@ export class CollectionSchemaView extends CollectionSubView {
     setTableDimensions = (r: any) => {
         this._tableWidth = r.entry.width;
     }
-    @action
-    setScaling = (r: any) => {
-        const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
-        const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
-        this._panelWidth = r.entry.width;
-        this._panelHeight = r.entry.height ? r.entry.height : this._panelHeight;
-        this._contentScaling = r.entry.width / selected!.GetNumber(KeyStore.NativeWidth, r.entry.width);
-    }
 
     @computed
     get borderWidth() { return COLLECTION_BORDER_WIDTH; }
-    getContentScaling = (): number => this._contentScaling;
-    getPanelWidth = (): number => this._panelWidth;
-    getPanelHeight = (): number => this._panelHeight;
-    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);
 
     onPointerDown = (e: React.PointerEvent): void => {
         if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) {
@@ -289,37 +276,63 @@ export class CollectionSchemaView extends CollectionSubView {
     onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         this.previewScript = e.currentTarget.value;
     }
+    private get _document(): Document | undefined {
+        const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
+        const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
+        return selected ? (this.previewScript ? selected.Get(new Key(this.previewScript)) as Document : selected) : undefined;
+    }
 
+    private nativeWidth = () => this._document!.GetNumber(KeyStore.NativeWidth, this._panelWidth);
+    private nativeHeight = () => this._document!.GetNumber(KeyStore.NativeHeight, this._panelHeight);
+    private panelWidth = () => this.nativeWidth() * this._contentScaling();
+    private panelHeight = () => this.nativeHeight() * this._contentScaling();
+    private _contentScaling = () => {
+        let wscale = this._panelWidth / (this.nativeWidth() ? this.nativeWidth() : this._panelWidth);
+        if (wscale * this.nativeHeight() > this._panelHeight)
+            return this._panelHeight / (this.nativeHeight() ? this.nativeHeight() : this._panelHeight);
+        return wscale;
+    }
+    getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- this.borderWidth - this.DIVIDER_WIDTH - this._dividerX - this._tableWidth - this.offset(), - this.borderWidth).scale(1 / this._contentScaling());
+    getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- this.borderWidth - this.DIVIDER_WIDTH - this._dividerX, - this.borderWidth).scale(1 / this._contentScaling());
+
+    offset = () => ((this.props as any).PanelWidth() * this.splitPercentage / 100 - this.nativeWidth() * this._contentScaling()) / 2;
+    @action
+    setScaling = (r: any) => {
+        this._panelWidth = r.entry.width;
+        this._panelHeight = r.entry.height ? r.entry.height : this._panelHeight;
+    }
     render() {
         library.add(faCog);
         library.add(faPlus);
+        if (!this._document)
+            return (null);
         const columns = this.columns;
-        const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
-        const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
-        //all the keys/columns that will be displayed in the schema
+        let doc = this._document;
+        const childs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
         const allKeys = this.findAllDocumentKeys;
-        let doc: any = selected ? (this.previewScript ? selected.Get(new Key(this.previewScript)) : selected) : undefined;
-
         // let doc = CompileScript(this.previewScript, { this: selected }, true)();
-        let content = this._selectedIndex === -1 || !selected ? (null) : (
+        let offset = this.offset();
+        let content = !doc ? (null) : (
             <Measure onResize={this.setScaling}>
                 {({ measureRef }) =>
                     <div className="collectionSchemaView-content" ref={measureRef}>
-                        {doc instanceof Document ?
-                            <DocumentView Document={doc}
-                                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}
-                                focus={emptyDocFunction}
-                                parentActive={this.props.active}
-                                whenActiveChanged={this.props.whenActiveChanged} /> : null}
-                        <input value={this.previewScript} onChange={this.onPreviewScriptChange}
-                            style={{ position: 'absolute', bottom: '0px' }} />
+                        <div style={{ transform: `translate(${offset}px, 0px)`, height: "100%", position: "absolute" }}>
+                            {doc instanceof Document ?
+                                <DocumentView Document={doc}
+                                    addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
+                                    isTopMost={false}
+                                    selectOnLoad={false}
+                                    ScreenToLocalTransform={this.getPreviewTransform}
+                                    ContentScaling={this._contentScaling}
+                                    PanelWidth={this.panelWidth}
+                                    PanelHeight={this.panelHeight}
+                                    ContainingCollectionView={this.props.CollectionView}
+                                    focus={emptyDocFunction}
+                                    parentActive={this.props.active}
+                                    whenActiveChanged={this.props.whenActiveChanged} /> : null}
+                            <input value={this.previewScript} onChange={this.onPreviewScriptChange}
+                                style={{ position: 'absolute', bottom: '0px', left: "0px" }} />
+                        </div>
                     </div>
                 }
             </Measure>
@@ -355,8 +368,8 @@ export class CollectionSchemaView extends CollectionSubView {
                         {({ measureRef }) =>
                             <div className="collectionSchemaView-tableContainer" ref={measureRef} style={{ width: `calc(100% - ${this.splitPercentage}%)` }}>
                                 <ReactTable
-                                    data={children}
-                                    pageSize={children.length}
+                                    data={childs}
+                                    pageSize={childs.length}
                                     page={0}
                                     showPagination={false}
                                     columns={columns.map(col => ({
-- 
cgit v1.2.3-70-g09d2


From 3ec6343ad5f15cfe43c233f29453f02c435aecda Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 17 Apr 2019 19:12:38 -0400
Subject: fixed schema layout

---
 .../views/collections/CollectionSchemaView.scss    |  11 ++
 .../views/collections/CollectionSchemaView.tsx     | 164 ++++++++++-----------
 2 files changed, 86 insertions(+), 89 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index 8c51ed752..580bce21b 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -71,6 +71,17 @@ ul {
         position: absolute;
         height: 100%;
         width: 100%;
+        .collectionSchemaView-previewDoc {
+            height: 100%;
+            width: 100%; 
+            position: absolute;
+        }
+        .collectionSchemaView-input {
+            position: absolute;
+            max-width: 150px;
+            width: 100%;
+            bottom: 0px;
+        }
         .documentView-node:first-child {
             position: relative;
             background: $light-color;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 9e740eb00..f7d69aee1 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -61,9 +61,6 @@ export class CollectionSchemaView extends CollectionSubView {
     private DIVIDER_WIDTH = 4;
 
     @observable _columns: Array<Key> = [KeyStore.Title, KeyStore.Data, KeyStore.Author];
-    @observable _dividerX = 0;
-    @observable _panelWidth = 0;
-    @observable _panelHeight = 0;
     @observable _selectedIndex = 0;
     @observable _columnsPercentage = 0;
     @observable _keys: Key[] = [];
@@ -229,11 +226,6 @@ export class CollectionSchemaView extends CollectionSubView {
         document.addEventListener('pointerup', this.onDividerUp);
     }
 
-    @observable _tableWidth = 0;
-    @action
-    setTableDimensions = (r: any) => {
-        this._tableWidth = r.entry.width;
-    }
 
     @computed
     get borderWidth() { return COLLECTION_BORDER_WIDTH; }
@@ -276,72 +268,65 @@ export class CollectionSchemaView extends CollectionSubView {
     onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         this.previewScript = e.currentTarget.value;
     }
-    private get _document(): Document | undefined {
+    private get previewDocument(): Document | undefined {
         const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
         const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
         return selected ? (this.previewScript ? selected.Get(new Key(this.previewScript)) as Document : selected) : undefined;
     }
 
-    private nativeWidth = () => this._document!.GetNumber(KeyStore.NativeWidth, this._panelWidth);
-    private nativeHeight = () => this._document!.GetNumber(KeyStore.NativeHeight, this._panelHeight);
-    private panelWidth = () => this.nativeWidth() * this._contentScaling();
-    private panelHeight = () => this.nativeHeight() * this._contentScaling();
-    private _contentScaling = () => {
-        let wscale = this._panelWidth / (this.nativeWidth() ? this.nativeWidth() : this._panelWidth);
-        if (wscale * this.nativeHeight() > this._panelHeight)
-            return this._panelHeight / (this.nativeHeight() ? this.nativeHeight() : this._panelHeight);
+    getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(
+        - this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth - this.previewPanelCenteringOffset,
+        - this.borderWidth).scale(1 / this.previewContentScaling());
+    getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- this.borderWidth - this.DIVIDER_WIDTH, - this.borderWidth).scale(1 / this.previewContentScaling());
+
+    get tableWidth() { return (this.props as any).PanelWidth() * (1 - this.splitPercentage / 100); }
+    get previewRegionHeight() { return (this.props as any).PanelHeight(); }
+    get previewRegionWidth() { return (this.props as any).PanelWidth() * this.splitPercentage / 100 - this.DIVIDER_WIDTH; }
+
+    get previewPanelCenteringOffset() { return (this.previewRegionWidth - this.previewDocNativeWidth() * this.previewContentScaling()) / 2; }
+    private previewPanelWidth = () => this.previewDocNativeWidth() * this.previewContentScaling();
+    private previewPanelHeight = () => this.previewDocNativeHeight() * this.previewContentScaling();
+    private previewContentScaling = () => {
+        let wscale = this.previewRegionWidth / (this.previewDocNativeWidth() ? this.previewDocNativeWidth() : this.previewRegionWidth);
+        if (wscale * this.previewDocNativeHeight() > this.previewRegionHeight)
+            return this.previewRegionHeight / (this.previewDocNativeHeight() ? this.previewDocNativeHeight() : this.previewRegionHeight);
         return wscale;
     }
-    getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- this.borderWidth - this.DIVIDER_WIDTH - this._dividerX - this._tableWidth - this.offset(), - this.borderWidth).scale(1 / this._contentScaling());
-    getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- this.borderWidth - this.DIVIDER_WIDTH - this._dividerX, - this.borderWidth).scale(1 / this._contentScaling());
+    private previewDocNativeWidth = () => this.previewDocument!.GetNumber(KeyStore.NativeWidth, this.previewRegionWidth);
+    private previewDocNativeHeight = () => this.previewDocument!.GetNumber(KeyStore.NativeHeight, this.previewRegionHeight);
 
-    offset = () => ((this.props as any).PanelWidth() * this.splitPercentage / 100 - this.nativeWidth() * this._contentScaling()) / 2;
-    @action
-    setScaling = (r: any) => {
-        this._panelWidth = r.entry.width;
-        this._panelHeight = r.entry.height ? r.entry.height : this._panelHeight;
-    }
-    render() {
-        library.add(faCog);
-        library.add(faPlus);
-        if (!this._document)
-            return (null);
-        const columns = this.columns;
-        let doc = this._document;
-        const childs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
-        const allKeys = this.findAllDocumentKeys;
+    @computed
+    get previewPanel() {
+        let doc = this.previewDocument;
         // let doc = CompileScript(this.previewScript, { this: selected }, true)();
-        let offset = this.offset();
-        let content = !doc ? (null) : (
-            <Measure onResize={this.setScaling}>
-                {({ measureRef }) =>
-                    <div className="collectionSchemaView-content" ref={measureRef}>
-                        <div style={{ transform: `translate(${offset}px, 0px)`, height: "100%", position: "absolute" }}>
-                            {doc instanceof Document ?
-                                <DocumentView Document={doc}
-                                    addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
-                                    isTopMost={false}
-                                    selectOnLoad={false}
-                                    ScreenToLocalTransform={this.getPreviewTransform}
-                                    ContentScaling={this._contentScaling}
-                                    PanelWidth={this.panelWidth}
-                                    PanelHeight={this.panelHeight}
-                                    ContainingCollectionView={this.props.CollectionView}
-                                    focus={emptyDocFunction}
-                                    parentActive={this.props.active}
-                                    whenActiveChanged={this.props.whenActiveChanged} /> : null}
-                            <input value={this.previewScript} onChange={this.onPreviewScriptChange}
-                                style={{ position: 'absolute', bottom: '0px', left: "0px" }} />
-                        </div>
+        let inputPanelWidth = Math.min(75, this.previewPanelWidth() / 2);
+        return !doc ? (null) : (
+            <div className="collectionSchemaView-previewRegion" style={{ width: `calc(${this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0)}% - ${this.DIVIDER_WIDTH}px)` }}>
+                <div className="collectionSchemaView-content" >
+                    <div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
+                        {doc instanceof Document ?
+                            <DocumentView Document={doc} isTopMost={false} selectOnLoad={false}
+                                addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
+                                ScreenToLocalTransform={this.getPreviewTransform}
+                                ContentScaling={this.previewContentScaling}
+                                PanelWidth={this.previewPanelWidth} PanelHeight={this.previewPanelHeight}
+                                ContainingCollectionView={this.props.CollectionView}
+                                focus={emptyDocFunction}
+                                parentActive={this.props.active}
+                                whenActiveChanged={this.props.whenActiveChanged}
+                            /> :
+                            null}
                     </div>
-                }
-            </Measure>
+                    <input className="collectionSchemaView-input" value={this.previewScript} onChange={this.onPreviewScriptChange}
+                        style={{ left: `calc(50% - ${inputPanelWidth}px)` }} />
+                </div>
+            </div>
         );
-        let dividerDragger = this.splitPercentage === 0 ? (null) :
-            <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />;
-
-        //options button and menu
-        let optionsMenu = !this.props.active() ? (null) : (<Flyout
+    }
+    @computed
+    get tableOptionsPanel() {
+        const allKeys = this.findAllDocumentKeys;
+        return !this.props.active() ? (null) : (<Flyout
             anchorPoint={anchorPoints.LEFT_TOP}
             content={<div>
                 <div id="schema-options-header"><h5><b>Options</b></h5></div>
@@ -360,37 +345,38 @@ export class CollectionSchemaView extends CollectionSubView {
             }>
             <button id="schemaOptionsMenuBtn" onPointerDown={this.OptionsMenuDown}><FontAwesomeIcon style={{ color: "white" }} icon="cog" size="sm" /></button>
         </Flyout>);
+    }
 
+    @computed
+    get dividerDragger() {
+        return this.splitPercentage === 0 ? (null) :
+            <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />;
+    }
+
+    render() {
+        library.add(faCog);
+        library.add(faPlus);
+        if (!this.previewDocument)
+            return (null);
+
+        const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
         return (
             <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 }) =>
-                            <div className="collectionSchemaView-tableContainer" ref={measureRef} style={{ width: `calc(100% - ${this.splitPercentage}%)` }}>
-                                <ReactTable
-                                    data={childs}
-                                    pageSize={childs.length}
-                                    page={0}
-                                    showPagination={false}
-                                    columns={columns.map(col => ({
-                                        Header: col.Name,
-                                        accessor: (doc: Document) => [doc, col],
-                                        id: col.Id
-                                    }))}
-                                    column={{
-                                        ...ReactTableDefaults.column,
-                                        Cell: this.renderCell,
-
-                                    }}
-                                    getTrProps={this.getTrProps}
-                                />
-                            </div>}
-                    </Measure>
-                    {dividerDragger}
-                    <div className="collectionSchemaView-previewRegion" style={{ width: `calc(${this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0)}% - ${this.DIVIDER_WIDTH}px)` }}>
-                        {content}
+                    <div className="collectionSchemaView-tableContainer" style={{ width: `calc(100% - ${this.splitPercentage}%)` }}>
+                        <ReactTable data={children} page={0} pageSize={children.length} showPagination={false}
+                            columns={this.columns.map(col => ({
+                                Header: col.Name,
+                                accessor: (doc: Document) => [doc, col],
+                                id: col.Id
+                            }))}
+                            column={{ ...ReactTableDefaults.column, Cell: this.renderCell, }}
+                            getTrProps={this.getTrProps}
+                        />
                     </div>
-                    {optionsMenu}
+                    {this.dividerDragger}
+                    {this.previewPanel}
+                    {this.tableOptionsPanel}
                 </div>
             </div >
         );
-- 
cgit v1.2.3-70-g09d2


From d998bdaa38677e4888393db01cf9b73bb49f2cc9 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 17 Apr 2019 19:55:12 -0400
Subject: some schema view simplifications

---
 .../views/collections/CollectionSchemaView.scss    | 125 ++++++++++-----------
 .../views/collections/CollectionSchemaView.tsx     |  98 ++++++++--------
 src/client/views/collections/CollectionSubView.tsx |   6 +-
 3 files changed, 112 insertions(+), 117 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index 580bce21b..bced0d5d4 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -1,61 +1,5 @@
 @import "../globalCssVariables";
 
-//options menu styling
-#schemaOptionsMenuBtn {
-    position: absolute;
-    height: 20px;
-    width: 20px;
-    border-radius: 50%;
-    z-index: 21; 
-    right: 4px;
-    top: 4px; 
-    pointer-events: auto;
-    background-color:black;
-    display:inline-block;
-    padding: 0px;
-    font-size: 100%;
-}
-
-ul {
-    list-style-type: disc;
-}
-
-#schema-options-header {
-    text-align: center;
-    padding: 0px;
-    margin: 0px;
-}
-.schema-options-subHeader {
-    color: $intermediate-color;
-    margin-bottom: 5px;
-}
-#schemaOptionsMenuBtn:hover {
-    transform: scale(1.15);
-}
-
-#preview-schema-checkbox-div {
-    margin-left: 20px;
-    font-size: 12px;
-}
-
-  #options-flyout-div {
-    text-align: left;
-    padding:0px;
-    z-index: 100;
-    font-family: $sans-serif;
-    padding-left: 5px;
-  }
-
-  #schema-col-checklist {
-    overflow: scroll;
-    text-align: left;
-    //background-color: $light-color-secondary;
-    line-height: 25px;
-    max-height: 175px;
-    font-family: $sans-serif;
-    font-size: 12px;
-  }
-  
 
 .collectionSchemaView-container {
     border-width: $COLLECTION_BORDER_WIDTH;
@@ -67,10 +11,11 @@ ul {
     width: 100%;
     height: 100%;
     
-    .collectionSchemaView-content {
-        position: absolute;
+    .collectionSchemaView-previewRegion {
+        position: relative;
+        background: $light-color;
+        float: left;
         height: 100%;
-        width: 100%;
         .collectionSchemaView-previewDoc {
             height: 100%;
             width: 100%; 
@@ -87,12 +32,6 @@ ul {
             background: $light-color;
         }
     }
-    .collectionSchemaView-previewRegion {
-        position: relative;
-        background: $light-color;
-        float: left;
-        height: 100%;
-    }
     .collectionSchemaView-previewHandle {
         position: absolute;
         height: 15px;
@@ -219,6 +158,62 @@ ul {
         }
     }
 }
+//options menu styling
+#schemaOptionsMenuBtn {
+    position: absolute;
+    height: 20px;
+    width: 20px;
+    border-radius: 50%;
+    z-index: 21; 
+    right: 4px;
+    top: 4px; 
+    pointer-events: auto;
+    background-color:black;
+    display:inline-block;
+    padding: 0px;
+    font-size: 100%;
+}
+
+ul {
+    list-style-type: disc;
+}
+
+#schema-options-header {
+    text-align: center;
+    padding: 0px;
+    margin: 0px;
+}
+.schema-options-subHeader {
+    color: $intermediate-color;
+    margin-bottom: 5px;
+}
+#schemaOptionsMenuBtn:hover {
+    transform: scale(1.15);
+}
+
+#preview-schema-checkbox-div {
+    margin-left: 20px;
+    font-size: 12px;
+}
+
+  #options-flyout-div {
+    text-align: left;
+    padding:0px;
+    z-index: 100;
+    font-family: $sans-serif;
+    padding-left: 5px;
+  }
+
+  #schema-col-checklist {
+    overflow: scroll;
+    text-align: left;
+    //background-color: $light-color-secondary;
+    line-height: 25px;
+    max-height: 175px;
+    font-family: $sans-serif;
+    font-size: 12px;
+  }
+  
 
 .Resizer {
     box-sizing: border-box;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index f7d69aee1..4e3149fe4 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -4,7 +4,6 @@ import { faCog, faPlus } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 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";
@@ -12,7 +11,7 @@ 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, returnOne, Utils } from "../../../Utils";
+import { emptyDocFunction, emptyFunction, returnFalse } from "../../../Utils";
 import { Server } from "../../Server";
 import { SetupDrag } from "../../util/DragManager";
 import { CompileScript, ToField } from "../../util/Scripting";
@@ -24,8 +23,7 @@ import { EditableView } from "../EditableView";
 import { DocumentView } from "../nodes/DocumentView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
-import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView";
-import { CollectionDockingView } from "./CollectionDockingView";
+import { CollectionSubView } from "./CollectionSubView";
 
 
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
@@ -56,7 +54,7 @@ class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggl
 
 @observer
 export class CollectionSchemaView extends CollectionSubView {
-    private _mainCont = React.createRef<HTMLDivElement>();
+    private _mainCont?: HTMLDivElement;
     private _startSplitPercent = 0;
     private DIVIDER_WIDTH = 4;
 
@@ -207,7 +205,7 @@ export class CollectionSchemaView extends CollectionSubView {
 
     @action
     onDividerMove = (e: PointerEvent): void => {
-        let nativeWidth = this._mainCont.current!.getBoundingClientRect();
+        let nativeWidth = this._mainCont!.getBoundingClientRect();
         this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100)));
     }
     @action
@@ -279,47 +277,41 @@ export class CollectionSchemaView extends CollectionSubView {
         - this.borderWidth).scale(1 / this.previewContentScaling());
     getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- this.borderWidth - this.DIVIDER_WIDTH, - this.borderWidth).scale(1 / this.previewContentScaling());
 
-    get tableWidth() { return (this.props as any).PanelWidth() * (1 - this.splitPercentage / 100); }
-    get previewRegionHeight() { return (this.props as any).PanelHeight(); }
-    get previewRegionWidth() { return (this.props as any).PanelWidth() * this.splitPercentage / 100 - this.DIVIDER_WIDTH; }
+    get tableWidth() { return this.props.PanelWidth() * (1 - this.splitPercentage / 100); }
+    get previewRegionHeight() { return this.props.PanelHeight(); }
+    get previewRegionWidth() { return this.props.PanelWidth() * this.splitPercentage / 100 - this.DIVIDER_WIDTH; }
 
-    get previewPanelCenteringOffset() { return (this.previewRegionWidth - this.previewDocNativeWidth() * this.previewContentScaling()) / 2; }
-    private previewPanelWidth = () => this.previewDocNativeWidth() * this.previewContentScaling();
-    private previewPanelHeight = () => this.previewDocNativeHeight() * this.previewContentScaling();
+    private previewDocNativeWidth = () => this.previewDocument!.GetNumber(KeyStore.NativeWidth, this.previewRegionWidth);
+    private previewDocNativeHeight = () => this.previewDocument!.GetNumber(KeyStore.NativeHeight, this.previewRegionHeight);
     private previewContentScaling = () => {
         let wscale = this.previewRegionWidth / (this.previewDocNativeWidth() ? this.previewDocNativeWidth() : this.previewRegionWidth);
         if (wscale * this.previewDocNativeHeight() > this.previewRegionHeight)
             return this.previewRegionHeight / (this.previewDocNativeHeight() ? this.previewDocNativeHeight() : this.previewRegionHeight);
         return wscale;
     }
-    private previewDocNativeWidth = () => this.previewDocument!.GetNumber(KeyStore.NativeWidth, this.previewRegionWidth);
-    private previewDocNativeHeight = () => this.previewDocument!.GetNumber(KeyStore.NativeHeight, this.previewRegionHeight);
+    private previewPanelWidth = () => this.previewDocNativeWidth() * this.previewContentScaling();
+    private previewPanelHeight = () => this.previewDocNativeHeight() * this.previewContentScaling();
+    get previewPanelCenteringOffset() { return (this.previewRegionWidth - this.previewDocNativeWidth() * this.previewContentScaling()) / 2; }
 
     @computed
     get previewPanel() {
-        let doc = this.previewDocument;
         // let doc = CompileScript(this.previewScript, { this: selected }, true)();
-        let inputPanelWidth = Math.min(75, this.previewPanelWidth() / 2);
-        return !doc ? (null) : (
+        return !this.previewDocument ? (null) : (
             <div className="collectionSchemaView-previewRegion" style={{ width: `calc(${this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0)}% - ${this.DIVIDER_WIDTH}px)` }}>
-                <div className="collectionSchemaView-content" >
-                    <div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
-                        {doc instanceof Document ?
-                            <DocumentView Document={doc} isTopMost={false} selectOnLoad={false}
-                                addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
-                                ScreenToLocalTransform={this.getPreviewTransform}
-                                ContentScaling={this.previewContentScaling}
-                                PanelWidth={this.previewPanelWidth} PanelHeight={this.previewPanelHeight}
-                                ContainingCollectionView={this.props.CollectionView}
-                                focus={emptyDocFunction}
-                                parentActive={this.props.active}
-                                whenActiveChanged={this.props.whenActiveChanged}
-                            /> :
-                            null}
-                    </div>
-                    <input className="collectionSchemaView-input" value={this.previewScript} onChange={this.onPreviewScriptChange}
-                        style={{ left: `calc(50% - ${inputPanelWidth}px)` }} />
+                <div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
+                    <DocumentView Document={this.previewDocument} isTopMost={false} selectOnLoad={false}
+                        addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
+                        ScreenToLocalTransform={this.getPreviewTransform}
+                        ContentScaling={this.previewContentScaling}
+                        PanelWidth={this.previewPanelWidth} PanelHeight={this.previewPanelHeight}
+                        ContainingCollectionView={this.props.CollectionView}
+                        focus={emptyDocFunction}
+                        parentActive={this.props.active}
+                        whenActiveChanged={this.props.whenActiveChanged}
+                    />
                 </div>
+                <input className="collectionSchemaView-input" value={this.previewScript} onChange={this.onPreviewScriptChange}
+                    style={{ left: `calc(50% - ${Math.min(75, this.previewPanelWidth() / 2)}px)` }} />
             </div>
         );
     }
@@ -353,6 +345,11 @@ export class CollectionSchemaView extends CollectionSubView {
             <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />;
     }
 
+    createTarget = (ele: HTMLDivElement) => {
+        this._mainCont = ele;
+        super.CreateDropTarget(ele);
+    }
+
     render() {
         library.add(faCog);
         library.add(faPlus);
@@ -361,24 +358,23 @@ export class CollectionSchemaView extends CollectionSubView {
 
         const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
         return (
-            <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}>
-                    <div className="collectionSchemaView-tableContainer" style={{ width: `calc(100% - ${this.splitPercentage}%)` }}>
-                        <ReactTable data={children} page={0} pageSize={children.length} showPagination={false}
-                            columns={this.columns.map(col => ({
-                                Header: col.Name,
-                                accessor: (doc: Document) => [doc, col],
-                                id: col.Id
-                            }))}
-                            column={{ ...ReactTableDefaults.column, Cell: this.renderCell, }}
-                            getTrProps={this.getTrProps}
-                        />
-                    </div>
-                    {this.dividerDragger}
-                    {this.previewPanel}
-                    {this.tableOptionsPanel}
+            <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel}
+                onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createTarget}>
+                <div className="collectionSchemaView-tableContainer" style={{ width: `calc(100% - ${this.splitPercentage}%)` }}>
+                    <ReactTable data={children} page={0} pageSize={children.length} showPagination={false}
+                        columns={this.columns.map(col => ({
+                            Header: col.Name,
+                            accessor: (doc: Document) => [doc, col],
+                            id: col.Id
+                        }))}
+                        column={{ ...ReactTableDefaults.column, Cell: this.renderCell, }}
+                        getTrProps={this.getTrProps}
+                    />
                 </div>
-            </div >
+                {this.dividerDragger}
+                {this.previewPanel}
+                {this.tableOptionsPanel}
+            </div>
         );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d3d69b1af..5c3b2e586 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -15,7 +15,6 @@ import { ServerUtils } from "../../../server/ServerUtil";
 import { Server } from "../../Server";
 import { FieldViewProps } from "../nodes/FieldView";
 import * as rp from 'request-promise';
-import { emptyFunction } from "../../../Utils";
 import { CollectionView } from "./CollectionView";
 import { CollectionPDFView } from "./CollectionPDFView";
 import { CollectionVideoView } from "./CollectionVideoView";
@@ -24,6 +23,8 @@ 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;
+    PanelWidth: () => number;
+    PanelHeight: () => number;
 }
 
 export interface SubCollectionViewProps extends CollectionViewProps {
@@ -42,6 +43,9 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
             this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
         }
     }
+    protected CreateDropTarget(ele: HTMLDivElement) {
+        this.createDropTarget(ele);
+    }
 
     @action
     protected setCursorPosition(position: [number, number]) {
-- 
cgit v1.2.3-70-g09d2


From 6c56cbad38efbde578e2c2c90cbce4b7ff033059 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 17 Apr 2019 21:02:12 -0400
Subject: final cleanup of schema views

---
 .../views/collections/CollectionSchemaView.tsx     | 144 +++++++++------------
 1 file changed, 59 insertions(+), 85 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 4e3149fe4..08c9740d0 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -33,22 +33,17 @@ import { CollectionSubView } from "./CollectionSubView";
 class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggle: (key: Key) => void }> {
     @observable key: Key | undefined;
 
-    componentWillReceiveProps() {
-        Server.GetField(this.props.keyId, action((field: Opt<Field>) => {
-            if (field instanceof Key) {
-                this.key = field;
-            }
-        }));
+    constructor(props: any) {
+        super(props);
+        Server.GetField(this.props.keyId, action((field: Opt<Field>) => field instanceof Key && (this.key = field)));
     }
 
     render() {
-        if (this.key) {
-            return (<div key={this.key.Id}>
+        return !this.key ? (null) :
+            (<div key={this.key.Id}>
                 <input type="checkbox" checked={this.props.checked} onChange={() => this.key && this.props.toggle(this.key)} />
                 {this.key.Name}
             </div>);
-        }
-        return (null);
     }
 }
 
@@ -62,8 +57,11 @@ export class CollectionSchemaView extends CollectionSubView {
     @observable _selectedIndex = 0;
     @observable _columnsPercentage = 0;
     @observable _keys: Key[] = [];
+    @observable _newKeyName: string = "";
 
     @computed get splitPercentage() { return this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0); }
+    @computed get columns() { return this.props.Document.GetList(KeyStore.ColumnsKey, [] as Key[]); }
+    @computed get borderWidth() { return COLLECTION_BORDER_WIDTH; }
 
     renderCell = (rowProps: CellInfo) => {
         let props: FieldViewProps = {
@@ -159,9 +157,9 @@ export class CollectionSchemaView extends CollectionSubView {
         };
     }
 
-    @computed
-    get columns() {
-        return this.props.Document.GetList(KeyStore.ColumnsKey, [] as Key[]);
+    private createTarget = (ele: HTMLDivElement) => {
+        this._mainCont = ele;
+        super.CreateDropTarget(ele);
     }
 
     @action
@@ -181,26 +179,7 @@ export class CollectionSchemaView extends CollectionSubView {
     //toggles preview side-panel of schema
     @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);
-        }
-    }
-
-    @computed
-    get findAllDocumentKeys(): { [id: string]: boolean } {
-        const docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
-        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
-            //  invalidated and re-rendered.   This workaround will inquire all of the document fields before the options button is clicked.
-            //  then by the time the options button is clicked, all of the fields should be in place.  If a new field is added while this menu
-            //  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);
-        return keys;
+        this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage === 0 ? 33 : 0);
     }
 
     @action
@@ -224,10 +203,6 @@ export class CollectionSchemaView extends CollectionSubView {
         document.addEventListener('pointerup', this.onDividerUp);
     }
 
-
-    @computed
-    get borderWidth() { return COLLECTION_BORDER_WIDTH; }
-
     onPointerDown = (e: React.PointerEvent): void => {
         if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) {
             if (this.props.isSelected())
@@ -236,29 +211,21 @@ export class CollectionSchemaView extends CollectionSubView {
         }
     }
 
-    @action
-    addColumn = () => {
-        this.columns.push(new Key(this.newKeyName));
-        this.newKeyName = "";
-    }
-
-    @observable
-    newKeyName: string = "";
-
-    @action
-    newKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
-        this.newKeyName = e.currentTarget.value;
-    }
     onWheel = (e: React.WheelEvent): void => {
         if (this.props.active()) {
             e.stopPropagation();
         }
     }
 
-    @observable _optionsActivated: number = 0;
     @action
-    OptionsMenuDown = (e: React.PointerEvent) => {
-        this._optionsActivated++;
+    addColumn = () => {
+        this.columns.push(new Key(this._newKeyName));
+        this._newKeyName = "";
+    }
+
+    @action
+    newKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this._newKeyName = e.currentTarget.value;
     }
 
     @observable previewScript: string = "";
@@ -266,17 +233,12 @@ export class CollectionSchemaView extends CollectionSubView {
     onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         this.previewScript = e.currentTarget.value;
     }
-    private get previewDocument(): Document | undefined {
+
+    get previewDocument(): Document | undefined {
         const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
         const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
         return selected ? (this.previewScript ? selected.Get(new Key(this.previewScript)) as Document : selected) : undefined;
     }
-
-    getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(
-        - this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth - this.previewPanelCenteringOffset,
-        - this.borderWidth).scale(1 / this.previewContentScaling());
-    getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- this.borderWidth - this.DIVIDER_WIDTH, - this.borderWidth).scale(1 / this.previewContentScaling());
-
     get tableWidth() { return this.props.PanelWidth() * (1 - this.splitPercentage / 100); }
     get previewRegionHeight() { return this.props.PanelHeight(); }
     get previewRegionWidth() { return this.props.PanelWidth() * this.splitPercentage / 100 - this.DIVIDER_WIDTH; }
@@ -292,6 +254,9 @@ export class CollectionSchemaView extends CollectionSubView {
     private previewPanelWidth = () => this.previewDocNativeWidth() * this.previewContentScaling();
     private previewPanelHeight = () => this.previewDocNativeHeight() * this.previewContentScaling();
     get previewPanelCenteringOffset() { return (this.previewRegionWidth - this.previewDocNativeWidth() * this.previewContentScaling()) / 2; }
+    getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(
+        - this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth - this.previewPanelCenteringOffset,
+        - this.borderWidth).scale(1 / this.previewContentScaling());
 
     @computed
     get previewPanel() {
@@ -315,28 +280,42 @@ export class CollectionSchemaView extends CollectionSubView {
             </div>
         );
     }
-    @computed
+
+    get documentKeysCheckList() {
+        const docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
+        let keys: { [id: string]: boolean } = {};
+        // 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
+        //  invalidated and re-rendered.   This workaround will inquire all of the document fields before the options button is clicked.
+        //  then by the time the options button is clicked, all of the fields should be in place.  If a new field is added while this menu
+        //  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);
+        return Array.from(Object.keys(keys)).map(item =>
+            (<KeyToggle checked={keys[item]} key={item} keyId={item} toggle={this.toggleKey} />));
+    }
+
     get tableOptionsPanel() {
-        const allKeys = this.findAllDocumentKeys;
-        return !this.props.active() ? (null) : (<Flyout
-            anchorPoint={anchorPoints.LEFT_TOP}
-            content={<div>
-                <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>
-                    <h6 className="schema-options-subHeader" >Displayed Columns</h6>
-                    <ul id="schema-col-checklist" >
-                        {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>
+        return !this.props.active() ? (null) :
+            (<Flyout
+                anchorPoint={anchorPoints.LEFT_TOP}
+                content={<div>
+                    <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>
+                        <h6 className="schema-options-subHeader" >Displayed Columns</h6>
+                        <ul id="schema-col-checklist" >
+                            {this.documentKeysCheckList}
+                        </ul>
+                        <input value={this._newKeyName} onChange={this.newKeyChange} />
+                        <button onClick={this.addColumn}><FontAwesomeIcon style={{ color: "white" }} icon="plus" size="lg" /></button>
+                    </div>
                 </div>
-            </div>
-            }>
-            <button id="schemaOptionsMenuBtn" onPointerDown={this.OptionsMenuDown}><FontAwesomeIcon style={{ color: "white" }} icon="cog" size="sm" /></button>
-        </Flyout>);
+                }>
+                <button id="schemaOptionsMenuBtn" ><FontAwesomeIcon style={{ color: "white" }} icon="cog" size="sm" /></button>
+            </Flyout>);
     }
 
     @computed
@@ -345,11 +324,6 @@ export class CollectionSchemaView extends CollectionSubView {
             <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />;
     }
 
-    createTarget = (ele: HTMLDivElement) => {
-        this._mainCont = ele;
-        super.CreateDropTarget(ele);
-    }
-
     render() {
         library.add(faCog);
         library.add(faPlus);
-- 
cgit v1.2.3-70-g09d2


From 23f4ce6107d64e4be8b05f528fc210ad6e85d174 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 18 Apr 2019 10:03:02 -0400
Subject: more minor schema view fixes.

---
 src/client/views/collections/CollectionSchemaView.scss |  3 ---
 src/client/views/collections/CollectionSchemaView.tsx  | 13 +++++--------
 src/client/views/globalCssVariables.scss.d.ts          |  2 +-
 3 files changed, 6 insertions(+), 12 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index bced0d5d4..6eabbe17c 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -153,9 +153,6 @@
     }
     .documentView-node:first-child {
         background: $light-color;
-        .imageBox-cont img {
-            object-fit: contain;
-        }
     }
 }
 //options menu styling
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 08c9740d0..b61eb342d 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -239,9 +239,9 @@ export class CollectionSchemaView extends CollectionSubView {
         const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
         return selected ? (this.previewScript ? selected.Get(new Key(this.previewScript)) as Document : selected) : undefined;
     }
-    get tableWidth() { return this.props.PanelWidth() * (1 - this.splitPercentage / 100); }
-    get previewRegionHeight() { return this.props.PanelHeight(); }
-    get previewRegionWidth() { return this.props.PanelWidth() * this.splitPercentage / 100 - this.DIVIDER_WIDTH; }
+    get tableWidth() { return (this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH) * (1 - this.splitPercentage / 100); }
+    get previewRegionHeight() { return this.props.PanelHeight() - 2 * this.borderWidth; }
+    get previewRegionWidth() { return (this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH) * this.splitPercentage / 100; }
 
     private previewDocNativeWidth = () => this.previewDocument!.GetNumber(KeyStore.NativeWidth, this.previewRegionWidth);
     private previewDocNativeHeight = () => this.previewDocument!.GetNumber(KeyStore.NativeHeight, this.previewRegionHeight);
@@ -262,7 +262,7 @@ export class CollectionSchemaView extends CollectionSubView {
     get previewPanel() {
         // let doc = CompileScript(this.previewScript, { this: selected }, true)();
         return !this.previewDocument ? (null) : (
-            <div className="collectionSchemaView-previewRegion" style={{ width: `calc(${this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0)}% - ${this.DIVIDER_WIDTH}px)` }}>
+            <div className="collectionSchemaView-previewRegion" style={{ width: `${this.previewRegionWidth}px` }}>
                 <div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
                     <DocumentView Document={this.previewDocument} isTopMost={false} selectOnLoad={false}
                         addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
@@ -327,14 +327,11 @@ export class CollectionSchemaView extends CollectionSubView {
     render() {
         library.add(faCog);
         library.add(faPlus);
-        if (!this.previewDocument)
-            return (null);
-
         const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
         return (
             <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel}
                 onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createTarget}>
-                <div className="collectionSchemaView-tableContainer" style={{ width: `calc(100% - ${this.splitPercentage}%)` }}>
+                <div className="collectionSchemaView-tableContainer" style={{ width: `${this.tableWidth}px` }}>
                     <ReactTable data={children} page={0} pageSize={children.length} showPagination={false}
                         columns={this.columns.map(col => ({
                             Header: col.Name,
diff --git a/src/client/views/globalCssVariables.scss.d.ts b/src/client/views/globalCssVariables.scss.d.ts
index e874b815d..ed8573f41 100644
--- a/src/client/views/globalCssVariables.scss.d.ts
+++ b/src/client/views/globalCssVariables.scss.d.ts
@@ -1,7 +1,7 @@
 
 interface IGlobalScss {
     contextMenuZindex: string;  // context menu shows up over everything
-    COLLECTION_BORDER_WIDTH: number;
+    COLLECTION_BORDER_WIDTH: string;
 }
 declare const globalCssVariables: IGlobalScss;
 
-- 
cgit v1.2.3-70-g09d2


From af6feb64510490da8d815f41ceb639d693b9eae3 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 19 Apr 2019 16:34:32 -0400
Subject: played with iconifying things in a different way.  fixed some things
 with schemas.

---
 src/Utils.ts                                       |  2 +-
 src/client/documents/Documents.ts                  | 16 ++++
 src/client/northstar/dash-nodes/HistogramBox.scss  |  6 +-
 src/client/northstar/dash-nodes/HistogramBox.tsx   |  2 +-
 src/client/views/DocumentDecorations.tsx           | 42 ++++++----
 src/client/views/Main.tsx                          |  8 +-
 .../views/collections/CollectionSchemaView.scss    |  9 ++-
 .../views/collections/CollectionSchemaView.tsx     |  5 +-
 .../CollectionFreeFormLinkView.tsx                 |  8 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 13 ++--
 .../collections/collectionFreeForm/MarqueeView.tsx |  9 +--
 src/client/views/globalCssVariables.scss           |  2 +
 src/client/views/globalCssVariables.scss.d.ts      |  1 +
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 26 +++----
 src/client/views/nodes/DocumentContentsView.tsx    |  3 +-
 src/client/views/nodes/DocumentView.scss           | 16 +---
 src/client/views/nodes/DocumentView.tsx            | 71 ++++++++---------
 src/client/views/nodes/FieldView.tsx               |  5 ++
 src/client/views/nodes/IconBox.scss                | 12 +++
 src/client/views/nodes/IconBox.tsx                 | 90 ++++++++++++++++++++++
 src/fields/IconFIeld.ts                            | 25 ++++++
 src/fields/KeyStore.ts                             | 10 +--
 src/server/Message.ts                              |  2 +-
 src/server/ServerUtil.ts                           |  2 +
 24 files changed, 264 insertions(+), 121 deletions(-)
 create mode 100644 src/client/views/nodes/IconBox.scss
 create mode 100644 src/client/views/nodes/IconBox.tsx
 create mode 100644 src/fields/IconFIeld.ts

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

diff --git a/src/Utils.ts b/src/Utils.ts
index dec6245ef..98f75d3b9 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -1,7 +1,7 @@
 import v4 = require('uuid/v4');
 import v5 = require("uuid/v5");
 import { Socket } from 'socket.io';
-import { Message, Types, Transferable } from './server/Message';
+import { Message } from './server/Message';
 import { Document } from './fields/Document';
 
 export class Utils {
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 4febfa7eb..b0bb74d89 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -32,6 +32,9 @@ import { action } from "mobx";
 import { ColumnAttributeModel } from "../northstar/core/attribute/AttributeModel";
 import { AttributeTransformationModel } from "../northstar/core/attribute/AttributeTransformationModel";
 import { AggregateFunction } from "../northstar/model/idea/idea";
+import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss";
+import { IconBox } from "../views/nodes/IconBox";
+import { IconField } from "../../fields/IconFIeld";
 
 export interface DocumentOptions {
     x?: number;
@@ -63,6 +66,7 @@ export namespace Documents {
     let videoProto: Document;
     let audioProto: Document;
     let pdfProto: Document;
+    let iconProto: Document;
     const textProtoId = "textProto";
     const histoProtoId = "histoProto";
     const pdfProtoId = "pdfProto";
@@ -72,6 +76,7 @@ export namespace Documents {
     const kvpProtoId = "kvpProto";
     const videoProtoId = "videoProto";
     const audioProtoId = "audioProto";
+    const iconProtoId = "iconProto";
 
     export function initProtos(): Promise<void> {
         return Server.GetFields([textProtoId, histoProtoId, collProtoId, pdfProtoId, imageProtoId, videoProtoId, audioProtoId, webProtoId, kvpProtoId]).then(fields => {
@@ -84,6 +89,7 @@ export namespace Documents {
             videoProto = fields[videoProtoId] as Document || CreateVideoPrototype();
             audioProto = fields[audioProtoId] as Document || CreateAudioPrototype();
             pdfProto = fields[pdfProtoId] as Document || CreatePdfPrototype();
+            iconProto = fields[iconProtoId] as Document || CreateIconPrototype();
         });
     }
     function assignOptions(doc: Document, options: DocumentOptions): Document {
@@ -92,6 +98,8 @@ export namespace Documents {
         if (options.title !== undefined) { doc.SetText(KeyStore.Title, options.title); }
         if (options.page !== undefined) { doc.SetNumber(KeyStore.Page, options.page); }
         if (options.scale !== undefined) { doc.SetNumber(KeyStore.Scale, options.scale); }
+        if (options.width !== undefined) { doc.SetNumber(KeyStore.Width, options.width); }
+        if (options.height !== undefined) { doc.SetNumber(KeyStore.Height, options.height); }
         if (options.viewType !== undefined) { doc.SetNumber(KeyStore.ViewType, options.viewType); }
         if (options.backgroundColor !== undefined) { doc.SetText(KeyStore.BackgroundColor, options.backgroundColor); }
         if (options.ink !== undefined) { doc.Set(KeyStore.Ink, new InkField(options.ink)); }
@@ -139,6 +147,11 @@ export namespace Documents {
         histoProto.SetText(KeyStore.BackgroundLayout, HistogramBox.LayoutString());
         return histoProto;
     }
+    function CreateIconPrototype(): Document {
+        let iconProto = setupPrototypeOptions(iconProtoId, "ICON_PROTO", IconBox.LayoutString(),
+            { x: 0, y: 0, width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE), layoutKeys: [KeyStore.Data] });
+        return iconProto;
+    }
     function CreateTextPrototype(): Document {
         let textProto = setupPrototypeOptions(textProtoId, "TEXT_PROTO", FormattedTextBox.LayoutString(),
             { x: 0, y: 0, width: 300, height: 150, layoutKeys: [KeyStore.Data] });
@@ -203,6 +216,9 @@ export namespace Documents {
     export function TextDocument(options: DocumentOptions = {}) {
         return assignToDelegate(SetInstanceOptions(textProto, options, ["", TextField]).MakeDelegate(), options);
     }
+    export function IconDocument(icon: string, options: DocumentOptions = {}) {
+        return assignToDelegate(SetInstanceOptions(iconProto, { width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE), layoutKeys: [KeyStore.Data], layout: IconBox.LayoutString(), ...options }, [icon, IconField]), options);
+    }
     export function PdfDocument(url: string, options: DocumentOptions = {}) {
         return assignToDelegate(SetInstanceOptions(pdfProto, options, [new URL(url), PDFField]).MakeDelegate(), options);
     }
diff --git a/src/client/northstar/dash-nodes/HistogramBox.scss b/src/client/northstar/dash-nodes/HistogramBox.scss
index e899cf15e..06d781263 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.scss
+++ b/src/client/northstar/dash-nodes/HistogramBox.scss
@@ -1,12 +1,12 @@
 .histogrambox-container {
     padding: 0vw;
     position: absolute;
-    top: 0;
-    left:0;
+    top: -50%;
+    left:-50%;
     text-align: center;
     width: 100%;
     height: 100%;
-    background: black;
+    background: black; 
   }
   .histogrambox-xaxislabel {
     position:absolute;
diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index 0e84ace50..e2ecc8c83 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -146,7 +146,7 @@ export class HistogramBox extends React.Component<FieldViewProps> {
         return (
             <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-container" ref={measureRef}>
                         <div className="histogrambox-yaxislabel" onPointerDown={this.yLabelPointerDown} ref={this._dropYRef} >
                             <span className="histogrambox-yaxislabel-text">
                                 {labelY}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index cfb9befd5..da2c7a3be 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -5,17 +5,18 @@ import { KeyStore } from "../../fields/KeyStore";
 import { ListField } from "../../fields/ListField";
 import { NumberField } from "../../fields/NumberField";
 import { TextField } from "../../fields/TextField";
+import { Document } from "../../fields/Document";
 import { emptyFunction } from "../../Utils";
 import { DragLinksAsDocuments, DragManager } from "../util/DragManager";
 import { SelectionManager } from "../util/SelectionManager";
 import { undoBatch } from "../util/UndoManager";
 import './DocumentDecorations.scss';
 import { MainOverlayTextBox } from "./MainOverlayTextBox";
-import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss";
 import { DocumentView } from "./nodes/DocumentView";
 import { LinkMenu } from "./nodes/LinkMenu";
 import React = require("react");
 import { CompileScript } from "../util/Scripting";
+import { IconBox } from "./nodes/IconBox";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -191,6 +192,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             document.addEventListener("pointerup", this.onMinimizeUp);
         }
     }
+
+    @observable _minimizedX = 0;
+    @observable _minimizedY = 0;
     @action
     onMinimizeMove = (e: PointerEvent): void => {
         e.stopPropagation();
@@ -201,12 +205,20 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             let xf = SelectionManager.SelectedDocuments()[0].props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
             let dx = e.pageX - xf[0];
             let dy = e.pageY - xf[1];
-            if (Math.abs(dx) < 20 && Math.abs(dy) < 20)
-                dx = dy = 0;
+            this._minimizedX = e.clientX;
+            this._minimizedY = e.clientY;
+            if (Math.abs(dx) < 20 && Math.abs(dy) < 20) {
+                this._minimizedX = xf[0];
+                this._minimizedY = xf[1];
+            }
             SelectionManager.SelectedDocuments().map(dv => {
-                let where = (dv.props.ScreenToLocalTransform()).scale(dv.props.ContentScaling()).transformDirection(dx, dy);
-                dv.props.Document.SetNumber(KeyStore.MinimizedX, where[0]);
-                dv.props.Document.SetNumber(KeyStore.MinimizedY, where[1]);
+                let minDoc = dv.props.Document.Get(KeyStore.MinimizedDoc);
+                if (minDoc instanceof Document) {
+                    let where = (dv.props.ScreenToLocalTransform()).scale(dv.props.ContentScaling()).transformPoint(this._minimizedX, this._minimizedY);
+                    let minDocument = minDoc as Document;
+                    minDocument.SetNumber(KeyStore.X, where[0] + dv.props.Document.GetNumber(KeyStore.X, 0));
+                    minDocument.SetNumber(KeyStore.Y, where[1] + dv.props.Document.GetNumber(KeyStore.Y, 0));
+                }
             });
         }
     }
@@ -219,6 +231,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             if (Math.abs(dx) < 4 && Math.abs(dy) < 4 && !this._iconifying) {
                 SelectionManager.SelectedDocuments().map(dv => dv.minimize());
                 SelectionManager.DeselectAll();
+            } else {
+                this._minimizedX = this._minimizedY = 0;
             }
             document.removeEventListener("pointermove", this.onMinimizeMove);
             document.removeEventListener("pointerup", this.onMinimizeUp);
@@ -404,24 +418,18 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         if (bounds.x === Number.MAX_VALUE || !seldoc) {
             return (null);
         }
-        let minvec = [seldoc.props.Document.GetNumber(KeyStore.MinimizedX, 0), seldoc.props.Document.GetNumber(KeyStore.MinimizedY, 0)];
-        minvec = seldoc.props.ScreenToLocalTransform().scale(seldoc.props.ContentScaling()).inverse().transformDirection(minvec[0], minvec[1]);
-        let selpos = minvec[0] !== 0 || minvec[1] !== 0 ?
-            [minvec[0] - 12 + (!this._iconifying ? 8 : 0), minvec[1] - 12 + (!this._iconifying ? 28 : 0)] :
+        let selpos = this._minimizedX !== 0 || this._minimizedY !== 0 ?
+            [this._minimizedX - 12 + (!this._iconifying ? 8 : 0), this._minimizedY - 12 + (!this._iconifying ? 28 : 0)] :
             [0, this._iconifying ? -18 : 0];
         let minimizeIcon = (
             <div className="documentDecorations-minimizeButton" onPointerDown={this.onMinimizeDown}
                 style={{ transform: `translate(${selpos[0]}px,${selpos[1]}px)`, }}>
-                {SelectionManager.SelectedDocuments().length == 1 ? SelectionManager.SelectedDocuments()[0].minimizedIcon : "..."}
+                {SelectionManager.SelectedDocuments().length == 1 ? IconBox.DocumentIcon(SelectionManager.SelectedDocuments()[0].props.Document.GetText(KeyStore.Layout, "...")) : "..."}
             </div>);
         if (this._iconifying) {
-            let xfpt = seldoc.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
-            return (<div className="documentDecorations-container" style={{ transform: `translate(${xfpt[0]}px,${xfpt[1]}px` }}>
-                {minimizeIcon}
-            </div>);
+            return (<div className="documentDecorations-container" > {minimizeIcon} </div>);
         }
-        // console.log(this._documents.length)
-        // let test = this._documents[0].props.Document.Title;
+
         if (this.Hidden) {
             return (null);
         }
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 503a11b35..09ef30f6b 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -85,11 +85,11 @@ export class Main extends React.Component {
         this.initEventListeners();
         this.initAuthenticationRouters();
 
-        // try {
-        //     this.initializeNorthstar();
-        // } catch (e) {
+        try {
+            this.initializeNorthstar();
+        } catch (e) {
 
-        // }
+        }
     }
 
     componentDidMount() { window.onpopstate = this.onHistory; }
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index 6eabbe17c..cfdb3ab22 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -1,6 +1,7 @@
 @import "../globalCssVariables";
 
 
+
 .collectionSchemaView-container {
     border-width: $COLLECTION_BORDER_WIDTH;
     border-color : $intermediate-color;
@@ -10,6 +11,10 @@
     position: absolute;
     width: 100%;
     height: 100%;
+
+    .collectionSchemaView-cellContents {
+        height: $MAX_ROW_HEIGHT;
+    }
     
     .collectionSchemaView-previewRegion {
         position: relative;
@@ -104,7 +109,7 @@
         }
         .rt-tr-group {
             direction: ltr;
-            max-height: 44px;
+            max-height: $MAX_ROW_HEIGHT;
         }
         .rt-td {
             border-width: 1px;
@@ -136,7 +141,7 @@
     }
     .ReactTable .rt-th,
     .ReactTable .rt-td {
-        max-height: 44;
+        max-height: $MAX_ROW_HEIGHT;
         padding: 3px 7px;
         font-size: 13px;
         text-align: center;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index b61eb342d..90077b053 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -5,6 +5,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable, untracked } from "mobx";
 import { observer } from "mobx-react";
 import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
+import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss'
 import "react-table/react-table.css";
 import { Document } from "../../../fields/Document";
 import { Field, Opt } from "../../../fields/Field";
@@ -99,11 +100,11 @@ export class CollectionSchemaView extends CollectionSubView {
             return false;
         };
         return (
-            <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "56px" }} key={props.Document.Id} ref={reference}>
+            <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} key={props.Document.Id} ref={reference}>
                 <EditableView
                     display={"inline"}
                     contents={contents}
-                    height={56}
+                    height={Number(MAX_ROW_HEIGHT)}
                     GetValue={() => {
                         let field = props.Document.Get(props.fieldKey);
                         if (field && field instanceof Field) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 8868f7df0..20c5a84bf 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.GetBoolean(KeyStore.Minimized, false) ? 5 : a.Width() / 2);
-        let y1 = a.GetNumber(KeyStore.Y, 0) + (a.GetBoolean(KeyStore.Minimized, false) ? 5 : a.Height() / 2);
-        let x2 = b.GetNumber(KeyStore.X, 0) + (b.GetBoolean(KeyStore.Minimized, false) ? 5 : b.Width() / 2);
-        let y2 = b.GetNumber(KeyStore.Y, 0) + (b.GetBoolean(KeyStore.Minimized, false) ? 5 : b.Height() / 2);
+        let x1 = a.GetNumber(KeyStore.X, 0) + (a.GetBoolean(KeyStore.IsMinimized, false) ? 5 : a.Width() / 2);
+        let y1 = a.GetNumber(KeyStore.Y, 0) + (a.GetBoolean(KeyStore.IsMinimized, false) ? 5 : a.Height() / 2);
+        let x2 = b.GetNumber(KeyStore.X, 0) + (b.GetBoolean(KeyStore.IsMinimized, false) ? 5 : b.Width() / 2);
+        let y2 = b.GetNumber(KeyStore.Y, 0) + (b.GetBoolean(KeyStore.IsMinimized, false) ? 5 : b.Height() / 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/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c50597655..945c01059 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -21,6 +21,7 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
+import { BooleanField } from "../../../../fields/BooleanField";
 
 @observer
 export class CollectionFreeFormView extends CollectionSubView {
@@ -71,17 +72,15 @@ export class CollectionFreeFormView extends CollectionSubView {
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (super.drop(e, de) && de.data instanceof DragManager.DocumentDragData) {
-            console.log("DROP Aat " + de.x + " off " + de.data.xOffset);
             const [x, y] = this.getTransform().transformPoint(de.x - de.data.xOffset, de.y - de.data.yOffset);
             if (de.data.droppedDocuments.length) {
                 let dragDoc = de.data.droppedDocuments[0];
                 let dropX = dragDoc.GetNumber(KeyStore.X, 0);
                 let dropY = dragDoc.GetNumber(KeyStore.Y, 0);
                 de.data.droppedDocuments.map(d => {
-                    let minimized = d.GetBoolean(KeyStore.Minimized, false);
-                    d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0) - (minimized ? d.GetNumber(KeyStore.MinimizedX, 0) : 0)) - dropX);
-                    d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0) - (minimized ? d.GetNumber(KeyStore.MinimizedY, 0) : 0)) - dropY);
-                    if (!minimized) {
+                    d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0)) - dropX);
+                    d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0)) - dropY);
+                    if (!d.GetBoolean(KeyStore.IsMinimized, false)) {
                         if (!d.GetNumber(KeyStore.Width, 0)) {
                             d.SetNumber(KeyStore.Width, 300);
                         }
@@ -264,7 +263,9 @@ export class CollectionFreeFormView extends CollectionSubView {
         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)} />);
+                let minim = doc.GetT(KeyStore.IsMinimized, BooleanField);
+                if (minim === undefined || (minim && !minim.Data))
+                    prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
             }
             return prev;
         }, [] as JSX.Element[]);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 8b94374fa..bf918beba 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -208,12 +208,11 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         let selRect = this.Bounds;
         let selection: Document[] = [];
         this.props.activeDocuments().map(doc => {
-            let minimized = doc.GetBoolean(KeyStore.Minimized, false);
             var z = doc.GetNumber(KeyStore.Zoom, 1);
-            var x = doc.GetNumber(KeyStore.X, 0) + (minimized ? doc.GetNumber(KeyStore.MinimizedX, 0) : 0);
-            var y = doc.GetNumber(KeyStore.Y, 0) + (minimized ? doc.GetNumber(KeyStore.MinimizedY, 0) : 0);
-            var w = minimized ? MINIMIZED_ICON_SIZE : doc.Width() / z;
-            var h = minimized ? MINIMIZED_ICON_SIZE : doc.Height() / z;
+            var x = doc.GetNumber(KeyStore.X, 0);
+            var y = doc.GetNumber(KeyStore.Y, 0);
+            var w = doc.Width() / z;
+            var h = doc.Height() / z;
             if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
                 selection.push(doc);
             }
diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss
index f154f8158..4f68b71b0 100644
--- a/src/client/views/globalCssVariables.scss
+++ b/src/client/views/globalCssVariables.scss
@@ -24,8 +24,10 @@ $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;
 $MINIMIZED_ICON_SIZE:25;
+$MAX_ROW_HEIGHT: 44px;
 :export  {
     contextMenuZindex: $contextMenu-zindex;
     COLLECTION_BORDER_WIDTH: $COLLECTION_BORDER_WIDTH;
     MINIMIZED_ICON_SIZE: $MINIMIZED_ICON_SIZE;
+    MAX_ROW_HEIGHT: $MAX_ROW_HEIGHT;
 }
\ No newline at end of file
diff --git a/src/client/views/globalCssVariables.scss.d.ts b/src/client/views/globalCssVariables.scss.d.ts
index cc77d987a..9788d31f7 100644
--- a/src/client/views/globalCssVariables.scss.d.ts
+++ b/src/client/views/globalCssVariables.scss.d.ts
@@ -3,6 +3,7 @@ interface IGlobalScss {
     contextMenuZindex: string;  // context menu shows up over everything
     COLLECTION_BORDER_WIDTH: string;
     MINIMIZED_ICON_SIZE: string;
+    MAX_ROW_HEIGHT: string;
 }
 declare const globalCssVariables: IGlobalScss;
 
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index a3689414d..1d42b3899 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -46,10 +46,10 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
     }
 
     get X() {
-        return this.props.Document.GetNumber(KeyStore.X, 0) + (this.isMinimized ? this.props.Document.GetNumber(KeyStore.MinimizedX, 0) : 0);
+        return this.props.Document.GetNumber(KeyStore.X, 0);
     }
     get Y() {
-        return this.props.Document.GetNumber(KeyStore.Y, 0) + (this.isMinimized ? this.props.Document.GetNumber(KeyStore.MinimizedY, 0) : 0);
+        return this.props.Document.GetNumber(KeyStore.Y, 0);
     }
     getTransform = (): Transform =>
         this.props.ScreenToLocalTransform()
@@ -57,8 +57,8 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
             .scale(1 / this.contentScaling()).scale(1 / this.zoom)
 
     contentScaling = () => this.nativeWidth > 0 ? this.width / this.nativeWidth : 1;
-    panelWidth = () => this.isMinimized ? 10 : this.props.PanelWidth();
-    panelHeight = () => this.isMinimized ? 10 : this.props.PanelHeight();
+    panelWidth = () => this.props.PanelWidth();
+    panelHeight = () => this.props.PanelHeight();
 
     @computed
     get docView() {
@@ -70,19 +70,17 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
         />;
     }
 
-    get isMinimized() { return this.props.Document.GetBoolean(KeyStore.Minimized, false); }
-
     render() {
         let zoomFade = 1;
         //var zoom = doc.GetNumber(KeyStore.Zoom, 1);
-        let transform = this.getTransform().scale(this.contentScaling()).inverse();
-        var [sptX, sptY] = transform.transformPoint(0, 0);
-        let [bptX, bptY] = transform.transformPoint(this.props.PanelWidth(), this.props.PanelHeight());
-        let w = bptX - sptX;
-        //zoomFade = area < 100 || area > 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1;
-        let fadeUp = .75 * 1800;
-        let fadeDown = .075 * 1800;
-        zoomFade = w < fadeDown  /* || w > fadeUp */ ? Math.max(0, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
+        // let transform = this.getTransform().scale(this.contentScaling()).inverse();
+        // var [sptX, sptY] = transform.transformPoint(0, 0);
+        // let [bptX, bptY] = transform.transformPoint(this.props.PanelWidth(), this.props.PanelHeight());
+        // let w = bptX - sptX;
+        // //zoomFade = area < 100 || area > 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1;
+        // let fadeUp = .75 * 1800;
+        // let fadeDown = .075 * 1800;
+        // zoomFade = w < fadeDown  /* || w > fadeUp */ ? Math.max(0, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
 
         return (
             <div className="collectionFreeFormDocumentView-container" ref={this._mainCont} style={{
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 76f852601..07599c345 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -15,6 +15,7 @@ import { DocumentViewProps } from "./DocumentView";
 import "./DocumentView.scss";
 import { FormattedTextBox } from "./FormattedTextBox";
 import { ImageBox } from "./ImageBox";
+import { IconBox } from "./IconBox";
 import { KeyValueBox } from "./KeyValueBox";
 import { PDFBox } from "./PDFBox";
 import { VideoBox } from "./VideoBox";
@@ -62,7 +63,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
             return <p>Error loading layout keys</p>;
         }
         return <JsxParser
-            components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
+            components={{ FormattedTextBox, ImageBox, IconBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
             bindings={this.CreateBindings()}
             jsx={this.layout}
             showWarnings={true}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index a0e829d26..7c72fb6e6 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -4,7 +4,7 @@
   position: inherit;
   top: 0;
   left:0;
-  background: $light-color; //overflow: hidden;
+ // background: $light-color; //overflow: hidden;
   transform-origin: left top;
 
   &.minimized {
@@ -30,18 +30,4 @@
 }
 .documentView-node-topmost {
     background: white;
-}
-.minimized-box {
-    position: absolute;
-    left:0;
-    top:0;
-    width:$MINIMIZED_ICON_SIZE;
-    height:$MINIMIZED_ICON_SIZE;
-    transform:translate(-50%, -50%);
-}
-
-.minimized-box:hover {
-  background: $main-accent;
-  transform:translate(-50%, -50%) scale(1.15);;
-  cursor: pointer;
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3ada3252c..558cbf017 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,6 +1,3 @@
-import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faCaretUp, faObjectGroup, faStickyNote, faFilePdf, faFilm, faImage } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { Document } from "../../../fields/Document";
@@ -20,18 +17,11 @@ import { CollectionDockingView } from "../collections/CollectionDockingView";
 import { CollectionPDFView } from "../collections/CollectionPDFView";
 import { CollectionVideoView } from "../collections/CollectionVideoView";
 import { CollectionView } from "../collections/CollectionView";
-import { MINIMIZED_ICON_SIZE } from "../../views/globalCssVariables.scss";
 import { ContextMenu } from "../ContextMenu";
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
 import React = require("react");
-
-
-library.add(faCaretUp);
-library.add(faObjectGroup);
-library.add(faStickyNote);
-library.add(faFilePdf);
-library.add(faFilm);
+import { TextField } from "../../../fields/TextField";
 
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
@@ -189,10 +179,10 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         document.removeEventListener("pointermove", this.onPointerMove);
         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) {
-            SelectionManager.SelectDoc(this, e.ctrlKey);
-        }
+        if (!SelectionManager.IsSelected(this) && e.button !== 2)
+            if (Math.abs(e.clientX - this._downX) < 4 && Math.abs(e.clientY - this._downY) < 4) {
+                SelectionManager.SelectDoc(this, e.ctrlKey);
+            }
     }
     stopPropagation = (e: React.SyntheticEvent) => {
         e.stopPropagation();
@@ -221,13 +211,33 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
     }
 
+    @action createIcon = (layoutString: string): void => {
+        let iconDoc = Documents.IconDocument(layoutString);
+        iconDoc.SetBoolean(KeyStore.IsMinimized, false);
+        iconDoc.SetNumber(KeyStore.NativeWidth, 0);
+        iconDoc.SetNumber(KeyStore.NativeHeight, 0);
+        iconDoc.Set(KeyStore.Prototype, this.props.Document);
+        iconDoc.Set(KeyStore.MaximizedDoc, this.props.Document);
+        this.props.Document.Set(KeyStore.MinimizedDoc, iconDoc);
+        this.props.addDocument && this.props.addDocument(iconDoc, false);
+    }
+
     @action
-    public minimize = (where: number[]): void => {
-        this.props.Document.SetBoolean(KeyStore.Minimized, true);
-        if (where[0] !== 0 || where[1] !== 0)
-            this.props.Document.SetNumber(KeyStore.MinimizedX, where[0]);
-        if (where[1] !== 0 || where[0] !== 0)
-            this.props.Document.SetNumber(KeyStore.MinimizedY, where[1]);
+    public minimize = (): void => {
+        this.props.Document.SetBoolean(KeyStore.IsMinimized, true);
+        this.props.Document.GetAsync(KeyStore.MinimizedDoc, mindoc => {
+            if (mindoc === undefined) {
+                this.props.Document.GetAsync(KeyStore.BackgroundLayout, field => {
+                    if (field instanceof TextField) this.createIcon(field.Data);
+                    else this.props.Document.GetAsync(KeyStore.Layout, field => {
+                        if (field instanceof TextField) this.createIcon(field.Data);
+                    });
+                });
+            }
+            else if (mindoc instanceof Document) {
+                this.props.addDocument && this.props.addDocument(mindoc, false);
+            }
+        });
     }
 
     @undoBatch
@@ -295,7 +305,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         }
         e.preventDefault();
 
-        !this.isMinimized() && ContextMenu.Instance.addItem({ description: "Minimize", event: () => this.minimize([0, 0]) });
         ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked });
         ContextMenu.Instance.addItem({ description: "Fields", event: this.fieldsClicked });
         ContextMenu.Instance.addItem({ description: "Center", event: () => this.props.focus(this.props.Document) });
@@ -309,9 +318,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             SelectionManager.SelectDoc(this, false);
     }
 
-    @action
-    expand = (e: React.MouseEvent) => { this.props.Document.SetBoolean(KeyStore.Minimized, false); SelectionManager.SelectDoc(this, e.ctrlKey); }
-    isMinimized = () => this.props.Document.GetBoolean(KeyStore.Minimized, false);
     isSelected = () => SelectionManager.IsSelected(this);
     select = (ctrlPressed: boolean) => SelectionManager.SelectDoc(this, ctrlPressed);
 
@@ -319,27 +325,12 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
     @computed get contents() { return (<DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} layoutKey={KeyStore.Layout} />); }
 
-    @computed get minimizedIcon() {
-        let button = this.layout.indexOf("PDFBox") !== -1 ? faFilePdf :
-            this.layout.indexOf("ImageBox") !== -1 ? faImage :
-                this.layout.indexOf("Formatted") !== -1 ? faStickyNote :
-                    this.layout.indexOf("Video") !== -1 ? faFilm :
-                        this.layout.indexOf("Collection") !== -1 ? faObjectGroup :
-                            faCaretUp;
-        return <FontAwesomeIcon icon={button} style={{ width: MINIMIZED_ICON_SIZE, height: MINIMIZED_ICON_SIZE }} className="documentView-minimizedIcon" />
-    }
 
     render() {
         var scaling = this.props.ContentScaling();
         var nativeHeight = this.nativeHeight > 0 ? this.nativeHeight.toString() + "px" : "100%";
         var nativeWidth = this.nativeWidth > 0 ? this.nativeWidth.toString() + "px" : "100%";
 
-        if (this.isMinimized()) {
-            return (
-                <div className="minimized-box" ref={this._mainCont} onClick={this.expand} onDrop={this.onDrop} onPointerDown={this.onPointerDown} >
-                    {this.minimizedIcon}
-                </div>);
-        }
         return (
             <div className={`documentView-node${this.props.isTopMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index e9f6950ff..93e385821 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -23,6 +23,8 @@ import { returnFalse, emptyDocFunction, emptyFunction, returnOne } from "../../.
 import { CollectionView } from "../collections/CollectionView";
 import { CollectionPDFView } from "../collections/CollectionPDFView";
 import { CollectionVideoView } from "../collections/CollectionVideoView";
+import { IconField } from "../../../fields/IconFIeld";
+import { IconBox } from "./IconBox";
 
 
 //
@@ -72,6 +74,9 @@ export class FieldView extends React.Component<FieldViewProps> {
         else if (field instanceof ImageField) {
             return <ImageBox {...this.props} />;
         }
+        else if (field instanceof IconField) {
+            return <IconBox {...this.props} />;
+        }
         else if (field instanceof VideoField) {
             return <VideoBox {...this.props} />;
         }
diff --git a/src/client/views/nodes/IconBox.scss b/src/client/views/nodes/IconBox.scss
new file mode 100644
index 000000000..ce0ee2e09
--- /dev/null
+++ b/src/client/views/nodes/IconBox.scss
@@ -0,0 +1,12 @@
+
+@import "../globalCssVariables";
+.iconBox-container {
+    position: absolute;
+    left:0;
+    top:0;
+    svg {
+        width: 100% !important;
+        height: 100%;
+        background: white;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
new file mode 100644
index 000000000..5ada2186d
--- /dev/null
+++ b/src/client/views/nodes/IconBox.tsx
@@ -0,0 +1,90 @@
+import React = require("react");
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, computed } from "mobx";
+import { observer } from "mobx-react";
+import { Document } from '../../../fields/Document';
+import { IconField } from "../../../fields/IconFIeld";
+import { KeyStore } from "../../../fields/KeyStore";
+import { SelectionManager } from "../../util/SelectionManager";
+import { FieldView, FieldViewProps } from './FieldView';
+import "./IconBox.scss";
+
+
+library.add(faCaretUp);
+library.add(faObjectGroup);
+library.add(faStickyNote);
+library.add(faFilePdf);
+library.add(faFilm);
+
+@observer
+export class IconBox extends React.Component<FieldViewProps> {
+    public static LayoutString() { return FieldView.LayoutString(IconBox); }
+
+    @computed get maximized() { return this.props.Document.GetT(KeyStore.MaximizedDoc, Document); }
+    @computed get layout(): string { return this.props.Document.GetData(this.props.fieldKey, IconField, "<p>Error loading layout data</p>" as string); }
+    @computed get minimizedIcon() { return IconBox.DocumentIcon(this.layout); }
+
+    public static DocumentIcon(layout: string) {
+        let button = layout.indexOf("PDFBox") !== -1 ? faFilePdf :
+            layout.indexOf("ImageBox") !== -1 ? faImage :
+                layout.indexOf("Formatted") !== -1 ? faStickyNote :
+                    layout.indexOf("Video") !== -1 ? faFilm :
+                        layout.indexOf("Collection") !== -1 ? faObjectGroup :
+                            faCaretUp;
+        return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />
+    }
+
+    animateTransition(icon: number[], targ: number[], width: number, height: number, stime: number, target: Document, maximizing: boolean) {
+        setTimeout(() => {
+            let now = Date.now();
+            let progress = Math.min(1, (now - stime) / 200);
+            let pval = maximizing ?
+                [icon[0] + (targ[0] - icon[0]) * progress, icon[1] + (targ[1] - icon[1]) * progress] :
+                [targ[0] + (icon[0] - targ[0]) * progress, targ[1] + (icon[1] - targ[1]) * progress];
+            target.SetNumber(KeyStore.Width, maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress);
+            target.SetNumber(KeyStore.Height, maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress);
+            target.SetNumber(KeyStore.X, pval[0]);
+            target.SetNumber(KeyStore.Y, pval[1]);
+            if (now < stime + 200) {
+                this.animateTransition(icon, targ, width, height, stime, target, maximizing);
+            }
+            else {
+                if (!maximizing) {
+                    target.SetBoolean(KeyStore.IsMinimized, true);
+                    target.SetNumber(KeyStore.X, targ[0]);
+                    target.SetNumber(KeyStore.Y, targ[1]);
+                    target.SetNumber(KeyStore.Width, width);
+                    target.SetNumber(KeyStore.Height, height);
+                }
+                this._completed = true;
+            }
+        },
+            2);
+    }
+
+    _completed = true;
+
+    @action
+    public toggleMinimize = (): void => {
+        SelectionManager.DeselectAll();
+        if (this.maximized instanceof Document && this._completed) {
+            this._completed = false;
+            let minimized = this.maximized.GetBoolean(KeyStore.IsMinimized, false);
+            this.maximized.SetBoolean(KeyStore.IsMinimized, false);
+            this.animateTransition(
+                [this.props.Document.GetNumber(KeyStore.X, 0), this.props.Document.GetNumber(KeyStore.Y, 0)],
+                [this.maximized.GetNumber(KeyStore.X, 0), this.maximized.GetNumber(KeyStore.Y, 0)],
+                this.maximized.GetNumber(KeyStore.Width, 0), this.maximized.GetNumber(KeyStore.Width, 0),
+                Date.now(), this.maximized, minimized);
+        }
+    }
+
+    render() {
+        return (
+            <div className="iconBox-container" onClick={this.toggleMinimize} >
+                {this.minimizedIcon}
+            </div>);
+    }
+}
\ No newline at end of file
diff --git a/src/fields/IconFIeld.ts b/src/fields/IconFIeld.ts
new file mode 100644
index 000000000..a6694cc49
--- /dev/null
+++ b/src/fields/IconFIeld.ts
@@ -0,0 +1,25 @@
+import { BasicField } from "./BasicField";
+import { FieldId } from "./Field";
+import { Types } from "../server/Message";
+
+export class IconField extends BasicField<string> {
+    constructor(data: string = "", id?: FieldId, save: boolean = true) {
+        super(data, save, id);
+    }
+
+    ToScriptString(): string {
+        return `new IconField("${this.Data}")`;
+    }
+
+    Copy() {
+        return new IconField(this.Data);
+    }
+
+    ToJson() {
+        return {
+            type: Types.Icon,
+            data: this.Data,
+            id: this.Id
+        };
+    }
+}
\ No newline at end of file
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index ff2f31003..a347f8bcf 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -4,8 +4,6 @@ export namespace KeyStore {
     export const Prototype = new Key("Prototype");
     export const X = new Key("X");
     export const Y = new Key("Y");
-    export const MinimizedX = new Key("MinimizedX");
-    export const MinimizedY = new Key("MinimizedY");
     export const Page = new Key("Page");
     export const Title = new Key("Title");
     export const Author = new Key("Author");
@@ -47,14 +45,16 @@ export namespace KeyStore {
     export const OptionalRightCollection = new Key("OptionalRightCollection");
     export const Archives = new Key("Archives");
     export const Workspaces = new Key("Workspaces");
-    export const Minimized = new Key("Minimized");
+    export const IsMinimized = new Key("IsMinimized");
+    export const MinimizedDoc = new Key("MinimizedDoc");
+    export const MaximizedDoc = new Key("MaximizedDoc");
     export const CopyDraggedItems = new Key("CopyDraggedItems");
 
-    export const KeyList: Key[] = [Prototype, X, Y, MinimizedX, MinimizedY, Page, Title, Author, PanX, PanY, Scale, NativeWidth, NativeHeight,
+    export const KeyList: Key[] = [Prototype, X, Y, Page, Title, Author, PanX, PanY, Scale, NativeWidth, NativeHeight,
         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
+        Archives, Workspaces, IsMinimized, MinimizedDoc, MaximizedDoc, CopyDraggedItems
     ];
     export function KeyLookup(keyid: string) {
         for (const key of KeyList) {
diff --git a/src/server/Message.ts b/src/server/Message.ts
index bbe4ffcad..15916ef12 100644
--- a/src/server/Message.ts
+++ b/src/server/Message.ts
@@ -14,7 +14,7 @@ export class Message<T> {
 }
 
 export enum Types {
-    Number, List, Key, Image, Web, Document, Text, RichText, DocumentReference,
+    Number, List, Key, Image, Web, Document, Text, Icon, RichText, DocumentReference,
     Html, Video, Audio, Ink, PDF, Tuple, HistogramOp, Boolean, Script,
 }
 
diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts
index 818230c1a..79ca5e55d 100644
--- a/src/server/ServerUtil.ts
+++ b/src/server/ServerUtil.ts
@@ -18,6 +18,7 @@ import { NumberField } from "./../fields/NumberField";
 import { RichTextField } from "./../fields/RichTextField";
 import { TextField } from "./../fields/TextField";
 import { Transferable, Types } from "./Message";
+import { IconField } from "../fields/IconFIeld";
 
 export class ServerUtils {
     public static prepend(extension: string): string {
@@ -37,6 +38,7 @@ export class ServerUtils {
             case Types.Boolean: return new BooleanField(json.data, json.id, false);
             case Types.Number: return new NumberField(json.data, json.id, false);
             case Types.Text: return new TextField(json.data, json.id, false);
+            case Types.Icon: return new IconField(json.data, json.id, false);
             case Types.Html: return new HtmlField(json.data, json.id, false);
             case Types.Web: return new WebField(new URL(json.data), json.id, false);
             case Types.RichText: return new RichTextField(json.data, json.id, false);
-- 
cgit v1.2.3-70-g09d2


From 49131cc84b0f3e739edb5ffcd64f3acc9469f658 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 23 Apr 2019 12:17:12 -0400
Subject: fixes to zoom scaling, moved and fixed icon stuff into
 CollectionFreeFormDocumentView.  several smaller bugs.

---
 src/client/views/DocumentDecorations.tsx           |  41 ++++-
 src/client/views/Main.tsx                          |   1 +
 .../views/collections/CollectionBaseView.tsx       |   4 +-
 .../views/collections/CollectionDockingView.tsx    |   1 +
 .../views/collections/CollectionSchemaView.tsx     |   3 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   1 +
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 174 +++++++++++++++-----
 src/client/views/nodes/DocumentView.tsx            | 180 +++++----------------
 src/client/views/nodes/ImageBox.tsx                |   5 +-
 9 files changed, 215 insertions(+), 195 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d704b6c47..95910c7be 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -18,6 +18,7 @@ import React = require("react");
 import { CompileScript } from "../util/Scripting";
 import { IconBox } from "./nodes/IconBox";
 import { FieldValue, Field } from "../../fields/Field";
+import { Documents } from "../documents/Documents";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -134,11 +135,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @action
     onBackgroundMove = (e: PointerEvent): void => {
         let dragDocView = SelectionManager.SelectedDocuments()[0];
-        const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+        const [left, top] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).inverse().transformPoint(0, 0);
         let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
+        const [xoff, yoff] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).transformDirection(e.x - left, e.y - top);
+        dragData.xOffset = xoff;
+        dragData.yOffset = yoff;
         dragData.aliasOnDrop = false;
-        dragData.xOffset = e.x - left;
-        dragData.yOffset = e.y - top;
         let move = SelectionManager.SelectedDocuments()[0].props.moveDocument;
         dragData.moveDocument = move;
         this.Interacting = this._dragging = true;
@@ -213,12 +215,39 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             this._minimizedX = snapped ? selDocPos[0] + 4 : e.clientX;
             this._minimizedY = snapped ? selDocPos[1] - 18 : e.clientY;
             let selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd);
-            Promise.all(selectedDocs.map(async selDoc => await selDoc.getIconDoc())).then(minDocSet =>
+            Promise.all(selectedDocs.map(async selDoc => await this.getIconDoc(selDoc))).then(minDocSet =>
                 this.moveIconDocs(SelectionManager.SelectedDocuments())
             );
             this._iconifying = snapped;
         }
     }
+
+
+    @action createIcon = (docView: DocumentView, layoutString: string): Document => {
+        let doc = docView.props.Document;
+        let iconDoc = Documents.IconDocument(layoutString);
+        iconDoc.SetText(KeyStore.Title, "ICON" + doc.Title)
+        iconDoc.SetBoolean(KeyStore.IsMinimized, false);
+        iconDoc.SetNumber(KeyStore.NativeWidth, 0);
+        iconDoc.SetNumber(KeyStore.NativeHeight, 0);
+        iconDoc.SetNumber(KeyStore.X, doc.GetNumber(KeyStore.X, 0));
+        iconDoc.SetNumber(KeyStore.Y, doc.GetNumber(KeyStore.Y, 0) - 24);
+        iconDoc.Set(KeyStore.Prototype, doc);
+        iconDoc.Set(KeyStore.MaximizedDoc, doc);
+        doc.Set(KeyStore.MinimizedDoc, iconDoc);
+        docView.props.addDocument && docView.props.addDocument(iconDoc, false);
+        return iconDoc;
+    }
+    @action
+    public getIconDoc = async (docView: DocumentView): Promise<Document | undefined> => {
+        let doc = docView.props.Document;
+        return await doc.GetTAsync(KeyStore.MinimizedDoc, Document).then(async mindoc =>
+            mindoc ? mindoc :
+                await doc.GetTAsync(KeyStore.BackgroundLayout, TextField).then(async field =>
+                    (field instanceof TextField) ? this.createIcon(docView, field.Data) :
+                        await doc.GetTAsync(KeyStore.Layout, TextField).then(field =>
+                            (field instanceof TextField) ? this.createIcon(docView, field.Data) : undefined)));
+    }
     @action
     onMinimizeUp = (e: PointerEvent): void => {
         e.stopPropagation();
@@ -226,7 +255,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             document.removeEventListener("pointermove", this.onMinimizeMove);
             document.removeEventListener("pointerup", this.onMinimizeUp);
             let selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd);
-            Promise.all(selectedDocs.map(async selDoc => await selDoc.getIconDoc())).then(minDocSet => {
+            Promise.all(selectedDocs.map(async selDoc => await this.getIconDoc(selDoc))).then(minDocSet => {
                 let minDocs = minDocSet.filter(minDoc => minDoc instanceof Document).map(minDoc => minDoc as Document);
                 minDocs.map(minDoc => {
                     minDoc.SetNumber(KeyStore.X, minDocs[0].GetNumber(KeyStore.X, 0));
@@ -238,7 +267,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                     }
                 });
                 runInAction(() => this._minimizedX = this._minimizedY = 0);
-                if (!this._iconifying) selectedDocs[0].toggleIcon();
+                if (!this._iconifying) selectedDocs[0].props.toggleMinimized();
                 this._iconifying = false;
             });
         }
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 09ef30f6b..934616aca 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -187,6 +187,7 @@ export class Main extends React.Component {
                 <div ref={measureRef} id="mainContent-div">
                     {!mainCont ? (null) :
                         <DocumentView Document={mainCont}
+                            toggleMinimized={emptyFunction}
                             addDocument={undefined}
                             removeDocument={undefined}
                             ScreenToLocalTransform={Transform.Identity}
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 9ae2993be..962cb9b65 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -94,7 +94,6 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
             doc.SetOnPrototype(KeyStore.AnnotationOn, this.props.Document);
         }
         if (!this.createsCycle(doc, this.props.Document)) {
-            doc.SetNumber(KeyStore.ZoomBasis, this.props.Document.GetNumber(KeyStore.Scale, 1));
             let value = this.props.Document.Get(this.props.fieldKey) as ListField<Document>;
             if (value) {
                 if (!value.Data.some(v => v.Id === doc.Id) || allowDuplicates) {
@@ -103,6 +102,9 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
             } else {
                 this.props.Document.Set(this.props.fieldKey, new ListField([doc]));
             }
+            // set the ZoomBasis only if hasn't already been set -- bcz: maybe set/resetting the ZoomBasis should be a parameter to addDocument?
+            doc.GetTAsync(KeyStore.ZoomBasis, NumberField, field => !field &&
+                doc.SetNumber(KeyStore.ZoomBasis, this.props.Document.GetNumber(KeyStore.Scale, 1)));
         }
         return true;
         // bcz: What is this code trying to do?
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index e4c647635..1ea18ed42 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -350,6 +350,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             <div className="collectionDockingView-content" ref={this._mainCont}
                 style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
                 <DocumentView key={this._document!.Id} Document={this._document!}
+                    toggleMinimized={emptyFunction}
                     addDocument={undefined}
                     removeDocument={undefined}
                     ContentScaling={this.contentScaling}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 90077b053..cb7a67991 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -62,7 +62,7 @@ export class CollectionSchemaView extends CollectionSubView {
 
     @computed get splitPercentage() { return this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0); }
     @computed get columns() { return this.props.Document.GetList(KeyStore.ColumnsKey, [] as Key[]); }
-    @computed get borderWidth() { return COLLECTION_BORDER_WIDTH; }
+    @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
 
     renderCell = (rowProps: CellInfo) => {
         let props: FieldViewProps = {
@@ -266,6 +266,7 @@ export class CollectionSchemaView extends CollectionSubView {
             <div className="collectionSchemaView-previewRegion" style={{ width: `${this.previewRegionWidth}px` }}>
                 <div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
                     <DocumentView Document={this.previewDocument} isTopMost={false} selectOnLoad={false}
+                        toggleMinimized={emptyFunction}
                         addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
                         ScreenToLocalTransform={this.getPreviewTransform}
                         ContentScaling={this.previewContentScaling}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 117343ef3..56d54696d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -247,6 +247,7 @@ export class CollectionFreeFormView extends CollectionSubView {
     getDocumentViewProps(document: Document): DocumentViewProps {
         return {
             Document: document,
+            toggleMinimized: emptyFunction,
             addDocument: this.props.addDocument,
             removeDocument: this.props.removeDocument,
             moveDocument: this.props.moveDocument,
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 9fa6a4f51..3a4425358 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,12 +1,17 @@
-import { computed, trace } from "mobx";
+import { computed, trace, action } from "mobx";
 import { observer } from "mobx-react";
 import { KeyStore } from "../../../fields/KeyStore";
 import { NumberField } from "../../../fields/NumberField";
+import { Document } from "../../../fields/Document";
 import { Transform } from "../../util/Transform";
 import { DocumentView, DocumentViewProps } from "./DocumentView";
 import "./DocumentView.scss";
 import React = require("react");
 import { OmitKeys } from "../../../Utils";
+import { SelectionManager } from "../../util/SelectionManager";
+import { ListField } from "../../../fields/ListField";
+import { BooleanField } from "../../../fields/BooleanField";
+import { matchedData } from "express-validator/filter";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
 }
@@ -14,55 +19,48 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
 @observer
 export class CollectionFreeFormDocumentView extends React.Component<CollectionFreeFormDocumentViewProps> {
     private _mainCont = React.createRef<HTMLDivElement>();
+    private _downX: number = 0;
+    private _downY: number = 0;
 
-    @computed
-    get transform(): string {
+    @computed get transform() {
         return `scale(${this.props.ContentScaling()}, ${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) scale(${this.zoom}, ${this.zoom}) `;
     }
-
-    @computed get zoom(): number { return 1 / this.props.Document.GetNumber(KeyStore.ZoomBasis, 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(); }
-    @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 X() { return this.props.Document.GetNumber(KeyStore.X, 0); }
+    @computed get Y() { return this.props.Document.GetNumber(KeyStore.Y, 0); }
+    @computed get zoom() { return 1 / this.props.Document.GetNumber(KeyStore.ZoomBasis, 1); }
+    @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
+    @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
+    @computed get width() { return this.props.Document.Width(); }
+    @computed get height() { return this.props.Document.Height(); }
+    @computed get zIndex() { return this.props.Document.GetNumber(KeyStore.ZIndex, 0); }
     set width(w: number) {
         this.props.Document.SetData(KeyStore.Width, w, NumberField);
         if (this.nativeWidth && this.nativeHeight) {
             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);
         }
     }
-
     set zIndex(h: number) {
         this.props.Document.SetData(KeyStore.ZIndex, h, NumberField);
     }
 
-    get X() {
-        return this.props.Document.GetNumber(KeyStore.X, 0);
-    }
-    get Y() {
-        return this.props.Document.GetNumber(KeyStore.Y, 0);
-    }
-    getTransform = (): Transform =>
-        this.props.ScreenToLocalTransform()
-            .translate(-this.X, -this.Y)
-            .scale(1 / this.contentScaling()).scale(1 / this.zoom)
-
     contentScaling = () => (this.nativeWidth > 0 ? this.width / this.nativeWidth : 1);
     panelWidth = () => this.props.PanelWidth();
     panelHeight = () => this.props.PanelHeight();
+    toggleMinimized = () => this.toggleIcon();
+    getTransform = (): Transform => this.props.ScreenToLocalTransform()
+        .translate(-this.X, -this.Y)
+        .scale(1 / this.contentScaling()).scale(1 / this.zoom)
 
     @computed
     get docView() {
         return <DocumentView {...OmitKeys(this.props, ['zoomFade'])}
+            toggleMinimized={this.toggleMinimized}
             ContentScaling={this.contentScaling}
             ScreenToLocalTransform={this.getTransform}
             PanelWidth={this.panelWidth}
@@ -70,30 +68,120 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
         />;
     }
 
+    animateBetweenIcon(first: boolean, icon: number[], targ: number[], width: number, height: number, stime: number, target: Document, maximizing: boolean) {
+        setTimeout(() => {
+            let now = Date.now();
+            let progress = Math.min(1, (now - stime) / 200);
+            let pval = maximizing ?
+                [icon[0] + (targ[0] - icon[0]) * progress, icon[1] + (targ[1] - icon[1]) * progress] :
+                [targ[0] + (icon[0] - targ[0]) * progress, targ[1] + (icon[1] - targ[1]) * progress];
+            target.SetNumber(KeyStore.Width, maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress);
+            target.SetNumber(KeyStore.Height, maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress);
+            target.SetNumber(KeyStore.X, pval[0]);
+            target.SetNumber(KeyStore.Y, pval[1]);
+            if (first) {
+                target.SetBoolean(KeyStore.IsMinimized, false);
+            }
+            if (now < stime + 200) {
+                this.animateBetweenIcon(false, icon, targ, width, height, stime, target, maximizing);
+            }
+            else {
+                if (!maximizing) {
+                    target.SetBoolean(KeyStore.IsMinimized, true);
+                    target.SetNumber(KeyStore.X, targ[0]);
+                    target.SetNumber(KeyStore.Y, targ[1]);
+                    target.SetNumber(KeyStore.Width, width);
+                    target.SetNumber(KeyStore.Height, height);
+                }
+                (target as any).isIconAnimating = false;
+            }
+        },
+            2);
+    }
+    @action
+    public toggleIcon = async (): Promise<void> => {
+        SelectionManager.DeselectAll();
+        let isMinimized: boolean | undefined;
+        let minDoc = await this.props.Document.GetTAsync(KeyStore.MinimizedDoc, Document);
+        if (!minDoc) return;
+        let minimizedDocSet = await minDoc.GetTAsync(KeyStore.LinkTags, ListField);
+        if (!minimizedDocSet) return;
+        minimizedDocSet.Data.map(async minimizedDoc => {
+            if (minimizedDoc instanceof Document) {
+                this.props.addDocument && this.props.addDocument(minimizedDoc, false);
+                let maximizedDoc = await minimizedDoc.GetTAsync(KeyStore.MaximizedDoc, Document);
+                if (maximizedDoc instanceof Document && !(maximizedDoc as any).isIconAnimating) {
+                    (maximizedDoc as any).isIconAnimating = true;
+                    if (isMinimized === undefined) {
+                        let maximizedDocMinimizedState = await maximizedDoc.GetTAsync(KeyStore.IsMinimized, BooleanField);
+                        isMinimized = (maximizedDocMinimizedState && maximizedDocMinimizedState.Data) ? true : false;
+                    }
+                    let minx = await minimizedDoc.GetTAsync(KeyStore.X, NumberField);
+                    let miny = await minimizedDoc.GetTAsync(KeyStore.Y, NumberField);
+                    let maxx = await maximizedDoc.GetTAsync(KeyStore.X, NumberField);
+                    let maxy = await maximizedDoc.GetTAsync(KeyStore.Y, NumberField);
+                    let maxw = await maximizedDoc.GetTAsync(KeyStore.Width, NumberField);
+                    let maxh = await maximizedDoc.GetTAsync(KeyStore.Height, NumberField);
+                    if (minx !== undefined && miny !== undefined && maxx !== undefined && maxy !== undefined &&
+                        maxw !== undefined && maxh !== undefined)
+                        this.animateBetweenIcon(
+                            true,
+                            [minx.Data, miny.Data], [maxx.Data, maxy.Data], maxw.Data, maxh.Data,
+                            Date.now(), maximizedDoc, isMinimized);
+                }
+
+            }
+        })
+    }
+    onPointerDown = (e: React.PointerEvent): void => {
+        e.stopPropagation();
+        this._downX = e.clientX;
+        this._downY = e.clientY;
+        document.removeEventListener("pointerup", this.onPointerUp);
+        document.addEventListener("pointerup", this.onPointerUp);
+    }
+    onPointerUp = (e: PointerEvent): void => {
+        document.removeEventListener("pointerup", this.onPointerUp);
+        e.stopPropagation();
+        if (Math.abs(e.clientX - this._downX) < 4 && Math.abs(e.clientY - this._downY) < 4) {
+            this.props.Document.GetTAsync(KeyStore.MaximizedDoc, Document).then(maxdoc => {
+                if (maxdoc instanceof Document) {   // bcz: need a better way to associate behaviors with click events on widget-documents
+                    SelectionManager.DeselectAll();
+                    this.props.addDocument && this.props.addDocument(maxdoc, false);
+                    this.toggleIcon();
+                }
+            });
+        }
+    }
+
     render() {
+        let maximizedDoc = this.props.Document.GetT(KeyStore.MaximizedDoc, Document);
         let zoomFade = 1;
         //var zoom = doc.GetNumber(KeyStore.ZoomBasis, 1);
-        // let transform = this.getTransform().scale(this.contentScaling()).inverse();
-        // var [sptX, sptY] = transform.transformPoint(0, 0);
-        // let [bptX, bptY] = transform.transformPoint(this.props.PanelWidth(), this.props.PanelHeight());
-        // let w = bptX - sptX;
-        // //zoomFade = area < 100 || area > 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1;
-        // let fadeUp = .75 * 1800;
-        // let fadeDown = .075 * 1800;
-        // zoomFade = w < fadeDown  /* || w > fadeUp */ ? Math.max(0, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
+        let transform = this.getTransform().scale(this.contentScaling()).inverse();
+        var [sptX, sptY] = transform.transformPoint(0, 0);
+        let [bptX, bptY] = transform.transformPoint(this.props.PanelWidth(), this.props.PanelHeight());
+        let w = bptX - sptX;
+        //zoomFade = area < 100 || area > 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1;
+        const screenWidth = 1800;
+        let fadeUp = .75 * screenWidth;
+        let fadeDown = (maximizedDoc ? .0075 : .075) * screenWidth;
+        zoomFade = w < fadeDown  /* || w > fadeUp */ ? Math.max(0, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
 
         return (
-            <div className="collectionFreeFormDocumentView-container" ref={this._mainCont} style={{
-                opacity: zoomFade,
-                transformOrigin: "left top",
-                transform: this.transform,
-                pointerEvents: (zoomFade < 0.09 ? "none" : "all"),
-                width: this.width,
-                height: this.height,
-                position: "absolute",
-                zIndex: this.zIndex,
-                backgroundColor: "transparent"
-            }} >
+            <div className="collectionFreeFormDocumentView-container" ref={this._mainCont}
+                onPointerDown={this.onPointerDown}
+                style={{
+                    opacity: zoomFade,
+                    transformOrigin: "left top",
+                    transform: this.transform,
+                    pointerEvents: (zoomFade < 0.09 ? "none" : "all"),
+                    width: this.width,
+                    height: this.height,
+                    position: "absolute",
+                    zIndex: this.zIndex,
+                    backgroundColor: "transparent"
+                }} >
                 {this.docView}
             </div>
         );
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2f8ac9f20..8922e4b81 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -5,8 +5,6 @@ import { Field, Opt } from "../../../fields/Field";
 import { Key } from "../../../fields/Key";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ListField } from "../../../fields/ListField";
-import { NumberField } from "../../../fields/NumberField";
-import { TextField } from "../../../fields/TextField";
 import { ServerUtils } from "../../../server/ServerUtil";
 import { emptyFunction, Utils } from "../../../Utils";
 import { Documents } from "../../documents/Documents";
@@ -40,6 +38,7 @@ export interface DocumentViewProps {
     selectOnLoad: boolean;
     parentActive: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
+    toggleMinimized: () => void;
 }
 export interface JsxArgs extends DocumentViewProps {
     Keys: { [name: string]: Key };
@@ -83,7 +82,6 @@ export function FakeJsxArgs(keys: string[], fields: string[] = []): JsxArgs {
 
 @observer
 export class DocumentView extends React.Component<DocumentViewProps> {
-    static _incompleteAnimations: Map<string, boolean> = new Map<string, boolean>();
     private _downX: number = 0;
     private _downY: number = 0;
     private _mainCont = React.createRef<HTMLDivElement>();
@@ -96,42 +94,16 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     @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>()); }
 
-    onPointerDown = (e: React.PointerEvent): void => {
-        this._downX = e.clientX;
-        this._downY = e.clientY;
-        if (CollectionFreeFormView.RIGHT_BTN_DRAG && (e.button === 2 || (e.button === 0 && e.altKey)) && !this.isSelected()) {
-            return;
-        }
-        if (e.shiftKey && e.buttons === 2) {
-            if (this.props.isTopMost) {
-                this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey);
-            } else {
-                CollectionDockingView.Instance.StartOtherDrag([this.props.Document], e);
-            }
-            e.stopPropagation();
-        } else {
-            let maxdoc = this.props.Document.GetT(KeyStore.MaximizedDoc, Document);
-            if (this.active ||
-                maxdoc instanceof Document // bcz: need a better way of allowing a document to handle pointer events when its not active (ie. be a top-level widget)
-            ) {
-                e.stopPropagation();
-                document.removeEventListener("pointermove", this.onPointerMove);
-                document.addEventListener("pointermove", this.onPointerMove);
-                document.removeEventListener("pointerup", this.onPointerUp);
-                document.addEventListener("pointerup", this.onPointerUp);
-            }
-        }
-    }
-
+    @action
     componentDidMount() {
         if (this._mainCont.current) {
             this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, {
                 handlers: { drop: this.drop.bind(this) }
             });
         }
-        runInAction(() => DocumentManager.Instance.DocumentViews.push(this));
+        DocumentManager.Instance.DocumentViews.push(this);
     }
-
+    @action
     componentDidUpdate() {
         if (this._dropDisposer) {
             this._dropDisposer();
@@ -142,12 +114,16 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             });
         }
     }
-
+    @action
     componentWillUnmount() {
         if (this._dropDisposer) {
             this._dropDisposer();
         }
-        runInAction(() => DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1));
+        DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
+    }
+
+    stopPropagation = (e: React.SyntheticEvent) => {
+        e.stopPropagation();
     }
 
     startDragging(x: number, y: number, dropAliasOfDraggedDoc: boolean) {
@@ -168,43 +144,51 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         }
     }
 
-    onPointerMove = (e: PointerEvent): void => {
-        if (e.cancelBubble) {
+    onPointerDown = (e: React.PointerEvent): void => {
+        this._downX = e.clientX;
+        this._downY = e.clientY;
+        if (CollectionFreeFormView.RIGHT_BTN_DRAG && (e.button === 2 || (e.button === 0 && e.altKey)) && !this.isSelected()) {
             return;
         }
-        if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
+        if (e.shiftKey && e.buttons === 2) {
+            if (this.props.isTopMost) {
+                this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey);
+            } else {
+                CollectionDockingView.Instance.StartOtherDrag([this.props.Document], e);
+            }
+            e.stopPropagation();
+        } else if (this.active) {
             document.removeEventListener("pointermove", this.onPointerMove);
+            document.addEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
-            if (!e.altKey && (!this.topMost || e.buttons === 2)) {
-                this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey);
+            document.addEventListener("pointerup", this.onPointerUp);
+        }
+    }
+    onPointerMove = (e: PointerEvent): void => {
+        if (!e.cancelBubble) {
+            if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
+                document.removeEventListener("pointermove", this.onPointerMove);
+                document.removeEventListener("pointerup", this.onPointerUp);
+                if (!e.altKey && (!this.topMost || e.buttons === 2)) {
+                    this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey);
+                }
             }
+            e.stopPropagation();
+            e.preventDefault();
         }
-        e.stopPropagation();
-        e.preventDefault();
     }
     onPointerUp = (e: PointerEvent): void => {
         document.removeEventListener("pointermove", this.onPointerMove);
         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) {
-            this.props.Document.GetTAsync(KeyStore.MaximizedDoc, Document).then(maxdoc => {
-                if (maxdoc instanceof Document) {   // bcz: need a better way to associate behaviors with click events on widget-documents
-                    this.props.addDocument && this.props.addDocument(maxdoc, false);
-                    this.toggleIcon();
-                } else
-                    SelectionManager.SelectDoc(this, e.ctrlKey);
-            });
+            SelectionManager.SelectDoc(this, e.ctrlKey);
         }
     }
-    stopPropagation = (e: React.SyntheticEvent) => {
-        e.stopPropagation();
-    }
 
     deleteClicked = (): void => {
         this.props.removeDocument && this.props.removeDocument(this.props.Document);
     }
-
     fieldsClicked = (e: React.MouseEvent): void => {
         let kvp = Documents.KVPDocument(this.props.Document, { width: 300, height: 300 });
         CollectionDockingView.Instance.AddRightSplit(kvp);
@@ -215,7 +199,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         ContextMenu.Instance.addItem({ description: "Close Full Screen", event: this.closeFullScreenClicked });
         ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
     }
-
     closeFullScreenClicked = (e: React.MouseEvent): void => {
         CollectionDockingView.Instance.CloseFullScreen();
         ContextMenu.Instance.clearItems();
@@ -223,92 +206,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
     }
 
-    @action createIcon = (layoutString: string): Document => {
-        let iconDoc = Documents.IconDocument(layoutString);
-        iconDoc.SetText(KeyStore.Title, "ICON" + this.props.Document.Title)
-        iconDoc.SetBoolean(KeyStore.IsMinimized, false);
-        iconDoc.SetNumber(KeyStore.NativeWidth, 0);
-        iconDoc.SetNumber(KeyStore.NativeHeight, 0);
-        iconDoc.SetNumber(KeyStore.X, this.props.Document.GetNumber(KeyStore.X, 0));
-        iconDoc.SetNumber(KeyStore.Y, this.props.Document.GetNumber(KeyStore.Y, 0) - 24);
-        iconDoc.Set(KeyStore.Prototype, this.props.Document);
-        iconDoc.Set(KeyStore.MaximizedDoc, this.props.Document);
-        this.props.Document.Set(KeyStore.MinimizedDoc, iconDoc);
-        this.props.addDocument && this.props.addDocument(iconDoc, false);
-        return iconDoc;
-    }
-
-    animateBetweenIcon(icon: number[], targ: number[], width: number, height: number, stime: number, target: Document, maximizing: boolean) {
-        setTimeout(() => {
-            let now = Date.now();
-            let progress = Math.min(1, (now - stime) / 200);
-            let pval = maximizing ?
-                [icon[0] + (targ[0] - icon[0]) * progress, icon[1] + (targ[1] - icon[1]) * progress] :
-                [targ[0] + (icon[0] - targ[0]) * progress, targ[1] + (icon[1] - targ[1]) * progress];
-            target.SetNumber(KeyStore.Width, maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress);
-            target.SetNumber(KeyStore.Height, maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress);
-            target.SetNumber(KeyStore.X, pval[0]);
-            target.SetNumber(KeyStore.Y, pval[1]);
-            if (now < stime + 200) {
-                this.animateBetweenIcon(icon, targ, width, height, stime, target, maximizing);
-            }
-            else {
-                if (!maximizing) {
-                    target.SetBoolean(KeyStore.IsMinimized, true);
-                    target.SetNumber(KeyStore.X, targ[0]);
-                    target.SetNumber(KeyStore.Y, targ[1]);
-                    target.SetNumber(KeyStore.Width, width);
-                    target.SetNumber(KeyStore.Height, height);
-                }
-                DocumentView._incompleteAnimations.set(target.Id, false);
-            }
-        },
-            2);
-    }
-
-    @action
-    public toggleIcon = async (): Promise<void> => {
-        SelectionManager.DeselectAll();
-        let isMinimized: boolean | undefined;
-        let minDoc = await this.props.Document.GetTAsync(KeyStore.MinimizedDoc, Document);
-        if (!minDoc) return;
-        let minimizedDocSet = await minDoc.GetTAsync(KeyStore.LinkTags, ListField);
-        if (!minimizedDocSet) return;
-        minimizedDocSet.Data.map(async minimizedDoc => {
-            if (minimizedDoc instanceof Document) {
-                this.props.addDocument && this.props.addDocument(minimizedDoc, false);
-                let maximizedDoc = await minimizedDoc.GetTAsync(KeyStore.MaximizedDoc, Document);
-                if (maximizedDoc instanceof Document && !DocumentView._incompleteAnimations.get(maximizedDoc.Id)) {
-                    DocumentView._incompleteAnimations.set(maximizedDoc.Id, true);
-                    isMinimized = isMinimized === undefined ? maximizedDoc.GetBoolean(KeyStore.IsMinimized, false) : isMinimized;
-                    maximizedDoc.SetBoolean(KeyStore.IsMinimized, false);
-                    let minx = await minimizedDoc.GetTAsync(KeyStore.X, NumberField);
-                    let miny = await minimizedDoc.GetTAsync(KeyStore.Y, NumberField);
-                    let maxx = await maximizedDoc.GetTAsync(KeyStore.X, NumberField);
-                    let maxy = await maximizedDoc.GetTAsync(KeyStore.Y, NumberField);
-                    let maxw = await maximizedDoc.GetTAsync(KeyStore.Width, NumberField);
-                    let maxh = await maximizedDoc.GetTAsync(KeyStore.Height, NumberField);
-                    if (minx !== undefined && miny !== undefined && maxx !== undefined && maxy !== undefined &&
-                        maxw !== undefined && maxh !== undefined)
-                        this.animateBetweenIcon(
-                            [minx.Data, miny.Data], [maxx.Data, maxy.Data], maxw.Data, maxh.Data,
-                            Date.now(), maximizedDoc, isMinimized);
-                }
-
-            }
-        })
-    }
-
-    @action
-    public getIconDoc = async (): Promise<Document | undefined> => {
-        return await this.props.Document.GetTAsync(KeyStore.MinimizedDoc, Document).then(async mindoc =>
-            mindoc ? mindoc :
-                await this.props.Document.GetTAsync(KeyStore.BackgroundLayout, TextField).then(async field =>
-                    (field instanceof TextField) ? this.createIcon(field.Data) :
-                        await this.props.Document.GetTAsync(KeyStore.Layout, TextField).then(field =>
-                            (field instanceof TextField) ? this.createIcon(field.Data) : undefined)));
-    }
-
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
@@ -353,6 +250,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         }
     }
 
+    @action
     onDrop = (e: React.DragEvent) => {
         let text = e.dataTransfer.getData("text/plain");
         if (!e.isDefaultPrevented() && text && text.startsWith("<div")) {
@@ -389,12 +287,10 @@ export class DocumentView extends React.Component<DocumentViewProps> {
 
     isSelected = () => SelectionManager.IsSelected(this);
     select = (ctrlPressed: boolean) => SelectionManager.SelectDoc(this, ctrlPressed);
-
     @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
     @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
     @computed get contents() { return (<DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} layoutKey={KeyStore.Layout} />); }
 
-
     render() {
         var scaling = this.props.ContentScaling();
         var nativeHeight = this.nativeHeight > 0 ? this.nativeHeight.toString() + "px" : "100%";
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 5d9aefd61..a16fd5007 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -16,7 +16,7 @@ import { FieldView, FieldViewProps } from './FieldView';
 import "./ImageBox.scss";
 import React = require("react");
 import { InkingControl } from '../InkingControl';
-import { InkTool } from '../../../fields/InkField';
+import { NumberField } from '../../../fields/NumberField';
 
 @observer
 export class ImageBox extends React.Component<FieldViewProps> {
@@ -42,7 +42,8 @@ export class ImageBox extends React.Component<FieldViewProps> {
         var w = this._imgRef.current!.naturalWidth;
         if (this._photoIndex === 0) {
             this.props.Document.SetNumber(KeyStore.NativeHeight, this.props.Document.GetNumber(KeyStore.NativeWidth, 0) * h / w);
-            this.props.Document.SetNumber(KeyStore.Height, this.props.Document.Width() * h / w);
+            this.props.Document.GetTAsync(KeyStore.Width, NumberField, field =>
+                field && this.props.Document.SetNumber(KeyStore.Height, field.Data * h / w));
         }
     }
 
-- 
cgit v1.2.3-70-g09d2


From 70a889c103139e43acae84e1a5a91240b3d195c2 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Wed, 24 Apr 2019 22:36:11 -0400
Subject: Almost all of schema view

---
 .../views/collections/CollectionSchemaView.tsx     | 149 ++++++++++-----------
 src/new_fields/Doc.ts                              |  11 +-
 2 files changed, 80 insertions(+), 80 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 90077b053..e556151f9 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -7,12 +7,7 @@ import { observer } from "mobx-react";
 import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
 import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss'
 import "react-table/react-table.css";
-import { Document } from "../../../fields/Document";
-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 { emptyFunction, returnFalse } from "../../../Utils";
 import { Server } from "../../Server";
 import { SetupDrag } from "../../util/DragManager";
 import { CompileScript, ToField } from "../../util/Scripting";
@@ -25,44 +20,46 @@ import { DocumentView } from "../nodes/DocumentView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
 import { CollectionSubView } from "./CollectionSubView";
+import { Opt, Field, Doc, Id } from "../../../new_fields/Doc";
+import { Cast, FieldValue } from "../../../new_fields/Types";
+import { listSpec } from "../../../new_fields/Schema";
+import { List } from "../../../new_fields/List";
 
 
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
 
 
 @observer
-class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggle: (key: Key) => void }> {
-    @observable key: Key | undefined;
-
+class KeyToggle extends React.Component<{ keyName: string, checked: boolean, toggle: (key: string) => void }> {
     constructor(props: any) {
         super(props);
-        Server.GetField(this.props.keyId, action((field: Opt<Field>) => field instanceof Key && (this.key = field)));
     }
 
     render() {
-        return !this.key ? (null) :
-            (<div key={this.key.Id}>
-                <input type="checkbox" checked={this.props.checked} onChange={() => this.key && this.props.toggle(this.key)} />
-                {this.key.Name}
-            </div>);
+        return (
+            <div key={this.props.keyName}>
+                <input type="checkbox" checked={this.props.checked} onChange={() => this.props.toggle(this.props.keyName)} />
+                {this.props.keyName}
+            </div>
+        );
     }
 }
 
 @observer
-export class CollectionSchemaView extends CollectionSubView {
+export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     private _mainCont?: HTMLDivElement;
     private _startSplitPercent = 0;
     private DIVIDER_WIDTH = 4;
 
-    @observable _columns: Array<Key> = [KeyStore.Title, KeyStore.Data, KeyStore.Author];
+    @observable _columns: Array<string> = ["title", "data", "author"];
     @observable _selectedIndex = 0;
     @observable _columnsPercentage = 0;
-    @observable _keys: Key[] = [];
+    @observable _keys: string[] = [];
     @observable _newKeyName: string = "";
 
-    @computed get splitPercentage() { return this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0); }
-    @computed get columns() { return this.props.Document.GetList(KeyStore.ColumnsKey, [] as Key[]); }
-    @computed get borderWidth() { return COLLECTION_BORDER_WIDTH; }
+    @computed get splitPercentage() { return Cast(this.props.Document.schemaSplitPercentage, "number", 0); }
+    @computed get columns() { return Cast(this.props.Document.columns, listSpec("string"), []); }
+    @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
 
     renderCell = (rowProps: CellInfo) => {
         let props: FieldViewProps = {
@@ -74,7 +71,7 @@ export class CollectionSchemaView extends CollectionSubView {
             isTopMost: false,
             selectOnLoad: false,
             ScreenToLocalTransform: Transform.Identity,
-            focus: emptyDocFunction,
+            focus: emptyFunction,
             active: returnFalse,
             whenActiveChanged: emptyFunction,
         };
@@ -83,34 +80,27 @@ export class CollectionSchemaView extends CollectionSubView {
         );
         let reference = React.createRef<HTMLDivElement>();
         let onItemDown = SetupDrag(reference, () => props.Document, this.props.moveDocument);
-        let applyToDoc = (doc: Document, run: (args?: { [name: string]: any }) => any) => {
+        let applyToDoc = (doc: Doc, run: (args?: { [name: string]: any }) => any) => {
             const res = run({ this: doc });
             if (!res.success) return false;
             const field = res.result;
-            if (field instanceof Field) {
-                doc.Set(props.fieldKey, field);
-                return true;
-            } else {
-                let dataField = ToField(field);
-                if (dataField) {
-                    doc.Set(props.fieldKey, dataField);
-                    return true;
-                }
-            }
-            return false;
+            doc[props.fieldKey] = field;
+            return true;
         };
         return (
-            <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} key={props.Document.Id} ref={reference}>
+            <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} key={props.Document[Id]} ref={reference}>
                 <EditableView
                     display={"inline"}
                     contents={contents}
                     height={Number(MAX_ROW_HEIGHT)}
                     GetValue={() => {
-                        let field = props.Document.Get(props.fieldKey);
-                        if (field && field instanceof Field) {
-                            return field.ToScriptString();
+                        let field = props.Document[props.fieldKey];
+                        if (field) {
+                            //TODO Types
+                            // return field.ToScriptString();
+                            return String(field);
                         }
-                        return field || "";
+                        return "";
                     }}
                     SetValue={(value: string) => {
                         let script = CompileScript(value, { addReturn: true, params: { this: Document.name } });
@@ -126,14 +116,13 @@ export class CollectionSchemaView extends CollectionSubView {
                         }
                         const run = script.run;
                         //TODO This should be able to be refactored to compile the script once
-                        this.props.Document.GetTAsync<ListField<Document>>(this.props.fieldKey, ListField).then((val) => {
-                            if (val) {
-                                val.Data.forEach(doc => applyToDoc(doc, run));
-                            }
-                        });
+                        const val = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc));
+                        if (val) {
+                            val.forEach(doc => applyToDoc(doc, run));
+                        }
                     }}>
                 </EditableView>
-            </div>
+            </div >
         );
     }
 
@@ -164,36 +153,37 @@ export class CollectionSchemaView extends CollectionSubView {
     }
 
     @action
-    toggleKey = (key: Key) => {
-        this.props.Document.GetOrCreateAsync<ListField<Key>>(KeyStore.ColumnsKey, ListField,
-            (field) => {
-                const index = field.Data.indexOf(key);
-                if (index === -1) {
-                    this.columns.push(key);
-                } else {
-                    this.columns.splice(index, 1);
-                }
-
-            });
+    toggleKey = (key: string) => {
+        let list = Cast(this.props.Document.columns, listSpec("string"));
+        if (list === undefined) {
+            this.props.Document.columns = list = new List<string>([key]);
+        } else {
+            const index = list.indexOf(key);
+            if (index === -1) {
+                list.push(key);
+            } else {
+                list.splice(index, 1);
+            }
+        }
     }
 
     //toggles preview side-panel of schema
     @action
     toggleExpander = (event: React.ChangeEvent<HTMLInputElement>) => {
-        this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage === 0 ? 33 : 0);
+        this.props.Document.schemaSplitPercentage = this.splitPercentage === 0 ? 33 : 0;
     }
 
     @action
     onDividerMove = (e: PointerEvent): void => {
         let nativeWidth = this._mainCont!.getBoundingClientRect();
-        this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100)));
+        this.props.Document.schemaSplitPercentage = Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100));
     }
     @action
     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);
+            this.props.Document.schemaSplitPercentage = this.splitPercentage === 0 ? 33 : 0;
         }
     }
     onDividerDown = (e: React.PointerEvent) => {
@@ -206,8 +196,7 @@ export class CollectionSchemaView extends CollectionSubView {
 
     onPointerDown = (e: React.PointerEvent): void => {
         if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) {
-            if (this.props.isSelected())
-                e.stopPropagation();
+            if (this.props.isSelected()) e.stopPropagation();
             else e.preventDefault();
         }
     }
@@ -220,7 +209,7 @@ export class CollectionSchemaView extends CollectionSubView {
 
     @action
     addColumn = () => {
-        this.columns.push(new Key(this._newKeyName));
+        this.columns.push(this._newKeyName);
         this._newKeyName = "";
     }
 
@@ -235,21 +224,22 @@ export class CollectionSchemaView extends CollectionSubView {
         this.previewScript = e.currentTarget.value;
     }
 
-    get previewDocument(): Document | undefined {
-        const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
+    get previewDocument(): Doc | undefined {
+        const children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
         const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
-        return selected ? (this.previewScript ? selected.Get(new Key(this.previewScript)) as Document : selected) : undefined;
+        return selected ? (this.previewScript ? FieldValue(Cast(selected[this.previewScript], Doc)) : selected) : undefined;
     }
     get tableWidth() { return (this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH) * (1 - this.splitPercentage / 100); }
     get previewRegionHeight() { return this.props.PanelHeight() - 2 * this.borderWidth; }
     get previewRegionWidth() { return (this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH) * this.splitPercentage / 100; }
 
-    private previewDocNativeWidth = () => this.previewDocument!.GetNumber(KeyStore.NativeWidth, this.previewRegionWidth);
-    private previewDocNativeHeight = () => this.previewDocument!.GetNumber(KeyStore.NativeHeight, this.previewRegionHeight);
+    private previewDocNativeWidth = () => Cast(this.previewDocument!.nativeWidth, "number", this.previewRegionWidth);
+    private previewDocNativeHeight = () => Cast(this.previewDocument!.nativeHeight, "number", this.previewRegionHeight);
     private previewContentScaling = () => {
         let wscale = this.previewRegionWidth / (this.previewDocNativeWidth() ? this.previewDocNativeWidth() : this.previewRegionWidth);
-        if (wscale * this.previewDocNativeHeight() > this.previewRegionHeight)
+        if (wscale * this.previewDocNativeHeight() > this.previewRegionHeight) {
             return this.previewRegionHeight / (this.previewDocNativeHeight() ? this.previewDocNativeHeight() : this.previewRegionHeight);
+        }
         return wscale;
     }
     private previewPanelWidth = () => this.previewDocNativeWidth() * this.previewContentScaling();
@@ -257,7 +247,7 @@ export class CollectionSchemaView extends CollectionSubView {
     get previewPanelCenteringOffset() { return (this.previewRegionWidth - this.previewDocNativeWidth() * this.previewContentScaling()) / 2; }
     getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(
         - this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth - this.previewPanelCenteringOffset,
-        - this.borderWidth).scale(1 / this.previewContentScaling());
+        - this.borderWidth).scale(1 / this.previewContentScaling())
 
     @computed
     get previewPanel() {
@@ -271,7 +261,7 @@ export class CollectionSchemaView extends CollectionSubView {
                         ContentScaling={this.previewContentScaling}
                         PanelWidth={this.previewPanelWidth} PanelHeight={this.previewPanelHeight}
                         ContainingCollectionView={this.props.CollectionView}
-                        focus={emptyDocFunction}
+                        focus={emptyFunction}
                         parentActive={this.props.active}
                         whenActiveChanged={this.props.whenActiveChanged}
                     />
@@ -283,18 +273,19 @@ export class CollectionSchemaView extends CollectionSubView {
     }
 
     get documentKeysCheckList() {
-        const docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
-        let keys: { [id: string]: boolean } = {};
+        const docs = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
+        let keys: { [key: string]: boolean } = {};
         // 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
         //  invalidated and re-rendered.   This workaround will inquire all of the document fields before the options button is clicked.
         //  then by the time the options button is clicked, all of the fields should be in place.  If a new field is added while this menu
         //  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))));
+        //TODO Types
+        untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).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] = true);
         return Array.from(Object.keys(keys)).map(item =>
-            (<KeyToggle checked={keys[item]} key={item} keyId={item} toggle={this.toggleKey} />));
+            (<KeyToggle checked={keys[item]} key={item} keyName={item} toggle={this.toggleKey} />));
     }
 
     get tableOptionsPanel() {
@@ -328,16 +319,16 @@ export class CollectionSchemaView extends CollectionSubView {
     render() {
         library.add(faCog);
         library.add(faPlus);
-        const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
+        const children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
         return (
             <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel}
                 onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createTarget}>
                 <div className="collectionSchemaView-tableContainer" style={{ width: `${this.tableWidth}px` }}>
                     <ReactTable data={children} page={0} pageSize={children.length} showPagination={false}
                         columns={this.columns.map(col => ({
-                            Header: col.Name,
-                            accessor: (doc: Document) => [doc, col],
-                            id: col.Id
+                            Header: col,
+                            accessor: (doc: Doc) => [doc, col],
+                            id: col
                         }))}
                         column={{ ...ReactTableDefaults.column, Cell: this.renderCell, }}
                         getTrProps={this.getTrProps}
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index cce578b60..bd10e5474 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -4,7 +4,7 @@ import { autoObject, SerializationHelper, Deserializable } from "../client/util/
 import { Utils } from "../Utils";
 import { DocServer } from "../client/DocServer";
 import { setter, getter, getField } from "./util";
-import { Cast, ToConstructor, PromiseValue } from "./Types";
+import { Cast, ToConstructor, PromiseValue, FieldValue } from "./Types";
 
 export type FieldId = string;
 export const HandleUpdate = Symbol("HandleUpdate");
@@ -93,6 +93,15 @@ export namespace Doc {
             proto[key] = value;
         }
     }
+    export function GetAllPrototypes(doc: Doc): Doc[] {
+        const protos: Doc[] = [];
+        let d: Opt<Doc> = doc;
+        while (d) {
+            protos.push(d);
+            d = FieldValue(d.proto);
+        }
+        return protos;
+    }
     export function assign<K extends string>(doc: Doc, fields: Partial<Record<K, Opt<Field>>>) {
         for (const key in fields) {
             if (fields.hasOwnProperty(key)) {
-- 
cgit v1.2.3-70-g09d2


From 575d8b339cdf74cdcb8d46dcd6e627aec2dda636 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 24 Apr 2019 23:17:44 -0400
Subject: added borderRadius and fixed a bunch of broken props along the way.

---
 src/client/documents/Documents.ts                          |  2 ++
 src/client/views/Main.scss                                 |  3 ++-
 src/client/views/Main.tsx                                  |  3 ++-
 src/client/views/MainOverlayTextBox.tsx                    |  4 ++--
 src/client/views/collections/CollectionBaseView.tsx        |  7 ++++---
 src/client/views/collections/CollectionDockingView.tsx     |  3 ++-
 src/client/views/collections/CollectionSchemaView.tsx      |  6 +++++-
 src/client/views/collections/CollectionSubView.tsx         |  1 +
 src/client/views/collections/CollectionTreeView.tsx        |  5 ++++-
 .../collectionFreeForm/CollectionFreeFormView.scss         |  1 +
 .../collectionFreeForm/CollectionFreeFormView.tsx          | 14 +++++++++++---
 .../views/collections/collectionFreeForm/MarqueeView.tsx   |  7 ++++---
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx  | 11 +++++++++++
 src/client/views/nodes/DocumentView.tsx                    |  2 ++
 src/client/views/nodes/FieldView.tsx                       |  7 ++++++-
 src/client/views/nodes/FormattedTextBox.scss               |  3 +--
 src/client/views/nodes/FormattedTextBox.tsx                |  5 ++++-
 src/client/views/nodes/ImageBox.scss                       |  3 ---
 src/client/views/nodes/ImageBox.tsx                        |  2 +-
 src/fields/KeyStore.ts                                     |  3 ++-
 20 files changed, 67 insertions(+), 25 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 0e6661819..879b114b6 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -55,6 +55,7 @@ export interface DocumentOptions {
     backgroundColor?: string;
     copyDraggedItems?: boolean;
     documentText?: string;
+    borderRounding?: number;
 }
 
 export namespace Documents {
@@ -108,6 +109,7 @@ export namespace Documents {
         if (options.layout !== undefined) { doc.SetText(KeyStore.Layout, options.layout); }
         if (options.layoutKeys !== undefined) { doc.Set(KeyStore.LayoutKeys, new ListField(options.layoutKeys)); }
         if (options.copyDraggedItems !== undefined) { doc.SetBoolean(KeyStore.CopyDraggedItems, options.copyDraggedItems); }
+        if (options.borderRounding !== undefined) { doc.SetNumber(KeyStore.BorderRounding, options.borderRounding); }
         return doc;
     }
 
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 4373534b2..3edc79d39 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -42,7 +42,8 @@ h1 {
 }
 
 .jsx-parser {
-    width:100%
+    width:100%;
+    pointer-events: none;
 }
 
 p {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 934616aca..057f68ed9 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -17,7 +17,7 @@ import { CurrentUserUtils } from '../../server/authentication/models/current_use
 import { MessageStore } from '../../server/Message';
 import { RouteStore } from '../../server/RouteStore';
 import { ServerUtils } from '../../server/ServerUtil';
-import { emptyDocFunction, emptyFunction, returnTrue, Utils, returnOne } from '../../Utils';
+import { emptyDocFunction, emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils';
 import { Documents } from '../documents/Documents';
 import { ColumnAttributeModel } from '../northstar/core/attribute/AttributeModel';
 import { AttributeTransformationModel } from '../northstar/core/attribute/AttributeTransformationModel';
@@ -197,6 +197,7 @@ export class Main extends React.Component {
                             isTopMost={true}
                             selectOnLoad={false}
                             focus={emptyDocFunction}
+                            borderRounding={returnZero}
                             parentActive={returnTrue}
                             whenActiveChanged={emptyFunction}
                             ContainingCollectionView={undefined} />}
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 9aaaad084..f98f6e8a5 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -5,7 +5,7 @@ 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 { emptyDocFunction, emptyFunction, returnTrue, returnZero } from '../../Utils';
 import '../northstar/model/ModelExtensions';
 import '../northstar/utils/Extensions';
 import { DragManager } from '../util/DragManager';
@@ -97,7 +97,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
                     style={{ width: `${this.TextDoc.Width()}px`, height: `${this.TextDoc.Height()}px` }}>
                     <FormattedTextBox fieldKey={this._textFieldKey} isOverlay={true} Document={this.TextDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
                         selectOnLoad={true} ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue}
-                        ScreenToLocalTransform={this._textXf} focus={emptyDocFunction} />
+                        ScreenToLocalTransform={this._textXf} borderRounding={returnZero} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyDocFunction} />
                 </div>
             </ div>;
         }
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 097236c1d..e65770137 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -2,13 +2,12 @@ import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Document } from '../../../fields/Document';
-import { Field, FieldValue, FieldWaiting } from '../../../fields/Field';
+import { FieldValue, FieldWaiting } from '../../../fields/Field';
 import { KeyStore } from '../../../fields/KeyStore';
 import { ListField } from '../../../fields/ListField';
 import { NumberField } from '../../../fields/NumberField';
 import { ContextMenu } from '../ContextMenu';
 import { FieldViewProps } from '../nodes/FieldView';
-import { TextField } from '../../../fields/TextField';
 
 export enum CollectionViewType {
     Invalid,
@@ -190,7 +189,9 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         };
         const viewtype = this.collectionViewType;
         return (
-            <div className={this.props.className || "collectionView-cont"} onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}>
+            <div className={this.props.className || "collectionView-cont"}
+                style={{ borderRadius: `${this.props.borderRounding()}px`, pointerEvents: "all" }}
+                onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}>
                 {viewtype !== undefined ? this.props.children(viewtype, props) : (null)}
             </div>
         );
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 84159635a..eb94a4e6d 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -8,7 +8,7 @@ import { Document } from "../../../fields/Document";
 import { KeyStore } from "../../../fields/KeyStore";
 import Measure from "react-measure";
 import { FieldId, Opt, Field, FieldWaiting } from "../../../fields/Field";
-import { Utils, returnTrue, emptyFunction, emptyDocFunction, returnOne } from "../../../Utils";
+import { Utils, returnTrue, emptyFunction, emptyDocFunction, returnOne, returnZero } from "../../../Utils";
 import { Server } from "../../Server";
 import { undoBatch } from "../../util/UndoManager";
 import { DocumentView } from "../nodes/DocumentView";
@@ -368,6 +368,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                     parentActive={returnTrue}
                     whenActiveChanged={emptyFunction}
                     focus={emptyDocFunction}
+                    borderRounding={returnZero}
                     ContainingCollectionView={undefined} />
             </div>);
     }
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index cb7a67991..dc2910ede 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -12,7 +12,7 @@ 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 { emptyDocFunction, emptyFunction, returnFalse, returnZero } from "../../../Utils";
 import { Server } from "../../Server";
 import { SetupDrag } from "../../util/DragManager";
 import { CompileScript, ToField } from "../../util/Scripting";
@@ -77,6 +77,9 @@ export class CollectionSchemaView extends CollectionSubView {
             focus: emptyDocFunction,
             active: returnFalse,
             whenActiveChanged: emptyFunction,
+            borderRounding: this.props.borderRounding,
+            PanelHeight: returnZero,
+            PanelWidth: returnZero,
         };
         let contents = (
             <FieldView {...props} />
@@ -275,6 +278,7 @@ export class CollectionSchemaView extends CollectionSubView {
                         focus={emptyDocFunction}
                         parentActive={this.props.active}
                         whenActiveChanged={this.props.whenActiveChanged}
+                        borderRounding={this.props.borderRounding}
                     />
                 </div>
                 <input className="collectionSchemaView-input" value={this.previewScript} onChange={this.onPreviewScriptChange}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index f9fc7be5a..bf59bec95 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -25,6 +25,7 @@ export interface CollectionViewProps extends FieldViewProps {
     moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
     PanelWidth: () => number;
     PanelHeight: () => number;
+    borderRounding: () => number;
 }
 
 export interface SubCollectionViewProps extends CollectionViewProps {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index e0387f4b4..b9d1f0bd9 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -136,7 +136,10 @@ 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}>
+            <div id="body" className="collectionTreeView-dropTarget"
+                style={{ borderRadius: `${this.props.borderRounding()}px` }}
+                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/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 57706b51e..67a0e532c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -6,6 +6,7 @@
   width: 100%;
   height: 100%;
   transform-origin: left top;
+  pointer-events: none;
 }
 .collectionfreeformview-container {
   .collectionfreeformview > .jsx-parser {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 7492d8744..9e2c9bf9a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -257,6 +257,7 @@ export class CollectionFreeFormView extends CollectionSubView {
             focus: this.focusDocument,
             parentActive: this.props.active,
             whenActiveChanged: this.props.active,
+            borderRounding: this.props.borderRounding,
         };
     }
 
@@ -288,11 +289,17 @@ export class CollectionFreeFormView extends CollectionSubView {
         const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
         return (
             <div className={containerName} ref={this.createDropTarget} onWheel={this.onPointerWheel}
+                style={{ borderRadius: `${this.props.borderRounding()}px` }}
                 onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} >
-                <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
+                {/* <svg viewBox="0 0 180 18" style={{ top: "50%", opacity: 0.05, position: "absolute" }}>
+                    <text y="15" >
+                        {this.props.Document.Title}
+                    </text>
+                </svg> */}
+                <MarqueeView container={this} borderRadius={this.props.borderRounding} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
                     addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox}
                     getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
-                    <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
+                    <CollectionFreeFormViewPannableContents borderRadius={this.props.borderRounding} centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
                         zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
                         <CollectionFreeFormLinksView {...this.props} key="freeformLinks">
                             <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
@@ -340,6 +347,7 @@ interface CollectionFreeFormViewPannableContentsProps {
     panX: () => number;
     panY: () => number;
     zoomScaling: () => number;
+    borderRadius: () => number;
 }
 
 @observer
@@ -350,7 +358,7 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
         const panx = -this.props.panX();
         const pany = -this.props.panY();
         const zoom = this.props.zoomScaling();// needs to be a variable outside of the <Measure> otherwise, reactions won't fire
-        return <div className="collectionfreeformview" style={{ transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}>
+        return <div className="collectionfreeformview" style={{ borderRadius: `${this.props.borderRadius()}px`, transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}>
             {this.props.children}
         </div>;
     }
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 898fff019..e724ab47e 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -14,7 +14,6 @@ import { CollectionFreeFormView } from "./CollectionFreeFormView";
 import "./MarqueeView.scss";
 import React = require("react");
 import { Utils } from "../../../../Utils";
-import { ListField } from "../../../../fields/ListField";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -25,6 +24,7 @@ interface MarqueeViewProps {
     selectDocuments: (docs: Document[]) => void;
     removeDocument: (doc: Document) => boolean;
     addLiveTextDocument: (doc: Document) => void;
+    borderRadius: () => number;
 }
 
 @observer
@@ -138,7 +138,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             this.cleanupInteractions(true);
             e.stopPropagation();
         }
-        if (e.key === "c" || e.key === "r") {
+        if (e.key === "c" || e.key === "r" || e.key === "e") {
             e.stopPropagation();
             let bounds = this.Bounds;
             let selected = this.marqueeSelect().map(d => {
@@ -156,6 +156,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 y: bounds.top,
                 panx: 0,
                 pany: 0,
+                borderRounding: e.key === "e" ? -1 : undefined,
                 backgroundColor: selected.length ? "white" : "",
                 scale: zoomBasis,
                 width: bounds.width * zoomBasis,
@@ -250,7 +251,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     }
 
     render() {
-        return <div className="marqueeView" onClick={this.onClick} onPointerDown={this.onPointerDown}>
+        return <div className="marqueeView" style={{ borderRadius: `${this.props.borderRadius()}px` }} onClick={this.onClick} onPointerDown={this.onPointerDown}>
             {this.props.children}
             {!this._visible ? (null) : this.marqueeDiv}
         </div>;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 420324d40..8caf5ff31 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -65,6 +65,7 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
             ScreenToLocalTransform={this.getTransform}
             PanelWidth={this.panelWidth}
             PanelHeight={this.panelHeight}
+            borderRounding={this.borderRounding}
         />;
     }
 
@@ -148,6 +149,15 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
         }
     }
 
+    borderRounding = () => {
+        let br = this.props.Document.GetNumber(KeyStore.BorderRounding, 0);
+        return br >= 0 ? br :
+            this.props.Document.GetNumber(KeyStore.NativeWidth, 0) === 0 ?
+                Math.min(this.props.PanelWidth(), this.props.PanelHeight())
+                :
+                Math.min(this.props.Document.GetNumber(KeyStore.NativeWidth, 0), this.props.Document.GetNumber(KeyStore.NativeHeight, 0));
+    }
+
     render() {
         let maximizedDoc = this.props.Document.GetT(KeyStore.MaximizedDoc, Document);
         let zoomFade = 1;
@@ -168,6 +178,7 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
                 onClick={this.onClick}
                 style={{
                     opacity: zoomFade,
+                    borderRadius: `${this.borderRounding()}px`,
                     transformOrigin: "left top",
                     transform: this.transform,
                     pointerEvents: (zoomFade < 0.09 ? "none" : "all"),
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c19b0c342..ddf99c109 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -41,6 +41,7 @@ export interface DocumentViewProps {
     parentActive: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     toggleMinimized: () => void;
+    borderRounding: () => number;
 }
 export interface JsxArgs extends DocumentViewProps {
     Keys: { [name: string]: Key };
@@ -277,6 +278,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             <div className={`documentView-node${this.props.isTopMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
                 style={{
+                    borderRadius: `${this.props.borderRounding()}px`,
                     background: this.props.Document.GetText(KeyStore.BackgroundColor, ""),
                     width: nativeWidth, height: nativeHeight,
                     transform: `scale(${scaling}, ${scaling})`
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 93e385821..8484194b0 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, emptyDocFunction, emptyFunction, returnOne } from "../../../Utils";
+import { returnFalse, emptyDocFunction, emptyFunction, returnOne, returnZero } from "../../../Utils";
 import { CollectionView } from "../collections/CollectionView";
 import { CollectionPDFView } from "../collections/CollectionPDFView";
 import { CollectionVideoView } from "../collections/CollectionVideoView";
@@ -47,6 +47,9 @@ export interface FieldViewProps {
     active: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     focus: (doc: Document) => void;
+    PanelWidth: () => number;
+    PanelHeight: () => number;
+    borderRounding: () => number;
 }
 
 @observer
@@ -100,6 +103,8 @@ export class FieldView extends React.Component<FieldViewProps> {
                     layoutKey={KeyStore.Layout}
                     ContainingCollectionView={this.props.ContainingCollectionView}
                     parentActive={this.props.active}
+                    borderRounding={returnZero}
+                    toggleMinimized={emptyFunction}
                     whenActiveChanged={this.props.whenActiveChanged} />
             );
         }
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 5eb2bf7ce..59857bab3 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -22,11 +22,10 @@
   overflow-x: hidden;
   color: initial;
   height: 100%;
-  pointer-events: all;
+  pointer-events: none;
 }
 .formattedTextBox-cont-hidden {
     overflow: hidden;
-    pointer-events: none;
 }
 
 .menuicon {
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 9cd58c73a..4b2fdbaff 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -273,7 +273,10 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
         let color = this.props.Document.GetText(KeyStore.BackgroundColor, "");
         return (
             <div className={`formattedTextBox-cont-${style}`}
-                style={{ background: color }}
+                style={{
+                    borderRadius: `${this.props.borderRounding()}px`,
+                    background: color,
+                }}
                 onKeyDown={this.onKeyPress}
                 onKeyPress={this.onKeyPress}
                 onFocus={this.onFocused}
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index db9a08fba..96a724867 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -8,9 +8,6 @@
   max-height: 100%;
   pointer-events: none;
 }
-.imageBox-cont-interactive {
-    pointer-events: all;
-}
 
 .imageBox-dot {
     position:absolute;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index a16fd5007..b22accb74 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -165,7 +165,7 @@ export class ImageBox extends React.Component<FieldViewProps> {
         let nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 1);
         let interactive = InkingControl.Instance.selectedTool ? "" : "interactive"
         return (
-            <div className={`imageBox-cont-${interactive}`} onPointerDown={this.onPointerDown} onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
+            <div className="imageBox-cont" onPointerDown={this.onPointerDown} onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
                 <img src={paths[Math.min(paths.length, this._photoIndex)]} style={{ objectFit: (this._photoIndex === 0 ? undefined : "contain") }} width={nativeWidth} alt="Image not found" ref={this._imgRef} onLoad={this.onLoad} />
                 {paths.length > 1 ? this.dots(paths) : (null)}
                 {this.lightbox(paths)}
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index 0a96beb50..aa18a9f33 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -49,12 +49,13 @@ export namespace KeyStore {
     export const MinimizedDoc = new Key("MinimizedDoc");
     export const MaximizedDoc = new Key("MaximizedDoc");
     export const CopyDraggedItems = new Key("CopyDraggedItems");
+    export const BorderRounding = new Key("BorderRounding");
 
     export const KeyList: Key[] = [Prototype, X, Y, Page, Title, Author, PanX, PanY, Scale, NativeWidth, NativeHeight,
         Width, Height, ZIndex, ZoomBasis, 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, IsMinimized, MinimizedDoc, MaximizedDoc, CopyDraggedItems
+        Archives, Workspaces, IsMinimized, MinimizedDoc, MaximizedDoc, CopyDraggedItems, BorderRounding
     ];
     export function KeyLookup(keyid: string) {
         for (const key of KeyList) {
-- 
cgit v1.2.3-70-g09d2


From 6a62e0a16abc3bbadfb179750463e3d361f1c759 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 25 Apr 2019 09:15:22 -0400
Subject: silly me - borderRadius can be inherited

---
 src/client/views/Main.scss                                     |  1 +
 src/client/views/Main.tsx                                      |  1 -
 src/client/views/MainOverlayTextBox.tsx                        |  2 +-
 src/client/views/collections/CollectionBaseView.tsx            |  2 +-
 src/client/views/collections/CollectionDockingView.tsx         |  1 -
 src/client/views/collections/CollectionSchemaView.tsx          |  2 --
 src/client/views/collections/CollectionSubView.tsx             |  1 -
 src/client/views/collections/CollectionTreeView.tsx            |  2 +-
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx  | 10 ++++------
 .../views/collections/collectionFreeForm/MarqueeView.tsx       |  3 +--
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx      |  1 -
 src/client/views/nodes/DocumentView.tsx                        |  3 +--
 src/client/views/nodes/FieldView.tsx                           |  2 --
 src/client/views/nodes/FormattedTextBox.tsx                    |  2 +-
 14 files changed, 11 insertions(+), 22 deletions(-)

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

diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 3edc79d39..2f899ff28 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -44,6 +44,7 @@ h1 {
 .jsx-parser {
     width:100%;
     pointer-events: none;
+    border-radius: inherit;
 }
 
 p {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 057f68ed9..c6b3f06d8 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -197,7 +197,6 @@ export class Main extends React.Component {
                             isTopMost={true}
                             selectOnLoad={false}
                             focus={emptyDocFunction}
-                            borderRounding={returnZero}
                             parentActive={returnTrue}
                             whenActiveChanged={emptyFunction}
                             ContainingCollectionView={undefined} />}
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index f98f6e8a5..be8d67925 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -97,7 +97,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
                     style={{ width: `${this.TextDoc.Width()}px`, height: `${this.TextDoc.Height()}px` }}>
                     <FormattedTextBox fieldKey={this._textFieldKey} isOverlay={true} Document={this.TextDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
                         selectOnLoad={true} ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue}
-                        ScreenToLocalTransform={this._textXf} borderRounding={returnZero} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyDocFunction} />
+                        ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyDocFunction} />
                 </div>
             </ div>;
         }
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index e65770137..87b767c93 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -190,7 +190,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         const viewtype = this.collectionViewType;
         return (
             <div className={this.props.className || "collectionView-cont"}
-                style={{ borderRadius: `${this.props.borderRounding()}px`, pointerEvents: "all" }}
+                style={{ borderRadius: "inherit", pointerEvents: "all" }}
                 onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}>
                 {viewtype !== undefined ? this.props.children(viewtype, props) : (null)}
             </div>
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index eb94a4e6d..3267f2455 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -368,7 +368,6 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                     parentActive={returnTrue}
                     whenActiveChanged={emptyFunction}
                     focus={emptyDocFunction}
-                    borderRounding={returnZero}
                     ContainingCollectionView={undefined} />
             </div>);
     }
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index dc2910ede..b6d5f1bfa 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -77,7 +77,6 @@ export class CollectionSchemaView extends CollectionSubView {
             focus: emptyDocFunction,
             active: returnFalse,
             whenActiveChanged: emptyFunction,
-            borderRounding: this.props.borderRounding,
             PanelHeight: returnZero,
             PanelWidth: returnZero,
         };
@@ -278,7 +277,6 @@ export class CollectionSchemaView extends CollectionSubView {
                         focus={emptyDocFunction}
                         parentActive={this.props.active}
                         whenActiveChanged={this.props.whenActiveChanged}
-                        borderRounding={this.props.borderRounding}
                     />
                 </div>
                 <input className="collectionSchemaView-input" value={this.previewScript} onChange={this.onPreviewScriptChange}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index bf59bec95..f9fc7be5a 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -25,7 +25,6 @@ export interface CollectionViewProps extends FieldViewProps {
     moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
     PanelWidth: () => number;
     PanelHeight: () => number;
-    borderRounding: () => number;
 }
 
 export interface SubCollectionViewProps extends CollectionViewProps {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index b9d1f0bd9..905b48db7 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -137,7 +137,7 @@ export class CollectionTreeView extends CollectionSubView {
 
         return (
             <div id="body" className="collectionTreeView-dropTarget"
-                style={{ borderRadius: `${this.props.borderRounding()}px` }}
+                style={{ borderRadius: "inherit" }}
                 onWheel={(e: React.WheelEvent) => e.stopPropagation()}
                 onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
                 <div className="coll-title">
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 9e2c9bf9a..56342c84c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -257,7 +257,6 @@ export class CollectionFreeFormView extends CollectionSubView {
             focus: this.focusDocument,
             parentActive: this.props.active,
             whenActiveChanged: this.props.active,
-            borderRounding: this.props.borderRounding,
         };
     }
 
@@ -289,17 +288,17 @@ export class CollectionFreeFormView extends CollectionSubView {
         const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
         return (
             <div className={containerName} ref={this.createDropTarget} onWheel={this.onPointerWheel}
-                style={{ borderRadius: `${this.props.borderRounding()}px` }}
+                style={{ borderRadius: "inherit" }}
                 onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} >
                 {/* <svg viewBox="0 0 180 18" style={{ top: "50%", opacity: 0.05, position: "absolute" }}>
                     <text y="15" >
                         {this.props.Document.Title}
                     </text>
                 </svg> */}
-                <MarqueeView container={this} borderRadius={this.props.borderRounding} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
+                <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
                     addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox}
                     getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
-                    <CollectionFreeFormViewPannableContents borderRadius={this.props.borderRounding} centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
+                    <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
                         zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
                         <CollectionFreeFormLinksView {...this.props} key="freeformLinks">
                             <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
@@ -347,7 +346,6 @@ interface CollectionFreeFormViewPannableContentsProps {
     panX: () => number;
     panY: () => number;
     zoomScaling: () => number;
-    borderRadius: () => number;
 }
 
 @observer
@@ -358,7 +356,7 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
         const panx = -this.props.panX();
         const pany = -this.props.panY();
         const zoom = this.props.zoomScaling();// needs to be a variable outside of the <Measure> otherwise, reactions won't fire
-        return <div className="collectionfreeformview" style={{ borderRadius: `${this.props.borderRadius()}px`, transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}>
+        return <div className="collectionfreeformview" style={{ borderRadius: "inherit", transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}>
             {this.props.children}
         </div>;
     }
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index e724ab47e..b511a6e3d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -24,7 +24,6 @@ interface MarqueeViewProps {
     selectDocuments: (docs: Document[]) => void;
     removeDocument: (doc: Document) => boolean;
     addLiveTextDocument: (doc: Document) => void;
-    borderRadius: () => number;
 }
 
 @observer
@@ -251,7 +250,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     }
 
     render() {
-        return <div className="marqueeView" style={{ borderRadius: `${this.props.borderRadius()}px` }} onClick={this.onClick} onPointerDown={this.onPointerDown}>
+        return <div className="marqueeView" style={{ borderRadius: "inherit" }} onClick={this.onClick} onPointerDown={this.onPointerDown}>
             {this.props.children}
             {!this._visible ? (null) : this.marqueeDiv}
         </div>;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 8caf5ff31..4537ce3eb 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -65,7 +65,6 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
             ScreenToLocalTransform={this.getTransform}
             PanelWidth={this.panelWidth}
             PanelHeight={this.panelHeight}
-            borderRounding={this.borderRounding}
         />;
     }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index ddf99c109..cec6e6468 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -41,7 +41,6 @@ export interface DocumentViewProps {
     parentActive: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     toggleMinimized: () => void;
-    borderRounding: () => number;
 }
 export interface JsxArgs extends DocumentViewProps {
     Keys: { [name: string]: Key };
@@ -278,7 +277,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             <div className={`documentView-node${this.props.isTopMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
                 style={{
-                    borderRadius: `${this.props.borderRounding()}px`,
+                    borderRadius: "inherit",
                     background: this.props.Document.GetText(KeyStore.BackgroundColor, ""),
                     width: nativeWidth, height: nativeHeight,
                     transform: `scale(${scaling}, ${scaling})`
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 8484194b0..8f360754e 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -49,7 +49,6 @@ export interface FieldViewProps {
     focus: (doc: Document) => void;
     PanelWidth: () => number;
     PanelHeight: () => number;
-    borderRounding: () => number;
 }
 
 @observer
@@ -103,7 +102,6 @@ export class FieldView extends React.Component<FieldViewProps> {
                     layoutKey={KeyStore.Layout}
                     ContainingCollectionView={this.props.ContainingCollectionView}
                     parentActive={this.props.active}
-                    borderRounding={returnZero}
                     toggleMinimized={emptyFunction}
                     whenActiveChanged={this.props.whenActiveChanged} />
             );
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 4b2fdbaff..5b17763e7 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -274,7 +274,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
         return (
             <div className={`formattedTextBox-cont-${style}`}
                 style={{
-                    borderRadius: `${this.props.borderRounding()}px`,
+                    borderRadius: "inherit",
                     background: color,
                 }}
                 onKeyDown={this.onKeyPress}
-- 
cgit v1.2.3-70-g09d2


From 513e9042ea815e964462e824d85fbd229381250f Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sat, 27 Apr 2019 01:19:52 -0400
Subject: A bunch more stuff

---
 src/client/DocServer.ts                            |   6 +-
 .../northstar/core/brusher/IBaseBrushable.ts       |   4 +-
 src/client/northstar/core/filter/FilterModel.ts    |  17 ++-
 .../northstar/core/filter/IBaseFilterConsumer.ts   |   4 +-
 src/client/northstar/dash-fields/HistogramField.ts |   3 +-
 src/client/util/TooltipTextMenu.tsx                |   4 +-
 src/client/views/InkingStroke.tsx                  |   2 +-
 src/client/views/Main.tsx                          | 152 ++++++++++-----------
 .../views/collections/CollectionDockingView.tsx    |   8 +-
 src/client/views/collections/CollectionPDFView.tsx |  10 +-
 .../views/collections/CollectionSchemaView.tsx     |   4 +-
 .../views/collections/CollectionVideoView.tsx      |   9 +-
 .../CollectionFreeFormLinkView.tsx                 |  20 +--
 .../CollectionFreeFormLinksView.tsx                |  63 +++++----
 .../CollectionFreeFormRemoteCursors.tsx            |   1 -
 src/client/views/nodes/IconBox.tsx                 |  12 +-
 16 files changed, 165 insertions(+), 154 deletions(-)

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

diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 02fd28a86..3f17baec6 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,5 +1,5 @@
 import * as OpenSocket from 'socket.io-client';
-import { MessageStore, Types } from "./../server/Message";
+import { MessageStore, Types, Message } from "./../server/Message";
 import { Opt, FieldWaiting, RefField, HandleUpdate } from '../new_fields/Doc';
 import { Utils } from '../Utils';
 import { SerializationHelper } from './util/SerializationHelper';
@@ -13,6 +13,10 @@ export namespace DocServer {
         return window.location.origin + extension;
     }
 
+    export function DeleteDatabase() {
+        Utils.Emit(_socket, MessageStore.DeleteAll, {});
+    }
+
     export async function GetRefField(id: string): Promise<Opt<RefField>> {
         let cached = _cache[id];
         if (cached === undefined) {
diff --git a/src/client/northstar/core/brusher/IBaseBrushable.ts b/src/client/northstar/core/brusher/IBaseBrushable.ts
index c46db4d22..87f4ba413 100644
--- a/src/client/northstar/core/brusher/IBaseBrushable.ts
+++ b/src/client/northstar/core/brusher/IBaseBrushable.ts
@@ -1,9 +1,9 @@
 import { PIXIPoint } from '../../utils/MathUtil';
 import { IEquatable } from '../../utils/IEquatable';
-import { Document } from '../../../../fields/Document';
+import { Doc } from '../../../../new_fields/Doc';
 
 export interface IBaseBrushable<T> extends IEquatable {
-    BrusherModels: Array<Document>;
+    BrusherModels: Array<Doc>;
     BrushColors: Array<number>;
     Position: PIXIPoint;
     Size: PIXIPoint;
diff --git a/src/client/northstar/core/filter/FilterModel.ts b/src/client/northstar/core/filter/FilterModel.ts
index e2ba3f652..6ab96b33d 100644
--- a/src/client/northstar/core/filter/FilterModel.ts
+++ b/src/client/northstar/core/filter/FilterModel.ts
@@ -2,10 +2,9 @@ import { ValueComparison } from "./ValueComparision";
 import { Utils } from "../../utils/Utils";
 import { IBaseFilterProvider } from "./IBaseFilterProvider";
 import { FilterOperand } from "./FilterOperand";
-import { KeyStore } from "../../../../fields/KeyStore";
-import { FieldWaiting } from "../../../../fields/Field";
-import { Document } from "../../../../fields/Document";
 import { HistogramField } from "../../dash-fields/HistogramField";
+import { Cast, FieldValue } from "../../../../new_fields/Types";
+import { Doc } from "../../../../new_fields/Doc";
 
 export class FilterModel {
     public ValueComparisons: ValueComparison[];
@@ -52,12 +51,12 @@ export class FilterModel {
             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) {
-                    let filterHistogram = filterDoc.GetT(KeyStore.Data, HistogramField);
-                    if (filterHistogram && filterHistogram !== FieldWaiting) {
-                        if (!visitedFilterProviders.has(filterHistogram.Data)) {
-                            let child = FilterModel.GetFilterModelsRecursive(filterHistogram.Data, visitedFilterProviders, filterModels, false);
+                let filterDoc = FieldValue(Cast(linkVm.linkedFrom, Doc));
+                if (filterDoc) {
+                    let filterHistogram = Cast(filterDoc.data, HistogramField);
+                    if (filterHistogram) {
+                        if (!visitedFilterProviders.has(filterHistogram.HistoOp)) {
+                            let child = FilterModel.GetFilterModelsRecursive(filterHistogram.HistoOp, visitedFilterProviders, filterModels, false);
                             if (child !== "") {
                                 // if (linkVm.IsInverted) {
                                 //     child = "! " + child;
diff --git a/src/client/northstar/core/filter/IBaseFilterConsumer.ts b/src/client/northstar/core/filter/IBaseFilterConsumer.ts
index 59d7adf4c..e7549d113 100644
--- a/src/client/northstar/core/filter/IBaseFilterConsumer.ts
+++ b/src/client/northstar/core/filter/IBaseFilterConsumer.ts
@@ -1,10 +1,10 @@
 import { FilterOperand } from '../filter/FilterOperand';
 import { IEquatable } from '../../utils/IEquatable';
-import { Document } from "../../../../fields/Document";
+import { Doc } from '../../../../new_fields/Doc';
 
 export interface IBaseFilterConsumer extends IEquatable {
     FilterOperand: FilterOperand;
-    Links: Document[];
+    Links: Doc[];
 }
 
 export function instanceOfIBaseFilterConsumer(object: any): object is IBaseFilterConsumer {
diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts
index baeaca1dd..118f4cf7f 100644
--- a/src/client/northstar/dash-fields/HistogramField.ts
+++ b/src/client/northstar/dash-fields/HistogramField.ts
@@ -7,8 +7,9 @@ import { ObjectField } from "../../../new_fields/Doc";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { OmitKeys } from "../../../Utils";
 import { Deserializable } from "../../util/SerializationHelper";
+
 function serialize(field: HistogramField) {
-    return OmitKeys(field.HistoOp, ['Links', 'BrushLinks', 'Result', 'BrushColors', 'FilterModels', 'FilterOperand']).omit
+    return OmitKeys(field.HistoOp, ['Links', 'BrushLinks', 'Result', 'BrushColors', 'FilterModels', 'FilterOperand']).omit;
 }
 
 function deserialize(jp: any) {
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 4f0eb7d63..1b6647003 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -35,8 +35,8 @@ export class TooltipTextMenu {
     private fontStylesToName: Map<MarkType, string>;
     private fontSizeIndicator: HTMLSpanElement = document.createElement("span");
     //dropdown doms
-    private fontSizeDom: Node;
-    private fontStyleDom: Node;
+    private fontSizeDom?: Node;
+    private fontStyleDom?: Node;
 
     constructor(view: EditorView, editorProps: FieldViewProps) {
         this.view = view;
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 0f05da22c..616299146 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -1,8 +1,8 @@
 import { observer } from "mobx-react";
 import { observable } from "mobx";
 import { InkingControl } from "./InkingControl";
-import { InkTool } from "../../fields/InkField";
 import React = require("react");
+import { InkTool } from "../../new_fields/InkField";
 
 
 interface StrokeProps {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index c6b3f06d8..1e3d4e259 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -8,17 +8,12 @@ import * as React from 'react';
 import * as ReactDOM from 'react-dom';
 import Measure from 'react-measure';
 import * as request from 'request';
-import { Document } from '../../fields/Document';
-import { Field, FieldWaiting, Opt, FIELD_WAITING } from '../../fields/Field';
-import { KeyStore } from '../../fields/KeyStore';
-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 { RouteStore } from '../../server/RouteStore';
-import { ServerUtils } from '../../server/ServerUtil';
-import { emptyDocFunction, emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils';
-import { Documents } from '../documents/Documents';
+import { emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils';
+import { Docs } from '../documents/Documents';
 import { ColumnAttributeModel } from '../northstar/core/attribute/AttributeModel';
 import { AttributeTransformationModel } from '../northstar/core/attribute/AttributeTransformationModel';
 import { Gateway, NorthstarSettings } from '../northstar/manager/Gateway';
@@ -26,7 +21,6 @@ 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 { Transform } from '../util/Transform';
 import { UndoManager } from '../util/UndoManager';
@@ -39,6 +33,10 @@ import { MainOverlayTextBox } from './MainOverlayTextBox';
 import { DocumentView } from './nodes/DocumentView';
 import { PreviewCursor } from './PreviewCursor';
 import { SelectionManager } from '../util/SelectionManager';
+import { FieldResult, Field, Doc, Id, Opt } from '../../new_fields/Doc';
+import { Cast, FieldValue, StrCast } from '../../new_fields/Types';
+import { DocServer } from '../DocServer';
+import { listSpec } from '../../new_fields/Schema';
 
 
 @observer
@@ -48,11 +46,13 @@ export class Main extends React.Component {
     @observable public pwidth: number = 0;
     @observable public pheight: number = 0;
 
-    @computed private get mainContainer(): Document | undefined | FIELD_WAITING {
-        return CurrentUserUtils.UserDocument.GetT(KeyStore.ActiveWorkspace, Document);
+    @computed private get mainContainer(): Opt<Doc> {
+        return FieldValue(Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc));
     }
-    private set mainContainer(doc: Document | undefined | FIELD_WAITING) {
-        doc && CurrentUserUtils.UserDocument.Set(KeyStore.ActiveWorkspace, doc);
+    private set mainContainer(doc: Opt<Doc>) {
+        if (doc) {
+            CurrentUserUtils.UserDocument.activeWorkspace = doc;
+        }
     }
 
     constructor(props: Readonly<{}>) {
@@ -100,8 +100,8 @@ export class Main extends React.Component {
         if (window.location.pathname !== RouteStore.home) {
             let pathname = window.location.pathname.split("/");
             CurrentUserUtils.MainDocId = pathname[pathname.length - 1];
-            Server.GetField(CurrentUserUtils.MainDocId, action((field: Opt<Field>) => {
-                if (field instanceof Document) {
+            DocServer.GetRefField(CurrentUserUtils.MainDocId).then(action((field: Opt<Field>) => {
+                if (field instanceof Doc) {
                     this.openWorkspace(field, true);
                 }
             }));
@@ -113,9 +113,9 @@ export class Main extends React.Component {
         window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler
         window.addEventListener("dragover", (e) => e.preventDefault(), false); // drag event handler
         window.addEventListener("keydown", (e) => {
-            if (e.key == "Escape") {
+            if (e.key === "Escape") {
                 DragManager.AbortDrag();
-                SelectionManager.DeselectAll()
+                SelectionManager.DeselectAll();
             }
         }, false); // drag event handler
         // click interactions for the context menu
@@ -126,54 +126,55 @@ export class Main extends React.Component {
         }), true);
     }
 
-    initAuthenticationRouters = () => {
+    initAuthenticationRouters = async () => {
         // Load the user's active workspace, or create a new one if initial session after signup
         if (!CurrentUserUtils.MainDocId) {
-            CurrentUserUtils.UserDocument.GetTAsync(KeyStore.ActiveWorkspace, Document).then(doc => {
-                if (doc) {
-                    CurrentUserUtils.MainDocId = doc.Id;
-                    this.openWorkspace(doc);
-                } else {
-                    this.createNewWorkspace();
-                }
-            });
+            const doc = await Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc);
+            if (doc) {
+                CurrentUserUtils.MainDocId = doc[Id];
+                this.openWorkspace(doc);
+            } else {
+                this.createNewWorkspace();
+            }
         } else {
-            Server.GetField(CurrentUserUtils.MainDocId).then(field =>
-                field instanceof Document ? this.openWorkspace(field) :
+            DocServer.GetRefField(CurrentUserUtils.MainDocId).then(field =>
+                field instanceof Doc ? this.openWorkspace(field) :
                     this.createNewWorkspace(CurrentUserUtils.MainDocId));
         }
     }
 
     @action
-    createNewWorkspace = (id?: string): void => {
-        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)] }] };
-                let mainDoc = Documents.DockDocument(JSON.stringify(dockingLayout), { title: `Main Container ${list.Data.length + 1}` }, id);
-                list.Data.push(mainDoc);
-                CurrentUserUtils.MainDocId = mainDoc.Id;
-                // 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" });
-                    mainDoc.Set(KeyStore.OptionalRightCollection, pendingDocument);
-                }, 0);
-            }
-        }));
+    createNewWorkspace = async (id?: string) => {
+        const list = Cast(CurrentUserUtils.UserDocument.workspaces, listSpec(Doc));
+        if (list) {
+            let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, title: "mini collection" });
+            var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc)] }] };
+            let mainDoc = Docs.DockDocument(JSON.stringify(dockingLayout), { title: `Main Container ${list.length + 1}` }, id);
+            list.push(mainDoc);
+            CurrentUserUtils.MainDocId = mainDoc[Id];
+            // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
+            setTimeout(() => {
+                this.openWorkspace(mainDoc);
+                let pendingDocument = Docs.SchemaDocument([], { title: "New Mobile Uploads" });
+                mainDoc.optionalRightCollection = pendingDocument;
+            }, 0);
+        }
     }
 
     @action
-    openWorkspace = (doc: Document, fromHistory = false): void => {
+    openWorkspace = async (doc: Doc, fromHistory = false) => {
         this.mainContainer = doc;
-        fromHistory || window.history.pushState(null, doc.Title, "/doc/" + doc.Id);
-        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(() =>
-                col && col.GetTAsync<ListField<Document>>(KeyStore.Data, ListField, (f: Opt<ListField<Document>>) =>
-                    f && f.Data.length > 0 && CollectionDockingView.Instance.AddRightSplit(col))
-                , 100)
-        );
+        fromHistory || window.history.pushState(null, StrCast(doc.title), "/doc/" + doc.Id);
+        const col = await Cast(CurrentUserUtils.UserDocument.optionalRightCollection, Doc);
+        // 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(async () => {
+            if (col) {
+                const l = Cast(col.data, listSpec(Doc));
+                if (l && l.length > 0) {
+                    CollectionDockingView.Instance.AddRightSplit(col);
+                }
+            }
+        }, 100);
     }
 
     @computed
@@ -196,7 +197,7 @@ export class Main extends React.Component {
                             PanelHeight={pheightFunc}
                             isTopMost={true}
                             selectOnLoad={false}
-                            focus={emptyDocFunction}
+                            focus={emptyFunction}
                             parentActive={returnTrue}
                             whenActiveChanged={emptyFunction}
                             ContainingCollectionView={undefined} />}
@@ -214,17 +215,17 @@ export class Main extends React.Component {
         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 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 }));
-        let addVideoNode = action(() => Documents.VideoDocument(videourl, { width: 200, title: "video node" }));
-        let addPDFNode = action(() => Documents.PdfDocument(pdfurl, { width: 200, height: 200, title: "a pdf doc" }));
-        let addImageNode = action(() => Documents.ImageDocument(imgurl, { width: 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 addTextNode = action(() => Docs.TextDocument({ width: 200, height: 200, title: "a text note" }));
+        let addColNode = action(() => Docs.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" }));
+        let addSchemaNode = action(() => Docs.SchemaDocument([], { width: 200, height: 200, title: "a schema collection" }));
+        let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", copyDraggedItems: true }));
+        let addVideoNode = action(() => Docs.VideoDocument(videourl, { width: 200, title: "video node" }));
+        let addPDFNode = action(() => Docs.PdfDocument(pdfurl, { width: 200, height: 200, title: "a pdf doc" }));
+        let addImageNode = action(() => Docs.ImageDocument(imgurl, { width: 200, title: "an image of a cat" }));
+        let addWebNode = action(() => Docs.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" }));
+        let addAudioNode = action(() => Docs.AudioDocument(audiourl, { width: 200, height: 200, title: "audio node" }));
 
-        let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Document][] = [
+        let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc][] = [
             [React.createRef<HTMLDivElement>(), "font", "Add Textbox", addTextNode],
             [React.createRef<HTMLDivElement>(), "image", "Add Image", addImageNode],
             [React.createRef<HTMLDivElement>(), "file-pdf", "Add PDF", addPDFNode],
@@ -260,9 +261,8 @@ export class Main extends React.Component {
         let logoutRef = React.createRef<HTMLDivElement>();
         let toggleWorkspaces = () => runInAction(() => this._workspacesShown = !this._workspacesShown);
 
-        let clearDatabase = action(() => Utils.Emit(Server.Socket, MessageStore.DeleteAll, {}));
         return [
-            <button className="clear-db-button" key="clear-db" onClick={clearDatabase}>Clear Database</button>,
+            <button className="clear-db-button" key="clear-db" onClick={DocServer.DeleteDatabase}>Clear Database</button>,
             <div id="toolbar" key="toolbar">
                 <button className="toolbar-button round-button" title="Undo" onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button>
                 <button className="toolbar-button round-button" title="Redo" onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button>
@@ -271,7 +271,7 @@ export class Main extends React.Component {
             <div className="main-buttonDiv" key="workspaces" style={{ top: '34px', left: '2px', position: 'absolute' }} ref={workspacesRef}>
                 <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>
+                <button onClick={() => request.get(DocServer.prepend(RouteStore.logout), emptyFunction)}>Log Out</button></div>
         ];
     }
 
@@ -279,10 +279,10 @@ export class Main extends React.Component {
     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 || this.mainContainer === FieldWaiting) ? (null) :
+        let workspaces = Cast(CurrentUserUtils.UserDocument.workspaces, listSpec(Doc));
+        return (!workspaces || !this.mainContainer) ? (null) :
             <WorkspacesMenu active={this.mainContainer} open={this.openWorkspace}
-                new={this.createNewWorkspace} allWorkspaces={workspaces.Data}
+                new={this.createNewWorkspace} allWorkspaces={workspaces}
                 isShown={areWorkspacesShown} toggle={toggleWorkspaces} />;
     }
 
@@ -303,17 +303,17 @@ export class Main extends React.Component {
     }
 
     // --------------- Northstar hooks ------------- /
-    private _northstarSchemas: Document[] = [];
+    private _northstarSchemas: Doc[] = [];
 
     @action SetNorthstarCatalog(ctlog: Catalog) {
         CurrentUserUtils.NorthstarDBCatalog = ctlog;
         if (ctlog && ctlog.schemas) {
             ctlog.schemas.map(schema => {
-                let schemaDocuments: Document[] = [];
+                let schemaDocuments: Doc[] = [];
                 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) {
+                    promises.push(DocServer.GetRefField(attr.displayName! + ".alias").then(action((field: Opt<Field>) => {
+                        if (field instanceof Doc) {
                             schemaDocuments.push(field);
                         } else {
                             var atmod = new ColumnAttributeModel(attr);
@@ -321,12 +321,12 @@ export class Main extends React.Component {
                                 new AttributeTransformationModel(atmod, AggregateFunction.None),
                                 new AttributeTransformationModel(atmod, AggregateFunction.Count),
                                 new AttributeTransformationModel(atmod, AggregateFunction.Count));
-                            schemaDocuments.push(Documents.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! }, undefined, attr.displayName! + ".alias"));
+                            schemaDocuments.push(Docs.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! }));
                         }
                     })));
                     return promises;
                 }, [] as Promise<void>[])).finally(() =>
-                    this._northstarSchemas.push(Documents.TreeDocument(schemaDocuments, { width: 50, height: 100, title: schema.displayName! })));
+                    this._northstarSchemas.push(Docs.TreeDocument(schemaDocuments, { width: 50, height: 100, title: schema.displayName! })));
             });
         }
     }
@@ -338,7 +338,7 @@ export class Main extends React.Component {
 }
 
 (async () => {
-    await Documents.initProtos();
+    await Docs.initProtos();
     await CurrentUserUtils.loadCurrentUser();
     ReactDOM.render(<Main />, document.getElementById('root'));
 })();
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 69401ceeb..2ff409b9b 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -6,13 +6,11 @@ import { observer } from "mobx-react";
 import * as ReactDOM from 'react-dom';
 import Measure from "react-measure";
 import { Utils, returnTrue, emptyFunction, returnOne, returnZero } from "../../../Utils";
-import { Server } from "../../Server";
 import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { DocumentView } from "../nodes/DocumentView";
 import "./CollectionDockingView.scss";
 import React = require("react");
 import { SubCollectionViewProps } from "./CollectionSubView";
-import { ServerUtils } from "../../../server/ServerUtil";
 import { DragManager, DragLinksAsDocuments } from "../../util/DragManager";
 import { Transform } from '../../util/Transform';
 import { Doc, Id, Opt, Field, FieldId } from "../../../new_fields/Doc";
@@ -206,7 +204,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             let y = e.clientY;
             let docid = (e.target as any).DashDocId;
             let tab = (e.target as any).parentElement as HTMLElement;
-            Server.GetField(docid, action(async (sourceDoc: Opt<Field>) =>
+            DocServer.GetRefField(docid).then(action(async (sourceDoc: Opt<Field>) =>
                 (sourceDoc instanceof Doc) && DragLinksAsDocuments(tab, x, y, sourceDoc)));
         } else
             if ((className === "lm_title" || className === "lm_tab lm_active") && !e.shiftKey) {
@@ -216,7 +214,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                 let y = e.clientY;
                 let docid = (e.target as any).DashDocId;
                 let tab = (e.target as any).parentElement as HTMLElement;
-                Server.GetField(docid, action((f: Opt<Field>) => {
+                DocServer.GetRefField(docid).then(action((f: Opt<Field>) => {
                     if (f instanceof Doc) {
                         DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f]), x, y,
                             {
@@ -301,7 +299,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         stack.header.controlsContainer.find('.lm_popout') //get the close icon
             .off('click') //unbind the current click handler
             .click(action(function () {
-                var url = ServerUtils.prepend("/doc/" + stack.contentItems[0].tab.contentItem.config.props.documentId);
+                var url = DocServer.prepend("/doc/" + stack.contentItems[0].tab.contentItem.config.props.documentId);
                 let win = window.open(url, stack.contentItems[0].tab.title, "width=300,height=400");
             }));
     }
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 229bc4059..e73b7b4a6 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -1,6 +1,5 @@
 import { action } from "mobx";
 import { observer } from "mobx-react";
-import { KeyStore } from "../../../fields/KeyStore";
 import { ContextMenu } from "../ContextMenu";
 import "./CollectionPDFView.scss";
 import React = require("react");
@@ -8,6 +7,7 @@ import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormV
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from "./CollectionBaseView";
 import { emptyFunction } from "../../../Utils";
+import { NumCast } from "../../../new_fields/Types";
 
 
 @observer
@@ -17,10 +17,10 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
         return FieldView.LayoutString(CollectionPDFView, fieldKey);
     }
 
-    private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, -1); }
-    private get numPages() { return this.props.Document.GetNumber(KeyStore.NumPages, 0); }
-    @action onPageBack = () => this.curPage > 1 ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage - 1) : -1;
-    @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : -1;
+    private get curPage() { return NumCast(this.props.Document.curPage, -1); }
+    private get numPages() { return NumCast(this.props.Document.numPages); }
+    @action onPageBack = () => this.curPage > 1 ? (this.props.Document.curPage = this.curPage - 1) : -1;
+    @action onPageForward = () => this.curPage < this.numPages ? (this.props.Document.curPage = this.curPage + 1) : -1;
 
     private get uIButtons() {
         let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale);
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 2e1175f28..874170f3d 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -5,7 +5,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable, untracked } from "mobx";
 import { observer } from "mobx-react";
 import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
-import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss'
+import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss';
 import "react-table/react-table.css";
 import { emptyFunction, returnFalse, returnZero } from "../../../Utils";
 import { SetupDrag } from "../../util/DragManager";
@@ -283,7 +283,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
         //  then by the time the options button is clicked, all of the fields should be in place.  If a new field is added while this menu
         //  is displayed (unlikely) it won't show up until something else changes.
         //TODO Types
-        untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => proto._proxies.forEach((val: any, key: string) => keys[key] = false))));
+        untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false))));
 
         this.columns.forEach(key => keys[key] = true);
         return Array.from(Object.keys(keys)).map(item =>
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 779dc8fc3..d314e3fc0 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -1,6 +1,5 @@
 import { action, observable, trace } from "mobx";
 import { observer } from "mobx-react";
-import { KeyStore } from "../../../fields/KeyStore";
 import { ContextMenu } from "../ContextMenu";
 import { CollectionViewType, CollectionBaseView, CollectionRenderProps } from "./CollectionBaseView";
 import React = require("react");
@@ -8,6 +7,7 @@ import "./CollectionVideoView.scss";
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import { emptyFunction } from "../../../Utils";
+import { NumCast } from "../../../new_fields/Types";
 
 
 @observer
@@ -44,8 +44,9 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
         if (ele) {
             this._player = ele.getElementsByTagName("video")[0];
             console.log(this._player);
-            if (this.props.Document.GetNumber(KeyStore.CurPage, -1) >= 0) {
-                this._currentTimecode = this.props.Document.GetNumber(KeyStore.CurPage, -1);
+            const curPage = NumCast(this.props.Document.curPage, -1);
+            if (curPage >= 0) {
+                this._currentTimecode = curPage;
             }
         }
     }
@@ -69,7 +70,7 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
                 (this._player as any).AHackBecauseSomethingResetsTheVideoToZero = -1;
             } else {
                 this._currentTimecode = this._player.currentTime;
-                this.props.Document.SetNumber(KeyStore.CurPage, Math.round(this._currentTimecode));
+                this.props.Document.curPage = Math.round(this._currentTimecode);
             }
         }
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 20c5a84bf..d4987fc18 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -1,15 +1,15 @@
 import { observer } from "mobx-react";
-import { Document } from "../../../../fields/Document";
-import { KeyStore } from "../../../../fields/KeyStore";
 import { Utils } from "../../../../Utils";
 import "./CollectionFreeFormLinkView.scss";
 import React = require("react");
 import v5 = require("uuid/v5");
+import { StrCast, NumCast, BoolCast } from "../../../../new_fields/Types";
+import { Doc } from "../../../../new_fields/Doc";
 
 export interface CollectionFreeFormLinkViewProps {
-    A: Document;
-    B: Document;
-    LinkDocs: Document[];
+    A: Doc;
+    B: Doc;
+    LinkDocs: Doc[];
 }
 
 @observer
@@ -17,16 +17,16 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
 
     onPointerDown = (e: React.PointerEvent) => {
         this.props.LinkDocs.map(l =>
-            console.log("Link:" + l.Title));
+            console.log("Link:" + StrCast(l.title)));
     }
     render() {
         let l = this.props.LinkDocs;
         let a = this.props.A;
         let b = this.props.B;
-        let x1 = a.GetNumber(KeyStore.X, 0) + (a.GetBoolean(KeyStore.IsMinimized, false) ? 5 : a.Width() / 2);
-        let y1 = a.GetNumber(KeyStore.Y, 0) + (a.GetBoolean(KeyStore.IsMinimized, false) ? 5 : a.Height() / 2);
-        let x2 = b.GetNumber(KeyStore.X, 0) + (b.GetBoolean(KeyStore.IsMinimized, false) ? 5 : b.Width() / 2);
-        let y2 = b.GetNumber(KeyStore.Y, 0) + (b.GetBoolean(KeyStore.IsMinimized, false) ? 5 : b.Height() / 2);
+        let x1 = NumCast(a.x) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.width) / 2);
+        let y1 = NumCast(a.y) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.height) / 2);
+        let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.width) / 2);
+        let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.height) / 2);
         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.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index ebdb0c75c..ce9995630 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,8 +1,5 @@
 import { computed, IReactionDisposer, reaction } from "mobx";
 import { observer } from "mobx-react";
-import { Document } from "../../../../fields/Document";
-import { KeyStore } from "../../../../fields/KeyStore";
-import { ListField } from "../../../../fields/ListField";
 import { Utils } from "../../../../Utils";
 import { DocumentManager } from "../../../util/DocumentManager";
 import { DocumentView } from "../../nodes/DocumentView";
@@ -10,54 +7,66 @@ import { CollectionViewProps } from "../CollectionSubView";
 import "./CollectionFreeFormLinksView.scss";
 import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
 import React = require("react");
+import { Doc, Id } from "../../../../new_fields/Doc";
+import { Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types";
+import { listSpec } from "../../../../new_fields/Schema";
+import { List } from "../../../../new_fields/List";
 
 @observer
 export class CollectionFreeFormLinksView extends React.Component<CollectionViewProps> {
 
     _brushReactionDisposer?: IReactionDisposer;
     componentDidMount() {
-        this._brushReactionDisposer = reaction(() => this.props.Document.GetList(this.props.fieldKey, [] as Document[]).map(doc => doc.GetNumber(KeyStore.X, 0)),
+        this._brushReactionDisposer = reaction(() => Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).map(doc => NumCast(doc.x)),
             () => {
-                let views = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc.GetText(KeyStore.BackgroundLayout, "").indexOf("istogram") !== -1);
+                let views = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).filter(doc => StrCast(doc.backgroundLayout, "").indexOf("istogram") !== -1);
                 for (let i = 0; i < views.length; i++) {
                     for (let j = 0; j < views.length; j++) {
                         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);
-                        let x2w = dstDoc.GetNumber(KeyStore.Width, -1);
+                        let x1 = NumCast(srcDoc.x);
+                        let x1w = NumCast(srcDoc.width, -1);
+                        let x2 = NumCast(dstDoc.x);
+                        let x2w = NumCast(dstDoc.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[]) : [];
+                        let findBrush = (field: List<Doc>) => field.findIndex(brush => {
+                            let bdocs = brush ? Cast(brush.brushingDocs, listSpec(Doc), []) : [];
                             return (bdocs.length && ((bdocs[0] === dstTarg && bdocs[1] === srcTarg)) ? true : false);
                         });
-                        let brushAction = (field: ListField<Document>) => {
+                        let brushAction = (field: List<Doc>) => {
                             let found = findBrush(field);
                             if (found !== -1) {
                                 console.log("REMOVE BRUSH " + srcTarg.Title + " " + dstTarg.Title);
-                                field.Data.splice(found, 1);
+                                field.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);
+                            let linkDoc: Doc = new Doc();
+                            linkDoc.title = "Histogram Brush";
+                            linkDoc.linkDescription = "Brush between " + StrCast(srcTarg.title) + " and " + StrCast(dstTarg.Title);
+                            linkDoc.brushingDocs = new List([dstTarg, srcTarg]);
 
-                            brushAction = (field: ListField<Document>) => {
+                            brushAction = (field: List<Doc>) => {
                                 if (findBrush(field) === -1) {
                                     console.log("ADD BRUSH " + srcTarg.Title + " " + dstTarg.Title);
-                                    (findBrush(field) === -1) && field.Data.push(linkDoc);
+                                    (findBrush(field) === -1) && field.push(linkDoc);
                                 }
                             };
                         }
-                        dstTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction);
-                        srcTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction);
+                        let dstBrushDocs = Cast(dstTarg.brushingDocs, listSpec(Doc));
+                        if (dstBrushDocs === undefined) {
+                            dstTarg.brushingDocs = dstBrushDocs = new List();
+                        }
+                        let srcBrushDocs = Cast(srcTarg.brushingDocs, listSpec(Doc));
+                        if (srcBrushDocs === undefined) {
+                            srcTarg.brushingDocs = srcBrushDocs = new List();
+                        }
+                        brushAction(dstBrushDocs);
+                        brushAction(srcBrushDocs);
 
                     }
                 }
@@ -70,9 +79,9 @@ 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()!);
+        let containerDoc = FieldValue(Cast(view.props.Document.annotationOn, Doc));
+        if (containerDoc) {
+            equalViews = DocumentManager.Instance.getDocumentViews(containerDoc.proto!);
         }
         return equalViews.filter(sv => sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === this.props.Document);
     }
@@ -82,12 +91,12 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
         let connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => {
             let srcViews = this.documentAnchors(connection.a);
             let targetViews = this.documentAnchors(connection.b);
-            let possiblePairs: { a: Document, b: Document, }[] = [];
+            let possiblePairs: { a: Doc, b: Doc, }[] = [];
             srcViews.map(sv => targetViews.map(tv => possiblePairs.push({ a: sv.props.Document, b: tv.props.Document })));
             possiblePairs.map(possiblePair =>
                 drawnPairs.reduce((found, drawnPair) => {
                     let match = (possiblePair.a === drawnPair.a && possiblePair.b === drawnPair.b);
-                    if (match && !drawnPair.l.reduce((found, link) => found || link.Id === connection.l.Id, false)) {
+                    if (match && !drawnPair.l.reduce((found, link) => found || link[Id] === connection.l[Id], false)) {
                         drawnPair.l.push(connection.l);
                     }
                     return match || found;
@@ -96,7 +105,7 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
                 drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] })
             );
             return drawnPairs;
-        }, [] as { a: Document, b: Document, l: Document[] }[]);
+        }, [] as { a: Doc, b: Doc, l: Doc[] }[]);
         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 cf0a6de00..036745eca 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -1,6 +1,5 @@
 import { computed } from "mobx";
 import { observer } from "mobx-react";
-import { KeyStore } from "../../../../fields/KeyStore";
 import { CollectionViewProps, CursorEntry } from "../CollectionSubView";
 import "./CollectionFreeFormView.scss";
 import React = require("react");
diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
index 9c90c0a0e..f7cceb3d4 100644
--- a/src/client/views/nodes/IconBox.tsx
+++ b/src/client/views/nodes/IconBox.tsx
@@ -4,12 +4,12 @@ import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } fr
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed } from "mobx";
 import { observer } from "mobx-react";
-import { Document } from '../../../fields/Document';
-import { IconField } from "../../../fields/IconFIeld";
-import { KeyStore } from "../../../fields/KeyStore";
 import { SelectionManager } from "../../util/SelectionManager";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./IconBox.scss";
+import { Cast } from "../../../new_fields/Types";
+import { Doc } from "../../../new_fields/Doc";
+import { IconField } from "../../../new_fields/IconField";
 
 
 library.add(faCaretUp);
@@ -22,8 +22,8 @@ library.add(faFilm);
 export class IconBox extends React.Component<FieldViewProps> {
     public static LayoutString() { return FieldView.LayoutString(IconBox); }
 
-    @computed get maximized() { return this.props.Document.GetT(KeyStore.MaximizedDoc, Document); }
-    @computed get layout(): string { return this.props.Document.GetData(this.props.fieldKey, IconField, "<p>Error loading layout data</p>" as string); }
+    @computed get maximized() { return Cast(this.props.Document.maximizedDoc, Doc); }
+    @computed get layout(): string { const field = Cast(this.props.Document[this.props.fieldKey], IconField); return field ? field.layout : "<p>Error loading layout data</p>"; }
     @computed get minimizedIcon() { return IconBox.DocumentIcon(this.layout); }
 
     public static DocumentIcon(layout: string) {
@@ -33,7 +33,7 @@ export class IconBox extends React.Component<FieldViewProps> {
                     layout.indexOf("Video") !== -1 ? faFilm :
                         layout.indexOf("Collection") !== -1 ? faObjectGroup :
                             faCaretUp;
-        return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />
+        return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />;
     }
 
     render() {
-- 
cgit v1.2.3-70-g09d2


From d4a77dd055685dd81a762ef40e0c3b7606586e9c Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sat, 27 Apr 2019 21:40:17 -0400
Subject: Split more files up

---
 src/client/DocServer.ts                            |  3 +-
 src/client/northstar/dash-fields/HistogramField.ts |  2 +-
 src/client/northstar/dash-nodes/HistogramBox.tsx   |  3 +-
 src/client/util/Scripting.ts                       |  1 +
 src/client/views/Main.tsx                          |  3 +-
 .../views/collections/CollectionBaseView.tsx       |  3 +-
 .../views/collections/CollectionDockingView.tsx    |  3 +-
 .../views/collections/CollectionSchemaView.tsx     |  3 +-
 src/client/views/collections/CollectionSubView.tsx |  3 +-
 .../views/collections/CollectionTreeView.tsx       |  3 +-
 .../CollectionFreeFormLinksView.tsx                |  3 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  3 +-
 src/client/views/nodes/DocumentView.tsx            |  3 +-
 src/client/views/nodes/FieldView.tsx               |  1 +
 src/client/views/nodes/LinkMenu.tsx                |  3 +-
 src/new_fields/Doc.ts                              | 37 +++-------------------
 src/new_fields/HtmlField.ts                        |  2 +-
 src/new_fields/IconField.ts                        |  2 +-
 src/new_fields/InkField.ts                         |  2 +-
 src/new_fields/List.ts                             |  3 +-
 src/new_fields/ObjectField.ts                      | 17 ++++++++++
 src/new_fields/Proxy.ts                            |  4 ++-
 src/new_fields/RefField.ts                         | 18 +++++++++++
 src/new_fields/RichTextField.ts                    |  2 +-
 src/new_fields/Schema.ts                           |  8 ++---
 src/new_fields/Types.ts                            |  2 +-
 src/new_fields/URLField.ts                         |  2 +-
 src/new_fields/util.ts                             |  4 ++-
 .../authentication/controllers/WorkspacesMenu.tsx  |  3 +-
 src/server/database.ts                             |  2 ++
 30 files changed, 88 insertions(+), 60 deletions(-)
 create mode 100644 src/new_fields/ObjectField.ts
 create mode 100644 src/new_fields/RefField.ts

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

diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 3f17baec6..c7cbfce37 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,8 +1,9 @@
 import * as OpenSocket from 'socket.io-client';
 import { MessageStore, Types, Message } from "./../server/Message";
-import { Opt, FieldWaiting, RefField, HandleUpdate } from '../new_fields/Doc';
+import { Opt, FieldWaiting } from '../new_fields/Doc';
 import { Utils } from '../Utils';
 import { SerializationHelper } from './util/SerializationHelper';
+import { RefField, HandleUpdate } from '../new_fields/RefField';
 
 export namespace DocServer {
     const _cache: { [id: string]: RefField | Promise<Opt<RefField>> } = {};
diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts
index 118f4cf7f..730289536 100644
--- a/src/client/northstar/dash-fields/HistogramField.ts
+++ b/src/client/northstar/dash-fields/HistogramField.ts
@@ -3,7 +3,7 @@ import { custom, serializable } from "serializr";
 import { ColumnAttributeModel } from "../../../client/northstar/core/attribute/AttributeModel";
 import { AttributeTransformationModel } from "../../../client/northstar/core/attribute/AttributeTransformationModel";
 import { HistogramOperation } from "../../../client/northstar/operations/HistogramOperation";
-import { ObjectField } from "../../../new_fields/Doc";
+import { ObjectField } from "../../../new_fields/ObjectField";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { OmitKeys } from "../../../Utils";
 import { Deserializable } from "../../util/SerializationHelper";
diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index 4a65b14cf..a9c68ccba 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -20,7 +20,8 @@ import { HistogramLabelPrimitives } from "./HistogramLabelPrimitives";
 import { StyleConstants } from "../utils/StyleContants";
 import { NumCast, Cast } from "../../../new_fields/Types";
 import { listSpec } from "../../../new_fields/Schema";
-import { Doc, Id } from "../../../new_fields/Doc";
+import { Doc } from "../../../new_fields/Doc";
+import { Id } from "../../../new_fields/RefField";
 
 
 @observer
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index dbec82340..e45f61c11 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -11,6 +11,7 @@ import { Docs } from "../documents/Documents";
 import { Doc, Field } from '../../new_fields/Doc';
 import { ImageField, PdfField, VideoField, AudioField } from '../../new_fields/URLField';
 import { List } from '../../new_fields/List';
+import { RichTextField } from '../../new_fields/RichTextField';
 
 export interface ScriptSucccess {
     success: true;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 1e3d4e259..4a68d1c68 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -33,10 +33,11 @@ import { MainOverlayTextBox } from './MainOverlayTextBox';
 import { DocumentView } from './nodes/DocumentView';
 import { PreviewCursor } from './PreviewCursor';
 import { SelectionManager } from '../util/SelectionManager';
-import { FieldResult, Field, Doc, Id, Opt } from '../../new_fields/Doc';
+import { FieldResult, Field, Doc, Opt } from '../../new_fields/Doc';
 import { Cast, FieldValue, StrCast } from '../../new_fields/Types';
 import { DocServer } from '../DocServer';
 import { listSpec } from '../../new_fields/Schema';
+import { Id } from '../../new_fields/RefField';
 
 
 @observer
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 4807dc40a..b2fba1415 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -4,9 +4,10 @@ import * as React from 'react';
 import { ContextMenu } from '../ContextMenu';
 import { FieldViewProps } from '../nodes/FieldView';
 import { Cast, FieldValue, PromiseValue, NumCast } from '../../../new_fields/Types';
-import { Doc, FieldResult, Opt, Id } from '../../../new_fields/Doc';
+import { Doc, FieldResult, Opt } from '../../../new_fields/Doc';
 import { listSpec } from '../../../new_fields/Schema';
 import { List } from '../../../new_fields/List';
+import { Id } from '../../../new_fields/RefField';
 
 export enum CollectionViewType {
     Invalid,
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 2ff409b9b..1574562c6 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -13,11 +13,12 @@ import React = require("react");
 import { SubCollectionViewProps } from "./CollectionSubView";
 import { DragManager, DragLinksAsDocuments } from "../../util/DragManager";
 import { Transform } from '../../util/Transform';
-import { Doc, Id, Opt, Field, FieldId } from "../../../new_fields/Doc";
+import { Doc, Opt, Field } from "../../../new_fields/Doc";
 import { Cast, NumCast } from "../../../new_fields/Types";
 import { List } from "../../../new_fields/List";
 import { DocServer } from "../../DocServer";
 import { listSpec } from "../../../new_fields/Schema";
+import { Id, FieldId } from "../../../new_fields/RefField";
 
 @observer
 export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 874170f3d..58d20819b 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -19,10 +19,11 @@ import { DocumentView } from "../nodes/DocumentView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
 import { CollectionSubView } from "./CollectionSubView";
-import { Opt, Field, Doc, Id } from "../../../new_fields/Doc";
+import { Opt, Field, Doc } from "../../../new_fields/Doc";
 import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
 import { listSpec } from "../../../new_fields/Schema";
 import { List } from "../../../new_fields/List";
+import { Id } from "../../../new_fields/RefField";
 
 
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 558a8728f..4d090b680 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -10,12 +10,13 @@ import * as rp from 'request-promise';
 import { CollectionView } from "./CollectionView";
 import { CollectionPDFView } from "./CollectionPDFView";
 import { CollectionVideoView } from "./CollectionVideoView";
-import { Doc, ObjectField, Opt } from "../../../new_fields/Doc";
+import { Doc, Opt } from "../../../new_fields/Doc";
 import { DocComponent } from "../DocComponent";
 import { listSpec } from "../../../new_fields/Schema";
 import { Cast, PromiseValue } from "../../../new_fields/Types";
 import { List } from "../../../new_fields/List";
 import { DocServer } from "../../DocServer";
+import { ObjectField } from "../../../new_fields/ObjectField";
 
 export interface CollectionViewProps extends FieldViewProps {
     addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index c9d8d83c8..7ec9a8549 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -10,7 +10,8 @@ import "./CollectionTreeView.scss";
 import React = require("react");
 import { Document, listSpec } from '../../../new_fields/Schema';
 import { Cast, StrCast, BoolCast } from '../../../new_fields/Types';
-import { Doc, Id } from '../../../new_fields/Doc';
+import { Doc } from '../../../new_fields/Doc';
+import { Id } from '../../../new_fields/RefField';
 
 
 export interface TreeViewProps {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index ce9995630..f693d55e8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -7,10 +7,11 @@ import { CollectionViewProps } from "../CollectionSubView";
 import "./CollectionFreeFormLinksView.scss";
 import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
 import React = require("react");
-import { Doc, Id } from "../../../../new_fields/Doc";
+import { Doc } from "../../../../new_fields/Doc";
 import { Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types";
 import { listSpec } from "../../../../new_fields/Schema";
 import { List } from "../../../../new_fields/List";
+import { Id } from "../../../../new_fields/RefField";
 
 @observer
 export class CollectionFreeFormLinksView extends React.Component<CollectionViewProps> {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 047fbad18..18107e98a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -19,10 +19,11 @@ import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
 import { createSchema, makeInterface, listSpec } from "../../../../new_fields/Schema";
-import { Doc, Id } from "../../../../new_fields/Doc";
+import { Doc } from "../../../../new_fields/Doc";
 import { FieldValue, Cast, NumCast } from "../../../../new_fields/Types";
 import { pageSchema } from "../../nodes/ImageBox";
 import { List } from "../../../../new_fields/List";
+import { Id } from "../../../../new_fields/RefField";
 
 export const panZoomSchema = createSchema({
     panX: "number",
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index aabc1633e..c304b6a35 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -15,7 +15,7 @@ import { ContextMenu } from "../ContextMenu";
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
 import React = require("react");
-import { Field, Opt, Doc, Id } from "../../../new_fields/Doc";
+import { Opt, Doc } from "../../../new_fields/Doc";
 import { DocComponent } from "../DocComponent";
 import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
 import { FieldValue, Cast, PromiseValue } from "../../../new_fields/Types";
@@ -24,6 +24,7 @@ import { CollectionFreeFormView } from "../collections/collectionFreeForm/Collec
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { MarqueeView } from "../collections/collectionFreeForm/MarqueeView";
 import { DocServer } from "../../DocServer";
+import { Id } from "../../../new_fields/RefField";
 
 const linkSchema = createSchema({
     title: "string",
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index c00c47fc4..dc36c5914 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -16,6 +16,7 @@ import { Opt, Doc, FieldResult } from "../../../new_fields/Doc";
 import { List } from "../../../new_fields/List";
 import { ImageField, VideoField, AudioField } from "../../../new_fields/URLField";
 import { IconField } from "../../../new_fields/IconField";
+import { RichTextField } from "../../../new_fields/RichTextField";
 
 
 //
diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx
index 3ecc8555d..e21adebbc 100644
--- a/src/client/views/nodes/LinkMenu.tsx
+++ b/src/client/views/nodes/LinkMenu.tsx
@@ -5,9 +5,10 @@ import { LinkBox } from "./LinkBox";
 import { LinkEditor } from "./LinkEditor";
 import './LinkMenu.scss';
 import React = require("react");
-import { Doc, Id } from "../../../new_fields/Doc";
+import { Doc } from "../../../new_fields/Doc";
 import { Cast, FieldValue } from "../../../new_fields/Types";
 import { listSpec } from "../../../new_fields/Schema";
+import { Id } from "../../../new_fields/RefField";
 
 interface Props {
     docView: DocumentView;
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index fb7b6e360..4ef2a465f 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -8,38 +8,8 @@ import { Cast, ToConstructor, PromiseValue, FieldValue } from "./Types";
 import { UndoManager, undoBatch } from "../client/util/UndoManager";
 import { listSpec } from "./Schema";
 import { List } from "./List";
-
-export type FieldId = string;
-export const HandleUpdate = Symbol("HandleUpdate");
-export const Id = Symbol("Id");
-export abstract class RefField {
-    @serializable(alias("id", primitive()))
-    private __id: FieldId;
-    readonly [Id]: FieldId;
-
-    constructor(id?: FieldId) {
-        this.__id = id || Utils.GenerateGuid();
-        this[Id] = this.__id;
-    }
-
-    protected [HandleUpdate]?(diff: any): void;
-}
-
-export const Update = Symbol("Update");
-export const OnUpdate = Symbol("OnUpdate");
-export const Parent = Symbol("Parent");
-export class ObjectField {
-    protected [OnUpdate]?: (diff?: any) => void;
-    private [Parent]?: Doc;
-    readonly [Id] = "";
-}
-
-export namespace ObjectField {
-    export function MakeCopy(field: ObjectField) {
-        //TODO Types
-        return field;
-    }
-}
+import { ObjectField } from "./ObjectField";
+import { RefField, FieldId, Id } from "./RefField";
 
 export function IsField(field: any): field is Field {
     return (typeof field === "string")
@@ -53,6 +23,7 @@ export type Opt<T> = T | undefined;
 export type FieldWaiting<T extends RefField = RefField> = T extends undefined ? never : Promise<T | undefined>;
 export type FieldResult<T extends Field = Field> = Opt<T> | FieldWaiting<Extract<T, RefField>>;
 
+export const Update = Symbol("Update");
 export const Self = Symbol("Self");
 
 @Deserializable("doc").withFields(["id"])
@@ -161,7 +132,7 @@ export namespace Doc {
                     copy[key] = field;
                 }
             }
-        })
+        });
         return copy;
     }
 
diff --git a/src/new_fields/HtmlField.ts b/src/new_fields/HtmlField.ts
index 76fdb1f62..808a3499b 100644
--- a/src/new_fields/HtmlField.ts
+++ b/src/new_fields/HtmlField.ts
@@ -1,6 +1,6 @@
 import { Deserializable } from "../client/util/SerializationHelper";
 import { serializable, primitive } from "serializr";
-import { ObjectField } from "./Doc";
+import { ObjectField } from "./ObjectField";
 
 @Deserializable("html")
 export class HtmlField extends ObjectField {
diff --git a/src/new_fields/IconField.ts b/src/new_fields/IconField.ts
index 32f3aa4d5..46f111f8e 100644
--- a/src/new_fields/IconField.ts
+++ b/src/new_fields/IconField.ts
@@ -1,6 +1,6 @@
 import { Deserializable } from "../client/util/SerializationHelper";
 import { serializable, primitive } from "serializr";
-import { ObjectField } from "./Doc";
+import { ObjectField } from "./ObjectField";
 
 @Deserializable("icon")
 export class IconField extends ObjectField {
diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts
index 49e6bf61e..42223c494 100644
--- a/src/new_fields/InkField.ts
+++ b/src/new_fields/InkField.ts
@@ -1,6 +1,6 @@
 import { Deserializable } from "../client/util/SerializationHelper";
 import { serializable, custom, createSimpleSchema, list, object, map } from "serializr";
-import { ObjectField } from "./Doc";
+import { ObjectField } from "./ObjectField";
 
 export enum InkTool {
     None,
diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts
index f01ac210a..428f661c9 100644
--- a/src/new_fields/List.ts
+++ b/src/new_fields/List.ts
@@ -1,8 +1,9 @@
 import { Deserializable, autoObject } from "../client/util/SerializationHelper";
-import { Field, ObjectField, Update, OnUpdate, Self } from "./Doc";
+import { Field, Update, Self } from "./Doc";
 import { setter, getter } from "./util";
 import { serializable, alias, list } from "serializr";
 import { observable } from "mobx";
+import { ObjectField, OnUpdate } from "./ObjectField";
 
 @Deserializable("list")
 class ListImpl<T extends Field> extends ObjectField {
diff --git a/src/new_fields/ObjectField.ts b/src/new_fields/ObjectField.ts
new file mode 100644
index 000000000..9cac2c528
--- /dev/null
+++ b/src/new_fields/ObjectField.ts
@@ -0,0 +1,17 @@
+import { Doc } from "./Doc";
+
+export const OnUpdate = Symbol("OnUpdate");
+export const Parent = Symbol("Parent");
+const Id = Symbol("Object Id");
+export class ObjectField {
+    protected [OnUpdate]?: (diff?: any) => void;
+    private [Parent]?: Doc;
+    readonly [Id] = "";
+}
+
+export namespace ObjectField {
+    export function MakeCopy(field: ObjectField) {
+        //TODO Types
+        return field;
+    }
+}
diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts
index 2aa78731e..56e41cc0f 100644
--- a/src/new_fields/Proxy.ts
+++ b/src/new_fields/Proxy.ts
@@ -1,8 +1,10 @@
 import { Deserializable } from "../client/util/SerializationHelper";
-import { RefField, Id, ObjectField, FieldWaiting } from "./Doc";
+import { FieldWaiting } from "./Doc";
 import { primitive, serializable } from "serializr";
 import { observable, action } from "mobx";
 import { DocServer } from "../client/DocServer";
+import { RefField, Id } from "./RefField";
+import { ObjectField } from "./ObjectField";
 
 @Deserializable("proxy")
 export class ProxyField<T extends RefField> extends ObjectField {
diff --git a/src/new_fields/RefField.ts b/src/new_fields/RefField.ts
new file mode 100644
index 000000000..202c65f21
--- /dev/null
+++ b/src/new_fields/RefField.ts
@@ -0,0 +1,18 @@
+import { serializable, primitive, alias } from "serializr";
+import { Utils } from "../Utils";
+
+export type FieldId = string;
+export const HandleUpdate = Symbol("HandleUpdate");
+export const Id = Symbol("Id");
+export abstract class RefField {
+    @serializable(alias("id", primitive()))
+    private __id: FieldId;
+    readonly [Id]: FieldId;
+
+    constructor(id?: FieldId) {
+        this.__id = id || Utils.GenerateGuid();
+        this[Id] = this.__id;
+    }
+
+    protected [HandleUpdate]?(diff: any): void;
+}
diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts
index 156e4efd9..0fa3cf73c 100644
--- a/src/new_fields/RichTextField.ts
+++ b/src/new_fields/RichTextField.ts
@@ -1,4 +1,4 @@
-import { ObjectField } from "./Doc";
+import { ObjectField } from "./ObjectField";
 import { serializable } from "serializr";
 
 export class RichTextField extends ObjectField {
diff --git a/src/new_fields/Schema.ts b/src/new_fields/Schema.ts
index 5081521c7..7444878fe 100644
--- a/src/new_fields/Schema.ts
+++ b/src/new_fields/Schema.ts
@@ -21,15 +21,15 @@ export function makeInterface<T extends Interface[]>(...schemas: T): (doc?: Doc)
         }
     }
     const proto = new Proxy({}, {
-        get(target: any, prop) {
-            const field = target.doc[prop];
+        get(target: any, prop, receiver) {
+            const field = receiver.doc[prop];
             if (prop in schema) {
                 return Cast(field, (schema as any)[prop]);
             }
             return field;
         },
-        set(target: any, prop, value) {
-            target.doc[prop] = value;
+        set(target: any, prop, value, receiver) {
+            receiver.doc[prop] = value;
             return true;
         }
     });
diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts
index 7fa18673f..3f8eabd45 100644
--- a/src/new_fields/Types.ts
+++ b/src/new_fields/Types.ts
@@ -1,4 +1,4 @@
-import { Field, Opt, FieldWaiting, FieldResult, RefField } from "./Doc";
+import { Field, Opt, FieldResult } from "./Doc";
 import { List } from "./List";
 
 export type ToType<T extends ToConstructor<Field> | ListSpec<Field>> =
diff --git a/src/new_fields/URLField.ts b/src/new_fields/URLField.ts
index 1da245e73..95c679df7 100644
--- a/src/new_fields/URLField.ts
+++ b/src/new_fields/URLField.ts
@@ -1,6 +1,6 @@
 import { Deserializable } from "../client/util/SerializationHelper";
 import { serializable, custom } from "serializr";
-import { ObjectField } from "./Doc";
+import { ObjectField } from "./ObjectField";
 
 function url() {
     return custom(
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index 2d9721b2e..011e8c8d9 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -1,8 +1,10 @@
 import { UndoManager } from "../client/util/UndoManager";
-import { Update, OnUpdate, Parent, ObjectField, RefField, Doc, Id, Field } from "./Doc";
+import { Update, Doc, Field } from "./Doc";
 import { SerializationHelper } from "../client/util/SerializationHelper";
 import { ProxyField } from "./Proxy";
 import { FieldValue } from "./Types";
+import { RefField, Id } from "./RefField";
+import { ObjectField, Parent, OnUpdate } from "./ObjectField";
 
 export function setter(target: any, prop: string | symbol | number, value: any, receiver: any): boolean {
     if (SerializationHelper.IsSerializing()) {
diff --git a/src/server/authentication/controllers/WorkspacesMenu.tsx b/src/server/authentication/controllers/WorkspacesMenu.tsx
index 29327e5ad..91756315d 100644
--- a/src/server/authentication/controllers/WorkspacesMenu.tsx
+++ b/src/server/authentication/controllers/WorkspacesMenu.tsx
@@ -3,8 +3,9 @@ import { observable, action, configure, reaction, computed, ObservableMap, runIn
 import { observer } from "mobx-react";
 import './WorkspacesMenu.css';
 import { EditableView } from '../../../client/views/EditableView';
-import { Doc, Id } from '../../../new_fields/Doc';
+import { Doc } from '../../../new_fields/Doc';
 import { StrCast } from '../../../new_fields/Types';
+import { Id } from '../../../new_fields/RefField';
 
 export interface WorkspaceMenuProps {
     active: Doc | undefined;
diff --git a/src/server/database.ts b/src/server/database.ts
index a61b4d823..6b3b6797f 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -60,11 +60,13 @@ export class Database {
     }
 
     public getDocument(id: string, fn: (result?: Transferable) => void, collectionName = Database.DocumentsCollection) {
+        console.log("getDocument");
         this.db && this.db.collection(collectionName).findOne({ id: id }, (err, result) =>
             fn(result ? ({ id: result._id, type: result.type, data: result.data }) : undefined));
     }
 
     public getDocuments(ids: string[], fn: (result: Transferable[]) => void, collectionName = Database.DocumentsCollection) {
+        console.log("getDocuments");
         this.db && this.db.collection(collectionName).find({ id: { "$in": ids } }).toArray((err, docs) => {
             if (err) {
                 console.log(err.message);
-- 
cgit v1.2.3-70-g09d2


From 6b116d69a5b1a991c7aa00e2b23af3f8db7c7d77 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 30 Apr 2019 09:27:24 -0400
Subject: fixed schema column default/persistence

---
 src/client/documents/Documents.ts                  |  8 ++++---
 .../views/collections/CollectionSchemaView.tsx     | 28 ++++++++++++----------
 src/fields/Document.ts                             |  2 +-
 3 files changed, 22 insertions(+), 16 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index a7514f1d6..847f604d0 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -60,6 +60,7 @@ export interface DocumentOptions {
     copyDraggedItems?: boolean;
     documentText?: string;
     borderRounding?: number;
+    columnsKey?: Array<Key>;
 }
 
 export namespace Documents {
@@ -116,6 +117,7 @@ export namespace Documents {
         if (options.layoutKeys !== undefined) { doc.Set(KeyStore.LayoutKeys, new ListField(options.layoutKeys)); }
         if (options.copyDraggedItems !== undefined) { doc.SetBoolean(KeyStore.CopyDraggedItems, options.copyDraggedItems); }
         if (options.borderRounding !== undefined) { doc.SetNumber(KeyStore.BorderRounding, options.borderRounding); }
+        if (options.columnsKey !== undefined) { doc.SetData(KeyStore.ColumnsKey, options.columnsKey, ListField); }
         return doc;
     }
 
@@ -271,13 +273,13 @@ export namespace Documents {
         if (!makePrototype) {
             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, { columnsKey: [KeyStore.Title], ...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, { columnsKey: [KeyStore.Title], ...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, { columnsKey: [KeyStore.Title], ...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);
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index b6d5f1bfa..b3cc3fbd5 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -2,13 +2,13 @@ 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, untracked } from "mobx";
+import { action, computed, observable, untracked, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
 import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss'
 import "react-table/react-table.css";
 import { Document } from "../../../fields/Document";
-import { Field, Opt } from "../../../fields/Field";
+import { Field, Opt, FieldWaiting } from "../../../fields/Field";
 import { Key } from "../../../fields/Key";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ListField } from "../../../fields/ListField";
@@ -165,18 +165,22 @@ export class CollectionSchemaView extends CollectionSubView {
         super.CreateDropTarget(ele);
     }
 
-    @action
     toggleKey = (key: Key) => {
-        this.props.Document.GetOrCreateAsync<ListField<Key>>(KeyStore.ColumnsKey, ListField,
-            (field) => {
-                const index = field.Data.indexOf(key);
-                if (index === -1) {
-                    this.columns.push(key);
-                } else {
-                    this.columns.splice(index, 1);
+        this.props.Document.GetTAsync<ListField<Key>>(KeyStore.ColumnsKey, ListField).then(field =>
+            runInAction(() => {
+                if (field !== FieldWaiting) {
+                    if (field) {
+                        const index = field.Data.indexOf(key);
+                        if (index === -1) {
+                            this.columns.push(key);
+                        } else {
+                            this.columns.splice(index, 1);
+                        }
+                    } else {
+                        this.props.Document.SetData(KeyStore.ColumnsKey, [key], ListField);
+                    }
                 }
-
-            });
+            }));
     }
 
     //toggles preview side-panel of schema
diff --git a/src/fields/Document.ts b/src/fields/Document.ts
index 2797efc09..55ac731dd 100644
--- a/src/fields/Document.ts
+++ b/src/fields/Document.ts
@@ -204,7 +204,7 @@ export class Document extends Field {
         callback: (field: T) => void
     ): void {
         //This currently doesn't deal with prototypes
-        if (this._proxies.has(key.Id)) {
+        if (this._proxies.has(key.Id) || this.fields.has(key.Id)) {
             Server.GetDocumentField(this, key, field => {
                 if (field && field instanceof ctor) {
                     callback(field);
-- 
cgit v1.2.3-70-g09d2


From 48365aaa19789d2631725ca143808d8df537da46 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Wed, 1 May 2019 18:56:11 -0400
Subject: Fixed schema view

---
 src/client/views/collections/CollectionSchemaView.tsx | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 6add6ada8..1f26941c2 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -228,7 +228,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
 
     get previewDocument(): Doc | undefined {
         const children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
-        const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
+        const selected = children.length > this._selectedIndex ? FieldValue(children[this._selectedIndex]) : undefined;
         return selected ? (this.previewScript ? FieldValue(Cast(selected[this.previewScript], Doc)) : selected) : undefined;
     }
     get tableWidth() { return (this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH) * (1 - this.splitPercentage / 100); }
@@ -254,10 +254,11 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     @computed
     get previewPanel() {
         // let doc = CompileScript(this.previewScript, { this: selected }, true)();
-        return !this.previewDocument ? (null) : (
+        const previewDoc = this.previewDocument;
+        return !previewDoc ? (null) : (
             <div className="collectionSchemaView-previewRegion" style={{ width: `${this.previewRegionWidth}px` }}>
                 <div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
-                    <DocumentView Document={this.previewDocument} isTopMost={false} selectOnLoad={false}
+                    <DocumentView Document={previewDoc} isTopMost={false} selectOnLoad={false}
                         toggleMinimized={emptyFunction}
                         addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
                         ScreenToLocalTransform={this.getPreviewTransform}
@@ -322,7 +323,8 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     render() {
         library.add(faCog);
         library.add(faPlus);
-        const children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
+        //This can't just pass FieldValue to filter because filter passes other arguments to the passed in function, which end up as default values in FieldValue
+        const children = (this.children || []).filter(doc => FieldValue(doc));
         return (
             <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel}
                 onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createTarget}>
-- 
cgit v1.2.3-70-g09d2


From 6d52cfb2a7b2b575f448e7d7c3f02a97afa0027b Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 1 May 2019 22:38:49 -0400
Subject: fixed drag drop making source documents disappear.  improved TreeView
 collapsing.  Chnaged schemaView options placement

---
 src/client/util/DragManager.ts                        | 17 +++++++++++------
 src/client/views/collections/CollectionSchemaView.tsx |  2 +-
 src/client/views/collections/CollectionTreeView.tsx   |  4 ++--
 3 files changed, 14 insertions(+), 9 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index a149da52f..4acf609f4 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -301,15 +301,20 @@ export namespace DragManager {
 
     function dispatchDrag(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];
+            // let parent = dragEle.parentElement;
+            // if (parent) parent.removeChild(dragEle);
+            let ret = [dragEle, dragEle.style.width, dragEle.style.height];
+            dragEle.style.width = "0";
+            dragEle.style.height = "0";
+            return ret;
         });
         const target = document.elementFromPoint(e.x, e.y);
         removed.map(r => {
-            let dragEle = r[0];
-            let parent = r[1];
-            if (parent && dragEle) parent.appendChild(dragEle);
+            let dragEle = r[0] as HTMLElement;
+            dragEle.style.width = r[1] as string;
+            dragEle.style.height = r[2] as string;
+            // let parent = r[1];
+            // if (parent && dragEle) parent.appendChild(dragEle);
         });
         if (target) {
             if (finishDrag) finishDrag(dragData);
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 1f26941c2..67784fa81 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -295,7 +295,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     get tableOptionsPanel() {
         return !this.props.active() ? (null) :
             (<Flyout
-                anchorPoint={anchorPoints.LEFT_TOP}
+                anchorPoint={anchorPoints.RIGHT_TOP}
                 content={<div>
                     <div id="schema-options-header"><h5><b>Options</b></h5></div>
                     <div id="options-flyout-div">
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 1a6f1121d..2a620a5e6 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -104,7 +104,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 childElements = <ul>
                     {/* // bcz: should this work? 
                      {children.map(value => <TreeView key={value[Id]} document={value} deleteDoc={this.remove} moveDocument={this.move} copyOnDrag={this.props.copyOnDrag} />)} */}
-                    {children.map(value => <TreeView key={Utils.GenerateGuid()} document={value} deleteDoc={this.remove} moveDocument={this.move} copyOnDrag={this.props.copyOnDrag} />)}
+                    {children.map(value => <TreeView key={value[Id] !== undefined ? value[Id] : Utils.GenerateGuid()} document={value} deleteDoc={this.remove} moveDocument={this.move} copyOnDrag={this.props.copyOnDrag} />)}
                 </ul >;
             }
             else bulletType = BulletType.Collapsed;
@@ -137,7 +137,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
             (children.map(value =>
                 //bcz: shouldn't this work?   - I think value[Id] is undefined sometimes
                 // <TreeView document={value} key={value[Id]} deleteDoc={this.remove} moveDocument={this.props.moveDocument} copyOnDrag={copyOnDrag} />)
-                <TreeView document={value} key={Utils.GenerateGuid()} deleteDoc={this.remove} moveDocument={this.props.moveDocument} copyOnDrag={copyOnDrag} />)
+                <TreeView document={value} key={value[Id] !== undefined ? value[Id] : Utils.GenerateGuid()} deleteDoc={this.remove} moveDocument={this.props.moveDocument} copyOnDrag={copyOnDrag} />)
             );
 
         return (
-- 
cgit v1.2.3-70-g09d2


From 68d18976359434c46cc9bde3df6fbe708021761e Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 5 May 2019 15:10:18 -0400
Subject: fixed problem with annotations being added to view document instead
 of proto.  added keyvalue indication for fields that only exist on prootype

---
 src/client/views/collections/CollectionBaseView.tsx   | 4 ++--
 src/client/views/collections/CollectionSchemaView.tsx | 1 +
 src/client/views/nodes/KeyValuePair.tsx               | 4 +++-
 3 files changed, 6 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 76adfcdcd..cbb568c07 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -100,12 +100,12 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
                     value.push(doc);
                 }
             } else {
-                this.props.Document[this.props.fieldKey] = new List([doc]);
+                Doc.SetOnPrototype(this.props.Document, this.props.fieldKey, new List([doc]));
             }
             // set the ZoomBasis only if hasn't already been set -- bcz: maybe set/resetting the ZoomBasis should be a parameter to addDocument?
             if (this.collectionViewType === CollectionViewType.Freeform || this.collectionViewType === CollectionViewType.Invalid) {
                 let zoom = NumCast(this.props.Document.scale, 1);
-                doc.zoomBasis = zoom;
+                Doc.SetOnPrototype(doc, "zoomBasis", zoom);
             }
         }
         return true;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 67784fa81..4fee9db85 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -268,6 +268,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                         focus={emptyFunction}
                         parentActive={this.props.active}
                         whenActiveChanged={this.props.whenActiveChanged}
+                        bringToFront={emptyFunction}
                     />
                 </div>
                 <input className="collectionSchemaView-input" value={this.previewScript} onChange={this.onPreviewScriptChange}
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 235792cbe..203fb5625 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -40,6 +40,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             PanelHeight: returnZero,
         };
         let contents = <FieldView {...props} />;
+        let fieldKey = Object.keys(props.Document).indexOf(props.fieldKey) !== -1 ? props.fieldKey : "(" + props.fieldKey + ")";
         return (
             <tr className={this.props.rowStyle}>
                 <td className="keyValuePair-td-key" style={{ width: `${this.props.keyWidth}%` }}>
@@ -50,11 +51,12 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
                         }}>
                             X
                         </button>
-                        <div className="keyValuePair-keyField">{this.props.keyName}</div>
+                        <div className="keyValuePair-keyField">{fieldKey}</div>
                     </div>
                 </td>
                 <td className="keyValuePair-td-value" style={{ width: `${100 - this.props.keyWidth}%` }}>
                     <EditableView contents={contents} height={36} GetValue={() => {
+
                         let field = FieldValue(props.Document[props.fieldKey]);
                         if (field) {
                             //TODO Types
-- 
cgit v1.2.3-70-g09d2


From e96e3382104a2ae8dbe41252c0b38206b983e94a Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 6 May 2019 13:10:19 -0400
Subject: addd doc list cast

---
 .../views/collections/CollectionSchemaView.tsx       | 10 ++++------
 .../views/nodes/CollectionFreeFormDocumentView.tsx   | 20 +++++++++-----------
 src/new_fields/Doc.ts                                |  5 +++++
 src/new_fields/List.ts                               |  2 +-
 src/new_fields/Types.ts                              |  2 +-
 5 files changed, 20 insertions(+), 19 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 4fee9db85..16818affd 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -19,7 +19,7 @@ import { DocumentView } from "../nodes/DocumentView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
 import { CollectionSubView } from "./CollectionSubView";
-import { Opt, Field, Doc } from "../../../new_fields/Doc";
+import { Opt, Field, Doc, DocListCast } from "../../../new_fields/Doc";
 import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
 import { listSpec } from "../../../new_fields/Schema";
 import { List } from "../../../new_fields/List";
@@ -111,17 +111,15 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                         }
                         return applyToDoc(props.Document, script.run);
                     }}
-                    OnFillDown={(value: string) => {
+                    OnFillDown={async (value: string) => {
                         let script = CompileScript(value, { addReturn: true, params: { this: Document.name } });
                         if (!script.compiled) {
                             return;
                         }
                         const run = script.run;
                         //TODO This should be able to be refactored to compile the script once
-                        const val = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc));
-                        if (val) {
-                            val.forEach(doc => applyToDoc(doc, run));
-                        }
+                        const val = await DocListCast(this.props.Document[this.props.fieldKey])
+                        val && val.forEach(doc => applyToDoc(doc, run));
                     }}>
                 </EditableView>
             </div >
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 2c8e6aef3..24a75049a 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -6,10 +6,10 @@ import "./DocumentView.scss";
 import React = require("react");
 import { DocComponent } from "../DocComponent";
 import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
-import { FieldValue, Cast, NumCast, BoolCast, PromiseValue } from "../../../new_fields/Types";
+import { FieldValue, Cast, NumCast, BoolCast } from "../../../new_fields/Types";
 import { OmitKeys, Utils } from "../../../Utils";
 import { SelectionManager } from "../../util/SelectionManager";
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, DocListCast } from "../../../new_fields/Doc";
 import { List } from "../../../new_fields/List";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
 
@@ -125,15 +125,15 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     public toggleIcon = async (): Promise<void> => {
         SelectionManager.DeselectAll();
         let isMinimized: boolean | undefined;
-        let maximizedDocs = Cast(this.props.Document.maximizedDocs, listSpec(Doc));
+        let maximizedDocs = await DocListCast(this.props.Document.maximizedDocs);
         let minimizedDoc: Doc | undefined = this.props.Document;
         if (!maximizedDocs) {
             minimizedDoc = await Cast(this.props.Document.minimizedDoc, Doc);
-            if (minimizedDoc) maximizedDocs = Cast(minimizedDoc.maximizedDocs, listSpec(Doc));
+            if (minimizedDoc) maximizedDocs = await DocListCast(minimizedDoc.maximizedDocs);
         }
         if (minimizedDoc && maximizedDocs) {
             let minimizedTarget = minimizedDoc;
-            (await Promise.all(maximizedDocs)).forEach(maximizedDoc => {
+            maximizedDocs.forEach(maximizedDoc => {
                 let iconAnimating = Cast(maximizedDoc.isIconAnimating, List);
                 if (!iconAnimating || (Date.now() - iconAnimating[6] > 1000)) {
                     if (isMinimized === undefined) {
@@ -167,16 +167,14 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
             Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
             if (BoolCast(this.props.Document.isButton, false)) {
-                let maximizedDocs = await Cast(this.props.Document.maximizedDocs, listSpec(Doc));
+                let maximizedDocs = await DocListCast(this.props.Document.maximizedDocs);
                 if (maximizedDocs) {   // bcz: need a better way to associate behaviors with click events on widget-documents
                     if ((metaKey && !this.props.Document.maximizeOnRight) || (!metaKey && this.props.Document.maximizeOnRight)) {
-                        let dataDocs = Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc));
+                        let dataDocs = await DocListCast(CollectionDockingView.Instance.props.Document.data);
                         if (dataDocs) {
-                            const docs = await Promise.all(dataDocs);
-                            const maxDocs = await Promise.all(maximizedDocs);
                             SelectionManager.DeselectAll();
-                            maxDocs.forEach(maxDoc => {
-                                if (!docs || docs.indexOf(maxDoc) == -1) {
+                            maximizedDocs.forEach(maxDoc => {
+                                if (!dataDocs || dataDocs.indexOf(maxDoc) == -1) {
                                     CollectionDockingView.Instance.AddRightSplit(maxDoc);
                                 } else {
                                     CollectionDockingView.Instance.CloseRightSplit(maxDoc);
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index b2863c632..70dd8361b 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -29,6 +29,11 @@ const SelfProxy = Symbol("SelfProxy");
 export const WidthSym = Symbol("Width");
 export const HeightSym = Symbol("Height");
 
+export function DocListCast(field: FieldResult) {
+    const list = Cast(field, listSpec(Doc))
+    return list ? Promise.all(list) : Promise.resolve(undefined);
+}
+
 @Deserializable("doc").withFields(["id"])
 export class Doc extends RefField {
     constructor(id?: FieldId, forceSave?: boolean) {
diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts
index 213ed21aa..5aba64406 100644
--- a/src/new_fields/List.ts
+++ b/src/new_fields/List.ts
@@ -236,5 +236,5 @@ class ListImpl<T extends Field> extends ObjectField {
 
     private [Self] = this;
 }
-export type List<T extends Field> = ListImpl<T> & T[];
+export type List<T extends Field> = ListImpl<T> & (T | (T extends RefField ? Promise<T> : never))[];
 export const List: { new <T extends Field>(fields?: T[]): List<T> } = ListImpl as any;
\ No newline at end of file
diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts
index 60f08dc90..c07d38786 100644
--- a/src/new_fields/Types.ts
+++ b/src/new_fields/Types.ts
@@ -1,4 +1,4 @@
-import { Field, Opt, FieldResult } from "./Doc";
+import { Field, Opt, FieldResult, Doc } from "./Doc";
 import { List } from "./List";
 
 export type ToType<T extends ToConstructor<Field> | ListSpec<Field>> =
-- 
cgit v1.2.3-70-g09d2


From 244d4d127e7e1a0faadbc5a8baed7922ef03522c Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 9 May 2019 14:11:35 -0400
Subject: cleaned up textlinking a little.

---
 src/client/util/DragManager.ts                     | 13 ++--
 src/client/util/SelectionManager.ts                |  4 +-
 src/client/util/TooltipTextMenu.tsx                | 25 +++---
 src/client/views/DocumentDecorations.tsx           |  8 +-
 src/client/views/MainOverlayTextBox.tsx            | 30 ++++---
 .../views/collections/CollectionSchemaView.tsx     |  3 +-
 src/client/views/collections/CollectionSubView.tsx |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  8 +-
 src/client/views/nodes/FieldView.tsx               |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx        | 91 ++++++++++++++++++----
 10 files changed, 122 insertions(+), 64 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index fbf20e244..eaf851a75 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,11 +1,9 @@
-import { action } from "mobx";
+import { action, runInAction } from "mobx";
+import { Doc, DocListCast } from "../../new_fields/Doc";
+import { Cast } from "../../new_fields/Types";
 import { emptyFunction } from "../../Utils";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import * as globalCssVariables from "../views/globalCssVariables.scss";
-import { MainOverlayTextBox } from "../views/MainOverlayTextBox";
-import { Doc, DocListCast } from "../../new_fields/Doc";
-import { Cast } from "../../new_fields/Types";
-import { listSpec } from "../../new_fields/Schema";
 
 export type dropActionType = "alias" | "copy" | undefined;
 export function SetupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Doc, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType) {
@@ -154,7 +152,10 @@ export namespace DragManager {
         [id: string]: any;
     }
 
+    export let StartDragFunctions: (() => void)[] = [];
+
     export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) {
+        runInAction(() => StartDragFunctions.map(func => func()));
         StartDrag(eles, dragData, downX, downY, options,
             (dropData: { [id: string]: any }) =>
                 (dropData.droppedDocuments = dragData.userDropAction == "alias" || (!dragData.userDropAction && dragData.dropAction == "alias") ?
@@ -189,7 +190,6 @@ export namespace DragManager {
             dragDiv.style.pointerEvents = "none";
             DragManager.Root().appendChild(dragDiv);
         }
-        if (!dragData.dontClearTextBox) MainOverlayTextBox.Instance.SetTextDoc();
 
         let scaleXs: number[] = [];
         let scaleYs: number[] = [];
@@ -217,6 +217,7 @@ export namespace DragManager {
             dragElement.style.top = "0";
             dragElement.style.bottom = "";
             dragElement.style.left = "0";
+            dragElement.style.color = "black";
             dragElement.style.transformOrigin = "0 0";
             dragElement.style.zIndex = globalCssVariables.contextMenuZindex;// "1000";
             dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index fe5acf4b4..a3a8172c7 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,7 +1,7 @@
 import { observable, action } from "mobx";
 import { Doc } from "../../new_fields/Doc";
-import { MainOverlayTextBox } from "../views/MainOverlayTextBox";
 import { DocumentView } from "../views/nodes/DocumentView";
+import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
 
 export namespace SelectionManager {
     class Manager {
@@ -25,7 +25,7 @@ export namespace SelectionManager {
         DeselectAll(): void {
             manager.SelectedDocuments.map(dv => dv.props.whenActiveChanged(false));
             manager.SelectedDocuments = [];
-            MainOverlayTextBox.Instance.SetTextDoc();
+            FormattedTextBox.InputBoxOverlay = undefined;
         }
         @action
         ReselectAll() {
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 15895fd1c..6eb654319 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -48,6 +48,7 @@ export class TooltipTextMenu {
     private fontSizeIndicator: HTMLSpanElement = document.createElement("span");
     private linkEditor?: HTMLDivElement;
     private linkText?: HTMLDivElement;
+    private linkDrag?: HTMLImageElement;
     //dropdown doms
     private fontSizeDom?: Node;
     private fontStyleDom?: Node;
@@ -168,8 +169,13 @@ export class TooltipTextMenu {
             this.linkText = document.createElement("div");
             this.linkText.style.cssFloat = "left";
             this.linkText.style.marginRight = "5px";
+            this.linkText.style.marginLeft = "5px";
             this.linkText.setAttribute("contenteditable", "true");
+            this.linkText.style.whiteSpace = "nowrap";
+            this.linkText.style.width = "150px";
+            this.linkText.style.overflow = "hidden";
             this.linkText.style.color = "white";
+            this.linkText.onpointerdown = (e: PointerEvent) => { e.stopPropagation(); }
             let linkBtn = document.createElement("div");
             linkBtn.textContent = ">>";
             linkBtn.style.width = "20px";
@@ -196,16 +202,17 @@ export class TooltipTextMenu {
                     e.preventDefault();
                 }
             }
-            let linkDrag = document.createElement("div");
-            linkDrag.textContent = "O";
-            linkDrag.style.width = "20px";
-            linkDrag.style.height = "20px";
-            linkDrag.style.color = "white";
-            linkDrag.style.cssFloat = "left";
-            linkDrag.onpointerdown = (e: PointerEvent) => {
+            this.linkDrag = document.createElement("img");
+            this.linkDrag.src = "https://seogurusnyc.com/wp-content/uploads/2016/12/link-1.png";
+            this.linkDrag.style.width = "20px";
+            this.linkDrag.style.height = "20px";
+            this.linkDrag.style.color = "white";
+            this.linkDrag.style.background = "black";
+            this.linkDrag.style.cssFloat = "left";
+            this.linkDrag.onpointerdown = (e: PointerEvent) => {
                 let dragData = new DragManager.LinkDragData(this.editorProps.Document);
                 dragData.dontClearTextBox = true;
-                DragManager.StartLinkDrag(this.linkEditor!, dragData, e.clientX, e.clientY,
+                DragManager.StartLinkDrag(this.linkDrag!, dragData, e.clientX, e.clientY,
                     {
                         handlers: {
                             dragComplete: action(() => {
@@ -216,9 +223,9 @@ export class TooltipTextMenu {
                         hideSource: false
                     })
             };
+            this.linkEditor.appendChild(this.linkDrag);
             this.linkEditor.appendChild(this.linkText);
             this.linkEditor.appendChild(linkBtn);
-            this.linkEditor.appendChild(linkDrag)
             this.tooltip.appendChild(this.linkEditor);
         }
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index e3eb034fa..85ddadd64 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -5,7 +5,6 @@ import { DragLinksAsDocuments, DragManager } from "../util/DragManager";
 import { SelectionManager } from "../util/SelectionManager";
 import { undoBatch } from "../util/UndoManager";
 import './DocumentDecorations.scss';
-import { MainOverlayTextBox } from "./MainOverlayTextBox";
 import { DocumentView, PositionDocument } from "./nodes/DocumentView";
 import { LinkMenu } from "./nodes/LinkMenu";
 import { TemplateMenu } from "./TemplateMenu";
@@ -25,11 +24,9 @@ import { faLink } from '@fortawesome/free-solid-svg-icons';
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss";
-import { CollectionFreeFormView } from "./collections/collectionFreeForm/CollectionFreeFormView";
 import { CollectionView } from "./collections/CollectionView";
-import { createCipher } from "crypto";
-import { FieldView } from "./nodes/FieldView";
 import { DocumentManager } from "../util/DocumentManager";
+import { FormattedTextBox } from "./nodes/FormattedTextBox";
 
 library.add(faLink);
 
@@ -328,6 +325,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             let selDoc = SelectionManager.SelectedDocuments()[0];
             let container = selDoc.props.ContainingCollectionView ? selDoc.props.ContainingCollectionView.props.Document.proto : undefined;
             let dragData = new DragManager.LinkDragData(selDoc.props.Document, container ? [container] : []);
+            FormattedTextBox.InputBoxOverlay = undefined;
             DragManager.StartLinkDrag(this._linkerButton.current, dragData, e.pageX, e.pageY, {
                 handlers: {
                     dragComplete: action(emptyFunction),
@@ -410,7 +408,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 break;
         }
 
-        MainOverlayTextBox.Instance.SetTextDoc();
+        FormattedTextBox.InputBoxOverlay = undefined;
         SelectionManager.SelectedDocuments().forEach(element => {
             const rect = element.ContentDiv ? element.ContentDiv.getBoundingClientRect() : new DOMRect();
 
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 3b75c248a..91f626737 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -1,16 +1,12 @@
-import { action, observable, trace } from 'mobx';
+import { action, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
-import "normalize.css";
 import * as React from 'react';
 import { emptyFunction, returnTrue, returnZero } from '../../Utils';
-import '../northstar/model/ModelExtensions';
-import '../northstar/utils/Extensions';
 import { DragManager } from '../util/DragManager';
 import { Transform } from '../util/Transform';
+import "normalize.css";
 import "./MainOverlayTextBox.scss";
 import { FormattedTextBox } from './nodes/FormattedTextBox';
-import { Doc } from '../../new_fields/Doc';
-import { NumCast } from '../../new_fields/Types';
 
 interface MainOverlayTextBoxProps {
 }
@@ -18,8 +14,6 @@ interface MainOverlayTextBoxProps {
 @observer
 export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps> {
     public static Instance: MainOverlayTextBox;
-    @observable public TextDoc?: Doc = undefined;
-    public TextScroll: number = 0;
     @observable _textXf: () => Transform = () => Transform.Identity();
     private _textFieldKey: string = "data";
     private _textColor: string | null = null;
@@ -30,15 +24,18 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
         super(props);
         this._textProxyDiv = React.createRef();
         MainOverlayTextBox.Instance = this;
+        reaction(() => FormattedTextBox.InputBoxOverlay,
+            (box?: FormattedTextBox) => {
+                if (box) this.setTextDoc(box.props.fieldKey, box.CurrentDiv, box.props.ScreenToLocalTransform);
+                else this.setTextDoc();
+            });
     }
 
     @action
-    SetTextDoc(textDoc?: Doc, textFieldKey?: string, div?: HTMLDivElement, tx?: () => Transform) {
+    private setTextDoc(textFieldKey?: string, div?: HTMLDivElement, tx?: () => Transform) {
         if (this._textTargetDiv) {
             this._textTargetDiv.style.color = this._textColor;
         }
-
-        this.TextDoc = textDoc;
         this._textFieldKey = textFieldKey!;
         this._textXf = tx ? tx : () => Transform.Identity();
         this._textTargetDiv = div;
@@ -46,15 +43,13 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
             if (div.parentElement && div.parentElement instanceof HTMLDivElement && div.parentElement.id === "screenSpace") this._textXf = () => Transform.Identity();
             this._textColor = div.style.color;
             div.style.color = "transparent";
-            this.TextScroll = div.scrollTop;
         }
     }
 
     @action
     textScroll = (e: React.UIEvent) => {
         if (this._textProxyDiv.current && this._textTargetDiv) {
-            this.TextScroll = (e as any)._targetInst.stateNode.scrollTop;//  this._textProxyDiv.current.children[0].scrollTop;
-            this._textTargetDiv.scrollTop = this.TextScroll;
+            this._textTargetDiv.scrollTop = (e as any)._targetInst.stateNode.scrollTop;
         }
     }
 
@@ -64,11 +59,12 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
             document.addEventListener('pointerup', this.textBoxUp);
         }
     }
+    @action
     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!]);
+            let dragData = new DragManager.DocumentDragData(FormattedTextBox.InputBoxOverlay ? [FormattedTextBox.InputBoxOverlay.props.Document] : []);
             const [left, top] = this._textXf().inverse().transformPoint(0, 0);
             dragData.xOffset = e.clientX - left;
             dragData.yOffset = e.clientY - top;
@@ -86,13 +82,13 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
     }
 
     render() {
-        if (this.TextDoc && this._textTargetDiv) {
+        if (FormattedTextBox.InputBoxOverlay && this._textTargetDiv) {
             let textRect = this._textTargetDiv.getBoundingClientRect();
             let s = this._textXf().Scale;
             return <div className="mainOverlayTextBox-textInput" style={{ transform: `translate(${textRect.left}px, ${textRect.top}px) scale(${1 / s},${1 / s})`, width: "auto", height: "auto" }} >
                 <div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll}
                     style={{ width: `${textRect.width * s}px`, height: `${textRect.height * s}px` }}>
-                    <FormattedTextBox fieldKey={this._textFieldKey} isOverlay={true} Document={this.TextDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
+                    <FormattedTextBox fieldKey={this._textFieldKey} isOverlay={true} Document={FormattedTextBox.InputBoxOverlay.props.Document} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
                         selectOnLoad={true} ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue}
                         ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} />
                 </div>
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 16818affd..6dd0e5935 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -322,8 +322,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     render() {
         library.add(faCog);
         library.add(faPlus);
-        //This can't just pass FieldValue to filter because filter passes other arguments to the passed in function, which end up as default values in FieldValue
-        const children = (this.children || []).filter(doc => FieldValue(doc));
+        const children = this.children;
         return (
             <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel}
                 onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createTarget}>
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 828ac880a..082692d8d 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -50,7 +50,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
         get children() {
             //TODO tfs: This might not be what we want?
             //This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue)
-            return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).filter(doc => FieldValue(doc));
+            return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).filter(doc => FieldValue(doc)).map(doc => doc as Doc);
         }
 
         @action
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 797f94d5f..c1d149098 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -70,7 +70,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
     public getActiveDocuments = () => {
         const curPage = FieldValue(this.Document.curPage, -1);
-        return FieldValue(this.children, [] as Doc[]).filter(doc => {
+        return this.children.filter(doc => {
             var page = NumCast(doc.page, -1);
             return page === curPage || page === -1;
         });
@@ -169,7 +169,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         // if (!this.props.active()) {
         //     return;
         // }
-        let childSelected = (this.children || []).filter(doc => doc).some(doc => {
+        let childSelected = this.children.some(doc => {
             var dv = DocumentManager.Instance.getDocumentView(doc);
             return dv && SelectionManager.IsSelected(dv) ? true : false;
         });
@@ -222,7 +222,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     bringToFront = (doc: Doc) => {
-        const docs = (this.children || []);
+        const docs = this.children;
         docs.slice().sort((doc1, doc2) => {
             if (doc1 === doc) return 1;
             if (doc2 === doc) return -1;
@@ -268,7 +268,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     @computed.struct
     get views() {
         let curPage = FieldValue(this.Document.curPage, -1);
-        let docviews = (this.children || []).filter(doc => doc).reduce((prev, doc) => {
+        let docviews = this.children.reduce((prev, doc) => {
             if (!(doc instanceof Doc)) return prev;
             var page = NumCast(doc.page, -1);
             if (page === curPage || page === -1) {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 613c24fa4..cdc1bdc85 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -1,6 +1,6 @@
 import React = require("react");
 import { observer } from "mobx-react";
-import { computed } from "mobx";
+import { computed, observable } from "mobx";
 import { FormattedTextBox } from "./FormattedTextBox";
 import { ImageBox } from "./ImageBox";
 import { VideoBox } from "./VideoBox";
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 445a834ee..e7fb94777 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1,29 +1,31 @@
-import { action, IReactionDisposer, reaction, trace, computed, _allowStateChangesInsideComputed } from "mobx";
+import { action, IReactionDisposer, observable, reaction } from "mobx";
+import { observer } from "mobx-react";
 import { baseKeymap } from "prosemirror-commands";
 import { history } from "prosemirror-history";
 import { keymap } from "prosemirror-keymap";
 import { EditorState, Plugin, Transaction } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
+import { Doc, Field, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
+import { RichTextField } from "../../../new_fields/RichTextField";
+import { createSchema, makeInterface } from "../../../new_fields/Schema";
+import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { DocServer } from "../../DocServer";
+import { DocumentManager } from "../../util/DocumentManager";
+import { DragManager } from "../../util/DragManager";
 import buildKeymap from "../../util/ProsemirrorKeymap";
 import { inpRules } from "../../util/RichTextRules";
-import { schema, ImageResizeView } from "../../util/RichTextSchema";
+import { ImageResizeView, schema } from "../../util/RichTextSchema";
+import { SelectionManager } from "../../util/SelectionManager";
 import { TooltipLinkingMenu } from "../../util/TooltipLinkingMenu";
 import { TooltipTextMenu } from "../../util/TooltipTextMenu";
+import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { ContextMenu } from "../../views/ContextMenu";
-import { MainOverlayTextBox } from "../MainOverlayTextBox";
+import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { DocComponent } from "../DocComponent";
+import { InkingControl } from "../InkingControl";
 import { FieldView, FieldViewProps } from "./FieldView";
 import "./FormattedTextBox.scss";
 import React = require("react");
-import { SelectionManager } from "../../util/SelectionManager";
-import { DocComponent } from "../DocComponent";
-import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { Opt, Doc, WidthSym, HeightSym } from "../../../new_fields/Doc";
-import { observer } from "mobx-react";
-import { InkingControl } from "../InkingControl";
-import { StrCast, Cast, NumCast, BoolCast } from "../../../new_fields/Types";
-import { RichTextField } from "../../../new_fields/RichTextField";
-import { Id } from "../../../new_fields/RefField";
-import { UndoManager } from "../../util/UndoManager";
 
 // FormattedTextBox: Displays an editable plain text node that maps to a specified Key of a Document
 //
@@ -62,15 +64,23 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
     private _proseRef: React.RefObject<HTMLDivElement>;
     private _editorView: Opt<EditorView>;
     private _gotDown: boolean = false;
+    private _dropDisposer?: DragManager.DragDropDisposer;
     private _reactionDisposer: Opt<IReactionDisposer>;
     private _inputReactionDisposer: Opt<IReactionDisposer>;
     private _proxyReactionDisposer: Opt<IReactionDisposer>;
+    public get CurrentDiv(): HTMLDivElement { return this._ref.current!; }
+
+    @observable public static InputBoxOverlay?: FormattedTextBox = undefined;
+    public static InputBoxOverlayScroll: number = 0;
 
     constructor(props: FieldViewProps) {
         super(props);
 
         this._ref = React.createRef();
         this._proseRef = React.createRef();
+        if (this.props.isOverlay) {
+            DragManager.StartDragFunctions.push(() => FormattedTextBox.InputBoxOverlay = undefined);
+        }
     }
 
     _applyingChange: boolean = false;
@@ -94,7 +104,27 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         }
     }
 
+    @undoBatch
+    @action
+    drop = async (e: Event, de: DragManager.DropEvent) => {
+        if (de.data instanceof DragManager.LinkDragData) {
+            let sourceDoc = de.data.linkSourceDocument;
+            let destDoc = this.props.Document;
+
+            const protoDest = destDoc.proto;
+            const protoSrc = sourceDoc.proto;
+            Doc.MakeLink(protoSrc ? protoSrc : sourceDoc, protoDest ? protoDest : destDoc);
+            de.data.droppedDocuments.push(destDoc);
+            e.stopPropagation();
+        }
+    }
+
     componentDidMount() {
+        if (this._ref.current) {
+            this._dropDisposer = DragManager.MakeDropTarget(this._ref.current, {
+                handlers: { drop: this.drop.bind(this) }
+            });
+        }
         const config = {
             schema,
             inpRules, //these currently don't do anything, but could eventually be helpful
@@ -117,7 +147,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         };
 
         if (this.props.isOverlay) {
-            this._inputReactionDisposer = reaction(() => MainOverlayTextBox.Instance.TextDoc && MainOverlayTextBox.Instance.TextDoc[Id],
+            this._inputReactionDisposer = reaction(() => FormattedTextBox.InputBoxOverlay,
                 () => {
                     if (this._editorView) {
                         this._editorView.destroy();
@@ -127,7 +157,12 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             );
         } else {
             this._proxyReactionDisposer = reaction(() => this.props.isSelected(),
-                () => this.props.isSelected() && MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform));
+                () => {
+                    if (this.props.isSelected()) {
+                        FormattedTextBox.InputBoxOverlay = this;
+                        FormattedTextBox.InputBoxOverlayScroll = this._ref.current!.scrollTop;
+                    }
+                });
         }
 
 
@@ -178,6 +213,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         if (this._proxyReactionDisposer) {
             this._proxyReactionDisposer();
         }
+        if (this._dropDisposer) {
+            this._dropDisposer();
+        }
     }
 
     onPointerDown = (e: React.PointerEvent): void => {
@@ -186,6 +224,24 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             if (this._toolTipTextMenu && this._toolTipTextMenu.tooltip)
                 this._toolTipTextMenu.tooltip.style.opacity = "0";
         }
+        if (e.button === 0 && ((!this.props.isSelected() && !e.ctrlKey) || (this.props.isSelected() && e.ctrlKey)) && !e.metaKey) {
+            if (e.target && (e.target as any).href) {
+                let href = (e.target as any).href;
+                if (href.indexOf(DocServer.prepend("/doc/")) === 0) {
+                    let docid = href.replace(DocServer.prepend("/doc/"), "");
+                    DocServer.GetRefField(docid).then(action((f: Opt<Field>) => {
+                        if (f instanceof Doc) {
+                            if (DocumentManager.Instance.getDocumentView(f))
+                                DocumentManager.Instance.getDocumentView(f)!.props.focus(f);
+                            else CollectionDockingView.Instance.AddRightSplit(f);
+                        }
+                    }));
+                }
+                e.stopPropagation();
+                e.preventDefault();
+            }
+
+        }
         if (e.button === 2 || (e.button === 0 && e.ctrlKey)) {
             this._gotDown = true;
             e.preventDefault();
@@ -199,12 +255,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         }
     }
 
+    @action
     onFocused = (e: React.FocusEvent): void => {
         if (!this.props.isOverlay) {
-            MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform);
+            FormattedTextBox.InputBoxOverlay = this;
         } else {
             if (this._proseRef.current) {
-                this._proseRef.current.scrollTop = MainOverlayTextBox.Instance.TextScroll;
+                this._proseRef.current.scrollTop = FormattedTextBox.InputBoxOverlayScroll;
             }
         }
     }
-- 
cgit v1.2.3-70-g09d2


From 168cd36282087bbf9e0157352a129d90b20b7394 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 9 May 2019 14:40:03 -0400
Subject: fixed some compile errors

---
 src/client/northstar/dash-nodes/HistogramBox.tsx      | 8 ++++----
 src/client/util/DocumentManager.ts                    | 2 +-
 src/client/views/DocumentDecorations.tsx              | 1 +
 src/client/views/collections/CollectionBaseView.tsx   | 4 ++--
 src/client/views/collections/CollectionSchemaView.tsx | 2 +-
 src/client/views/nodes/FieldView.tsx                  | 3 ++-
 src/client/views/nodes/LinkMenu.tsx                   | 4 ++--
 7 files changed, 13 insertions(+), 11 deletions(-)

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

diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index 765ecf8f0..5e7b867b3 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -117,15 +117,15 @@ export class HistogramBox extends React.Component<FieldViewProps> {
             runInAction(() => {
                 this.HistoOp = histoOp ? histoOp.HistoOp : HistogramOperation.Empty;
                 if (this.HistoOp !== HistogramOperation.Empty) {
-                    reaction(() => Cast(this.props.Document.linkedFromDocs, listSpec(Doc), []), (docs) => this.HistoOp.Links.splice(0, this.HistoOp.Links.length, ...docs), { fireImmediately: true });
+                    reaction(() => Cast(this.props.Document.linkedFromDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc), (docs) => this.HistoOp.Links.splice(0, this.HistoOp.Links.length, ...docs), { fireImmediately: true });
                     reaction(() => Cast(this.props.Document.brushingDocs, listSpec(Doc), []).length,
                         () => {
-                            let brushingDocs = Cast(this.props.Document.brushingDocs, listSpec(Doc), []);
+                            let brushingDocs = Cast(this.props.Document.brushingDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc);
                             const proto = this.props.Document.proto;
                             if (proto) {
                                 this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...brushingDocs.map((brush, i) => {
-                                    brush.bckgroundColor = StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length];
-                                    let brushed = Cast(brush.brushingDocs, listSpec(Doc), []);
+                                    brush.backgroundColor = StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length];
+                                    let brushed = Cast(brush.brushingDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc);
                                     return { l: brush, b: brushed[0][Id] === proto[Id] ? brushed[1] : brushed[0] };
                                 }));
                             }
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 69964e2c9..4c264c7ec 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -71,7 +71,7 @@ export class DocumentManager {
     @computed
     public get LinkedDocumentViews() {
         return DocumentManager.Instance.DocumentViews.reduce((pairs, dv) => {
-            let linksList = Cast(dv.props.Document.linkedToDocs, listSpec(Doc));
+            let linksList = Cast(dv.props.Document.linkedToDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc);
             if (linksList && linksList.length) {
                 pairs.push(...linksList.reduce((pairs, link) => {
                     if (link) {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 2bde4e0c8..705e7a6d8 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -27,6 +27,7 @@ import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss";
 import { CollectionView } from "./collections/CollectionView";
 import { DocumentManager } from "../util/DocumentManager";
 import { FormattedTextBox } from "./nodes/FormattedTextBox";
+import { FieldView } from "./nodes/FieldView";
 
 library.add(faLink);
 
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 14b92af48..2b1f7bb37 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -63,13 +63,13 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         if (!(documentToAdd instanceof Doc)) {
             return false;
         }
-        let data = Cast(documentToAdd.data, listSpec(Doc), []);
+        let data = Cast(documentToAdd.data, listSpec(Doc), []).filter(d => d).map(d => d as Doc);
         for (const doc of data.filter(d => d instanceof Document)) {
             if (this.createsCycle(doc, containerDocument)) {
                 return true;
             }
         }
-        let annots = Cast(documentToAdd.annotations, listSpec(Doc), []);
+        let annots = Cast(documentToAdd.annotations, listSpec(Doc), []).filter(d => d).map(d => d as Doc);
         for (const annot of annots) {
             if (this.createsCycle(annot, containerDocument)) {
                 return true;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 6dd0e5935..ae949b2ed 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -276,7 +276,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     }
 
     get documentKeysCheckList() {
-        const docs = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
+        const docs = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).filter(d => d).map(d => d as Doc);
         let keys: { [key: string]: boolean } = {};
         // 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
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index cdc1bdc85..8bdf34181 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -99,7 +99,8 @@ export class FieldView extends React.Component<FieldViewProps> {
                     ContainingCollectionView={this.props.ContainingCollectionView}
                     parentActive={this.props.active}
                     toggleMinimized={emptyFunction}
-                    whenActiveChanged={this.props.whenActiveChanged} />
+                    whenActiveChanged={this.props.whenActiveChanged}
+                    bringToFront={emptyFunction} />
             );
         }
         else if (field instanceof List) {
diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx
index e21adebbc..24901913d 100644
--- a/src/client/views/nodes/LinkMenu.tsx
+++ b/src/client/views/nodes/LinkMenu.tsx
@@ -31,8 +31,8 @@ export class LinkMenu extends React.Component<Props> {
 
     render() {
         //get list of links from document
-        let linkFrom: Doc[] = Cast(this.props.docView.props.Document.linkedFromDocs, listSpec(Doc), []);
-        let linkTo: Doc[] = Cast(this.props.docView.props.Document.linkedToDocs, listSpec(Doc), []);
+        let linkFrom = Cast(this.props.docView.props.Document.linkedFromDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc);
+        let linkTo = Cast(this.props.docView.props.Document.linkedToDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc);
         if (this._editingLink === undefined) {
             return (
                 <div id="linkMenu-container">
-- 
cgit v1.2.3-70-g09d2


From 7da76d2538ebde21d7a878b5096d5a673e5d6375 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 10 May 2019 23:01:08 -0400
Subject: added summary screen grab icon as a template view of a text document.
  converted Cast(...ListSpec(Doc)) pattern to DocListCast

---
 src/client/northstar/dash-nodes/HistogramBox.tsx   | 10 +++----
 src/client/util/DocumentManager.ts                 |  6 ++--
 src/client/util/DragManager.ts                     |  6 ++--
 src/client/views/PresentationView.tsx              | 11 +++++--
 src/client/views/Templates.tsx                     | 35 +++++++++++++++-------
 .../views/collections/CollectionBaseView.tsx       |  8 ++---
 .../views/collections/CollectionSchemaView.tsx     |  6 ++--
 src/client/views/collections/CollectionSubView.tsx |  4 +--
 .../views/collections/CollectionTreeView.tsx       | 22 +++++++-------
 .../CollectionFreeFormLinksView.tsx                | 12 ++++----
 .../collections/collectionFreeForm/MarqueeView.tsx | 24 +++++++++++----
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 28 +++++++++--------
 src/client/views/nodes/DocumentView.tsx            | 23 +++++++-------
 src/client/views/nodes/IconBox.tsx                 |  4 +--
 src/client/views/nodes/ImageBox.tsx                | 14 ++++++---
 src/client/views/nodes/LinkMenu.tsx                |  6 ++--
 src/new_fields/Doc.ts                              | 10 +++++--
 17 files changed, 137 insertions(+), 92 deletions(-)

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

diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index 5e7b867b3..ed556cf45 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -19,7 +19,7 @@ import { HistogramLabelPrimitives } from "./HistogramLabelPrimitives";
 import { StyleConstants } from "../utils/StyleContants";
 import { NumCast, Cast } from "../../../new_fields/Types";
 import { listSpec } from "../../../new_fields/Schema";
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, DocListCast } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/RefField";
 
 
@@ -117,15 +117,15 @@ export class HistogramBox extends React.Component<FieldViewProps> {
             runInAction(() => {
                 this.HistoOp = histoOp ? histoOp.HistoOp : HistogramOperation.Empty;
                 if (this.HistoOp !== HistogramOperation.Empty) {
-                    reaction(() => Cast(this.props.Document.linkedFromDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc), (docs) => this.HistoOp.Links.splice(0, this.HistoOp.Links.length, ...docs), { fireImmediately: true });
-                    reaction(() => Cast(this.props.Document.brushingDocs, listSpec(Doc), []).length,
+                    reaction(() => DocListCast(this.props.Document.linkedFromDocs), (docs) => this.HistoOp.Links.splice(0, this.HistoOp.Links.length, ...docs), { fireImmediately: true });
+                    reaction(() => DocListCast(this.props.Document.brushingDocs).length,
                         () => {
-                            let brushingDocs = Cast(this.props.Document.brushingDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc);
+                            let brushingDocs = DocListCast(this.props.Document.brushingDocs);
                             const proto = this.props.Document.proto;
                             if (proto) {
                                 this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...brushingDocs.map((brush, i) => {
                                     brush.backgroundColor = StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length];
-                                    let brushed = Cast(brush.brushingDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc);
+                                    let brushed = DocListCast(brush.brushingDocs);
                                     return { l: brush, b: brushed[0][Id] === proto[Id] ? brushed[1] : brushed[0] };
                                 }));
                             }
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index a8b643d4d..9a7a94228 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,6 +1,6 @@
 import { computed, observable } from 'mobx';
 import { DocumentView } from '../views/nodes/DocumentView';
-import { Doc } from '../../new_fields/Doc';
+import { Doc, DocListCast } from '../../new_fields/Doc';
 import { FieldValue, Cast, NumCast, BoolCast } from '../../new_fields/Types';
 import { listSpec } from '../../new_fields/Schema';
 import { undoBatch } from './UndoManager';
@@ -73,7 +73,7 @@ export class DocumentManager {
     @computed
     public get LinkedDocumentViews() {
         return DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => {
-            let linksList = Cast(dv.props.Document.linkedToDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc);
+            let linksList = DocListCast(dv.props.Document.linkedToDocs);
             if (linksList && linksList.length) {
                 pairs.push(...linksList.reduce((pairs, link) => {
                     if (link) {
@@ -86,7 +86,7 @@ export class DocumentManager {
                     return pairs;
                 }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]));
             }
-            linksList = Cast(dv.props.Document.linkedFromDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc);
+            linksList = DocListCast(dv.props.Document.linkedFromDocs);
             if (linksList && linksList.length) {
                 pairs.push(...linksList.reduce((pairs, link) => {
                     if (link) {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index c0402f0c9..2da0d5b51 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,5 +1,5 @@
 import { action, runInAction } from "mobx";
-import { Doc, DocListCast } from "../../new_fields/Doc";
+import { Doc, DocListCastAsync } from "../../new_fields/Doc";
 import { Cast } from "../../new_fields/Types";
 import { emptyFunction } from "../../Utils";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
@@ -43,8 +43,8 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n
     let draggedDocs: Doc[] = [];
     let draggedFromDocs: Doc[] = []
     if (srcTarg) {
-        let linkToDocs = await DocListCast(srcTarg.linkedToDocs);
-        let linkFromDocs = await DocListCast(srcTarg.linkedFromDocs);
+        let linkToDocs = await DocListCastAsync(srcTarg.linkedToDocs);
+        let linkFromDocs = await DocListCastAsync(srcTarg.linkedFromDocs);
         if (linkToDocs) draggedDocs = linkToDocs.map(linkDoc => Cast(linkDoc.linkedTo, Doc) as Doc);
         if (linkFromDocs) draggedFromDocs = linkFromDocs.map(linkDoc => Cast(linkDoc.linkedFrom, Doc) as Doc);
     }
diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx
index 4853eb151..3fb24a339 100644
--- a/src/client/views/PresentationView.tsx
+++ b/src/client/views/PresentationView.tsx
@@ -141,8 +141,15 @@ export class PresentationView extends React.Component<PresViewProps>  {
             (activeW) => {
                 if (activeW && activeW instanceof Doc) {
                     PromiseValue(Cast(activeW.presentationView, Doc)).
-                        then(pv => runInAction(() =>
-                            self.Document = pv ? pv : (activeW.presentationView = new Doc())))
+                        then(pv => runInAction(() => {
+                            if (pv) self.Document = pv;
+                            else {
+                                pv = new Doc();
+                                pv.title = "Presentation Doc";
+                                activeW.presentationView = pv;
+                                self.Document = pv;
+                            }
+                        }))
                 }
             },
             { fireImmediately: true });
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index bbedc95f1..a98870b04 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -5,6 +5,7 @@ export enum TemplatePosition {
     InnerBottom,
     InnerRight,
     InnerLeft,
+    TopRight,
     OutterTop,
     OutterBottom,
     OutterRight,
@@ -39,30 +40,42 @@ export namespace Templates {
     // export const BasicLayout = new Template("Basic layout", "{layout}");
 
     export const Caption = new Template("Caption", TemplatePosition.OutterBottom,
-        `<div id="screenSpace" style="top: 100%; font-size:14px; background:yellow; width:100%; position:absolute"><FormattedTextBox {...props} fieldKey={"caption"} /></div>`
-    );
+        `<div id="screenSpace" style="top: 100%; font-size:14px; background:yellow; width:100%; position:absolute">
+            <FormattedTextBox {...props} fieldKey={"caption"} />
+        </div>` );
+
     export const TitleOverlay = new Template("TitleOverlay", TemplatePosition.InnerTop,
-        `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div>
-        <div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; ">
-        <span style="text-align:center;width:100%;font-size:20px;position:absolute;overflow:hidden;white-space:nowrap;text-overflow:ellipsis">{props.Document.title}</span>
-        </div></div>`
-    );
+        `<div>
+            <div style="height:100%; width:100%;position:absolute;">{layout}</div>
+            <div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; ">
+                <span style="text-align:center;width:100%;font-size:20px;position:absolute;overflow:hidden;white-space:nowrap;text-overflow:ellipsis">{props.Document.title}</span>
+            </div>
+        </div>` );
+
     export const Title = new Template("Title", TemplatePosition.InnerTop,
         `<div><div style="height:calc(100% - 25px); margin-top: 25px; width:100%;position:absolute;">{layout}</div>
         <div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; ">
             <span style="text-align:center;width:100%;font-size:20px;position:absolute;overflow:hidden;white-space:nowrap;text-overflow:ellipsis">{props.Document.title}</span>
-        </div></div>`
-    );
+        </div></div>` );
 
     export const Bullet = new Template("Bullet", TemplatePosition.InnerTop,
         `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div>
-        <div id="isBullet" style="height:15px; width:15px; margin-left:-16px; pointer-events:all; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white;">
-        <img  id="isBullet"  src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAZlBMVEX///8AAABmZmb7+/tYWFhgYGBFRUVSUlL4+Pg/Pz9jY2N5eXmcnJyioqKBgYFzc3NtbW1LS0s3NzfW1taWlpaOjo6IiIgvLy9WVlampqZcXFw5OTlvb28mJiYxMTHe3t7l5eUjIyMY8kIZAAAD2UlEQVR4nO2d61biMBRGW1FBEVHxfp15/5ecOVa5lHxtArmck/Xtn1BotjtNoXQtm4YQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEFIrX6UHEA1gsmrneceRjHm7cj28attKFOf/TRyKIliH4vzbZE+xE2zbZYkxRWX5Y9JT/BW0X3G+NtlR3Ahar7jcMtlS3Ba0XXG+Y7JW3BW0XHHZM/lR7AvaVewL/ijuC1pV3Bf8VnQJ2lR0CYriq/Nxg4puwfa1aZ7dz9yUHnEgN26NZ3luWkPFd7fEtHsWVDwpO+YgTgYKCuYn6tAU7TBecaygcGpZEQie7m5luKJPQQFUvCwx5iAuvQoK4KShvSIoOHVtCz7dnOUecxBn7kG/urc2eCz6T9EOcxXDCgpAUetyAwoOCBqrGF5QMKR4mCA8L+pTBIJwkRl95eifJjPHTDYTFQ8vePyrs3BsBfXLzfFHkvKKMY4j1ctNnCmmuGKslfCQT0RZiPdFVmnFmOcy36sDWYn7DU9hxdifRkKuEGQh/pWW0K/QiUlxtUxVxTTXyhQtN6kuI6mpmO5qpxJFIBjl1yMVimmvV4PfrnIq3iYsKICTRj7F9L84gIq38fYwCCj4HnMfRY/FPL8ZFayYo6BQbLlJeZrYpVDFXAUFcMtKWkUgmOhmnwKKOQsK4NaxdIp5CwqZj8X8gv27jNecJ9nZuXtnie/SzjhRQcHkt6Fnq1imoAAUY1csVVDIUrFcQSGDIhC8jriLQZIrli0oXKdVLF1QSFqxfEEBVLyI8NYXCgoKySaqhinakajimxrBRBX1FBQSVNRyDP4SXVGbYHRFfYJN8xhTESwyj5HHHEjEihoLCqDiXfAb3aksKESqCAoqEIxUUW9BAS03E+93mOhcZDYcXVF3QeHBPcI3v4qo4EPiUQcBKr75vHaiv6AAKt6NV0SCqgoKqOKYovpFZgOo+DmsOHkyUlA4ZKKamaIdQPEJK5oqKKCKM7D9zFZBIayiuYICWm5cFWef7o3vs486CP8VdQIEVRcU7sFE7VecgSmqvKDgVxEJqi8ogIof2xVnH2YLCuMT1fAU7RirOPtrXHCsovmCwlDFCgoKWNH4IrMBTdQ/NUzRjiu3CeCq9HAPAVSspaDgX9FkQcG3ollB34qGBf0UTQv6KBoXHFc0LzimWIFg0ywGBBelBxcHXLGKggKqWElBwV2xIkF3xaoEXYqVCe4rVifYV3wpPZwULOouKLzUXVBY1F1QeKm7oLCoXVAqVi7YNM7/F0YIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCG+/ANh4i1CHdc63QAAAABJRU5ErkJggg==" 
+        <div id="isExpander" style="height:15px; width:15px; margin-left:-16px; pointer-events:all; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white;">
+        <img  id="isExpander"  src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAZlBMVEX///8AAABmZmb7+/tYWFhgYGBFRUVSUlL4+Pg/Pz9jY2N5eXmcnJyioqKBgYFzc3NtbW1LS0s3NzfW1taWlpaOjo6IiIgvLy9WVlampqZcXFw5OTlvb28mJiYxMTHe3t7l5eUjIyMY8kIZAAAD2UlEQVR4nO2d61biMBRGW1FBEVHxfp15/5ecOVa5lHxtArmck/Xtn1BotjtNoXQtm4YQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEFIrX6UHEA1gsmrneceRjHm7cj28attKFOf/TRyKIliH4vzbZE+xE2zbZYkxRWX5Y9JT/BW0X3G+NtlR3Ahar7jcMtlS3Ba0XXG+Y7JW3BW0XHHZM/lR7AvaVewL/ijuC1pV3Bf8VnQJ2lR0CYriq/Nxg4puwfa1aZ7dz9yUHnEgN26NZ3luWkPFd7fEtHsWVDwpO+YgTgYKCuYn6tAU7TBecaygcGpZEQie7m5luKJPQQFUvCwx5iAuvQoK4KShvSIoOHVtCz7dnOUecxBn7kG/urc2eCz6T9EOcxXDCgpAUetyAwoOCBqrGF5QMKR4mCA8L+pTBIJwkRl95eifJjPHTDYTFQ8vePyrs3BsBfXLzfFHkvKKMY4j1ctNnCmmuGKslfCQT0RZiPdFVmnFmOcy36sDWYn7DU9hxdifRkKuEGQh/pWW0K/QiUlxtUxVxTTXyhQtN6kuI6mpmO5qpxJFIBjl1yMVimmvV4PfrnIq3iYsKICTRj7F9L84gIq38fYwCCj4HnMfRY/FPL8ZFayYo6BQbLlJeZrYpVDFXAUFcMtKWkUgmOhmnwKKOQsK4NaxdIp5CwqZj8X8gv27jNecJ9nZuXtnie/SzjhRQcHkt6Fnq1imoAAUY1csVVDIUrFcQSGDIhC8jriLQZIrli0oXKdVLF1QSFqxfEEBVLyI8NYXCgoKySaqhinakajimxrBRBX1FBQSVNRyDP4SXVGbYHRFfYJN8xhTESwyj5HHHEjEihoLCqDiXfAb3aksKESqCAoqEIxUUW9BAS03E+93mOhcZDYcXVF3QeHBPcI3v4qo4EPiUQcBKr75vHaiv6AAKt6NV0SCqgoKqOKYovpFZgOo+DmsOHkyUlA4ZKKamaIdQPEJK5oqKKCKM7D9zFZBIayiuYICWm5cFWef7o3vs486CP8VdQIEVRcU7sFE7VecgSmqvKDgVxEJqi8ogIof2xVnH2YLCuMT1fAU7RirOPtrXHCsovmCwlDFCgoKWNH4IrMBTdQ/NUzRjiu3CeCq9HAPAVSspaDgX9FkQcG3ollB34qGBf0UTQv6KBoXHFc0LzimWIFg0ywGBBelBxcHXLGKggKqWElBwV2xIkF3xaoEXYqVCe4rVifYV3wpPZwULOouKLzUXVBY1F1QeKm7oLCoXVAqVi7YNM7/F0YIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCG+/ANh4i1CHdc63QAAAABJRU5ErkJggg==" 
          width="15px" height="15px"/>
          </div>
         </div>`
     );
 
+    export function ImageOverlay(width: number, height: number, field: string = "thumbnail") {
+        return (`<div>
+                <div style="height:100%; width:100%;position:absolute;">{layout}</div>
+                <div style="width:${width}px; height:${height}px; top:0; right:0; background:rgba(0,0,0,0.25); position:absolute;overflow:hidden;">
+                    <ImageBox id="isExpander" {...props} PanelWidth={${width}} fieldKey={"${field}"} />
+                </div>
+            </div>`);
+    }
+
     export const TemplateList: Template[] = [Title, TitleOverlay, Caption, Bullet];
 
     export function sortTemplates(a: Template, b: Template) {
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 2b1f7bb37..645296d27 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -4,7 +4,7 @@ import * as React from 'react';
 import { ContextMenu } from '../ContextMenu';
 import { FieldViewProps } from '../nodes/FieldView';
 import { Cast, FieldValue, PromiseValue, NumCast } from '../../../new_fields/Types';
-import { Doc, FieldResult, Opt } from '../../../new_fields/Doc';
+import { Doc, FieldResult, Opt, DocListCast } from '../../../new_fields/Doc';
 import { listSpec } from '../../../new_fields/Schema';
 import { List } from '../../../new_fields/List';
 import { Id } from '../../../new_fields/RefField';
@@ -63,13 +63,13 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         if (!(documentToAdd instanceof Doc)) {
             return false;
         }
-        let data = Cast(documentToAdd.data, listSpec(Doc), []).filter(d => d).map(d => d as Doc);
-        for (const doc of data.filter(d => d instanceof Document)) {
+        let data = DocListCast(documentToAdd.data);
+        for (const doc of data) {
             if (this.createsCycle(doc, containerDocument)) {
                 return true;
             }
         }
-        let annots = Cast(documentToAdd.annotations, listSpec(Doc), []).filter(d => d).map(d => d as Doc);
+        let annots = DocListCast(documentToAdd.annotations);
         for (const annot of annots) {
             if (this.createsCycle(annot, containerDocument)) {
                 return true;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index ae949b2ed..506e60a65 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -19,7 +19,7 @@ import { DocumentView } from "../nodes/DocumentView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
 import { CollectionSubView } from "./CollectionSubView";
-import { Opt, Field, Doc, DocListCast } from "../../../new_fields/Doc";
+import { Opt, Field, Doc, DocListCastAsync, DocListCast } from "../../../new_fields/Doc";
 import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
 import { listSpec } from "../../../new_fields/Schema";
 import { List } from "../../../new_fields/List";
@@ -118,7 +118,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                         }
                         const run = script.run;
                         //TODO This should be able to be refactored to compile the script once
-                        const val = await DocListCast(this.props.Document[this.props.fieldKey])
+                        const val = await DocListCastAsync(this.props.Document[this.props.fieldKey])
                         val && val.forEach(doc => applyToDoc(doc, run));
                     }}>
                 </EditableView>
@@ -276,7 +276,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     }
 
     get documentKeysCheckList() {
-        const docs = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).filter(d => d).map(d => d as Doc);
+        const docs = DocListCast(this.props.Document[this.props.fieldKey]);
         let keys: { [key: string]: boolean } = {};
         // 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
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 0b08e150a..a86d250bd 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -10,7 +10,7 @@ import * as rp from 'request-promise';
 import { CollectionView } from "./CollectionView";
 import { CollectionPDFView } from "./CollectionPDFView";
 import { CollectionVideoView } from "./CollectionVideoView";
-import { Doc, Opt, FieldResult } from "../../../new_fields/Doc";
+import { Doc, Opt, FieldResult, DocListCast } from "../../../new_fields/Doc";
 import { DocComponent } from "../DocComponent";
 import { listSpec } from "../../../new_fields/Schema";
 import { Cast, PromiseValue, FieldValue, ListSpec } from "../../../new_fields/Types";
@@ -49,7 +49,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
         get children() {
             //TODO tfs: This might not be what we want?
             //This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue)
-            return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).filter(doc => FieldValue(doc)).map(doc => doc as Doc);
+            return DocListCast(this.props.Document[this.props.fieldKey]);
         }
 
         @action
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 33787f06b..78c84cc89 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -18,9 +18,7 @@ import { Main } from '../Main';
 import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
 import { CollectionDockingView } from './CollectionDockingView';
 import { DocumentManager } from '../../util/DocumentManager';
-import { Utils } from '../../../Utils';
 import { List } from '../../../new_fields/List';
-import { indexOf } from 'typescript-collections/dist/lib/arrays';
 
 
 export interface TreeViewProps {
@@ -155,16 +153,21 @@ class TreeView extends React.Component<TreeViewProps> {
         let keys = Array.from(Object.keys(this.props.document));
         if (this.props.document.proto instanceof Doc) {
             keys.push(...Array.from(Object.keys(this.props.document.proto)));
+            while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1);
         }
         keys.map(key => {
-            let docList = Cast(this.props.document[key], listSpec(Doc));
-            if (docList instanceof List && docList.length && docList[0] instanceof Doc) {
+            let docList = DocListCast(this.props.document[key]);
+            let doc = Cast(this.props.document[key], Doc);
+            if (doc instanceof Doc || docList.length) {
                 if (!this._collapsed) {
                     bulletType = BulletType.Collapsible;
+                    let spacing = (key === "data") ? 0 : -10;
                     contentElement.push(<ul key={key + "more"}>
                         {(key === "data") ? (null) :
-                            <span className="collectionTreeView-keyHeader" key={key}>{key}</span>}
-                        {TreeView.GetChildElements(docList, key !== "data", (doc: Doc) => this.remove(doc, key), this.move, this.props.dropAction)}
+                            <span className="collectionTreeView-keyHeader" style={{ display: "block", marginTop: "7px" }} key={key}>{key}</span>}
+                        <div style={{ display: "block", marginTop: `${spacing}px` }}>
+                            {TreeView.GetChildElements(doc instanceof Doc ? [doc] : docList, key !== "data", (doc: Doc) => this.remove(doc, key), this.move, this.props.dropAction)}
+                        </div>
                     </ul >);
                 } else
                     bulletType = BulletType.Collapsed;
@@ -179,7 +182,7 @@ class TreeView extends React.Component<TreeViewProps> {
             </li>
         </div>;
     }
-    public static GetChildElements(docs: (Doc | Promise<Doc>)[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) {
+    public static GetChildElements(docs: Doc[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) {
         return docs.filter(child => child instanceof Doc && !child.excludeFromLibrary && (allowMinimized || !child.isMinimized)).filter(doc => FieldValue(doc)).map(child =>
             <TreeView document={child as Doc} key={(child as Doc)[Id]} deleteDoc={remove} moveDocument={move} dropAction={dropAction} />);
     }
@@ -203,12 +206,11 @@ export class CollectionTreeView extends CollectionSubView(Document) {
         }
     }
     render() {
-        const children = this.children;
         let dropAction = StrCast(this.props.Document.dropAction, "alias") as dropActionType;
-        if (!children) {
+        if (!this.children) {
             return (null);
         }
-        let childElements = TreeView.GetChildElements(children, false, this.remove, this.props.moveDocument, dropAction);
+        let childElements = TreeView.GetChildElements(this.children, false, this.remove, this.props.moveDocument, dropAction);
 
         return (
             <div id="body" className="collectionTreeView-dropTarget"
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 62b1456cf..d5ce4e1e7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -7,7 +7,7 @@ import { CollectionViewProps } from "../CollectionSubView";
 import "./CollectionFreeFormLinksView.scss";
 import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
 import React = require("react");
-import { Doc, DocListCast } from "../../../../new_fields/Doc";
+import { Doc, DocListCastAsync, DocListCast } from "../../../../new_fields/Doc";
 import { Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types";
 import { listSpec } from "../../../../new_fields/Schema";
 import { List } from "../../../../new_fields/List";
@@ -20,11 +20,11 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
     componentDidMount() {
         this._brushReactionDisposer = reaction(
             () => {
-                let doclist = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
-                return { doclist: doclist ? doclist : [], xs: doclist instanceof List ? doclist.map(d => d instanceof Doc && d.x) : [] };
+                let doclist = DocListCast(this.props.Document[this.props.fieldKey]);
+                return { doclist: doclist ? doclist : [], xs: doclist.map(d => d.x) };
             },
-            async () => {
-                let doclist = await DocListCast(this.props.Document[this.props.fieldKey]);
+            () => {
+                let doclist = DocListCast(this.props.Document[this.props.fieldKey]);
                 let views = doclist ? doclist.filter(doc => StrCast(doc.backgroundLayout).indexOf("istogram") !== -1) : [];
                 views.forEach((dstDoc, i) => {
                     views.forEach((srcDoc, j) => {
@@ -84,7 +84,7 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
         }
         if (view.props.ContainingCollectionView) {
             let collid = view.props.ContainingCollectionView.props.Document[Id];
-            Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).filter(d => d).map(d => d as Doc).
+            DocListCast(this.props.Document[this.props.fieldKey]).
                 filter(child =>
                     child[Id] === collid).map(view =>
                         DocumentManager.Instance.getDocumentViews(view).map(view =>
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 1bf39e335..9ace0272a 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,3 +1,4 @@
+import * as htmlToImage from "html-to-image";
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Docs } from "../../../documents/Documents";
@@ -14,6 +15,8 @@ import { Doc } from "../../../../new_fields/Doc";
 import { NumCast, Cast } from "../../../../new_fields/Types";
 import { InkField, StrokeData } from "../../../../new_fields/InkField";
 import { List } from "../../../../new_fields/List";
+import { ImageField } from "../../../../new_fields/URLField";
+import { Template, Templates } from "../../Templates";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -30,6 +33,7 @@ interface MarqueeViewProps {
 @observer
 export class MarqueeView extends React.Component<MarqueeViewProps>
 {
+    private _mainCont = React.createRef<HTMLDivElement>();
     @observable _lastX: number = 0;
     @observable _lastY: number = 0;
     @observable _downX: number = 0;
@@ -166,7 +170,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
 
     @undoBatch
     @action
-    marqueeCommand = (e: KeyboardEvent) => {
+    marqueeCommand = async (e: KeyboardEvent) => {
         if (this._commandExecuted) {
             return;
         }
@@ -224,13 +228,17 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 let scrpt = this.props.getTransform().inverse().transformPoint(bounds.left, bounds.top);
                 let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" });
 
+                let dataUrl = await htmlToImage.toPng(this._mainCont.current!, { width: bounds.width, height: bounds.height, quality: 1 });
+                summary.proto!.thumbnail = new ImageField(new URL(dataUrl));
+
+                summary.proto!.templates = new List<string>([Templates.ImageOverlay(Math.min(50, bounds.width), bounds.height * Math.min(50, bounds.width) / bounds.width, "thumbnail")]);
                 if (e.key === "s" || e.key === "p") {
                     summary.proto!.maximizeOnRight = true;
                     newCollection.proto!.summaryDoc = summary;
                     selected = [newCollection];
                 }
                 summary.proto!.summarizedDocs = new List<Doc>(selected);
-                summary.proto!.isButton = true;
+                //summary.proto!.isButton = true;
                 selected.map(summarizedDoc => {
                     let maxx = NumCast(summarizedDoc.x, undefined);
                     let maxy = NumCast(summarizedDoc.y, undefined);
@@ -313,17 +321,21 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
 
     @computed
     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={{ width: `${Math.abs(v[0])}`, height: `${Math.abs(v[1])}` }} >
             <span className="marquee-legend" />
         </div>;
     }
 
     render() {
+        let p = this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
         return <div className="marqueeView" style={{ borderRadius: "inherit" }} onClick={this.onClick} onPointerDown={this.onPointerDown}>
-            {this.props.children}
-            {!this._visible ? (null) : this.marqueeDiv}
+            <div style={{ position: "absolute", transform: `translate(${p[0]}px, ${p[1]}px)` }} >
+                <div ref={this._mainCont} style={{ position: "absolute", transform: `translate(${-p[0]}px, ${-p[1]}px)` }} >
+                    {this.props.children}
+                </div>
+                {!this._visible ? null : this.marqueeDiv}
+            </div>
         </div>;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index b05f2eea2..39d216da0 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -9,10 +9,10 @@ import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schem
 import { FieldValue, Cast, NumCast, BoolCast, StrCast } from "../../../new_fields/Types";
 import { OmitKeys, Utils } from "../../../Utils";
 import { SelectionManager } from "../../util/SelectionManager";
-import { Doc, DocListCast, HeightSym } from "../../../new_fields/Doc";
+import { Doc, DocListCastAsync, DocListCast, } from "../../../new_fields/Doc";
 import { List } from "../../../new_fields/List";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { undoBatch, UndoManager } from "../../util/UndoManager";
+import { UndoManager } from "../../util/UndoManager";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
 }
@@ -65,7 +65,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     contentScaling = () => this.nativeWidth > 0 ? this.width / this.nativeWidth : 1;
     panelWidth = () => this.props.PanelWidth();
     panelHeight = () => this.props.PanelHeight();
-    toggleMinimized = async () => this.toggleIcon(await DocListCast(this.props.Document.maximizedDocs));
+    toggleMinimized = async () => this.toggleIcon(await DocListCastAsync(this.props.Document.maximizedDocs));
     getTransform = (): Transform => this.props.ScreenToLocalTransform()
         .translate(-this.X, -this.Y)
         .scale(1 / this.contentScaling()).scale(1 / this.zoom)
@@ -132,7 +132,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         let minimizedDoc: Doc | undefined = this.props.Document;
         if (!maximizedDocs) {
             minimizedDoc = await Cast(this.props.Document.minimizedDoc, Doc);
-            if (minimizedDoc) maximizedDocs = await DocListCast(minimizedDoc.maximizedDocs);
+            if (minimizedDoc) maximizedDocs = await DocListCastAsync(minimizedDoc.maximizedDocs);
         }
         if (minimizedDoc && maximizedDocs) {
             let minimizedTarget = minimizedDoc;
@@ -176,16 +176,18 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         let altKey = e.altKey;
         if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
             Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
-            let isBullet = (e.target as any).id === "isBullet";
-            let isIcon = StrCast(this.props.Document.layout).indexOf("IconBox") !== -1;
-            if (BoolCast(this.props.Document.isButton, false) || isBullet) {
-                let maximizedDocs = await DocListCast(isBullet ? this.props.Document.subBulletDocs : isIcon ? this.props.Document.maximizedDocs : this.props.Document.summarizedDocs);
-                if (maximizedDocs) {   // bcz: need a better way to associate behaviors with click events on widget-documents
+            let isExpander = (e.target as any).id === "isExpander";
+            if (BoolCast(this.props.Document.isButton, false) || isExpander) {
+                let subBulletDocs = await DocListCastAsync(this.props.Document.subBulletDocs);
+                let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs);
+                let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs);
+                let expandedDocs = [...(subBulletDocs ? subBulletDocs : []), ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : [])];
+                if (expandedDocs) {   // bcz: need a better way to associate behaviors with click events on widget-documents
                     if ((altKey && !this.props.Document.maximizeOnRight) || (!altKey && this.props.Document.maximizeOnRight)) {
-                        let dataDocs = await DocListCast(CollectionDockingView.Instance.props.Document.data);
+                        let dataDocs = DocListCast(CollectionDockingView.Instance.props.Document.data);
                         if (dataDocs) {
                             SelectionManager.DeselectAll();
-                            maximizedDocs.forEach(maxDoc => {
+                            expandedDocs.forEach(maxDoc => {
                                 maxDoc.isMinimized = false;
                                 if (!dataDocs || dataDocs.indexOf(maxDoc) == -1) {
                                     CollectionDockingView.Instance.AddRightSplit(maxDoc);
@@ -195,8 +197,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                             });
                         }
                     } else {
-                        this.props.addDocument && maximizedDocs.forEach(async maxDoc => this.props.addDocument!(await maxDoc, false));
-                        this.toggleIcon(maximizedDocs);
+                        this.props.addDocument && expandedDocs.forEach(async maxDoc => this.props.addDocument!(await maxDoc, false));
+                        this.toggleIcon(expandedDocs);
                     }
                 }
             }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5aa74c703..90f67db7c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -12,13 +12,13 @@ import { CollectionPDFView } from "../collections/CollectionPDFView";
 import { CollectionVideoView } from "../collections/CollectionVideoView";
 import { CollectionView } from "../collections/CollectionView";
 import { ContextMenu } from "../ContextMenu";
-import { Template, Templates } from "./../Templates";
+import { Template } from "./../Templates";
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
 import React = require("react");
-import { Opt, Doc, WidthSym, HeightSym, DocListCast } from "../../../new_fields/Doc";
+import { Opt, Doc, WidthSym, HeightSym, DocListCastAsync, DocListCast } from "../../../new_fields/Doc";
 import { DocComponent } from "../DocComponent";
-import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
+import { createSchema, makeInterface } from "../../../new_fields/Schema";
 import { FieldValue, StrCast, BoolCast, Cast } from "../../../new_fields/Types";
 import { List } from "../../../new_fields/List";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
@@ -109,8 +109,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
         // bcz: kind of ugly .. setup a reaction to update the title of a summary document's target (maximizedDocs) whenver the summary doc's title changes
         this._reactionDisposer = reaction(() => [this.props.Document.maximizedDocs, this.props.Document.summaryDoc, this.props.Document.summaryDoc instanceof Doc ? this.props.Document.summaryDoc.title : ""],
-            async () => {
-                let maxDoc = await DocListCast(this.props.Document.maximizedDocs);
+            () => {
+                let maxDoc = DocListCast(this.props.Document.maximizedDocs);
                 if (maxDoc && StrCast(this.props.Document.layout).indexOf("IconBox") !== -1) {
                     this.props.Document.title = (maxDoc && maxDoc.length === 1 ? maxDoc[0].title + ".icon" : "");
                 }
@@ -145,7 +145,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean) {
         if (this._mainCont.current) {
-            let allConnected = dragSubBullets ? [this.props.Document, ...Cast(this.props.Document.subBulletDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc)] : [this.props.Document];
+            let allConnected = [this.props.Document, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])];
             const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0);
             let dragData = new DragManager.DocumentDragData(allConnected);
             const [xoff, yoff] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top);
@@ -169,17 +169,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             SelectionManager.SelectDoc(this, e.ctrlKey);
         }
     }
-    _hitIsBullet = false;
+    _hitExpander = false;
     onPointerDown = (e: React.PointerEvent): void => {
         this._downX = e.clientX;
         this._downY = e.clientY;
         if (CollectionFreeFormView.RIGHT_BTN_DRAG && (e.button === 2 || (e.button === 0 && e.altKey)) && !this.isSelected()) {
             return;
         }
-        this._hitIsBullet = (e.target && (e.target as any).id === "isBullet") || Cast(this.props.Document.subBulletDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc).length > 0;
+        this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0;
         if (e.shiftKey && e.buttons === 1) {
             if (this.props.isTopMost) {
-                this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey ? "alias" : undefined, this._hitIsBullet);
+                this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey ? "alias" : undefined, this._hitExpander);
             } else if (this.props.Document) {
                 CollectionDockingView.Instance.StartOtherDrag([Doc.MakeAlias(this.props.Document)], e);
             }
@@ -197,7 +197,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 document.removeEventListener("pointermove", this.onPointerMove);
                 document.removeEventListener("pointerup", this.onPointerUp);
                 if (!e.altKey && !this.topMost && (!CollectionFreeFormView.RIGHT_BTN_DRAG && e.buttons === 1) || (CollectionFreeFormView.RIGHT_BTN_DRAG && e.buttons === 2)) {
-                    this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitIsBullet);
+                    this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitExpander);
                 }
             }
             e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
@@ -232,6 +232,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         ContextMenu.Instance.clearItems();
         SelectionManager.DeselectAll();
     }
+
     @undoBatch
     @action
     drop = async (e: Event, de: DragManager.DropEvent) => {
@@ -259,7 +260,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
     }
 
-
     @action
     addTemplate = (template: Template) => {
         this.templates.push(template.Layout);
@@ -307,7 +307,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
     }
 
-
     isSelected = () => SelectionManager.IsSelected(this);
     select = (ctrlPressed: boolean) => SelectionManager.SelectDoc(this, ctrlPressed);
 
diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
index 4bcb4c636..48aecdcd0 100644
--- a/src/client/views/nodes/IconBox.tsx
+++ b/src/client/views/nodes/IconBox.tsx
@@ -2,12 +2,12 @@ import React = require("react");
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { computed, observable, runInAction, reaction, IReactionDisposer } from "mobx";
+import { computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./IconBox.scss";
 import { Cast, StrCast, BoolCast } from "../../../new_fields/Types";
-import { Doc, DocListCast } from "../../../new_fields/Doc";
+import { Doc } from "../../../new_fields/Doc";
 import { IconField } from "../../../new_fields/IconField";
 import { ContextMenu } from "../ContextMenu";
 import Measure from "react-measure";
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 0e9e904a8..f9659a4b2 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,5 +1,5 @@
 
-import { action, observable } from 'mobx';
+import { action, observable, trace } from 'mobx';
 import { observer } from "mobx-react";
 import Lightbox from 'react-image-lightbox';
 import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
@@ -157,15 +157,21 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
     }
 
     render() {
+        trace();
         let field = this.Document[this.props.fieldKey];
         let paths: string[] = ["http://www.cs.brown.edu/~bcz/face.gif"];
         if (field instanceof ImageField) paths = [field.url.href];
         else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => (p as ImageField).url.href);
-        let nativeWidth = FieldValue(this.Document.nativeWidth, 1);
+        let nativeWidth = FieldValue(this.Document.nativeWidth, (this.props.PanelWidth as any) as string ? Number((this.props.PanelWidth as any) as string) : 50);
         let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive";
+        let id = this.props.id;
         return (
-            <div className={`imageBox-cont${interactive}`} onPointerDown={this.onPointerDown} onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
-                <img src={paths[Math.min(paths.length, this._photoIndex)]} style={{ objectFit: (this._photoIndex === 0 ? undefined : "contain") }} width={nativeWidth} alt="Image not found" ref={this._imgRef} onLoad={this.onLoad} />
+            <div id={id} className={`imageBox-cont${interactive}`} onPointerDown={this.onPointerDown} onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
+                <img id={id} src={paths[Math.min(paths.length, this._photoIndex)]}
+                    style={{ objectFit: (this._photoIndex === 0 ? undefined : "contain") }}
+                    width={nativeWidth}
+                    ref={this._imgRef}
+                    onLoad={this.onLoad} />
                 {paths.length > 1 ? this.dots(paths) : (null)}
                 {this.lightbox(paths)}
             </div>);
diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx
index 24901913d..7bf13d5f9 100644
--- a/src/client/views/nodes/LinkMenu.tsx
+++ b/src/client/views/nodes/LinkMenu.tsx
@@ -5,7 +5,7 @@ import { LinkBox } from "./LinkBox";
 import { LinkEditor } from "./LinkEditor";
 import './LinkMenu.scss';
 import React = require("react");
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, DocListCast } from "../../../new_fields/Doc";
 import { Cast, FieldValue } from "../../../new_fields/Types";
 import { listSpec } from "../../../new_fields/Schema";
 import { Id } from "../../../new_fields/RefField";
@@ -31,8 +31,8 @@ export class LinkMenu extends React.Component<Props> {
 
     render() {
         //get list of links from document
-        let linkFrom = Cast(this.props.docView.props.Document.linkedFromDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc);
-        let linkTo = Cast(this.props.docView.props.Document.linkedToDocs, listSpec(Doc), []).filter(d => d).map(d => d as Doc);
+        let linkFrom = DocListCast(this.props.docView.props.Document.linkedFromDocs);
+        let linkTo = DocListCast(this.props.docView.props.Document.linkedToDoc);
         if (this._editingLink === undefined) {
             return (
                 <div id="linkMenu-container">
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 2ab145fa3..46ccb3e90 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -29,13 +29,17 @@ export const SelfProxy = Symbol("SelfProxy");
 export const WidthSym = Symbol("Width");
 export const HeightSym = Symbol("Height");
 
-export function DocListCast(field: FieldResult): Promise<Doc[] | undefined>;
-export function DocListCast(field: FieldResult, defaultValue: Doc[]): Promise<Doc[]>;
-export function DocListCast(field: FieldResult, defaultValue?: Doc[]) {
+export function DocListCastAsync(field: FieldResult): Promise<Doc[] | undefined>;
+export function DocListCastAsync(field: FieldResult, defaultValue: Doc[]): Promise<Doc[]>;
+export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) {
     const list = Cast(field, listSpec(Doc));
     return list ? Promise.all(list) : Promise.resolve(defaultValue);
 }
 
+export function DocListCast(field: FieldResult) {
+    return Cast(field, listSpec(Doc), []).filter(d => d && d instanceof Doc).map(d => d as Doc)
+}
+
 @Deserializable("doc").withFields(["id"])
 export class Doc extends RefField {
     constructor(id?: FieldId, forceSave?: boolean) {
-- 
cgit v1.2.3-70-g09d2


From 47424484409938367f47068274af4c0f51d4490c Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 12 May 2019 20:24:26 -0400
Subject: fixed issues with scrolled text input.  preview region for schema. 
 and pasting in tables with ctrl-t.

---
 src/client/util/TooltipTextMenu.scss               | 26 +++++++-------
 src/client/views/PreviewCursor.tsx                 |  9 ++++-
 .../views/collections/CollectionSchemaView.tsx     |  2 +-
 .../collections/collectionFreeForm/MarqueeView.tsx | 40 ++++++++++++++++++++++
 src/client/views/nodes/FormattedTextBox.scss       |  3 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  4 +--
 6 files changed, 65 insertions(+), 19 deletions(-)

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

diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss
index 70d9ad772..437da0d63 100644
--- a/src/client/util/TooltipTextMenu.scss
+++ b/src/client/util/TooltipTextMenu.scss
@@ -162,19 +162,6 @@
   .ProseMirror-icon span {
     vertical-align: text-top;
   }  
-.ProseMirror-example-setup-style hr {
-    padding: 2px 10px;
-    border: none;
-    margin: 1em 0;
-  }
-  
-  .ProseMirror-example-setup-style hr:after {
-    content: "";
-    display: block;
-    height: 1px;
-    background-color: silver;
-    line-height: 2px;
-  }
   
   .ProseMirror ul, .ProseMirror ol {
     padding-left: 30px;
@@ -255,6 +242,19 @@
     -webkit-transform: translateX(-50%);
     transform: translateX(-50%);
     pointer-events: all;
+    .ProseMirror-example-setup-style hr {
+      padding: 2px 10px;
+      border: none;
+      margin: 1em 0;
+    }
+    
+    .ProseMirror-example-setup-style hr:after {
+      content: "";
+      display: block;
+      height: 1px;
+      background-color: silver;
+      line-height: 2px;
+    }
 }
 
 .tooltipMenu:before {
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 78024a58c..4218ea7c9 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -18,6 +18,13 @@ export class PreviewCursor extends React.Component<{}> {
     constructor(props: any) {
         super(props);
         document.addEventListener("keydown", this.onKeyPress);
+        document.addEventListener("paste", this.paste);
+    }
+    paste = (e: ClipboardEvent) => {
+        console.log(e.clipboardData);
+        console.log(e.clipboardData.getData("text/html"));
+        console.log(e.clipboardData.getData("text/csv"));
+        console.log(e.clipboardData.getData("text/plain"));
     }
 
     @action
@@ -28,7 +35,7 @@ export class PreviewCursor extends React.Component<{}> {
         //if not these keys, make a textbox if preview cursor is active!
         if (e.key.startsWith("F") && !e.key.endsWith("F")) {
         } else if (e.key !== "Escape" && e.key !== "Alt" && e.key !== "Shift" && e.key !== "Meta" && e.key !== "Control" && !e.defaultPrevented && !(e as any).DASHFormattedTextBoxHandled) {
-            if ((!e.ctrlKey && !e.metaKey) || e.key === "v" || e.key === "q") {
+            if ((!e.ctrlKey && !e.metaKey) || (e.key >= "a" && e.key <= "z")) {
                 PreviewCursor.Visible && PreviewCursor._onKeyPress && PreviewCursor._onKeyPress(e);
                 PreviewCursor.Visible = false;
             }
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 506e60a65..18319dc77 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -253,7 +253,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     get previewPanel() {
         // let doc = CompileScript(this.previewScript, { this: selected }, true)();
         const previewDoc = this.previewDocument;
-        return !previewDoc ? (null) : (
+        return !previewDoc || !this.previewRegionWidth ? (null) : (
             <div className="collectionSchemaView-previewRegion" style={{ width: `${this.previewRegionWidth}px` }}>
                 <div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
                     <DocumentView Document={previewDoc} isTopMost={false} selectOnLoad={false}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 718a75770..611ee7fd6 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -79,6 +79,46 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                     y += 40 * this.props.getTransform().Scale;
                 })
             })();
+        } else if (e.key === "t" && e.ctrlKey) {
+            //heuristically converts pasted text into a table.
+            // assumes each entry is separated by a tab
+            // skips all rows until it gets to a row with more than one entry
+            // assumes that 1st row has header entry for each column
+            // assumes subsequent rows have entries for each column header OR
+            //         any row that has only one column is a section header-- this header is then added as a column to subsequent rows until the next header
+            // assumes each cell is a string or a number
+            e.preventDefault();
+            (async () => {
+                let text: string = await navigator.clipboard.readText();
+                let ns = text.split("\n").filter(t => t.trim() != "\r" && t.trim() != "");
+                while (ns.length > 0 && ns[0].split("\t").length < 2)
+                    ns.splice(0, 1);
+                if (ns.length > 0) {
+                    let columns = ns[0].split("\t");
+                    let docList: Doc[] = [];
+                    let groupAttr: string | number = "";
+                    for (let i = 1; i < ns.length - 1; i++) {
+                        let values = ns[i].split("\t");
+                        if (values.length === 1 && columns.length > 1) {
+                            groupAttr = values[0];
+                            continue;
+                        }
+                        let doc = new Doc();
+                        columns.forEach((col, i) => {
+                            console.log(values[i] + " " + Number(values[i]).toString());
+                            doc[columns[i]] = (values.length > i ? ((values[i].indexOf(Number(values[i]).toString()) !== -1) ? Number(values[i]) : values[i]) : undefined);
+                        });
+                        if (groupAttr) {
+                            doc["_group"] = groupAttr;
+                        }
+                        doc.title = i.toString();
+                        docList.push(doc);
+                    }
+                    let newCol = Docs.SchemaDocument(docList, { x: x, y: y, title: "-dropped table-", width: 300, height: 100 });
+                    newCol.proto!.schemaColumns = new List<string>([...(groupAttr ? ["_group"] : []), ...columns.filter(c => c)]);
+                    this.props.addDocument(newCol, false);
+                }
+            })();
         } else {
             let newBox = Docs.TextDocument({ width: 200, height: 100, x: x, y: y, title: "-typed text-" });
             this.props.addLiveTextDocument(newBox);
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 458a62c5b..4a29c1949 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -19,7 +19,7 @@
   box-sizing: border-box;
   background-color: inherit;
   border-style: solid;
-  overflow-y: scroll;
+  overflow-y: auto;
   overflow-x: hidden;
   color: initial;
   height: 100%;
@@ -27,7 +27,6 @@
 }
 
 .formattedTextBox-cont-hidden {
-    overflow: hidden;
     pointer-events: none;
 }
 .formattedTextBox-inner-rounded {
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 37dc05ca5..7ebc31b0a 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -262,8 +262,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         if (!this.props.isOverlay) {
             FormattedTextBox.InputBoxOverlay = this;
         } else {
-            if (this._proseRef.current) {
-                this._proseRef.current.scrollTop = FormattedTextBox.InputBoxOverlayScroll;
+            if (this._ref.current) {
+                this._ref.current.scrollTop = FormattedTextBox.InputBoxOverlayScroll;
             }
         }
     }
-- 
cgit v1.2.3-70-g09d2


From 9047fbe7dc69572bd8178bc29616ed2c855933ce Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 13 May 2019 00:37:03 -0400
Subject: fixed putting link info on protoype. making images and videos show up
 reasonably in schema and kvp.  dragging of docs from schema.

---
 .../views/collections/CollectionSchemaView.scss    |  4 +++
 .../views/collections/CollectionSchemaView.tsx     | 32 +++++++++++-----------
 .../views/collections/CollectionVideoView.scss     |  2 +-
 src/client/views/nodes/FieldView.tsx               | 10 ++++---
 src/client/views/nodes/IconBox.scss                |  4 +--
 src/client/views/nodes/KeyValuePair.scss           |  8 ++++++
 src/new_fields/Doc.ts                              |  8 +++---
 7 files changed, 41 insertions(+), 27 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index cfdb3ab22..b9ed99155 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -14,6 +14,10 @@
 
     .collectionSchemaView-cellContents {
         height: $MAX_ROW_HEIGHT;
+        img {
+            width:auto;
+            max-height: $MAX_ROW_HEIGHT;
+        }
     }
     
     .collectionSchemaView-previewRegion {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 18319dc77..4984e26d1 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -24,6 +24,7 @@ import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
 import { listSpec } from "../../../new_fields/Schema";
 import { List } from "../../../new_fields/List";
 import { Id } from "../../../new_fields/RefField";
+import { isUndefined } from "typescript-collections/dist/lib/util";
 
 
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
@@ -77,23 +78,22 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             PanelHeight: returnZero,
             PanelWidth: returnZero,
         };
-        let contents = (
-            <FieldView {...props} />
-        );
+        let fieldContentView = <FieldView {...props} />;
         let reference = React.createRef<HTMLDivElement>();
-        let onItemDown = SetupDrag(reference, () => props.Document, this.props.moveDocument);
+        let onItemDown = (e: React.PointerEvent) =>
+            (this.props.CollectionView!.props.isSelected() ?
+                SetupDrag(reference, () => props.Document, this.props.moveDocument)(e) : undefined);
         let applyToDoc = (doc: Doc, run: (args?: { [name: string]: any }) => any) => {
             const res = run({ this: doc });
             if (!res.success) return false;
-            const field = res.result;
-            doc[props.fieldKey] = field;
+            doc[props.fieldKey] = res.result;
             return true;
         };
         return (
             <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} key={props.Document[Id]} ref={reference}>
                 <EditableView
                     display={"inline"}
-                    contents={contents}
+                    contents={fieldContentView}
                     height={Number(MAX_ROW_HEIGHT)}
                     GetValue={() => {
                         let field = props.Document[props.fieldKey];
@@ -224,10 +224,11 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
         this.previewScript = e.currentTarget.value;
     }
 
+    @computed
     get previewDocument(): Doc | undefined {
-        const children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
+        const children = DocListCast(this.props.Document[this.props.fieldKey]);
         const selected = children.length > this._selectedIndex ? FieldValue(children[this._selectedIndex]) : undefined;
-        return selected ? (this.previewScript ? FieldValue(Cast(selected[this.previewScript], Doc)) : selected) : undefined;
+        return selected ? (this.previewScript && this.previewScript != "this" ? FieldValue(Cast(selected[this.previewScript], Doc)) : selected) : undefined;
     }
     get tableWidth() { return (this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH) * (1 - this.splitPercentage / 100); }
     get previewRegionHeight() { return this.props.PanelHeight() - 2 * this.borderWidth; }
@@ -253,8 +254,8 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     get previewPanel() {
         // let doc = CompileScript(this.previewScript, { this: selected }, true)();
         const previewDoc = this.previewDocument;
-        return !previewDoc || !this.previewRegionWidth ? (null) : (
-            <div className="collectionSchemaView-previewRegion" style={{ width: `${this.previewRegionWidth}px` }}>
+        return (<div className="collectionSchemaView-previewRegion" style={{ width: `${this.previewRegionWidth}px` }}>
+            {!previewDoc || !this.previewRegionWidth ? (null) : (
                 <div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
                     <DocumentView Document={previewDoc} isTopMost={false} selectOnLoad={false}
                         toggleMinimized={emptyFunction}
@@ -268,11 +269,10 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                         whenActiveChanged={this.props.whenActiveChanged}
                         bringToFront={emptyFunction}
                     />
-                </div>
-                <input className="collectionSchemaView-input" value={this.previewScript} onChange={this.onPreviewScriptChange}
-                    style={{ left: `calc(50% - ${Math.min(75, this.previewPanelWidth() / 2)}px)` }} />
-            </div>
-        );
+                </div>)}
+            <input className="collectionSchemaView-input" value={this.previewScript} onChange={this.onPreviewScriptChange}
+                style={{ left: `calc(50% - ${Math.min(75, (previewDoc ? this.previewPanelWidth() / 2 : 75))}px)` }} />
+        </div>);
     }
 
     get documentKeysCheckList() {
diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss
index ed56ad268..db8b84832 100644
--- a/src/client/views/collections/CollectionVideoView.scss
+++ b/src/client/views/collections/CollectionVideoView.scss
@@ -2,7 +2,7 @@
 .collectionVideoView-cont{
     width: 100%;
     height: 100%;
-    position: absolute;
+    position: inherit;
     top: 0;
     left:0;
  
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 8bdf34181..34b6c5e70 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -7,7 +7,7 @@ import { VideoBox } from "./VideoBox";
 import { AudioBox } from "./AudioBox";
 import { DocumentContentsView } from "./DocumentContentsView";
 import { Transform } from "../../util/Transform";
-import { returnFalse, emptyFunction } from "../../../Utils";
+import { returnFalse, emptyFunction, returnOne } from "../../../Utils";
 import { CollectionView } from "../collections/CollectionView";
 import { CollectionPDFView } from "../collections/CollectionPDFView";
 import { CollectionVideoView } from "../collections/CollectionVideoView";
@@ -18,6 +18,7 @@ import { ImageField, VideoField, AudioField } from "../../../new_fields/URLField
 import { IconField } from "../../../new_fields/IconField";
 import { RichTextField } from "../../../new_fields/RichTextField";
 import { DateField } from "../../../new_fields/DateField";
+import { NumCast } from "../../../new_fields/Types";
 
 
 //
@@ -82,14 +83,15 @@ export class FieldView extends React.Component<FieldViewProps> {
             return <p>{field.date.toLocaleString()}</p>;
         }
         else if (field instanceof Doc) {
+            let returnHundred = () => 100;
             return (
                 <DocumentContentsView Document={field}
                     addDocument={undefined}
                     removeDocument={undefined}
                     ScreenToLocalTransform={Transform.Identity}
-                    ContentScaling={() => 1}
-                    PanelWidth={() => 100}
-                    PanelHeight={() => 100}
+                    ContentScaling={returnOne}
+                    PanelWidth={returnHundred}
+                    PanelHeight={returnHundred}
                     isTopMost={true} //TODO Why is this top most?
                     selectOnLoad={false}
                     focus={emptyFunction}
diff --git a/src/client/views/nodes/IconBox.scss b/src/client/views/nodes/IconBox.scss
index 85bbdeb59..f6d9860a3 100644
--- a/src/client/views/nodes/IconBox.scss
+++ b/src/client/views/nodes/IconBox.scss
@@ -1,7 +1,7 @@
 
 @import "../globalCssVariables";
 .iconBox-container {
-    position: absolute;
+    position: inherit;
     left:0;
     top:0;
     height: 100%;
@@ -14,7 +14,7 @@
         background: white;
     }
     .iconBox-label {
-        position: inherit;
+        position: absolute;
         width:max-content;
         font-size: 14px;
         margin-top: 3px;
diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss
index ff6885965..4f305dc91 100644
--- a/src/client/views/nodes/KeyValuePair.scss
+++ b/src/client/views/nodes/KeyValuePair.scss
@@ -26,4 +26,12 @@
 .keyValuePair-td-value {
     display:inline-block;
     overflow: scroll;
+    img { 
+        max-height: 36px;
+        width: auto;
+    }
+    .videobox-cont{
+        width: auto; 
+        max-height: 36px; 
+    }
 }
\ No newline at end of file
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 46ccb3e90..42d04e93f 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -198,11 +198,11 @@ export namespace Doc {
             let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 });
             //let linkDoc = new Doc;
             linkDoc.proto!.title = "-link name-";
-            linkDoc.linkDescription = "";
-            linkDoc.linkTags = "Default";
+            linkDoc.proto!.linkDescription = "";
+            linkDoc.proto!.linkTags = "Default";
 
-            linkDoc.linkedTo = target;
-            linkDoc.linkedFrom = source;
+            linkDoc.proto!.linkedTo = target;
+            linkDoc.proto!.linkedFrom = source;
 
             let linkedFrom = Cast(target.linkedFromDocs, listSpec(Doc));
             if (!linkedFrom) {
-- 
cgit v1.2.3-70-g09d2


From 3be42131bc08024f06e0daec8d09e45bf3f1ddab Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 13 May 2019 16:21:28 -0400
Subject: a bunch of fixes to schemas, marquees, and an experimental feature to
 set a document's data with linking UI

---
 src/client/util/DragManager.ts                             |  6 ++++--
 src/client/views/DocumentDecorations.tsx                   |  2 ++
 src/client/views/collections/CollectionDockingView.tsx     |  7 ++-----
 src/client/views/collections/CollectionSchemaView.tsx      |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx          |  4 ++--
 .../views/collections/collectionFreeForm/MarqueeView.tsx   |  8 +++-----
 src/client/views/nodes/DocumentContentsView.tsx            |  9 +++++++--
 src/client/views/nodes/DocumentView.tsx                    | 13 +++++++++++--
 src/client/views/nodes/ImageBox.tsx                        |  3 ++-
 src/client/views/nodes/KeyValueBox.tsx                     |  6 +++---
 src/client/views/nodes/LinkMenu.tsx                        |  2 +-
 src/client/views/nodes/PDFBox.tsx                          |  4 ++--
 src/new_fields/Doc.ts                                      | 14 +++++++++-----
 13 files changed, 49 insertions(+), 31 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 2da0d5b51..26da34e67 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -105,7 +105,8 @@ export namespace DragManager {
         constructor(
             readonly x: number,
             readonly y: number,
-            readonly data: { [id: string]: any }
+            readonly data: { [id: string]: any },
+            readonly mods: string
         ) { }
     }
 
@@ -334,7 +335,8 @@ export namespace DragManager {
                     detail: {
                         x: e.x,
                         y: e.y,
-                        data: dragData
+                        data: dragData,
+                        mods: e.ctrlKey ? "Control" : ""
                     }
                 })
             );
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 4786b4de6..5aa3d804d 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -239,6 +239,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             this._removeIcon = snapped;
         }
     }
+    @undoBatch
     @action
     onMinimizeUp = (e: PointerEvent): void => {
         e.stopPropagation();
@@ -270,6 +271,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         runInAction(() => this._minimizedX = this._minimizedY = 0);
     }
 
+    @undoBatch
     @action createIcon = (selected: DocumentView[], layoutString: string): Doc => {
         let doc = selected[0].props.Document;
         let iconDoc = Docs.IconDocument(layoutString);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 0aa067972..a755e0f91 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -5,7 +5,7 @@ import { observer } from "mobx-react";
 import * as ReactDOM from 'react-dom';
 import Measure from "react-measure";
 import * as GoldenLayout from "../../../client/goldenLayout";
-import { Doc, Field, Opt } from "../../../new_fields/Doc";
+import { Doc, Field, Opt, DocListCast } from "../../../new_fields/Doc";
 import { FieldId, Id } from "../../../new_fields/RefField";
 import { listSpec } from "../../../new_fields/Schema";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
@@ -313,10 +313,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                     counter.DashDocId = tab.contentItem.config.props.documentId;
                     tab.reactionDisposer = reaction(() => [doc.linkedFromDocs, doc.LinkedToDocs, doc.title],
                         () => {
-                            const lf = Cast(doc.linkedFromDocs, listSpec(Doc), []);
-                            const lt = Cast(doc.linkedToDocs, listSpec(Doc), []);
-                            let count = (lf ? lf.length : 0) + (lt ? lt.length : 0);
-                            counter.innerHTML = count;
+                            counter.innerHTML = DocListCast(doc.linkedFromDocs).length + DocListCast(doc.linkedToDocs).length;
                             tab.titleElement[0].textContent = doc.title;
                         }, { fireImmediately: true });
                     tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 4984e26d1..9ecccc559 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -254,7 +254,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     get previewPanel() {
         // let doc = CompileScript(this.previewScript, { this: selected }, true)();
         const previewDoc = this.previewDocument;
-        return (<div className="collectionSchemaView-previewRegion" style={{ width: `${this.previewRegionWidth}px` }}>
+        return (<div className="collectionSchemaView-previewRegion" style={{ width: `${Math.max(0, this.previewRegionWidth - 1)}px` }}>
             {!previewDoc || !this.previewRegionWidth ? (null) : (
                 <div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
                     <DocumentView Document={previewDoc} isTopMost={false} selectOnLoad={false}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index dc86f38b6..c36c708db 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -339,7 +339,7 @@ class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps &
             isTopMost={this.props.isTopMost} isSelected={this.props.isSelected} select={emptyFunction} />);
     }
     render() {
-        return this.overlayView;
+        return this.props.Document.overlayLayout ? this.overlayView : (null);
     }
 }
 
@@ -350,7 +350,7 @@ class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps
             isTopMost={this.props.isTopMost} isSelected={this.props.isSelected} select={emptyFunction} />);
     }
     render() {
-        return this.backgroundView;
+        return this.props.Document.backgroundLayout ? this.backgroundView : (null);
     }
 }
 
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 1c29ebaae..24dea200e 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -104,10 +104,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                             continue;
                         }
                         let doc = new Doc();
-                        columns.forEach((col, i) => {
-                            console.log(values[i] + " " + Number(values[i]).toString());
-                            doc[columns[i]] = (values.length > i ? ((values[i].indexOf(Number(values[i]).toString()) !== -1) ? Number(values[i]) : values[i]) : undefined);
-                        });
+                        columns.forEach((col, i) => doc[columns[i]] = (values.length > i ? ((values[i].indexOf(Number(values[i]).toString()) !== -1) ? Number(values[i]) : values[i]) : undefined));
                         if (groupAttr) {
                             doc["_group"] = groupAttr;
                         }
@@ -207,7 +204,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     @undoBatch
     @action
     marqueeCommand = async (e: KeyboardEvent) => {
-        if (this._commandExecuted) {
+        if (this._commandExecuted || (e as any).propagationIsStopped) {
             return;
         }
         if (e.key === "Backspace" || e.key === "Delete" || e.key === "d") {
@@ -224,6 +221,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         if (e.key === "c" || e.key === "r" || e.key === "s" || e.key === "e" || e.key === "p") {
             this._commandExecuted = true;
             e.stopPropagation();
+            (e as any).propagationIsStopped = true;
             let bounds = this.Bounds;
             let selected = this.marqueeSelect().map(d => {
                 if (e.key === "s") {
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index f404b7bc6..3ddf8eb00 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -43,9 +43,14 @@ const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
 export class DocumentContentsView extends React.Component<DocumentViewProps & {
     isSelected: () => boolean,
     select: (ctrl: boolean) => void,
-    layoutKey: string
+    layoutKey: string,
 }> {
-    @computed get layout(): string { return Cast(this.props.Document[this.props.layoutKey], "string", this.props.layoutKey === "layout" ? "<p>Error loading layout data</p>" : ""); }
+    @computed get layout(): string {
+        return StrCast(this.props.Document[this.props.layoutKey],
+            this.props.Document.data ?
+                "<FieldView {...props} fieldKey='data' />" :
+                KeyValueBox.LayoutString(this.props.Document.proto ? "proto" : ""));
+    }
 
     CreateBindings(): JsxBindings {
         return { props: OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit };
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index edc2158f0..b7ad9249a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -243,8 +243,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
             const protoDest = destDoc.proto;
             const protoSrc = sourceDoc.proto;
-            Doc.MakeLink(protoSrc ? protoSrc : sourceDoc, protoDest ? protoDest : destDoc);
-            de.data.droppedDocuments.push(destDoc);
+            if (de.mods == "Control") {
+                let src = protoSrc ? protoSrc : sourceDoc;
+                let dst = protoDest ? protoDest : destDoc;
+                dst.data = src;
+                dst.nativeWidth = src.nativeWidth;
+                dst.nativeHeight = src.nativeHeight;
+            }
+            else {
+                Doc.MakeLink(protoSrc ? protoSrc : sourceDoc, protoDest ? protoDest : destDoc);
+                de.data.droppedDocuments.push(destDoc);
+            }
             e.stopPropagation();
         }
     }
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index f9659a4b2..05742f8d1 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -42,8 +42,9 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
     onLoad = (target: any) => {
         var h = this._imgRef.current!.naturalHeight;
         var w = this._imgRef.current!.naturalWidth;
+        console.log("title: " + this.Document.title);
         if (this._photoIndex === 0) {
-            this.Document.nativeHeight = FieldValue(this.Document.nativeWidth, 0) * h / w;
+            Doc.SetOnPrototype(this.Document, "nativeHeight", FieldValue(this.Document.nativeWidth, 0) * h / w);
             this.Document.height = FieldValue(this.Document.width, 0) * h / w;
         }
     }
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 876a3c173..c9d665ceb 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -18,7 +18,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     @observable private _keyInput: string = "";
     @observable private _valueInput: string = "";
     @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage, 50); }
-
+    get fieldDocToLayout() { return this.props.fieldKey ? FieldValue(Cast(this.props.Document[this.props.fieldKey], Doc)) : this.props.Document }
 
     constructor(props: FieldViewProps) {
         super(props);
@@ -28,7 +28,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     onEnterKey = (e: React.KeyboardEvent): void => {
         if (e.key === 'Enter') {
             if (this._keyInput && this._valueInput) {
-                let doc = FieldValue(Cast(this.props.Document.data, Doc));
+                let doc = this.fieldDocToLayout;
                 if (!doc) {
                     return;
                 }
@@ -60,7 +60,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     }
 
     createTable = () => {
-        let doc = FieldValue(Cast(this.props.Document.data, Doc));
+        let doc = this.fieldDocToLayout;
         if (!doc) {
             return <tr><td>Loading...</td></tr>;
         }
diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx
index 7bf13d5f9..5dabfc30d 100644
--- a/src/client/views/nodes/LinkMenu.tsx
+++ b/src/client/views/nodes/LinkMenu.tsx
@@ -32,7 +32,7 @@ export class LinkMenu extends React.Component<Props> {
     render() {
         //get list of links from document
         let linkFrom = DocListCast(this.props.docView.props.Document.linkedFromDocs);
-        let linkTo = DocListCast(this.props.docView.props.Document.linkedToDoc);
+        let linkTo = DocListCast(this.props.docView.props.Document.linkedToDocs);
         if (this._editingLink === undefined) {
             return (
                 <div id="linkMenu-container">
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index cb27b3f1b..ff8737192 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, IReactionDisposer, observable, reaction, Reaction, trace } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, Reaction, trace, runInAction } from 'mobx';
 import { observer } from "mobx-react";
 import 'react-image-lightbox/style.css';
 import Measure from "react-measure";
@@ -223,7 +223,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
             document.addEventListener("pointerup", this.onPointerUp);
         }
         if (this.props.isSelected() && e.buttons === 2) {
-            this._alt = true;
+            runInAction(() => this._alt = true);
             document.removeEventListener("pointerup", this.onPointerUp);
             document.addEventListener("pointerup", this.onPointerUp);
         }
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index a8c9d28e1..4c837fcbd 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -173,11 +173,15 @@ export namespace Doc {
     export function MakeAlias(doc: Doc) {
         const alias = new Doc;
 
-        PromiseValue(Cast(doc.proto, Doc)).then(proto => {
-            if (proto) {
-                alias.proto = proto;
-            }
-        });
+        if (!doc.proto) {
+            alias.proto = doc;
+        } else {
+            PromiseValue(Cast(doc.proto, Doc)).then(proto => {
+                if (proto) {
+                    alias.proto = proto;
+                }
+            });
+        }
 
         return alias;
     }
-- 
cgit v1.2.3-70-g09d2


From 281bfa589bd1ca66823003ca91cc11ce895fe5e2 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 14 May 2019 18:59:32 -0400
Subject: fixes for northstar

---
 src/client/documents/Documents.ts                  |  9 ++-
 src/client/northstar/dash-fields/HistogramField.ts | 11 ++--
 .../dash-nodes/HistogramBinPrimitiveCollection.ts  |  4 +-
 src/client/northstar/dash-nodes/HistogramBox.tsx   | 16 +++---
 .../dash-nodes/HistogramBoxPrimitives.tsx          |  3 -
 src/client/northstar/manager/Gateway.ts            | 12 ++++
 src/client/northstar/model/ModelHelpers.ts         | 10 +++-
 src/client/northstar/model/idea/idea.ts            |  6 ++
 src/client/northstar/operations/BaseOperation.ts   | 17 ------
 .../northstar/operations/HistogramOperation.ts     | 12 ++--
 src/client/views/Main.tsx                          | 54 +-----------------
 .../views/collections/CollectionSchemaView.tsx     | 39 ++++++++++++-
 .../collections/collectionFreeForm/MarqueeView.tsx |  7 ++-
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 .../authentication/models/current_user_utils.ts    | 65 ++++++++++++++++++++--
 15 files changed, 162 insertions(+), 105 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 11929455c..ed260d42e 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -33,6 +33,7 @@ import { DocServer } from "../DocServer";
 import { StrokeData, InkField } from "../../new_fields/InkField";
 import { dropActionType } from "../util/DragManager";
 import { DateField } from "../../new_fields/DateField";
+import { schema } from "prosemirror-schema-basic";
 
 export interface DocumentOptions {
     x?: number;
@@ -207,16 +208,18 @@ export namespace Docs {
     export function PdfDocument(url: string, options: DocumentOptions = {}) {
         return CreateInstance(pdfProto, new PdfField(new URL(url)), options);
     }
+
     export async function DBDocument(url: string, options: DocumentOptions = {}) {
         let schemaName = options.title ? options.title : "-no schema-";
         let ctlog = await Gateway.Instance.GetSchema(url, schemaName);
         if (ctlog && ctlog.schemas) {
             let schema = ctlog.schemas[0];
             let schemaDoc = Docs.TreeDocument([], { ...options, nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: schema.displayName! });
-            let schemaDocuments = Cast(schemaDoc.data, listSpec(Doc));
+            let schemaDocuments = Cast(schemaDoc.data, listSpec(Doc), []);
             if (!schemaDocuments) {
                 return;
             }
+            CurrentUserUtils.AddNorthstarSchema(schema, schemaDoc);
             const docs = schemaDocuments;
             CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => {
                 DocServer.GetRefField(attr.displayName! + ".alias").then(action((field: Opt<Field>) => {
@@ -251,8 +254,8 @@ export namespace Docs {
         }
         return CreateInstance(collProto, new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Freeform });
     }
-    export function SchemaDocument(documents: Array<Doc>, options: DocumentOptions) {
-        return CreateInstance(collProto, new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Schema });
+    export function SchemaDocument(schemaColumns: string[], documents: Array<Doc>, options: DocumentOptions) {
+        return CreateInstance(collProto, new List(documents), { schemaColumns: new List(schemaColumns), ...options, viewType: CollectionViewType.Schema });
     }
     export function TreeDocument(documents: Array<Doc>, options: DocumentOptions) {
         return CreateInstance(collProto, new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Tree });
diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts
index f01f08487..aabc77bb2 100644
--- a/src/client/northstar/dash-fields/HistogramField.ts
+++ b/src/client/northstar/dash-fields/HistogramField.ts
@@ -9,7 +9,8 @@ import { OmitKeys } from "../../../Utils";
 import { Deserializable } from "../../util/SerializationHelper";
 
 function serialize(field: HistogramField) {
-    return OmitKeys(field.HistoOp, ['Links', 'BrushLinks', 'Result', 'BrushColors', 'FilterModels', 'FilterOperand']).omit;
+    let obj = OmitKeys(field, ['Links', 'BrushLinks', 'Result', 'BrushColors', 'FilterModels', 'FilterOperand']).omit;
+    return obj;
 }
 
 function deserialize(jp: any) {
@@ -31,10 +32,10 @@ function deserialize(jp: any) {
             }
         });
         if (X && Y && V) {
-            return new HistogramField(new HistogramOperation(jp.SchemaName, X, Y, V, jp.Normalization));
+            return new HistogramOperation(jp.SchemaName, X, Y, V, jp.Normalization);
         }
     }
-    return new HistogramField(HistogramOperation.Empty);
+    return HistogramOperation.Empty;
 }
 
 @Deserializable("histogramField")
@@ -50,6 +51,8 @@ export class HistogramField extends ObjectField {
     }
 
     [Copy]() {
-        return new HistogramField(this.HistoOp.Copy());
+        let y = this.HistoOp;
+        let z = this.HistoOp["Copy"];
+        return new HistogramField(HistogramOperation.Duplicate(this.HistoOp));
     }
 }
\ No newline at end of file
diff --git a/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts b/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts
index 6291ec1fc..3e9145a1b 100644
--- a/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts
+++ b/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts
@@ -198,8 +198,8 @@ export class HistogramBinPrimitiveCollection {
         var marginParams = new MarginAggregateParameters();
         marginParams.aggregateFunction = axis.AggregateFunction;
         var marginAggregateKey = ModelHelpers.CreateAggregateKey(this.histoOp.Schema!.distinctAttributeParameters, axis, this.histoResult, brush.brushIndex!, marginParams);
-        var marginResult = ModelHelpers.GetAggregateResult(bin, marginAggregateKey) as MarginAggregateResult;
-        return !marginResult ? 0 : marginResult.absolutMargin!;
+        let aggResult = ModelHelpers.GetAggregateResult(bin, marginAggregateKey);
+        return aggResult instanceof MarginAggregateResult && aggResult.absolutMargin ? aggResult.absolutMargin : 0;
     }
 
     private createBinPrimitive(barAxis: number, brush: Brush, marginRect: PIXIRectangle,
diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index ed556cf45..eb1ad69b7 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -17,9 +17,8 @@ import "./HistogramBox.scss";
 import { HistogramBoxPrimitives } from './HistogramBoxPrimitives';
 import { HistogramLabelPrimitives } from "./HistogramLabelPrimitives";
 import { StyleConstants } from "../utils/StyleContants";
-import { NumCast, Cast } from "../../../new_fields/Types";
-import { listSpec } from "../../../new_fields/Schema";
-import { Doc, DocListCast } from "../../../new_fields/Doc";
+import { Cast } from "../../../new_fields/Types";
+import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/RefField";
 
 
@@ -119,15 +118,16 @@ export class HistogramBox extends React.Component<FieldViewProps> {
                 if (this.HistoOp !== HistogramOperation.Empty) {
                     reaction(() => DocListCast(this.props.Document.linkedFromDocs), (docs) => this.HistoOp.Links.splice(0, this.HistoOp.Links.length, ...docs), { fireImmediately: true });
                     reaction(() => DocListCast(this.props.Document.brushingDocs).length,
-                        () => {
-                            let brushingDocs = DocListCast(this.props.Document.brushingDocs);
+                        async () => {
+                            let brushingDocs = await DocListCastAsync(this.props.Document.brushingDocs);
                             const proto = this.props.Document.proto;
-                            if (proto) {
-                                this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...brushingDocs.map((brush, i) => {
+                            if (proto && brushingDocs) {
+                                let mapped = brushingDocs.map((brush, i) => {
                                     brush.backgroundColor = StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length];
                                     let brushed = DocListCast(brush.brushingDocs);
                                     return { l: brush, b: brushed[0][Id] === proto[Id] ? brushed[1] : brushed[0] };
-                                }));
+                                });
+                                this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...mapped);
                             }
                         }, { 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 721bf6a89..350987695 100644
--- a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
@@ -11,9 +11,6 @@ import { StyleConstants } from "../../northstar/utils/StyleContants";
 import { HistogramBinPrimitiveCollection, HistogramBinPrimitive } from "./HistogramBinPrimitiveCollection";
 import { HistogramBox } from "./HistogramBox";
 import "./HistogramBoxPrimitives.scss";
-import { JSXElement } from "babel-types";
-import { Utils } from "../utils/Utils";
-import { all } from "bluebird";
 
 export interface HistogramPrimitivesProps {
     HistoBox: HistogramBox;
diff --git a/src/client/northstar/manager/Gateway.ts b/src/client/northstar/manager/Gateway.ts
index d26f2724f..c541cce6a 100644
--- a/src/client/northstar/manager/Gateway.ts
+++ b/src/client/northstar/manager/Gateway.ts
@@ -23,6 +23,18 @@ export class Gateway {
         }
     }
 
+    public async PostSchema(csvdata: string, schemaname: string): Promise<string> {
+        try {
+            const json = await this.MakePostJsonRequest("postSchema", { csv: csvdata, schema: schemaname });
+            // const cat = Catalog.fromJS(json);
+            // return cat;
+            return json;
+        }
+        catch (error) {
+            throw new Error("can not reach northstar's backend");
+        }
+    }
+
     public async GetSchema(pathname: string, schemaname: string): Promise<Catalog> {
         try {
             const json = await this.MakeGetRequest("schema", undefined, { path: pathname, schema: schemaname });
diff --git a/src/client/northstar/model/ModelHelpers.ts b/src/client/northstar/model/ModelHelpers.ts
index ac807b41f..80bb71224 100644
--- a/src/client/northstar/model/ModelHelpers.ts
+++ b/src/client/northstar/model/ModelHelpers.ts
@@ -31,7 +31,15 @@ export class ModelHelpers {
     }
 
     public static GetAggregateParametersIndex(histogramResult: HistogramResult, aggParameters?: AggregateParameters): number {
-        return ArrayUtil.IndexOfWithEqual(histogramResult.aggregateParameters!, aggParameters);
+        return Array.from(histogramResult.aggregateParameters!).findIndex((value, i, set) => {
+            if (set[i] instanceof CountAggregateParameters && value instanceof CountAggregateParameters)
+                return true;
+            if (set[i] instanceof MarginAggregateParameters && value instanceof MarginAggregateParameters)
+                return true;
+            if (set[i] instanceof SumAggregateParameters && value instanceof SumAggregateParameters)
+                return true;
+            return false;
+        });
     }
 
     public static GetAggregateParameter(distinctAttributeParameters: AttributeParameters | undefined, atm: AttributeTransformationModel): AggregateParameters | undefined {
diff --git a/src/client/northstar/model/idea/idea.ts b/src/client/northstar/model/idea/idea.ts
index 9d9d60678..c73a822c7 100644
--- a/src/client/northstar/model/idea/idea.ts
+++ b/src/client/northstar/model/idea/idea.ts
@@ -22,6 +22,9 @@ export abstract class AggregateParameters implements IAggregateParameters {
 
     protected _discriminator: string;
 
+    public Equals(other: Object): boolean {
+        return this == other;
+    }
     constructor(data?: IAggregateParameters) {
         if (data) {
             for (var property in data) {
@@ -204,6 +207,9 @@ export interface IAverageAggregateParameters extends ISingleDimensionAggregatePa
 export abstract class AttributeParameters implements IAttributeParameters {
     visualizationHints?: VisualizationHint[] | undefined;
     rawName?: string | undefined;
+    public Equals(other: Object): boolean {
+        return this == other;
+    }
 
     protected _discriminator: string;
 
diff --git a/src/client/northstar/operations/BaseOperation.ts b/src/client/northstar/operations/BaseOperation.ts
index c6d5f0a15..0d1361ebf 100644
--- a/src/client/northstar/operations/BaseOperation.ts
+++ b/src/client/northstar/operations/BaseOperation.ts
@@ -25,23 +25,6 @@ export abstract class BaseOperation {
 
     @computed
     public get FilterString(): string {
-
-        // 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(" || ") + ")";
-        // }
-        // let rdg = MainManager.Instance.MainViewModel.FilterReverseDependencyGraph;
-        // let sliceModel = this.TypedViewModel.IncomingSliceModel;
-        // 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);
-        // }
-
-        // if (rdg.has(this.TypedViewModel)) {
-        //     let filterModels = [];
-        //     return FilterModel.GetFilterModelsRecursive(rdg.get(this.TypedViewModel), new Set<GraphNode<BaseOperationViewModel, FilterLinkViewModel>>(), filterModels, true)
-        // }
         return "";
     }
 
diff --git a/src/client/northstar/operations/HistogramOperation.ts b/src/client/northstar/operations/HistogramOperation.ts
index 78b206bdc..74e23ea48 100644
--- a/src/client/northstar/operations/HistogramOperation.ts
+++ b/src/client/northstar/operations/HistogramOperation.ts
@@ -30,7 +30,7 @@ export class HistogramOperation extends BaseOperation implements IBaseFilterCons
     @observable public V: AttributeTransformationModel;
     @observable public SchemaName: string;
     @observable public QRange: QuantitativeBinRange | undefined;
-    @computed public get Schema() { return CurrentUserUtils.GetNorthstarSchema(this.SchemaName); }
+    public get Schema() { return CurrentUserUtils.GetNorthstarSchema(this.SchemaName); }
 
     constructor(schemaName: string, x: AttributeTransformationModel, y: AttributeTransformationModel, v: AttributeTransformationModel, normalized?: number) {
         super();
@@ -41,7 +41,11 @@ export class HistogramOperation extends BaseOperation implements IBaseFilterCons
         this.SchemaName = schemaName;
     }
 
-    Copy(): HistogramOperation {
+    public static Duplicate(op: HistogramOperation) {
+
+        return new HistogramOperation(op.SchemaName, op.X, op.Y, op.V, op.Normalization);
+    }
+    public Copy(): HistogramOperation {
         return new HistogramOperation(this.SchemaName, this.X, this.Y, this.V, this.Normalization);
     }
 
@@ -50,7 +54,7 @@ export class HistogramOperation extends BaseOperation implements IBaseFilterCons
     }
 
 
-    @computed public get FilterModels() {
+    public get FilterModels() {
         return this.BarFilterModels;
     }
     @action
@@ -71,9 +75,7 @@ export class HistogramOperation extends BaseOperation implements IBaseFilterCons
         return FilterModel.GetFilterModelsRecursive(this, new Set<IBaseFilterProvider>(), filterModels, true);
     }
 
-    @computed
     public get BrushString(): string[] {
-        trace();
         let brushes: string[] = [];
         this.BrushLinks.map(brushLink => {
             let brushHistogram = Cast(brushLink.b.data, HistogramField);
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 66205f8ca..385fc6ead 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -12,13 +12,6 @@ import { CurrentUserUtils } from '../../server/authentication/models/current_use
 import { RouteStore } from '../../server/RouteStore';
 import { emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils';
 import { Docs } from '../documents/Documents';
-import { ColumnAttributeModel } from '../northstar/core/attribute/AttributeModel';
-import { AttributeTransformationModel } from '../northstar/core/attribute/AttributeTransformationModel';
-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 { SetupDrag, DragManager } from '../util/DragManager';
 import { Transform } from '../util/Transform';
 import { UndoManager } from '../util/UndoManager';
@@ -70,8 +63,6 @@ export class Main extends React.Component {
             }
         }
 
-        CurrentUserUtils.loadCurrentUser();
-
         library.add(faFont);
         library.add(faImage);
         library.add(faFilePdf);
@@ -84,15 +75,8 @@ export class Main extends React.Component {
         library.add(faFilm);
         library.add(faMusic);
         library.add(faTree);
-
         this.initEventListeners();
         this.initAuthenticationRouters();
-
-        // try {
-        //     this.initializeNorthstar();
-        // } catch (e) {
-
-        // }
     }
 
     componentDidMount() { window.onpopstate = this.onHistory; }
@@ -155,7 +139,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 = Docs.SchemaDocument([], { title: "New Mobile Uploads" });
+                let pendingDocument = Docs.SchemaDocument(["title"], [], { title: "New Mobile Uploads" });
                 mainDoc.optionalRightCollection = pendingDocument;
             }, 0);
         }
@@ -229,7 +213,7 @@ export class Main extends React.Component {
 
         let addTextNode = action(() => Docs.TextDocument({ borderRounding: -1, width: 200, height: 200, title: "a text note" }));
         let addColNode = action(() => Docs.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" }));
-        let addSchemaNode = action(() => Docs.SchemaDocument([], { width: 200, height: 200, title: "a schema collection" }));
+        let addSchemaNode = action(() => Docs.SchemaDocument(["title"], [], { width: 200, height: 200, title: "a schema collection" }));
         let addTreeNode = action(() => Docs.TreeDocument([CurrentUserUtils.UserDocument], { width: 250, height: 400, title: "Library:" + CurrentUserUtils.email, dropAction: "alias" }));
         // let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", dropAction: "copy"  }));
         let addVideoNode = action(() => Docs.VideoDocument(videourl, { width: 200, title: "video node" }));
@@ -300,40 +284,6 @@ export class Main extends React.Component {
             </div>
         );
     }
-
-    // --------------- Northstar hooks ------------- /
-    private _northstarSchemas: Doc[] = [];
-
-    @action SetNorthstarCatalog(ctlog: Catalog) {
-        CurrentUserUtils.NorthstarDBCatalog = ctlog;
-        if (ctlog && ctlog.schemas) {
-            ctlog.schemas.map(schema => {
-                let schemaDocuments: Doc[] = [];
-                let attributesToBecomeDocs = CurrentUserUtils.GetAllNorthstarColumnAttributes(schema);
-                Promise.all(attributesToBecomeDocs.reduce((promises, attr) => {
-                    promises.push(DocServer.GetRefField(attr.displayName! + ".alias").then(action((field: Opt<Field>) => {
-                        if (field instanceof Doc) {
-                            schemaDocuments.push(field);
-                        } else {
-                            var atmod = new ColumnAttributeModel(attr);
-                            let histoOp = new HistogramOperation(schema.displayName!,
-                                new AttributeTransformationModel(atmod, AggregateFunction.None),
-                                new AttributeTransformationModel(atmod, AggregateFunction.Count),
-                                new AttributeTransformationModel(atmod, AggregateFunction.Count));
-                            schemaDocuments.push(Docs.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! }));
-                        }
-                    })));
-                    return promises;
-                }, [] as Promise<void>[])).finally(() =>
-                    this._northstarSchemas.push(Docs.TreeDocument(schemaDocuments, { width: 50, height: 100, title: schema.displayName! })));
-            });
-        }
-    }
-    async initializeNorthstar(): Promise<void> {
-        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()));
-    }
 }
 
 (async () => {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 9ecccc559..f4ad5b357 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -20,11 +20,20 @@ import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
 import { CollectionSubView } from "./CollectionSubView";
 import { Opt, Field, Doc, DocListCastAsync, DocListCast } from "../../../new_fields/Doc";
-import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
+import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
 import { listSpec } from "../../../new_fields/Schema";
 import { List } from "../../../new_fields/List";
 import { Id } from "../../../new_fields/RefField";
 import { isUndefined } from "typescript-collections/dist/lib/util";
+import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
+import { Gateway } from "../../northstar/manager/Gateway";
+import { DocServer } from "../../DocServer";
+import { ColumnAttributeModel } from "../../northstar/core/attribute/AttributeModel";
+import { HistogramOperation } from "../../northstar/operations/HistogramOperation";
+import { AggregateFunction } from "../../northstar/model/idea/idea";
+import { AttributeTransformationModel } from "../../northstar/core/attribute/AttributeTransformationModel";
+import { Docs } from "../../documents/Documents";
+import { ContextMenu } from "../ContextMenu";
 
 
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
@@ -207,6 +216,32 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
         }
     }
 
+    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: "Make DB", event: this.makeDB });
+        }
+    }
+
+    @action
+    makeDB = async () => {
+        let csv: string = this.columns.reduce((val, col) => val + col + ",", "");
+        csv = csv.substr(0, csv.length - 1) + "\n";
+        let self = this;
+        DocListCast(this.props.Document.data).map(doc => {
+            csv += self.columns.reduce((val, col) => val + (doc[col] ? doc[col]!.toString() : "") + ",", "");
+            csv = csv.substr(0, csv.length - 1) + "\n";
+        })
+        csv.substring(0, csv.length - 1);
+        let dbName = StrCast(this.props.Document.title);
+        let res = await Gateway.Instance.PostSchema(csv, dbName);
+        if (self.props.CollectionView.props.addDocument) {
+            let schemaDoc = await Docs.DBDocument("https://www.cs.brown.edu/" + dbName, { title: dbName });
+            if (schemaDoc) {
+                self.props.CollectionView.props.addDocument(schemaDoc, false); 
+            }
+        }
+    }
+
     @action
     addColumn = () => {
         this.columns.push(this._newKeyName);
@@ -325,7 +360,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
         const children = this.children;
         return (
             <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel}
-                onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createTarget}>
+                onDrop={(e: React.DragEvent) => this.onDrop(e, {})} onContextMenu={this.onContextMenu} ref={this.createTarget}>
                 <div className="collectionSchemaView-tableContainer" style={{ width: `${this.tableWidth}px` }}>
                     <ReactTable data={children} page={0} pageSize={children.length} showPagination={false}
                         columns={this.columns.map(col => ({
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 24dea200e..be7cddca6 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -17,6 +17,9 @@ import { InkField, StrokeData } from "../../../../new_fields/InkField";
 import { List } from "../../../../new_fields/List";
 import { ImageField } from "../../../../new_fields/URLField";
 import { Template, Templates } from "../../Templates";
+import { Gateway } from "../../../northstar/manager/Gateway";
+import { DocServer } from "../../../DocServer";
+import { Id } from "../../../../new_fields/RefField";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -111,8 +114,8 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                         doc.title = i.toString();
                         docList.push(doc);
                     }
-                    let newCol = Docs.SchemaDocument(docList, { x: x, y: y, title: "-dropped table-", width: 300, height: 100 });
-                    newCol.proto!.schemaColumns = new List<string>([...(groupAttr ? ["_group"] : []), ...columns.filter(c => c)]);
+                    let newCol = Docs.SchemaDocument([...(groupAttr ? ["_group"] : []), ...columns.filter(c => c)], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 });
+
                     this.props.addDocument(newCol, false);
                 }
             })();
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b7ad9249a..c97bcbd94 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -306,7 +306,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         cm.addItem({
             description: "Find aliases", event: async () => {
                 const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document);
-                CollectionDockingView.Instance.AddRightSplit(Docs.SchemaDocument(aliases, {}));
+                CollectionDockingView.Instance.AddRightSplit(Docs.SchemaDocument(["title"], aliases, {}));
             }
         });
         cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document[Id])) });
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 5f45d7bcc..5b63ac356 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -1,15 +1,21 @@
 import { computed, observable, action, runInAction } from "mobx";
 import * as rp from 'request-promise';
 import { Docs } from "../../../client/documents/Documents";
-import { Attribute, AttributeGroup, Catalog, Schema } from "../../../client/northstar/model/idea/idea";
+import { Attribute, AttributeGroup, Catalog, Schema, AggregateFunction } from "../../../client/northstar/model/idea/idea";
 import { ArrayUtil } from "../../../client/northstar/utils/ArrayUtil";
 import { RouteStore } from "../../RouteStore";
 import { DocServer } from "../../../client/DocServer";
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, Opt, Field } from "../../../new_fields/Doc";
 import { List } from "../../../new_fields/List";
 import { CollectionViewType } from "../../../client/views/collections/CollectionBaseView";
 import { CollectionTreeView } from "../../../client/views/collections/CollectionTreeView";
 import { CollectionView } from "../../../client/views/collections/CollectionView";
+import { NorthstarSettings, Gateway } from "../../../client/northstar/manager/Gateway";
+import { AttributeTransformationModel } from "../../../client/northstar/core/attribute/AttributeTransformationModel";
+import { ColumnAttributeModel } from "../../../client/northstar/core/attribute/AttributeModel";
+import { HistogramOperation } from "../../../client/northstar/operations/HistogramOperation";
+import { Cast, PromiseValue } from "../../../new_fields/Types";
+import { listSpec } from "../../../new_fields/Schema";
 
 export class CurrentUserUtils {
     private static curr_email: string;
@@ -31,13 +37,13 @@ export class CurrentUserUtils {
         doc.title = this.email;
         doc.data = new List<Doc>();
         doc.excludeFromLibrary = true;
-        doc.optionalRightCollection = Docs.SchemaDocument([], { title: "Pending documents" });
+        doc.optionalRightCollection = Docs.SchemaDocument(["title"], [], { title: "Pending documents" });
         // doc.library = Docs.TreeDocument([doc], { title: `Library: ${CurrentUserUtils.email}` });
         // (doc.library as Doc).excludeFromLibrary = true;
         return doc;
     }
 
-    public static loadCurrentUser(): Promise<any> {
+    public static async loadCurrentUser(): Promise<any> {
         let userPromise = rp.get(DocServer.prepend(RouteStore.getCurrUser)).then(response => {
             if (response) {
                 let obj = JSON.parse(response);
@@ -47,7 +53,7 @@ export class CurrentUserUtils {
                 throw new Error("There should be a user! Why does Dash think there isn't one?");
             }
         });
-        let userDocPromise = rp.get(DocServer.prepend(RouteStore.getUserDocumentId)).then(id => {
+        let userDocPromise = await rp.get(DocServer.prepend(RouteStore.getUserDocumentId)).then(id => {
             if (id) {
                 return DocServer.GetRefField(id).then(field =>
                     runInAction(() => this.user_document = field instanceof Doc ? field : this.createUserDocument(id)));
@@ -55,14 +61,63 @@ export class CurrentUserUtils {
                 throw new Error("There should be a user id! Why does Dash think there isn't one?");
             }
         });
+        try {
+            const getEnvironment = await fetch("/assets/env.json", { redirect: "follow", method: "GET", credentials: "include" });
+            NorthstarSettings.Instance.UpdateEnvironment(await getEnvironment.json());
+            await Gateway.Instance.ClearCatalog();
+            const extraSchemas = Cast(CurrentUserUtils.UserDocument.DBSchemas, listSpec("string"), []);
+            let extras = await Promise.all(extraSchemas.map(async sc => await Gateway.Instance.GetSchema("", sc)));
+            let catprom = CurrentUserUtils.SetNorthstarCatalog(await Gateway.Instance.GetCatalog(), extras);
+            if (catprom) await Promise.all(catprom);
+        } catch (e) {
+
+        }
         return Promise.all([userPromise, userDocPromise]);
     }
 
     /* Northstar catalog ... really just for testing so this should eventually go away */
+    // --------------- Northstar hooks ------------- /
+    static _northstarSchemas: Doc[] = [];
     @observable private static _northstarCatalog?: Catalog;
     @computed public static get NorthstarDBCatalog() { return this._northstarCatalog; }
+
+    @action static SetNorthstarCatalog(ctlog: Catalog, extras: Catalog[]) {
+        CurrentUserUtils.NorthstarDBCatalog = ctlog;
+        if (ctlog && ctlog.schemas) {
+            extras.map(ex => ctlog.schemas!.push(ex));
+            return ctlog.schemas.map(async schema => {
+                let schemaDocuments: Doc[] = [];
+                let attributesToBecomeDocs = CurrentUserUtils.GetAllNorthstarColumnAttributes(schema);
+                await Promise.all(attributesToBecomeDocs.reduce((promises, attr) => {
+                    promises.push(DocServer.GetRefField(attr.displayName! + ".alias").then(action((field: Opt<Field>) => {
+                        if (field instanceof Doc) {
+                            schemaDocuments.push(field);
+                        } else {
+                            var atmod = new ColumnAttributeModel(attr);
+                            let histoOp = new HistogramOperation(schema.displayName!,
+                                new AttributeTransformationModel(atmod, AggregateFunction.None),
+                                new AttributeTransformationModel(atmod, AggregateFunction.Count),
+                                new AttributeTransformationModel(atmod, AggregateFunction.Count));
+                            schemaDocuments.push(Docs.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! }));
+                        }
+                    })));
+                    return promises;
+                }, [] as Promise<void>[]));
+                return CurrentUserUtils._northstarSchemas.push(Docs.TreeDocument(schemaDocuments, { width: 50, height: 100, title: schema.displayName! }));
+            });
+        }
+    }
     public static set NorthstarDBCatalog(ctlog: Catalog | undefined) { this._northstarCatalog = ctlog; }
 
+    public static AddNorthstarSchema(schema: Schema, schemaDoc: Doc) {
+        if (this._northstarCatalog && CurrentUserUtils._northstarSchemas) {
+            this._northstarCatalog.schemas!.push(schema);
+            CurrentUserUtils._northstarSchemas.push(schemaDoc);
+            let schemas = Cast(CurrentUserUtils.UserDocument.DBSchemas, listSpec("string"), []);
+            schemas.push(schema.displayName!);
+            CurrentUserUtils.UserDocument.DBSchemas = new List<string>(schemas);
+        }
+    }
     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);
-- 
cgit v1.2.3-70-g09d2


From b29a0d1cef60b55f609fcd94ad38232ae557e681 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Thu, 16 May 2019 18:32:57 -0400
Subject: Fixed linter errors

---
 src/client/goldenLayout.d.ts                       |  3 +++
 src/client/northstar/dash-fields/HistogramField.ts |  2 +-
 src/client/northstar/model/ModelHelpers.ts         |  9 +++----
 src/client/util/DragManager.ts                     |  2 +-
 src/client/util/RichTextSchema.tsx                 | 16 +++++------
 src/client/util/TooltipTextMenu.tsx                | 13 ++++-----
 src/client/views/DocumentDecorations.tsx           | 31 +++++++++++-----------
 src/client/views/PresentationView.tsx              |  4 +--
 src/client/views/TemplateMenu.tsx                  |  4 +--
 .../views/collections/CollectionDockingView.tsx    |  5 ++--
 .../views/collections/CollectionSchemaView.tsx     | 10 +++----
 .../views/collections/CollectionTreeView.tsx       |  9 ++++---
 .../collections/collectionFreeForm/MarqueeView.tsx | 13 ++++-----
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  8 +++---
 src/client/views/nodes/ImageBox.tsx                |  2 +-
 src/client/views/nodes/KeyValueBox.tsx             |  2 +-
 src/client/views/nodes/KeyValuePair.tsx            |  3 ++-
 src/client/views/nodes/VideoBox.tsx                | 22 +++++++--------
 src/new_fields/CursorField.ts                      |  6 ++---
 src/new_fields/Doc.ts                              |  4 +--
 src/new_fields/ObjectField.ts                      |  2 +-
 .../authentication/models/current_user_utils.ts    |  2 +-
 24 files changed, 91 insertions(+), 85 deletions(-)
 create mode 100644 src/client/goldenLayout.d.ts

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

diff --git a/src/client/goldenLayout.d.ts b/src/client/goldenLayout.d.ts
new file mode 100644
index 000000000..b50240563
--- /dev/null
+++ b/src/client/goldenLayout.d.ts
@@ -0,0 +1,3 @@
+
+declare const GoldenLayout: any;
+export = GoldenLayout;
\ No newline at end of file
diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts
index aabc77bb2..1ee2189b9 100644
--- a/src/client/northstar/dash-fields/HistogramField.ts
+++ b/src/client/northstar/dash-fields/HistogramField.ts
@@ -52,7 +52,7 @@ export class HistogramField extends ObjectField {
 
     [Copy]() {
         let y = this.HistoOp;
-        let z = this.HistoOp["Copy"];
+        let z = this.HistoOp.Copy;
         return new HistogramField(HistogramOperation.Duplicate(this.HistoOp));
     }
 }
\ No newline at end of file
diff --git a/src/client/northstar/model/ModelHelpers.ts b/src/client/northstar/model/ModelHelpers.ts
index 80bb71224..88e6e72b8 100644
--- a/src/client/northstar/model/ModelHelpers.ts
+++ b/src/client/northstar/model/ModelHelpers.ts
@@ -32,12 +32,9 @@ export class ModelHelpers {
 
     public static GetAggregateParametersIndex(histogramResult: HistogramResult, aggParameters?: AggregateParameters): number {
         return Array.from(histogramResult.aggregateParameters!).findIndex((value, i, set) => {
-            if (set[i] instanceof CountAggregateParameters && value instanceof CountAggregateParameters)
-                return true;
-            if (set[i] instanceof MarginAggregateParameters && value instanceof MarginAggregateParameters)
-                return true;
-            if (set[i] instanceof SumAggregateParameters && value instanceof SumAggregateParameters)
-                return true;
+            if (set[i] instanceof CountAggregateParameters && value instanceof CountAggregateParameters) return true;
+            if (set[i] instanceof MarginAggregateParameters && value instanceof MarginAggregateParameters) return true;
+            if (set[i] instanceof SumAggregateParameters && value instanceof SumAggregateParameters) return true;
             return false;
         });
     }
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 26da34e67..05eb5c38a 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -41,7 +41,7 @@ export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: ()
 export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Doc) {
     let srcTarg = sourceDoc.proto;
     let draggedDocs: Doc[] = [];
-    let draggedFromDocs: Doc[] = []
+    let draggedFromDocs: Doc[] = [];
     if (srcTarg) {
         let linkToDocs = await DocListCastAsync(srcTarg.linkedToDocs);
         let linkFromDocs = await DocListCastAsync(srcTarg.linkedFromDocs);
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index c0e6f7899..3e3e98206 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -97,13 +97,13 @@ export const nodes: { [index: string]: NodeSpec } = {
                     title: dom.getAttribute("title"),
                     alt: dom.getAttribute("alt"),
                     width: Math.min(100, Number(dom.getAttribute("width"))),
-                }
+                };
             }
         }],
         // TODO if we don't define toDom, something weird happens: dragging the image will not move it but clone it. Why?
         toDOM(node) {
-            const attrs = { style: `width: ${node.attrs.width}` }
-            return ["img", { ...node.attrs, ...attrs }]
+            const attrs = { style: `width: ${node.attrs.width}` };
+            return ["img", { ...node.attrs, ...attrs }];
         }
     },
 
@@ -375,7 +375,7 @@ export class ImageResizeView {
                 const currentX = e.pageX;
                 const diffInPx = currentX - startX;
                 self._outer.style.width = `${startWidth + diffInPx}`;
-            }
+            };
 
             const onpointerup = () => {
                 document.removeEventListener("pointermove", onpointermove);
@@ -384,11 +384,11 @@ export class ImageResizeView {
                     view.state.tr.setNodeMarkup(getPos(), null,
                         { src: node.attrs.src, width: self._outer.style.width })
                         .setSelection(view.state.selection));
-            }
+            };
 
-            document.addEventListener("pointermove", onpointermove)
-            document.addEventListener("pointerup", onpointerup)
-        }
+            document.addEventListener("pointermove", onpointermove);
+            document.addEventListener("pointerup", onpointerup);
+        };
 
         this._outer.appendChild(this._handle);
         this._outer.appendChild(this._img);
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 6eb654319..223921428 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -175,7 +175,7 @@ export class TooltipTextMenu {
             this.linkText.style.width = "150px";
             this.linkText.style.overflow = "hidden";
             this.linkText.style.color = "white";
-            this.linkText.onpointerdown = (e: PointerEvent) => { e.stopPropagation(); }
+            this.linkText.onpointerdown = (e: PointerEvent) => { e.stopPropagation(); };
             let linkBtn = document.createElement("div");
             linkBtn.textContent = ">>";
             linkBtn.style.width = "20px";
@@ -192,8 +192,9 @@ export class TooltipTextMenu {
                         let docid = href.replace(DocServer.prepend("/doc/"), "");
                         DocServer.GetRefField(docid).then(action((f: Opt<Field>) => {
                             if (f instanceof Doc) {
-                                if (DocumentManager.Instance.getDocumentView(f))
+                                if (DocumentManager.Instance.getDocumentView(f)) {
                                     DocumentManager.Instance.getDocumentView(f)!.props.focus(f);
+                                }
                                 else CollectionDockingView.Instance.AddRightSplit(f);
                             }
                         }));
@@ -201,7 +202,7 @@ export class TooltipTextMenu {
                     e.stopPropagation();
                     e.preventDefault();
                 }
-            }
+            };
             this.linkDrag = document.createElement("img");
             this.linkDrag.src = "https://seogurusnyc.com/wp-content/uploads/2016/12/link-1.png";
             this.linkDrag.style.width = "20px";
@@ -216,12 +217,12 @@ export class TooltipTextMenu {
                     {
                         handlers: {
                             dragComplete: action(() => {
-                                let m = dragData.droppedDocuments as Doc[];
+                                let m = dragData.droppedDocuments;
                                 this.makeLink(DocServer.prepend("/doc/" + m[0][Id]));
                             }),
                         },
                         hideSource: false
-                    })
+                    });
             };
             this.linkEditor.appendChild(this.linkDrag);
             this.linkEditor.appendChild(this.linkText);
@@ -239,7 +240,7 @@ export class TooltipTextMenu {
                 e.stopPropagation();
                 e.preventDefault();
             }
-        }
+        };
         this.tooltip.appendChild(this.linkEditor);
     }
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index b2c65a31f..7083b1003 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -250,22 +250,21 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 selectedDocs[0].props.removeDocument && selectedDocs[0].props.removeDocument(this._iconDoc);
             }
             if (!this._removeIcon) {
-                if (selectedDocs.length === 1)
+                if (selectedDocs.length === 1) {
                     this.getIconDoc(selectedDocs[0]).then(icon => selectedDocs[0].props.toggleMinimized());
-                else
-                    if (Math.abs(e.pageX - this._downX) < Utils.DRAG_THRESHOLD &&
-                        Math.abs(e.pageY - this._downY) < Utils.DRAG_THRESHOLD) {
-                        let docViews = SelectionManager.ViewsSortedVertically();
-                        let topDocView = docViews[0];
-                        let ind = topDocView.templates.indexOf(Templates.Bullet.Layout);
-                        if (ind !== -1) {
-                            topDocView.templates.splice(ind, 1);
-                            topDocView.props.Document.subBulletDocs = undefined;
-                        } else {
-                            topDocView.addTemplate(Templates.Bullet);
-                            topDocView.props.Document.subBulletDocs = new List<Doc>(docViews.filter(v => v !== topDocView).map(v => v.props.Document.proto!));
-                        }
+                } else if (Math.abs(e.pageX - this._downX) < Utils.DRAG_THRESHOLD &&
+                    Math.abs(e.pageY - this._downY) < Utils.DRAG_THRESHOLD) {
+                    let docViews = SelectionManager.ViewsSortedVertically();
+                    let topDocView = docViews[0];
+                    let ind = topDocView.templates.indexOf(Templates.Bullet.Layout);
+                    if (ind !== -1) {
+                        topDocView.templates.splice(ind, 1);
+                        topDocView.props.Document.subBulletDocs = undefined;
+                    } else {
+                        topDocView.addTemplate(Templates.Bullet);
+                        topDocView.props.Document.subBulletDocs = new List<Doc>(docViews.filter(v => v !== topDocView).map(v => v.props.Document.proto!));
                     }
+                }
             }
             this._removeIcon = false;
         }
@@ -537,9 +536,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                         if (temp !== Templates.Bullet.Layout || i === 0) {
                             res.push(temp);
                         }
-                    })
+                    });
                 }
-                return res
+                return res;
             }, [] as string[]);
             let checked = false;
             docTemps.forEach(temp => {
diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx
index 098e725c7..7d0dc2913 100644
--- a/src/client/views/PresentationView.tsx
+++ b/src/client/views/PresentationView.tsx
@@ -1,7 +1,7 @@
 import { observer } from "mobx-react";
-import React = require("react")
+import React = require("react");
 import { observable, action, runInAction, reaction } from "mobx";
-import "./PresentationView.scss"
+import "./PresentationView.scss";
 import "./Main.tsx";
 import { DocumentManager } from "../util/DocumentManager";
 import { Utils } from "../../Utils";
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index cfe1b0663..e5b679e24 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -43,7 +43,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     @action
     toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
         if (event.target.checked) {
-            if (template.Name == "Bullet") {
+            if (template.Name === "Bullet") {
                 let topDocView = this.props.docs[0];
                 topDocView.addTemplate(template);
                 topDocView.props.Document.subBulletDocs = new List<Doc>(this.props.docs.filter(v => v !== topDocView).map(v => v.props.Document.proto!));
@@ -52,7 +52,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
             }
             this.props.templates.set(template, true);
         } else {
-            if (template.Name == "Bullet") {
+            if (template.Name === "Bullet") {
                 let topDocView = this.props.docs[0];
                 topDocView.removeTemplate(template);
                 topDocView.props.Document.subBulletDocs = undefined;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 58f1e33a1..deec64225 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -83,7 +83,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                     child.contentItems[0].remove();
                     this.layoutChanged(document);
                     return true;
-                } else
+                } else {
                     Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => {
                         if (Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)!.Document, document)) {
                             child.contentItems[j].remove();
@@ -94,8 +94,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                         }
                         return false;
                     });
+                }
                 return false;
-            })
+            });
         }
         if (retVal) {
             this.stateChanged();
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index f4ad5b357..497dfde88 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -90,7 +90,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
         let fieldContentView = <FieldView {...props} />;
         let reference = React.createRef<HTMLDivElement>();
         let onItemDown = (e: React.PointerEvent) =>
-            (this.props.CollectionView!.props.isSelected() ?
+            (this.props.CollectionView.props.isSelected() ?
                 SetupDrag(reference, () => props.Document, this.props.moveDocument)(e) : undefined);
         let applyToDoc = (doc: Doc, run: (args?: { [name: string]: any }) => any) => {
             const res = run({ this: doc });
@@ -127,7 +127,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                         }
                         const run = script.run;
                         //TODO This should be able to be refactored to compile the script once
-                        const val = await DocListCastAsync(this.props.Document[this.props.fieldKey])
+                        const val = await DocListCastAsync(this.props.Document[this.props.fieldKey]);
                         val && val.forEach(doc => applyToDoc(doc, run));
                     }}>
                 </EditableView>
@@ -230,14 +230,14 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
         DocListCast(this.props.Document.data).map(doc => {
             csv += self.columns.reduce((val, col) => val + (doc[col] ? doc[col]!.toString() : "") + ",", "");
             csv = csv.substr(0, csv.length - 1) + "\n";
-        })
+        });
         csv.substring(0, csv.length - 1);
         let dbName = StrCast(this.props.Document.title);
         let res = await Gateway.Instance.PostSchema(csv, dbName);
         if (self.props.CollectionView.props.addDocument) {
             let schemaDoc = await Docs.DBDocument("https://www.cs.brown.edu/" + dbName, { title: dbName });
             if (schemaDoc) {
-                self.props.CollectionView.props.addDocument(schemaDoc, false); 
+                self.props.CollectionView.props.addDocument(schemaDoc, false);
             }
         }
     }
@@ -263,7 +263,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     get previewDocument(): Doc | undefined {
         const children = DocListCast(this.props.Document[this.props.fieldKey]);
         const selected = children.length > this._selectedIndex ? FieldValue(children[this._selectedIndex]) : undefined;
-        return selected ? (this.previewScript && this.previewScript != "this" ? FieldValue(Cast(selected[this.previewScript], Doc)) : selected) : undefined;
+        return selected ? (this.previewScript && this.previewScript !== "this" ? FieldValue(Cast(selected[this.previewScript], Doc)) : selected) : undefined;
     }
     get tableWidth() { return (this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH) * (1 - this.splitPercentage / 100); }
     get previewRegionHeight() { return this.props.PanelHeight() - 2 * this.borderWidth; }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 70c09d97c..6acef434e 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -56,7 +56,7 @@ class TreeView extends React.Component<TreeViewProps> {
         } else {
             CollectionDockingView.Instance.AddRightSplit(this.props.document);
         }
-    };
+    }
 
     get children() {
         return Cast(this.props.document.data, listSpec(Doc), []); // bcz: needed?    .filter(doc => FieldValue(doc));
@@ -184,8 +184,9 @@ class TreeView extends React.Component<TreeViewProps> {
                             {TreeView.GetChildElements(doc instanceof Doc ? [doc] : docList, key !== "data", (doc: Doc) => this.remove(doc, key), this.move, this.props.dropAction)}
                         </div>
                     </ul >);
-                } else
+                } else {
                     bulletType = BulletType.Collapsed;
+                }
             }
         });
         return <div className="treeViewItem-container"
@@ -198,8 +199,8 @@ class TreeView extends React.Component<TreeViewProps> {
         </div>;
     }
     public static GetChildElements(docs: Doc[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) {
-        return docs.filter(child => child instanceof Doc && !child.excludeFromLibrary && (allowMinimized || !child.isMinimized)).filter(doc => FieldValue(doc)).map(child =>
-            <TreeView document={child as Doc} key={(child as Doc)[Id]} deleteDoc={remove} moveDocument={move} dropAction={dropAction} />);
+        return docs.filter(child => !child.excludeFromLibrary && (allowMinimized || !child.isMinimized)).map(child =>
+            <TreeView document={child} key={child[Id]} deleteDoc={remove} moveDocument={move} dropAction={dropAction} />);
     }
 }
 
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 12edb2c2a..c3c4115b8 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -63,7 +63,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             e.preventDefault();
             (async () => {
                 let text: string = await navigator.clipboard.readText();
-                let ns = text.split("\n").filter(t => t.trim() != "\r" && t.trim() != "");
+                let ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
                 for (let i = 0; i < ns.length - 1; i++) {
                     while (!(ns[i].trim() === "" || ns[i].endsWith("-\r") || ns[i].endsWith("-") ||
                         ns[i].endsWith(";\r") || ns[i].endsWith(";") ||
@@ -80,7 +80,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                     let newBox = Docs.TextDocument({ width: 200, height: 35, x: x + indent / 3 * 10, y: y, documentText: "@@@" + line, title: line });
                     this.props.addDocument(newBox, false);
                     y += 40 * this.props.getTransform().Scale;
-                })
+                });
             })();
         } else if (e.key === "b" && e.ctrlKey) {
             //heuristically converts pasted text into a table.
@@ -93,9 +93,10 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             e.preventDefault();
             (async () => {
                 let text: string = await navigator.clipboard.readText();
-                let ns = text.split("\n").filter(t => t.trim() != "\r" && t.trim() != "");
-                while (ns.length > 0 && ns[0].split("\t").length < 2)
+                let ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
+                while (ns.length > 0 && ns[0].split("\t").length < 2) {
                     ns.splice(0, 1);
+                }
                 if (ns.length > 0) {
                     let columns = ns[0].split("\t");
                     let docList: Doc[] = [];
@@ -109,7 +110,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                         let doc = new Doc();
                         columns.forEach((col, i) => doc[columns[i]] = (values.length > i ? ((values[i].indexOf(Number(values[i]).toString()) !== -1) ? Number(values[i]) : values[i]) : undefined));
                         if (groupAttr) {
-                            doc["_group"] = groupAttr;
+                            doc._group = groupAttr;
                         }
                         doc.title = i.toString();
                         docList.push(doc);
@@ -284,7 +285,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                     //     summarizedDoc.isIconAnimating = new List<number>([scrpt[0], scrpt[1], maxx, maxy, maxw, maxh, Date.now(), 0]);
                     // });
                     this.props.addLiveTextDocument(summary);
-                })
+                });
             }
             else {
                 this.props.addDocument(newCollection, false);
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 7c7ca9e25..d4f660b3f 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -149,7 +149,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                         let scrpt = this.props.ScreenToLocalTransform().inverse().transformPoint(minx, miny);
                         maximizedDoc.willMaximize = isMinimized;
                         maximizedDoc.isMinimized = false;
-                        maximizedDoc.isIconAnimating = new List<number>([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0])
+                        maximizedDoc.isIconAnimating = new List<number>([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0]);
                     }
                 }
             });
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index cf8bcbb42..428dd9b36 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -244,7 +244,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
             const protoDest = destDoc.proto;
             const protoSrc = sourceDoc.proto;
-            if (de.mods == "Control") {
+            if (de.mods === "Control") {
                 let src = protoSrc ? protoSrc : sourceDoc;
                 let dst = protoDest ? protoDest : destDoc;
                 dst.data = src;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index f24d4ae88..f30022508 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -184,7 +184,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 state: field && field.Data ? EditorState.fromJSON(config, JSON.parse(field.Data)) : EditorState.create(config),
                 dispatchTransaction: this.dispatchTransaction,
                 nodeViews: {
-                    image(node, view, getPos) { return new ImageResizeView(node, view, getPos) }
+                    image(node, view, getPos) { return new ImageResizeView(node, view, getPos); }
                 }
             });
             let text = StrCast(this.props.Document.documentText);
@@ -232,9 +232,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                     let docid = href.replace(DocServer.prepend("/doc/"), "").split("?")[0];
                     DocServer.GetRefField(docid).then(action((f: Opt<Field>) => {
                         if (f instanceof Doc) {
-                            if (DocumentManager.Instance.getDocumentView(f))
+                            if (DocumentManager.Instance.getDocumentView(f)) {
                                 DocumentManager.Instance.getDocumentView(f)!.props.focus(f);
-                            else CollectionDockingView.Instance.AddRightSplit(f);
+                            } else {
+                                CollectionDockingView.Instance.AddRightSplit(f);
+                            }
                         }
                     }));
                 }
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 6472ae711..3cc60a6c5 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -165,7 +165,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
         else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => (p as ImageField).url.href);
         let nativeWidth = FieldValue(this.Document.nativeWidth, (this.props.PanelWidth as any) as string ? Number((this.props.PanelWidth as any) as string) : 50);
         let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive";
-        let id = this.props.id; // bcz: used to set id = "isExpander" in templates.tsx
+        let id = (this.props as any).id; // bcz: used to set id = "isExpander" in templates.tsx
         return (
             <div id={id} className={`imageBox-cont${interactive}`} onPointerDown={this.onPointerDown} onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
                 <img id={id} src={paths[Math.min(paths.length, this._photoIndex)]}
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index c9d665ceb..86437a6c1 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -18,7 +18,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     @observable private _keyInput: string = "";
     @observable private _valueInput: string = "";
     @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage, 50); }
-    get fieldDocToLayout() { return this.props.fieldKey ? FieldValue(Cast(this.props.Document[this.props.fieldKey], Doc)) : this.props.Document }
+    get fieldDocToLayout() { return this.props.fieldKey ? FieldValue(Cast(this.props.Document[this.props.fieldKey], Doc)) : this.props.Document; }
 
     constructor(props: FieldViewProps) {
         super(props);
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 5de660d57..4f7919f50 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -46,8 +46,9 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
                 <td className="keyValuePair-td-key" style={{ width: `${this.props.keyWidth}%` }}>
                     <div className="keyValuePair-td-key-container">
                         <button className="keyValuePair-td-key-delete" onClick={() => {
-                            if (Object.keys(props.Document).indexOf(props.fieldKey) !== -1)
+                            if (Object.keys(props.Document).indexOf(props.fieldKey) !== -1) {
                                 props.Document[props.fieldKey] = undefined;
+                            }
                             else props.Document.proto![props.fieldKey] = undefined;
                         }}>
                             X
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 96dc884c8..81c429a02 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -70,7 +70,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
     }
 
     @action
-    updateTimecode = () => this.player && (this.props.Document.curPage = this.player.currentTime);
+    updateTimecode = () => this.player && (this.props.Document.curPage = this.player.currentTime)
 
     componentDidMount() {
         if (this.props.setVideoBox) this.props.setVideoBox(this);
@@ -106,31 +106,31 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
                     "user-agent": 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:43.0) Gecko/20100101 Firefox/46.0',
                 },
 
-            }
+            };
             try {
                 let responseSchema: any = {};
-                const videoInfoResponse = await rp.get(DocServer.prepend(RouteStore.corsProxy + "/" + `https://www.youtube.com/watch?v=${videoId}`), videoInfoRequestConfig)
+                const videoInfoResponse = await rp.get(DocServer.prepend(RouteStore.corsProxy + "/" + `https://www.youtube.com/watch?v=${videoId}`), videoInfoRequestConfig);
                 const dataHtml = videoInfoResponse;
                 const start = dataHtml.indexOf('ytplayer.config = ') + 18;
                 const end = dataHtml.indexOf(';ytplayer.load');
-                const subString = dataHtml.substring(start, end)
+                const subString = dataHtml.substring(start, end);
                 const subJson = JSON.parse(subString);
                 const stringSub = subJson.args.player_response;
                 const stringSubJson = JSON.parse(stringSub);
                 const adaptiveFormats = stringSubJson.streamingData.adaptiveFormats;
-                const videoDetails = stringSubJson.videoDetails
-                responseSchema["adaptiveFormats"] = adaptiveFormats;
-                responseSchema["videoDetails"] = videoDetails;
-                resolve(responseSchema)
+                const videoDetails = stringSubJson.videoDetails;
+                responseSchema.adaptiveFormats = adaptiveFormats;
+                responseSchema.videoDetails = videoDetails;
+                resolve(responseSchema);
             }
             catch (err) {
                 console.log(`
                 --- Youtube ---
                 Function: getMp4ForVideo
-                Error: `, err)
-                reject(err)
+                Error: `, err);
+                reject(err);
             }
-        })
+        });
     }
     onPointerDown = (e: React.PointerEvent) => {
         e.preventDefault();
diff --git a/src/new_fields/CursorField.ts b/src/new_fields/CursorField.ts
index 7fd326a5f..fc144222c 100644
--- a/src/new_fields/CursorField.ts
+++ b/src/new_fields/CursorField.ts
@@ -6,17 +6,17 @@ import { serializable, createSimpleSchema, object } from "serializr";
 export type CursorPosition = {
     x: number,
     y: number
-}
+};
 
 export type CursorMetadata = {
     id: string,
     identifier: string
-}
+};
 
 export type CursorData = {
     metadata: CursorMetadata,
     position: CursorPosition
-}
+};
 
 const PositionSchema = createSimpleSchema({
     x: true,
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index de72be3ea..c898f697b 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -42,8 +42,8 @@ export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) {
     return list ? Promise.all(list).then(() => list) : Promise.resolve(defaultValue);
 }
 
-export function DocListCast(field: FieldResult) {
-    return Cast(field, listSpec(Doc), []).filter(d => d && d instanceof Doc).map(d => d as Doc);
+export function DocListCast(field: FieldResult): Doc[] {
+    return Cast(field, listSpec(Doc), []).filter(d => d && d instanceof Doc) as Doc[];
 }
 
 @Deserializable("doc").withFields(["id"])
diff --git a/src/new_fields/ObjectField.ts b/src/new_fields/ObjectField.ts
index f276bfa67..51768c6db 100644
--- a/src/new_fields/ObjectField.ts
+++ b/src/new_fields/ObjectField.ts
@@ -6,7 +6,7 @@ export const Parent = Symbol("Parent");
 export const Copy = Symbol("Copy");
 
 export abstract class ObjectField {
-    protected [OnUpdate](diff?: any) { };
+    protected [OnUpdate](diff?: any) { }
     private [Parent]?: RefField | ObjectField;
     abstract [Copy](): ObjectField;
 }
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 5b63ac356..aef2d3f4a 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -66,7 +66,7 @@ export class CurrentUserUtils {
             NorthstarSettings.Instance.UpdateEnvironment(await getEnvironment.json());
             await Gateway.Instance.ClearCatalog();
             const extraSchemas = Cast(CurrentUserUtils.UserDocument.DBSchemas, listSpec("string"), []);
-            let extras = await Promise.all(extraSchemas.map(async sc => await Gateway.Instance.GetSchema("", sc)));
+            let extras = await Promise.all(extraSchemas.map(sc => Gateway.Instance.GetSchema("", sc)));
             let catprom = CurrentUserUtils.SetNorthstarCatalog(await Gateway.Instance.GetCatalog(), extras);
             if (catprom) await Promise.all(catprom);
         } catch (e) {
-- 
cgit v1.2.3-70-g09d2


From 2c8aaae6f026199e3675f5e7b4677724d5089ae7 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 17 May 2019 14:53:09 -0400
Subject: added something to follow links in current tab ?

---
 src/client/util/DocumentManager.ts                 | 33 +++++++++++++---------
 .../views/collections/CollectionSchemaView.tsx     |  7 -----
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  2 +-
 3 files changed, 20 insertions(+), 22 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index c6962ad8d..d06af6dd5 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,11 +1,14 @@
 import { computed, observable } from 'mobx';
 import { DocumentView } from '../views/nodes/DocumentView';
-import { Doc, DocListCast } from '../../new_fields/Doc';
+import { Doc, DocListCast, Opt } from '../../new_fields/Doc';
 import { FieldValue, Cast, NumCast, BoolCast } from '../../new_fields/Types';
 import { listSpec } from '../../new_fields/Schema';
 import { undoBatch } from './UndoManager';
 import { CollectionDockingView } from '../views/collections/CollectionDockingView';
 import { Id } from '../../new_fields/RefField';
+import { CollectionView } from '../views/collections/CollectionView';
+import { CollectionPDFView } from '../views/collections/CollectionPDFView';
+import { CollectionVideoView } from '../views/collections/CollectionVideoView';
 
 
 export class DocumentManager {
@@ -27,31 +30,33 @@ export class DocumentManager {
         // this.DocumentViews = new Array<DocumentView>();
     }
 
-    public getDocumentViewById(id: string): DocumentView | null {
+    public getDocumentViewById(id: string, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | null {
 
         let toReturn: DocumentView | null = null;
+        let passes = preferredCollection ? [preferredCollection, undefined] : [undefined];
 
-        //gets document view that is in a freeform canvas collection
-        DocumentManager.Instance.DocumentViews.map(view => {
-            if (view.props.Document[Id] === id) {
-                toReturn = view;
-                return;
-            }
-        });
-        if (!toReturn) {
+        for (let i = 0; i < passes.length; i++) {
             DocumentManager.Instance.DocumentViews.map(view => {
-                let doc = view.props.Document.proto;
-                if (doc && doc[Id] === id) {
+                if (view.props.Document[Id] === id && (!passes[i] || view.props.ContainingCollectionView === preferredCollection)) {
                     toReturn = view;
+                    return;
                 }
             });
+            if (!toReturn) {
+                DocumentManager.Instance.DocumentViews.map(view => {
+                    let doc = view.props.Document.proto;
+                    if (doc && doc[Id] === id && (!passes[i] || view.props.ContainingCollectionView === preferredCollection)) {
+                        toReturn = view;
+                    }
+                });
+            }
         }
 
         return toReturn;
     }
 
-    public getDocumentView(toFind: Doc): DocumentView | null {
-        return this.getDocumentViewById(toFind[Id]);
+    public getDocumentView(toFind: Doc, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | null {
+        return this.getDocumentViewById(toFind[Id], preferredCollection);
     }
 
     public getDocumentViews(toFind: Doc): DocumentView[] {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 497dfde88..e2b90e26d 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -24,14 +24,7 @@ import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
 import { listSpec } from "../../../new_fields/Schema";
 import { List } from "../../../new_fields/List";
 import { Id } from "../../../new_fields/RefField";
-import { isUndefined } from "typescript-collections/dist/lib/util";
-import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { Gateway } from "../../northstar/manager/Gateway";
-import { DocServer } from "../../DocServer";
-import { ColumnAttributeModel } from "../../northstar/core/attribute/AttributeModel";
-import { HistogramOperation } from "../../northstar/operations/HistogramOperation";
-import { AggregateFunction } from "../../northstar/model/idea/idea";
-import { AttributeTransformationModel } from "../../northstar/core/attribute/AttributeTransformationModel";
 import { Docs } from "../../documents/Documents";
 import { ContextMenu } from "../ContextMenu";
 
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 001ce3095..00aa71656 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -187,7 +187,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 // ...(maximizedDocs ? maximizedDocs : []),
                 // ...(summarizedDocs ? summarizedDocs : []),];
                 if (expandedDocs.length) {   // bcz: need a better way to associate behaviors with click events on widget-documents
-                    let hasView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedDocs[0]);
+                    let hasView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedDocs[0], this.props.ContainingCollectionView);
                     if (!hasView && ((altKey && !this.props.Document.maximizeOnRight) || (!altKey && this.props.Document.maximizeOnRight))) {
                         let dataDocs = DocListCast(CollectionDockingView.Instance.props.Document.data);
                         if (dataDocs) {
-- 
cgit v1.2.3-70-g09d2

