From 9596bd5b6505fa6e9d32630ed7d824d263cd1e63 Mon Sep 17 00:00:00 2001
From: Eleanor Eng <eleanoreng@eleanors-mbp.devices.brown.edu>
Date: Sat, 16 Feb 2019 17:18:02 -0500
Subject: first commit

---
 src/client/views/ContextMenu.tsx | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

(limited to 'src/client/views/ContextMenu.tsx')

diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 4f26a75d2..4a216ca21 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -59,10 +59,27 @@ export class ContextMenu extends React.Component {
     render() {
         return (
             <div className="contextMenu-cont" style={{ left: this._pageX, top: this._pageY, display: this._display }} ref={this.ref}>
+                <input className="contextMenu-item" type="text" id="mySearch" placeholder="Search . . ." onKeyUp={this.search}></input>
                 {this._items.map(prop => {
                     return <ContextMenuItem {...prop} key={prop.description} />
                 })}
             </div>
         )
     }
+
+    search() {
+        let input = document.getElementById("mySearch");
+        let filter = (input as HTMLSelectElement).value.toUpperCase();
+        let li = document.getElementById("options");
+        let a = (li as HTMLSelectElement).getElementsByTagName("div");
+        for (let i = 0; i < a.length; i++) {
+            let txtValue = a[i].textContent || a[i].innerText;
+            if (txtValue.toUpperCase().indexOf(filter) > -1) {
+                a[i].style.display = "";
+            }
+            else {
+                a[i].style.display = "none";
+            }
+        }
+    }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From e5a14d6dddccc922bb253323b461ac2500c33b7c Mon Sep 17 00:00:00 2001
From: Eleanor Eng <eleanoreng@eleanors-mbp.devices.brown.edu>
Date: Tue, 19 Feb 2019 18:24:41 -0500
Subject: Different options based on selection

---
 src/client/views/ContextMenu.scss                         |  7 +++----
 src/client/views/ContextMenu.tsx                          |  6 ++++--
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx |  2 --
 src/client/views/nodes/FormattedTextBox.tsx               | 12 +++++++++++-
 src/client/views/nodes/ImageBox.tsx                       | 11 ++++++++++-
 5 files changed, 28 insertions(+), 10 deletions(-)

(limited to 'src/client/views/ContextMenu.tsx')

diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss
index dc5907f14..2ac5d3b52 100644
--- a/src/client/views/ContextMenu.scss
+++ b/src/client/views/ContextMenu.scss
@@ -13,7 +13,6 @@
     display: flex;
     justify-content: center;
     align-items: center;
-    flex-direction: column;
     -webkit-touch-callout: none;
     -webkit-user-select: none;
     -khtml-user-select: none;
@@ -34,6 +33,6 @@
     width: 8vw;
 }
 
-// #mySearch {
-//     font-size: 1.5vw;
-// }
\ No newline at end of file
+#mySearch {
+    font-size: 1.4vw;
+}
\ No newline at end of file
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 4a216ca21..2359c673d 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -1,6 +1,6 @@
 import React = require("react");
 import { ContextMenuItem, ContextMenuProps } from "./ContextMenuItem";
-import { observable } from "mobx";
+import { observable, action } from "mobx";
 import { observer } from "mobx-react";
 import "./ContextMenu.scss"
 
@@ -23,11 +23,13 @@ export class ContextMenu extends React.Component {
         ContextMenu.Instance = this;
     }
 
+    @action
     clearItems() {
         this._items = []
         this._display = "none"
     }
 
+    @action
     addItem(item: ContextMenuProps) {
         if (this._items.indexOf(item) === -1) {
             this._items.push(item);
@@ -58,7 +60,7 @@ export class ContextMenu extends React.Component {
 
     render() {
         return (
-            <div className="contextMenu-cont" style={{ left: this._pageX, top: this._pageY, display: this._display }} ref={this.ref}>
+            <div className="contextMenu-cont" id="options" style={{ left: this._pageX, top: this._pageY, display: this._display }} ref={this.ref}>
                 <input className="contextMenu-item" type="text" id="mySearch" placeholder="Search . . ." onKeyUp={this.search}></input>
                 {this._items.map(prop => {
                     return <ContextMenuItem {...prop} key={prop.description} />
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 54d3a1b56..f37232c08 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -203,7 +203,6 @@ export class CollectionFreeFormDocumentView extends DocumentView {
         }
 
         if (this.topMost) {
-            ContextMenu.Instance.clearItems()
             ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked })
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15)
         }
@@ -211,7 +210,6 @@ export class CollectionFreeFormDocumentView extends DocumentView {
             // DocumentViews should stop propogation of this event
             e.stopPropagation();
 
-            ContextMenu.Instance.clearItems();
             ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked })
             ContextMenu.Instance.addItem({ description: "Open Right", event: this.openRight })
             ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked })
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 8f959762a..0fe6befda 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -13,6 +13,7 @@ import { RichTextField } from "../../../fields/RichTextField";
 import { FieldViewProps, FieldView } from "./FieldView";
 import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
 import { observer } from "mobx-react";
+import { ContextMenu } from "../../views/ContextMenu";
 
 
 // FormattedTextBox: Displays an editable plain text node that maps to a specified Key of a Document
@@ -114,6 +115,15 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
             e.stopPropagation();
         }
     }
+
+    //REPLACE THIS WITH CAPABILITIES SPECIFC TO THIS TYPE OF NODE
+    textCapability = (e: React.MouseEvent): void => {
+    }
+
+    specificContextMenu = (e: React.MouseEvent): void => {
+        ContextMenu.Instance.addItem({ description: "Text Capability", event: this.textCapability });
+    }
+
     render() {
         return (<div className="formattedTextBox-cont"
             style={{
@@ -121,6 +131,6 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
                 whiteSpace: "initial"
             }}
             onPointerDown={this.onPointerDown}
-            ref={this._ref} />)
+            ref={this._ref} onContextMenu={this.specificContextMenu} />)
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 60be5f94d..f363487c3 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -11,6 +11,7 @@ import { FieldWaiting } from '../../../fields/Field';
 import { observer } from "mobx-react"
 import { observable, action, spy } from 'mobx';
 import { KeyStore } from '../../../fields/Key';
+import { ContextMenu } from "../../views/ContextMenu";
 
 @observer
 export class ImageBox extends React.Component<FieldViewProps> {
@@ -81,13 +82,21 @@ export class ImageBox extends React.Component<FieldViewProps> {
         }
     }
 
+    //REPLACE THIS WITH CAPABILITIES SPECIFC TO THIS TYPE OF NODE
+    imageCapability = (e: React.MouseEvent): void => {
+    }
+
+    specificContextMenu = (e: React.MouseEvent): void => {
+        ContextMenu.Instance.addItem({ description: "Image Capability", event: this.imageCapability });
+    }
+
     render() {
         let field = this.props.doc.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";
 
         return (
-            <div className="imageBox-cont" onPointerDown={this.onPointerDown} ref={this._ref} >
+            <div className="imageBox-cont" onPointerDown={this.onPointerDown} ref={this._ref} onContextMenu={this.specificContextMenu}>
                 <img src={path} width="100%" alt="Image not found" />
                 {this.lightbox(path)}
             </div>)
-- 
cgit v1.2.3-70-g09d2


From fa2be8b245e4ed69b771e70a98e65176785751eb Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Mon, 25 Feb 2019 18:40:11 -0500
Subject: Got rid of ids and dom manipulation

---
 src/client/views/ContextMenu.tsx | 30 +++++++++++++-----------------
 1 file changed, 13 insertions(+), 17 deletions(-)

(limited to 'src/client/views/ContextMenu.tsx')

diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 2359c673d..fcb934860 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -12,6 +12,8 @@ export class ContextMenu extends React.Component {
     @observable private _pageX: number = 0;
     @observable private _pageY: number = 0;
     @observable private _display: string = "none";
+    @observable private _searchString: string = "";
+
 
     private ref: React.RefObject<HTMLDivElement>;
 
@@ -40,10 +42,13 @@ export class ContextMenu extends React.Component {
         return this._items;
     }
 
+    @action
     displayMenu(x: number, y: number) {
         this._pageX = x
         this._pageY = y
 
+        this._searchString = "";
+
         this._display = "flex"
     }
 
@@ -60,28 +65,19 @@ export class ContextMenu extends React.Component {
 
     render() {
         return (
-            <div className="contextMenu-cont" id="options" style={{ left: this._pageX, top: this._pageY, display: this._display }} ref={this.ref}>
-                <input className="contextMenu-item" type="text" id="mySearch" placeholder="Search . . ." onKeyUp={this.search}></input>
-                {this._items.map(prop => {
+            <div className="contextMenu-cont" style={{ left: this._pageX, top: this._pageY, display: this._display }} ref={this.ref}>
+                <input className="contextMenu-item" type="text" placeholder="Search . . ." value={this._searchString} onChange={this.onChange}></input>
+                {this._items.filter(prop => {
+                    return prop.description.toLowerCase().indexOf(this._searchString.toLowerCase()) !== -1;
+                }).map(prop => {
                     return <ContextMenuItem {...prop} key={prop.description} />
                 })}
             </div>
         )
     }
 
-    search() {
-        let input = document.getElementById("mySearch");
-        let filter = (input as HTMLSelectElement).value.toUpperCase();
-        let li = document.getElementById("options");
-        let a = (li as HTMLSelectElement).getElementsByTagName("div");
-        for (let i = 0; i < a.length; i++) {
-            let txtValue = a[i].textContent || a[i].innerText;
-            if (txtValue.toUpperCase().indexOf(filter) > -1) {
-                a[i].style.display = "";
-            }
-            else {
-                a[i].style.display = "none";
-            }
-        }
+    @action
+    onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this._searchString = e.target.value;
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From b792c59240c61326c4eec0eb0e3bc34efd792861 Mon Sep 17 00:00:00 2001
From: Eleanor Eng <eleanor_eng@brown.edu>
Date: Thu, 28 Feb 2019 00:53:30 -0500
Subject: functional context menu

---
 package-lock.json                       | 309 +++++++++++++++++++++-----------
 package.json                            |   7 +-
 src/client/views/ContextMenu.tsx        |   3 +
 src/client/views/nodes/DocumentView.tsx |   8 +-
 4 files changed, 214 insertions(+), 113 deletions(-)

(limited to 'src/client/views/ContextMenu.tsx')

diff --git a/package-lock.json b/package-lock.json
index 7b173a75f..d94777075 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,16 +20,33 @@
       }
     },
     "@fortawesome/fontawesome-common-types": {
-      "version": "0.2.14",
-      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.14.tgz",
-      "integrity": "sha512-LOcvERCI96KioXSIfOYN4IATv2ROJOyf72dYnpoWfOIkuyLp45oMej1kL03kYdzvktLCzYhykgHwiu6nkg9Xbw=="
+      "version": "0.2.15",
+      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.15.tgz",
+      "integrity": "sha512-ATBRyKJw1d2ko+0DWN9+BXau0EK3I/Q6pPzPv3LhJD7r052YFAkAdfb1Bd7ZqhBsJrdse/S7jKxWUOZ61qBD4g=="
     },
     "@fortawesome/fontawesome-svg-core": {
-      "version": "1.2.14",
-      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.14.tgz",
-      "integrity": "sha512-T1qCqkwm9PuvK53J64D1ovfrOTa1kG+SrHNj5cFst/rrskhCnbxpRdbqFIdc/thmXC0ebBX8nOUyja2/mrxe4g==",
+      "version": "1.2.15",
+      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.15.tgz",
+      "integrity": "sha512-M/sHyl4g2VBtKYkay1Z+XImMyTVcaBPmehYtPw4HKD9zg2E7eovB7Yx98aUfZjPbroGqa+IL4/+KhWBMOGlHIQ==",
       "requires": {
-        "@fortawesome/fontawesome-common-types": "^0.2.14"
+        "@fortawesome/fontawesome-common-types": "^0.2.15"
+      }
+    },
+    "@fortawesome/free-solid-svg-icons": {
+      "version": "5.7.2",
+      "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.7.2.tgz",
+      "integrity": "sha512-iujcXMyAvIbWM8W3jkOLpvJbR+rPpdN1QyqhZeJaLRdHPH4JmuovIAYP4vx5Sa1csZVXfRD1eDWqVZ/jGM620A==",
+      "requires": {
+        "@fortawesome/fontawesome-common-types": "^0.2.15"
+      }
+    },
+    "@fortawesome/react-fontawesome": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.4.tgz",
+      "integrity": "sha512-GwmxQ+TK7PEdfSwvxtGnMCqrfEm0/HbRHArbUudsYiy9KzVCwndxa2KMcfyTQ8El0vROrq8gOOff09RF1oQe8g==",
+      "requires": {
+        "humps": "^2.0.1",
+        "prop-types": "^15.5.10"
       }
     },
     "@types/anymatch": {
@@ -3366,21 +3383,25 @@
       "dependencies": {
         "abbrev": {
           "version": "1.1.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
           "optional": true
         },
         "ansi-regex": {
           "version": "2.1.1",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
         },
         "aproba": {
           "version": "1.2.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
           "optional": true
         },
         "are-we-there-yet": {
           "version": "1.1.5",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
           "optional": true,
           "requires": {
             "delegates": "^1.0.0",
@@ -3389,11 +3410,13 @@
         },
         "balanced-match": {
           "version": "1.0.0",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
         },
         "brace-expansion": {
           "version": "1.1.11",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
           "requires": {
             "balanced-match": "^1.0.0",
             "concat-map": "0.0.1"
@@ -3401,29 +3424,35 @@
         },
         "chownr": {
           "version": "1.1.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
           "optional": true
         },
         "code-point-at": {
           "version": "1.1.0",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
         },
         "concat-map": {
           "version": "0.0.1",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
         },
         "console-control-strings": {
           "version": "1.1.0",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
         },
         "core-util-is": {
           "version": "1.0.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
           "optional": true
         },
         "debug": {
           "version": "2.6.9",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
           "optional": true,
           "requires": {
             "ms": "2.0.0"
@@ -3431,22 +3460,26 @@
         },
         "deep-extend": {
           "version": "0.6.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
           "optional": true
         },
         "delegates": {
           "version": "1.0.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
           "optional": true
         },
         "detect-libc": {
           "version": "1.0.3",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
           "optional": true
         },
         "fs-minipass": {
           "version": "1.2.5",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
           "optional": true,
           "requires": {
             "minipass": "^2.2.1"
@@ -3454,12 +3487,14 @@
         },
         "fs.realpath": {
           "version": "1.0.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
           "optional": true
         },
         "gauge": {
           "version": "2.7.4",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
           "optional": true,
           "requires": {
             "aproba": "^1.0.3",
@@ -3474,7 +3509,8 @@
         },
         "glob": {
           "version": "7.1.3",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
           "optional": true,
           "requires": {
             "fs.realpath": "^1.0.0",
@@ -3487,12 +3523,14 @@
         },
         "has-unicode": {
           "version": "2.0.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
           "optional": true
         },
         "iconv-lite": {
           "version": "0.4.24",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
           "optional": true,
           "requires": {
             "safer-buffer": ">= 2.1.2 < 3"
@@ -3500,7 +3538,8 @@
         },
         "ignore-walk": {
           "version": "3.0.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
           "optional": true,
           "requires": {
             "minimatch": "^3.0.4"
@@ -3508,7 +3547,8 @@
         },
         "inflight": {
           "version": "1.0.6",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
           "optional": true,
           "requires": {
             "once": "^1.3.0",
@@ -3517,39 +3557,46 @@
         },
         "inherits": {
           "version": "2.0.3",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
         },
         "ini": {
           "version": "1.3.5",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
           "optional": true
         },
         "is-fullwidth-code-point": {
           "version": "1.0.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
           "requires": {
             "number-is-nan": "^1.0.0"
           }
         },
         "isarray": {
           "version": "1.0.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
           "optional": true
         },
         "minimatch": {
           "version": "3.0.4",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
           "requires": {
             "brace-expansion": "^1.1.7"
           }
         },
         "minimist": {
           "version": "0.0.8",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
         },
         "minipass": {
           "version": "2.3.5",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
           "requires": {
             "safe-buffer": "^5.1.2",
             "yallist": "^3.0.0"
@@ -3557,7 +3604,8 @@
         },
         "minizlib": {
           "version": "1.2.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
           "optional": true,
           "requires": {
             "minipass": "^2.2.1"
@@ -3565,19 +3613,22 @@
         },
         "mkdirp": {
           "version": "0.5.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
           "requires": {
             "minimist": "0.0.8"
           }
         },
         "ms": {
           "version": "2.0.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
           "optional": true
         },
         "needle": {
           "version": "2.2.4",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==",
           "optional": true,
           "requires": {
             "debug": "^2.1.2",
@@ -3587,7 +3638,8 @@
         },
         "node-pre-gyp": {
           "version": "0.10.3",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A==",
           "optional": true,
           "requires": {
             "detect-libc": "^1.0.2",
@@ -3604,7 +3656,8 @@
         },
         "nopt": {
           "version": "4.0.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
           "optional": true,
           "requires": {
             "abbrev": "1",
@@ -3613,12 +3666,14 @@
         },
         "npm-bundled": {
           "version": "1.0.5",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==",
           "optional": true
         },
         "npm-packlist": {
           "version": "1.2.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-7Mni4Z8Xkx0/oegoqlcao/JpPCPEMtUvsmB0q7mgvlMinykJLSRTYuFqoQLYgGY8biuxIeiHO+QNJKbCfljewQ==",
           "optional": true,
           "requires": {
             "ignore-walk": "^3.0.1",
@@ -3627,7 +3682,8 @@
         },
         "npmlog": {
           "version": "4.1.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
           "optional": true,
           "requires": {
             "are-we-there-yet": "~1.1.2",
@@ -3638,33 +3694,39 @@
         },
         "number-is-nan": {
           "version": "1.0.1",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
         },
         "object-assign": {
           "version": "4.1.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
           "optional": true
         },
         "once": {
           "version": "1.4.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
           "requires": {
             "wrappy": "1"
           }
         },
         "os-homedir": {
           "version": "1.0.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
           "optional": true
         },
         "os-tmpdir": {
           "version": "1.0.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
           "optional": true
         },
         "osenv": {
           "version": "0.1.5",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
           "optional": true,
           "requires": {
             "os-homedir": "^1.0.0",
@@ -3673,17 +3735,20 @@
         },
         "path-is-absolute": {
           "version": "1.0.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
           "optional": true
         },
         "process-nextick-args": {
           "version": "2.0.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
           "optional": true
         },
         "rc": {
           "version": "1.2.8",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
           "optional": true,
           "requires": {
             "deep-extend": "^0.6.0",
@@ -3694,14 +3759,16 @@
           "dependencies": {
             "minimist": {
               "version": "1.2.0",
-              "bundled": true,
+              "resolved": false,
+              "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
               "optional": true
             }
           }
         },
         "readable-stream": {
           "version": "2.3.6",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
           "optional": true,
           "requires": {
             "core-util-is": "~1.0.0",
@@ -3715,7 +3782,8 @@
         },
         "rimraf": {
           "version": "2.6.3",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
           "optional": true,
           "requires": {
             "glob": "^7.1.3"
@@ -3723,36 +3791,43 @@
         },
         "safe-buffer": {
           "version": "5.1.2",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
         },
         "safer-buffer": {
           "version": "2.1.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
           "optional": true
         },
         "sax": {
           "version": "1.2.4",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
           "optional": true
         },
         "semver": {
           "version": "5.6.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
           "optional": true
         },
         "set-blocking": {
           "version": "2.0.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
           "optional": true
         },
         "signal-exit": {
           "version": "3.0.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
           "optional": true
         },
         "string-width": {
           "version": "1.0.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
           "requires": {
             "code-point-at": "^1.0.0",
             "is-fullwidth-code-point": "^1.0.0",
@@ -3761,7 +3836,8 @@
         },
         "string_decoder": {
           "version": "1.1.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
           "optional": true,
           "requires": {
             "safe-buffer": "~5.1.0"
@@ -3769,19 +3845,22 @@
         },
         "strip-ansi": {
           "version": "3.0.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
           "requires": {
             "ansi-regex": "^2.0.0"
           }
         },
         "strip-json-comments": {
           "version": "2.0.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
           "optional": true
         },
         "tar": {
           "version": "4.4.8",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
           "optional": true,
           "requires": {
             "chownr": "^1.1.1",
@@ -3795,12 +3874,14 @@
         },
         "util-deprecate": {
           "version": "1.0.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
           "optional": true
         },
         "wide-align": {
           "version": "1.1.3",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
           "optional": true,
           "requires": {
             "string-width": "^1.0.2 || 2"
@@ -3808,11 +3889,13 @@
         },
         "wrappy": {
           "version": "1.0.2",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
         },
         "yallist": {
           "version": "3.0.3",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A=="
         }
       }
     },
@@ -4304,6 +4387,16 @@
       "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
       "dev": true
     },
+    "humps": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/humps/-/humps-2.0.1.tgz",
+      "integrity": "sha1-3QLqYIG9BWjcXQcxhEY5V7qe+ao="
+    },
+    "i": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz",
+      "integrity": "sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0="
+    },
     "iconv-lite": {
       "version": "0.4.23",
       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
@@ -5915,9 +6008,9 @@
       "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg=="
     },
     "npm": {
-      "version": "6.7.0",
-      "resolved": "https://registry.npmjs.org/npm/-/npm-6.7.0.tgz",
-      "integrity": "sha512-OtxCLzx+pcsjMGrjZpBp214ZjxzHcAe3zLYIlaVpRYqFHff6bgggyTLf2OZPO8lfxN0RHLJnFFUU016JCzM/Ww==",
+      "version": "6.8.0",
+      "resolved": "https://registry.npmjs.org/npm/-/npm-6.8.0.tgz",
+      "integrity": "sha512-xMH6V0OCSJ5ZET6yWPI3BmJSqMMCuVJSIcLx3LSH/SrratFSt6EDuCuGRFMQYty98Q1l6x/7vKmfURosoyWgrA==",
       "requires": {
         "JSONStream": "^1.3.5",
         "abbrev": "~1.1.1",
@@ -5988,22 +6081,22 @@
         "move-concurrently": "^1.0.1",
         "node-gyp": "^3.8.0",
         "nopt": "~4.0.1",
-        "normalize-package-data": "~2.4.0",
+        "normalize-package-data": "^2.5.0",
         "npm-audit-report": "^1.3.2",
         "npm-cache-filename": "~1.0.2",
         "npm-install-checks": "~3.0.0",
         "npm-lifecycle": "^2.1.0",
         "npm-package-arg": "^6.1.0",
-        "npm-packlist": "^1.2.0",
+        "npm-packlist": "^1.3.0",
         "npm-pick-manifest": "^2.2.3",
         "npm-profile": "*",
-        "npm-registry-fetch": "^3.8.0",
+        "npm-registry-fetch": "^3.9.0",
         "npm-user-validate": "~1.0.0",
         "npmlog": "~4.1.2",
         "once": "~1.4.0",
         "opener": "^1.5.1",
         "osenv": "^0.1.5",
-        "pacote": "^9.4.0",
+        "pacote": "^9.4.1",
         "path-is-inside": "~1.0.2",
         "promise-inflight": "~1.0.1",
         "qrcode-terminal": "^0.12.0",
@@ -6013,7 +6106,7 @@
         "read-cmd-shim": "~1.0.1",
         "read-installed": "~4.0.3",
         "read-package-json": "^2.0.13",
-        "read-package-tree": "^5.2.1",
+        "read-package-tree": "^5.2.2",
         "readable-stream": "^3.1.1",
         "readdir-scoped-modules": "*",
         "request": "^2.88.0",
@@ -6258,11 +6351,6 @@
           "resolved": false,
           "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA=="
         },
-        "builtin-modules": {
-          "version": "1.1.1",
-          "resolved": false,
-          "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8="
-        },
         "builtins": {
           "version": "1.0.3",
           "resolved": false,
@@ -7304,14 +7392,6 @@
           "resolved": false,
           "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk="
         },
-        "is-builtin-module": {
-          "version": "1.0.0",
-          "resolved": false,
-          "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
-          "requires": {
-            "builtin-modules": "^1.0.0"
-          }
-        },
         "is-ci": {
           "version": "1.1.0",
           "resolved": false,
@@ -7998,14 +8078,24 @@
           }
         },
         "normalize-package-data": {
-          "version": "2.4.0",
+          "version": "2.5.0",
           "resolved": false,
-          "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
+          "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
           "requires": {
             "hosted-git-info": "^2.1.4",
-            "is-builtin-module": "^1.0.0",
+            "resolve": "^1.10.0",
             "semver": "2 || 3 || 4 || 5",
             "validate-npm-package-license": "^3.0.1"
+          },
+          "dependencies": {
+            "resolve": {
+              "version": "1.10.0",
+              "resolved": false,
+              "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
+              "requires": {
+                "path-parse": "^1.0.6"
+              }
+            }
           }
         },
         "npm-audit-report": {
@@ -8018,9 +8108,9 @@
           }
         },
         "npm-bundled": {
-          "version": "1.0.5",
+          "version": "1.0.6",
           "resolved": false,
-          "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g=="
+          "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g=="
         },
         "npm-cache-filename": {
           "version": "1.0.2",
@@ -8067,9 +8157,9 @@
           }
         },
         "npm-packlist": {
-          "version": "1.2.0",
+          "version": "1.3.0",
           "resolved": false,
-          "integrity": "sha512-7Mni4Z8Xkx0/oegoqlcao/JpPCPEMtUvsmB0q7mgvlMinykJLSRTYuFqoQLYgGY8biuxIeiHO+QNJKbCfljewQ==",
+          "integrity": "sha512-qPBc6CnxEzpOcc4bjoIBJbYdy0D/LFFPUdxvfwor4/w3vxeE0h6TiOVurCEPpQ6trjN77u/ShyfeJGsbAfB3dA==",
           "requires": {
             "ignore-walk": "^3.0.1",
             "npm-bundled": "^1.0.1"
@@ -8096,9 +8186,9 @@
           }
         },
         "npm-registry-fetch": {
-          "version": "3.8.0",
+          "version": "3.9.0",
           "resolved": false,
-          "integrity": "sha512-hrw8UMD+Nob3Kl3h8Z/YjmKamb1gf7D1ZZch2otrIXM3uFLB5vjEY6DhMlq80z/zZet6eETLbOXcuQudCB3Zpw==",
+          "integrity": "sha512-srwmt8YhNajAoSAaDWndmZgx89lJwIZ1GWxOuckH4Coek4uHv5S+o/l9FLQe/awA+JwTnj4FJHldxhlXdZEBmw==",
           "requires": {
             "JSONStream": "^1.3.4",
             "bluebird": "^3.5.1",
@@ -8227,9 +8317,9 @@
           }
         },
         "pacote": {
-          "version": "9.4.0",
+          "version": "9.4.1",
           "resolved": false,
-          "integrity": "sha512-WQ1KL/phGMkedYEQx9ODsjj7xvwLSpdFJJdEXrLyw5SILMxcTNt5DTxT2Z93fXuLFYJBlZJdnwdalrQdB/rX5w==",
+          "integrity": "sha512-YKSRsQqmeHxgra0KCdWA2FtVxDPUlBiCdmew+mSe44pzlx5t1ViRMWiQg18T+DREA+vSqYfKzynaToFR4hcKHw==",
           "requires": {
             "bluebird": "^3.5.3",
             "cacache": "^11.3.2",
@@ -8338,6 +8428,11 @@
           "resolved": false,
           "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
         },
+        "path-parse": {
+          "version": "1.0.6",
+          "resolved": false,
+          "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
+        },
         "performance-now": {
           "version": "2.1.0",
           "resolved": false,
@@ -8535,9 +8630,9 @@
           }
         },
         "read-package-tree": {
-          "version": "5.2.1",
+          "version": "5.2.2",
           "resolved": false,
-          "integrity": "sha512-2CNoRoh95LxY47LvqrehIAfUVda2JbuFE/HaGYs42bNrGG+ojbw1h3zOcPcQ+1GQ3+rkzNndZn85u1XyZ3UsIA==",
+          "integrity": "sha512-rW3XWUUkhdKmN2JKB4FL563YAgtINifso5KShykufR03nJ5loGFlkUMe1g/yxmqX073SoYYTsgXu7XdDinKZuA==",
           "requires": {
             "debuglog": "^1.0.1",
             "dezalgo": "^1.0.0",
diff --git a/package.json b/package.json
index 16174268e..53df10934 100644
--- a/package.json
+++ b/package.json
@@ -32,7 +32,9 @@
     "webpack-hot-middleware": "^2.24.3"
   },
   "dependencies": {
-    "@fortawesome/fontawesome-svg-core": "^1.2.14",
+    "@fortawesome/fontawesome-svg-core": "^1.2.15",
+    "@fortawesome/free-solid-svg-icons": "^5.7.2",
+    "@fortawesome/react-fontawesome": "^0.1.4",
     "@types/bcrypt-nodejs": "0.0.30",
     "@types/bluebird": "^3.5.25",
     "@types/body-parser": "^1.17.0",
@@ -76,6 +78,7 @@
     "expressjs": "^1.0.1",
     "flexlayout-react": "^0.3.3",
     "golden-layout": "^1.5.9",
+    "i": "^0.3.6",
     "jsonwebtoken": "^8.4.0",
     "jsx-to-string": "^1.4.0",
     "lodash": "^4.17.11",
@@ -87,7 +90,7 @@
     "node-sass": "^4.11.0",
     "nodemon": "^1.18.10",
     "normalize.css": "^8.0.1",
-    "npm": "^6.7.0",
+    "npm": "^6.8.0",
     "passport": "^0.4.0",
     "passport-local": "^1.0.0",
     "prosemirror-commands": "^1.0.7",
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index fcb934860..ed35021d3 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -3,6 +3,9 @@ import { ContextMenuItem, ContextMenuProps } from "./ContextMenuItem";
 import { observable, action } from "mobx";
 import { observer } from "mobx-react";
 import "./ContextMenu.scss"
+// import { library } from '@fortawesome/fontawesome-svg-core'
+// import { faIgloo } from '@fortawesome/free-solid-svg-icons'
+// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
 
 @observer
 export class ContextMenu extends React.Component {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 87f2e205b..4c302ed87 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -187,10 +187,10 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked })
             ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document) })
             ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked })
-            ContextMenu.Instance.addItem({ description: "Freeform", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform) })
-            ContextMenu.Instance.addItem({ description: "Schema", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema) })
-            ContextMenu.Instance.addItem({ description: "Treeview", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Tree) })
-            ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) })
+            // ContextMenu.Instance.addItem({ description: "Freeform", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform) })
+            // ContextMenu.Instance.addItem({ description: "Schema", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema) })
+            // ContextMenu.Instance.addItem({ description: "Treeview", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Tree) })
+            // ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) })
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15)
             SelectionManager.SelectDoc(this, e.ctrlKey);
         }
-- 
cgit v1.2.3-70-g09d2


From 6d456bbe4a2de50abe1a4b338d1f446d3123b321 Mon Sep 17 00:00:00 2001
From: Eleanor Eng <eleanor_eng@brown.edu>
Date: Sat, 2 Mar 2019 19:07:00 -0500
Subject: with collection view

---
 src/client/views/ContextMenu.scss                  |  13 +-
 src/client/views/ContextMenu.tsx                   |   3 -
 src/client/views/ContextMenuItem.tsx               |   9 +
 src/client/views/Main.tsx                          |  20 ++-
 .../views/collections/CollectionSchemaView.tsx     |   9 +-
 src/client/views/collections/CollectionView.tsx    |  26 ++-
 src/client/views/nodes/DocumentView.tsx            | 181 +++++++++++++--------
 src/client/views/nodes/FormattedTextBox.tsx        |  13 ++
 src/server/index.ts                                |   5 +-
 9 files changed, 174 insertions(+), 105 deletions(-)

(limited to 'src/client/views/ContextMenu.tsx')

diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss
index b7fd60ffb..ea40c8e99 100644
--- a/src/client/views/ContextMenu.scss
+++ b/src/client/views/ContextMenu.scss
@@ -3,16 +3,15 @@
     display: flex;
     z-index: 1000;
     box-shadow: #AAAAAA .2vw .2vw .4vw;
-    flex-direction: column; //E
-    // border-radius: 20px;
+    flex-direction: column;
 }
 
 .contextMenu-item {
-    width: auto; //10vw
-    height: auto; //4vh
-    background: #F0F8FF; // background: #DDDDDD;
+    width: auto;
+    height: auto;
+    background: #F0F8FF;
     display: flex;
-    justify-content: left; //center
+    justify-content: left;
     align-items: center;
     -webkit-touch-callout: none;
     -webkit-user-select: none;
@@ -32,7 +31,7 @@
 
 .contextMenu-item:hover {
     transition: all .1s;
-    background: #B0E0E6; // background: #AAAAAA
+    background: #B0E0E6;
 }
 
 .contextMenu-description {
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index ed35021d3..fcb934860 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -3,9 +3,6 @@ import { ContextMenuItem, ContextMenuProps } from "./ContextMenuItem";
 import { observable, action } from "mobx";
 import { observer } from "mobx-react";
 import "./ContextMenu.scss"
-// import { library } from '@fortawesome/fontawesome-svg-core'
-// import { faIgloo } from '@fortawesome/free-solid-svg-icons'
-// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
 
 @observer
 export class ContextMenu extends React.Component {
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index 8f00f8b3d..723606dcf 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -6,6 +6,15 @@ export interface ContextMenuProps {
     event: (e: React.MouseEvent<HTMLDivElement>) => void;
 }
 
+export interface SubmenuProps {
+    description: string;
+    subitems: ContextMenuProps[];
+}
+
+export interface ContextMenuItemProps {
+    type: ContextMenuProps | SubmenuProps
+}
+
 export class ContextMenuItem extends React.Component<ContextMenuProps> {
     render() {
         return (
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 17dda899d..23438c91f 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -66,17 +66,19 @@ Documents.initProtos(() => {
             mainContainer.GetAsync(KeyStore.ActiveFrame, field => mainfreeform = field as Document);
         }
         else {
-            mainContainer = Documents.DockDocument(JSON.stringify({ content: [{ type: 'row', content: [] }] }), { title: "main container" }, mainDocId);
-            Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(mainContainer.ToJson()))
+            // mainContainer = Documents.DockDocument(JSON.stringify({ content: [{ type: 'row', content: [] }] }), { title: "main container" }, mainDocId);
+            // Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(mainContainer.ToJson()))
 
-            setTimeout(() => {
-                mainfreeform = Documents.FreeformDocument([], { x: 0, y: 400, title: "mini collection" });
-                Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(mainfreeform.ToJson()));
+            // setTimeout(() => {
+            //     mainfreeform = Documents.FreeformDocument([], { x: 0, y: 400, title: "mini collection" });
+            //     Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(mainfreeform.ToJson()));
 
-                var docs = [mainfreeform].map(doc => CollectionDockingView.makeDocumentConfig(doc));
-                mainContainer.SetText(KeyStore.Data, JSON.stringify({ content: [{ type: 'row', content: docs }] }));
-                mainContainer.Set(KeyStore.ActiveFrame, mainfreeform);
-            }, 0);
+            //     var docs = [mainfreeform].map(doc => CollectionDockingView.makeDocumentConfig(doc));
+            //     mainContainer.SetText(KeyStore.Data, JSON.stringify({ content: [{ type: 'row', content: docs }] }));
+            //     mainContainer.Set(KeyStore.ActiveFrame, mainfreeform);
+            // }, 0);
+            mainContainer = Documents.FreeformDocument([], {});
+            mainfreeform = mainContainer;
         }
 
         let clearDatabase = action(() => Utils.Emit(Server.Socket, MessageStore.DeleteAll, {}))
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 22b5989a0..4beb0aea1 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -181,13 +181,6 @@ export class CollectionSchemaView extends CollectionViewBase {
         }
     }
 
-    //REPLACE THIS WITH CAPABILITIES SPECIFIC TO THIS TYPE OF NODE
-    collectionCapability = (e: React.MouseEvent): void => {
-    }
-
-    specificContextMenu = (e: React.MouseEvent): void => {
-        ContextMenu.Instance.addItem({ description: "Collection Capability", event: this.collectionCapability });
-    }
     @action
     setScaling = (r: any) => {
         const children = this.props.Document.GetList<Document>(this.props.fieldKey, []);
@@ -227,7 +220,7 @@ export class CollectionSchemaView extends CollectionViewBase {
         let handle = !this.props.active() ? (null) : (
             <div style={{ position: "absolute", height: "37px", width: "20px", zIndex: 20, right: 0, top: 0, background: "Black" }} onPointerDown={this.onExpanderDown} />);
         return (
-            <div onPointerDown={this.onPointerDown} onContextMenu={this.specificContextMenu} ref={this._mainCont} className="collectionSchemaView-container" style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} >
+            <div onPointerDown={this.onPointerDown} ref={this._mainCont} className="collectionSchemaView-container" style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} >
                 <Measure onResize={action((r: any) => {
                     this._dividerX = r.entry.width;
                     this._panelHeight = r.entry.height;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 03e1f1fa4..a7db07a42 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -87,27 +87,43 @@ export class CollectionView extends React.Component<CollectionViewProps> {
         Document.SetData(KeyStore.ViewType, type, NumberField);
     }
 
+    specificContextMenu = (e: React.MouseEvent): void => {
+        ContextMenu.Instance.addItem({ description: "Freeform", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform) })
+        ContextMenu.Instance.addItem({ description: "Schema", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema) })
+        ContextMenu.Instance.addItem({ description: "Treeview", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Tree) })
+        ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) })
+    }
+
     render() {
         let viewType = this.collectionViewType;
+        let subView: JSX.Element;
         switch (viewType) {
             case CollectionViewType.Freeform:
-                return (<CollectionFreeFormView {...this.props}
+                subView = (<CollectionFreeFormView {...this.props}
                     addDocument={this.addDocument} removeDocument={this.removeDocument} active={this.active}
                     CollectionView={this} />)
+                break;
             case CollectionViewType.Schema:
-                return (<CollectionSchemaView {...this.props}
+                subView = (<CollectionSchemaView {...this.props}
                     addDocument={this.addDocument} removeDocument={this.removeDocument} active={this.active}
                     CollectionView={this} />)
+                break;
             case CollectionViewType.Docking:
-                return (<CollectionDockingView {...this.props}
+                subView = (<CollectionDockingView {...this.props}
                     addDocument={this.addDocument} removeDocument={this.removeDocument} active={this.active}
                     CollectionView={this} />)
+                break;
             case CollectionViewType.Tree:
-                return (<CollectionTreeView {...this.props}
+                subView = (<CollectionTreeView {...this.props}
                     addDocument={this.addDocument} removeDocument={this.removeDocument} active={this.active}
                     CollectionView={this} />)
+                break;
             default:
-                return <div></div>
+                subView = <div></div>
+                break;
         }
+        return (<div onContextMenu={this.specificContextMenu}>
+            {subView}
+        </div>)
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4c302ed87..a14239e94 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -18,17 +18,15 @@ import { FormattedTextBox } from "../nodes/FormattedTextBox";
 import { ImageBox } from "../nodes/ImageBox";
 import "./DocumentView.scss";
 import React = require("react");
-const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this?
+const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView>;
-
     Document: Document;
     AddDocument?: (doc: Document) => void;
     RemoveDocument?: (doc: Document) => boolean;
     ScreenToLocalTransform: () => Transform;
-    isTopMost: boolean;
-    //tfs: This shouldn't be necessary I don't think
+    isTopMost: boolean; //tfs: This shouldn't be necessary I don't think
     ContentScaling: () => number;
     PanelWidth: () => number;
     PanelHeight: () => number;
@@ -53,6 +51,7 @@ Example usage of this function:
         )
     }
 */
+
 export function FakeJsxArgs(keys: string[], fields: string[] = []): JsxArgs {
     let Keys: { [name: string]: any } = {}
     let Fields: { [name: string]: any } = {}
@@ -75,23 +74,28 @@ export function FakeJsxArgs(keys: string[], fields: string[] = []): JsxArgs {
     return args;
 }
 
-@observer
-export class DocumentView extends React.Component<DocumentViewProps> {
-
+@observer export class DocumentView extends React.Component<DocumentViewProps> {
     private _mainCont = React.createRef<HTMLDivElement>();
     private _documentBindings: any = null;
     private _contextMenuCanOpen = false;
     private _downX: number = 0;
     private _downY: number = 0;
-
-    @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 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>()); }
-
+    @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 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>());
+    }
     screenRect = (): ClientRect | DOMRect => this._mainCont.current ? this._mainCont.current.getBoundingClientRect() : new DOMRect();
-
     onPointerDown = (e: React.PointerEvent): void => {
         this._downX = e.clientX;
         this._downY = e.clientY;
@@ -112,7 +116,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             }
         }
     }
-
     onPointerMove = (e: PointerEvent): void => {
         if (e.cancelBubble) {
             this._contextMenuCanOpen = false;
@@ -122,23 +125,29 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             this._contextMenuCanOpen = false;
             if (this._mainCont.current != null && !this.topMost) {
                 this._contextMenuCanOpen = false;
-                const [left, top] = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
-                let dragData: { [id: string]: any } = {};
+                const [left,
+                    top] = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+                let dragData: {
+                    [id: string]: any
+                }
+                    = {}
+                    ;
                 dragData["documentView"] = this;
                 dragData["xOffset"] = e.x - left;
                 dragData["yOffset"] = e.y - top;
                 DragManager.StartDrag(this._mainCont.current, dragData, {
                     handlers: {
-                        dragComplete: action((e: DragManager.DragCompleteEvent) => { }),
-                    },
-                    hideSource: true
-                })
+                        dragComplete: action((e: DragManager.DragCompleteEvent) => { }
+                        ),
+                    }
+                    , hideSource: true
+                }
+                )
             }
         }
         e.stopPropagation();
         e.preventDefault();
     }
-
     onPointerUp = (e: PointerEvent): void => {
         document.removeEventListener("pointermove", this.onPointerMove)
         document.removeEventListener("pointerup", this.onPointerUp)
@@ -147,7 +156,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             SelectionManager.SelectDoc(this, e.ctrlKey);
         }
     }
-
     deleteClicked = (e: React.MouseEvent): void => {
         if (this.props.RemoveDocument) {
             this.props.RemoveDocument(this.props.Document);
@@ -156,93 +164,124 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     fullScreenClicked = (e: React.MouseEvent): void => {
         CollectionDockingView.Instance.OpenFullScreen(this.props.Document);
         ContextMenu.Instance.clearItems();
-        ContextMenu.Instance.addItem({ description: "Close Full Screen", event: this.closeFullScreenClicked });
+        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();
-        ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked })
+        ContextMenu.Instance.addItem({
+            description: "Full Screen", event: this.fullScreenClicked
+        })
         ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15)
     }
-
-    @action
-    onContextMenu = (e: React.MouseEvent): void => {
+    @action onContextMenu = (e: React.MouseEvent): void => {
         e.preventDefault()
         e.stopPropagation();
         if (!SelectionManager.IsSelected(this) || !this._contextMenuCanOpen) {
             return;
         }
-
         if (this.topMost) {
-            ContextMenu.Instance.clearItems()
-            ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked })
+            ContextMenu.Instance.addItem({
+                description: "Full Screen", event: this.fullScreenClicked
+            })
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15)
         }
         else {
             // DocumentViews should stop propagation of this event
-            e.stopPropagation();
-
-            ContextMenu.Instance.clearItems();
-            ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked })
-            ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document) })
-            ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked })
-            // ContextMenu.Instance.addItem({ description: "Freeform", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform) })
-            // ContextMenu.Instance.addItem({ description: "Schema", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema) })
-            // ContextMenu.Instance.addItem({ description: "Treeview", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Tree) })
-            // ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) })
+            ContextMenu.Instance.addItem({
+                description: "Full Screen", event: this.fullScreenClicked
+            }
+            )
+            ContextMenu.Instance.addItem({
+                description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document)
+            }
+            )
+            ContextMenu.Instance.addItem({
+                description: "Delete", event: this.deleteClicked
+            }
+            )
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15)
             SelectionManager.SelectDoc(this, e.ctrlKey);
         }
     }
-
     @computed get mainContent() {
         var val = this.props.Document.Id;
-        return <JsxParser
-            components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebView }}
-            bindings={this._documentBindings}
-            jsx={this.layout}
-            showWarnings={true}
-            onError={(test: any) => { console.log(test) }}
+        return <JsxParser components={
+            {
+                FormattedTextBox,
+                ImageBox,
+                CollectionFreeFormView,
+                CollectionDockingView,
+                CollectionSchemaView,
+                CollectionView,
+                WebView
+            }
+        }
+            bindings={
+                this._documentBindings
+            }
+            jsx={
+                this.layout
+            }
+            showWarnings={
+                true
+            }
+            onError={
+                (test: any) => {
+                    console.log(test)
+                }
+            }
         />
     }
     render() {
-        if (!this.props.Document)
-            return <div></div>
+        if (!this.props.Document) return <div></div>
         let lkeys = this.props.Document.GetT(KeyStore.LayoutKeys, ListField);
         if (!lkeys || lkeys === "<Waiting>") {
             return <p>Error loading layout keys</p>;
         }
         this._documentBindings = {
             ...this.props,
-            isSelected: () => SelectionManager.IsSelected(this),
-            select: (ctrlPressed: boolean) => SelectionManager.SelectDoc(this, ctrlPressed)
-        };
+            isSelected: () => SelectionManager.IsSelected(this), select: (ctrlPressed: boolean) => SelectionManager.SelectDoc(this, ctrlPressed)
+        }
+            ;
         for (const key of this.layoutKeys) {
-            this._documentBindings[key.Name + "Key"] = key;  // this maps string values of the form <keyname>Key to an actual key Kestore.keyname  e.g,   "DataKey" => KeyStore.Data
+            this._documentBindings[key.Name + "Key"] = key; // this maps string values of the form <keyname>Key to an actual key Kestore.keyname  e.g,   "DataKey" => KeyStore.Data
         }
         for (const key of this.layoutFields) {
             let field = this.props.Document.Get(key);
             this._documentBindings[key.Name] = field && field != FieldWaiting ? field.GetValue() : field;
         }
         this._documentBindings.bindings = this._documentBindings;
-
         var scaling = this.props.ContentScaling();
         var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
         var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
-        return (
-            <div className="documentView-node" ref={this._mainCont}
-                style={{
-                    width: nativeWidth > 0 ? nativeWidth.toString() + "px" : "100%",
-                    height: nativeHeight > 0 ? nativeHeight.toString() + "px" : "100%",
-                    transformOrigin: "left top",
-                    transform: `scale(${scaling},${scaling})`
-                }}
-                onContextMenu={this.onContextMenu}
-                onPointerDown={this.onPointerDown}
-            >
-                {this.mainContent}
-            </div>
-        )
+        return (<div className="documentView-node" ref={
+            this._mainCont
+        }
+            style={
+                {
+                    width: nativeWidth > 0 ? nativeWidth.toString() + "px" : "100%", height: nativeHeight > 0 ? nativeHeight.toString() + "px" : "100%", transformOrigin: "left top", transform: `scale($ {
+                    scaling
+                }
+                , $ {
+                    scaling
+                }
+                )`
+                }
+            }
+            onContextMenu={
+                this.onContextMenu
+            }
+            onPointerDown={
+                this.onPointerDown
+            }
+        > {
+                this.mainContent
+            }
+        </div>)
     }
-}
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index d0dd9e91f..8b3deeb8b 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -119,6 +119,19 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
 
     specificContextMenu = (e: React.MouseEvent): void => {
         ContextMenu.Instance.addItem({ description: "Text Capability", event: this.textCapability });
+        // ContextMenu.Instance.addItem({
+        //     description: "Submenu",
+        //     items: [
+        //         {
+        //             description: "item 1", event:
+        //     },
+        //         {
+        //             description: "item 2", event:
+        //     }
+        //     ]
+        // })
+        // e.stopPropagation()
+
     }
 
     onPointerWheel = (e: React.WheelEvent): void => {
diff --git a/src/server/index.ts b/src/server/index.ts
index eb0527ee7..84acb72cb 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -125,7 +125,6 @@ function deleteAll() {
 
 function barReceived(guid: String) {
     clients[guid.toString()] = new Client(guid.toString());
-    // Database.Instance.print()
 }
 
 function addDocument(document: Document) {
@@ -148,7 +147,9 @@ function getFields([ids, callback]: [string[], (result: any) => void]) {
 }
 
 function setField(socket: Socket, newValue: Transferable) {
-    Database.Instance.update(newValue._id, newValue)
+    let val = { ...newValue };
+    delete val._id;
+    Database.Instance.update(newValue._id, val)
     socket.broadcast.emit(MessageStore.SetField.Message, newValue)
 }
 
-- 
cgit v1.2.3-70-g09d2

