From d58195a0470b3882d6b43b18f1f4ab7a373a671f Mon Sep 17 00:00:00 2001
From: Stanley Yip <stanley_yip@brown.edu>
Date: Sat, 12 Oct 2019 17:02:31 -0400
Subject: marquee now relies on pdf-style menu. pdf-style menu is now
 componentized so that other things can use it

---
 src/client/views/MainView.tsx | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 660b42290..53951224c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,6 +1,5 @@
 import { IconName, library } from '@fortawesome/fontawesome-svg-core';
-import { faLink, faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faUndoAlt, faTv, faChevronRight, faEllipsisV } from '@fortawesome/free-solid-svg-icons';
-import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt } from '@fortawesome/free-solid-svg-icons';
+import { faLink, faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faUndoAlt, faTv, faChevronRight, faEllipsisV, faCompressArrowsAlt } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
@@ -42,6 +41,7 @@ import { OverlayView } from './OverlayView';
 import PDFMenu from './pdf/PDFMenu';
 import { PreviewCursor } from './PreviewCursor';
 import { FilterBox } from './search/FilterBox';
+import MarqueeOptionsMenu from './collections/collectionFreeForm/MarqueeOptionsMenu';
 
 @observer
 export class MainView extends React.Component {
@@ -202,6 +202,7 @@ export class MainView extends React.Component {
         library.add(faMusic);
         library.add(faTree);
         library.add(faPlay);
+        library.add(faCompressArrowsAlt);
         library.add(faPause);
         library.add(faClone);
         library.add(faCut);
@@ -687,6 +688,7 @@ export class MainView extends React.Component {
                 {this.nodesMenu()}
                 {this.miscButtons}
                 <PDFMenu />
+                <MarqueeOptionsMenu />
                 <MainOverlayTextBox firstinstance={true} />
                 <OverlayView />
             </div >
-- 
cgit v1.2.3-70-g09d2


From aad42660123c227cbe2152fbbbc159f6a38fca17 Mon Sep 17 00:00:00 2001
From: Stanley Yip <stanley_yip@brown.edu>
Date: Tue, 15 Oct 2019 16:29:08 -0400
Subject: library flyout changes

---
 src/client/views/MainView.scss | 24 +++++++++++++++---------
 src/client/views/MainView.tsx  | 24 +++++++++++++-----------
 2 files changed, 28 insertions(+), 20 deletions(-)

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

diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index e61494e71..851818099 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -4,8 +4,9 @@
 
 .mainView-tabButtons {
     position: relative;
-    width:100%;
+    width: 100%;
 }
+
 // add nodes menu. Note that the + button is actually an input label, not an actual button.
 .mainView-docButtons {
     position: absolute;
@@ -22,22 +23,26 @@
     overflow: auto;
     z-index: 1;
 }
+
 .mainView-mainContent {
-    width:100%;
-    height:100%;
-    position:absolute;
+    width: 100%;
+    height: 100%;
+    position: absolute;
 }
-.mainView-flyoutContainer{
-    display:flex;
+
+.mainView-flyoutContainer {
+    display: flex;
     flex-direction: column;
     position: absolute;
-    width:100%;
-    height:100%;
+    width: 100%;
+    height: 100%;
     border: black 1px solid;
+
     .documentView-node-topmost {
         background: lightgrey;
     }
 }
+
 .mainView-mainDiv {
     width: 100%;
     height: 100%;
@@ -63,7 +68,7 @@
 
 .mainView-expandFlyoutButton {
     position: absolute;
-    top: 30px;
+    top: 100px;
     right: 30px;
     cursor: pointer;
 }
@@ -76,6 +81,7 @@
     border-radius: 5px;
     position: absolute;
     z-index: 1;
+    touch-action: none;
 }
 
 .mainView-workspace {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index b329717c4..dd4e07165 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -287,13 +287,15 @@ export class MainView extends React.Component {
     }
 
     onPointerDown = (e: React.PointerEvent) => {
-        this._flyoutSizeOnDown = e.clientX;
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.removeEventListener("pointerup", this.onPointerUp);
-        document.addEventListener("pointermove", this.onPointerMove);
-        document.addEventListener("pointerup", this.onPointerUp);
-        e.stopPropagation();
-        e.preventDefault();
+        if (this._flyoutTranslate) {
+            this._flyoutSizeOnDown = e.clientX;
+            document.removeEventListener("pointermove", this.onPointerMove);
+            document.removeEventListener("pointerup", this.onPointerUp);
+            document.addEventListener("pointermove", this.onPointerMove);
+            document.addEventListener("pointerup", this.onPointerUp);
+            e.stopPropagation();
+            e.preventDefault();
+        }
     }
 
     @action
@@ -412,10 +414,10 @@ export class MainView extends React.Component {
                         style={{ cursor: "ew-resize", left: `${(this.flyoutWidth * (this._flyoutTranslate ? 1 : 0)) - 10}px`, backgroundColor: `${StrCast(sidebar.backgroundColor, "lightGray")}` }}
                         onPointerDown={this.onPointerDown} onPointerOver={this.pointerOverDragger}>
                         <span title="library View Dragger" style={{
-                            width: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "5vw",
-                            height: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "30vh",
-                            position: "absolute",
-                            top: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "" : "-10vh"
+                            width: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "3vw",
+                            height: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "100vh",
+                            position: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "absolute" : "fixed",
+                            top: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "" : "0"
                         }} />
                     </div>
                     <div className="mainView-libraryFlyout" style={{
-- 
cgit v1.2.3-70-g09d2


From fe673d8cc7073b5bbe94efde6ef3346364ad1c58 Mon Sep 17 00:00:00 2001
From: Stanley Yip <stanley_yip@brown.edu>
Date: Sat, 19 Oct 2019 15:38:50 -0400
Subject: staff view added

---
 src/client/views/MainView.tsx                      |  1 +
 .../views/collections/CollectionBaseView.tsx       |  1 +
 .../views/collections/CollectionStaffView.scss     | 13 +++++
 .../views/collections/CollectionStaffView.tsx      | 65 ++++++++++++++++++++++
 src/client/views/collections/CollectionView.tsx    |  3 +
 src/client/views/nodes/FontIconBox.tsx             |  4 +-
 6 files changed, 85 insertions(+), 2 deletions(-)
 create mode 100644 src/client/views/collections/CollectionStaffView.scss
 create mode 100644 src/client/views/collections/CollectionStaffView.tsx

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 2f48b66c0..0ede0b770 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -125,6 +125,7 @@ export class MainView extends React.Component {
         library.add(faBolt);
         library.add(faChevronRight);
         library.add(faEllipsisV);
+        library.add(faMusic);
         this.initEventListeners();
         this.initAuthenticationRouters();
     }
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 58f1f2883..46d2582bd 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -25,6 +25,7 @@ export enum CollectionViewType {
     Masonry,
     Pivot,
     Linear,
+    Staff
 }
 
 export namespace CollectionViewType {
diff --git a/src/client/views/collections/CollectionStaffView.scss b/src/client/views/collections/CollectionStaffView.scss
new file mode 100644
index 000000000..493a5f670
--- /dev/null
+++ b/src/client/views/collections/CollectionStaffView.scss
@@ -0,0 +1,13 @@
+.collectionStaffView {
+    .collectionStaffView-staff {
+        width: 100%;
+        margin-top: 100px;
+        margin-bottom: 100px;
+    }
+
+    .collectionStaffView-line {
+        margin: 10px;
+        height: 2px;
+        background: black;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionStaffView.tsx b/src/client/views/collections/CollectionStaffView.tsx
new file mode 100644
index 000000000..5b1224b96
--- /dev/null
+++ b/src/client/views/collections/CollectionStaffView.tsx
@@ -0,0 +1,65 @@
+import { CollectionSubView } from "./CollectionSubView";
+import { InkingCanvas } from "../InkingCanvas";
+import { Transform } from "../../util/Transform";
+import React = require("react")
+import { computed, action, IReactionDisposer, reaction, runInAction, observable } from "mobx";
+import { Doc, HeightSym } from "../../../new_fields/Doc";
+import { NumCast } from "../../../new_fields/Types";
+import "./CollectionStaffView.scss";
+import { observer } from "mobx-react";
+
+@observer
+export class CollectionStaffView extends CollectionSubView(doc => doc) {
+    private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(0, -this._mainCont.current!.scrollTop);
+    private _mainCont = React.createRef<HTMLDivElement>();
+    private _reactionDisposer: IReactionDisposer | undefined;
+    @observable private _staves = NumCast(this.props.Document.staves);
+
+    componentDidMount = () => {
+        this._reactionDisposer = reaction(
+            () => NumCast(this.props.Document.staves),
+            (staves) => runInAction(() => this._staves = staves)
+        );
+
+        this.props.Document.staves = 5;
+    }
+
+    @computed get fieldExtensionDoc() {
+        return Doc.fieldExtensionDoc(this.props.DataDoc || this.props.Document, this.props.fieldKey);
+    }
+
+    @computed get addStaffButton() {
+        return <div onPointerDown={this.addStaff}>+</div>;
+    }
+
+    @computed get staves() {
+        let staves = [];
+        for (let i = 0; i < this._staves; i++) {
+            let rows = [];
+            for (let j = 0; j < 5; j++) {
+                rows.push(<div key={`staff-${i}-${j}`} className="collectionStaffView-line"></div>)
+            }
+            staves.push(<div key={`staff-${i}`} className="collectionStaffView-staff">
+                {rows}
+            </div>);
+        }
+        return staves;
+    }
+
+    @action
+    addStaff = (e: React.PointerEvent) => {
+        this.props.Document.staves = this._staves + 1;
+    }
+
+    render() {
+        return (
+            <div className="collectionStaffView" ref={this._mainCont}>
+                <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} AnnotationDocument={this.fieldExtensionDoc} inkFieldKey={"ink"} >
+                    {() => []}
+                </InkingCanvas>
+                {this.staves}
+                {this.addStaffButton}
+            </div>
+        )
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 3d5b4e562..db9b002cf 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -20,6 +20,7 @@ import { CollectionTreeView } from "./CollectionTreeView";
 import { CollectionViewBaseChrome } from './CollectionViewChromes';
 import { ImageUtils } from '../../util/Import & Export/ImageUtils';
 import { CollectionLinearView } from '../CollectionLinearView';
+import { CollectionStaffView } from './CollectionStaffView';
 export const COLLECTION_BORDER_WIDTH = 2;
 
 library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy);
@@ -67,6 +68,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
             case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (<CollectionStackingView chromeCollapsed={this._collapsed} key="collview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />); }
             case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (<CollectionStackingView chromeCollapsed={this._collapsed} key="collview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />); }
             case CollectionViewType.Pivot: { this.props.Document.freeformLayoutEngine = "pivot"; return (<CollectionFreeFormView chromeCollapsed={this._collapsed} key="collview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />); }
+            case CollectionViewType.Staff: return (<CollectionStaffView chromeCollapsed={true} key="collview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />)
             case CollectionViewType.Linear: { return (<CollectionLinearView chromeCollapsed={this._collapsed} key="collview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />); }
             case CollectionViewType.Freeform:
             default:
@@ -112,6 +114,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
                     this.props.Document.autoHeight = true;
                 }, icon: "ellipsis-v"
             });
+            subItems.push({ description: "Staff", event: () => this.props.Document.viewType = CollectionViewType.Staff, icon: "music" });
             subItems.push({ description: "Masonry", event: () => this.props.Document.viewType = CollectionViewType.Masonry, icon: "columns" });
             subItems.push({ description: "Pivot", event: () => this.props.Document.viewType = CollectionViewType.Pivot, icon: "columns" });
             switch (this.props.Document.viewType) {
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index efe47d8a8..ca0f7b0e5 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -17,8 +17,8 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
     public static LayoutString() { return FieldView.LayoutString(FontIconBox); }
 
     render() {
-        return <button className="fontIconBox-outerDiv" style={{ background: StrCast(this.props.Document.backgroundColor) }}>
-            <FontAwesomeIcon className="fontIconBox-icon" icon={this.Document.icon as any} size="sm" opacity={this.props.Document.unchecked ? "0.5" : "1"} />
+        return <button className="fontIconBox-outerDiv" style={{ background: StrCast(this.props.Document.backgroundColor), opacity: this.props.Document.unchecked ? 0.5 : 1 }}>
+            <FontAwesomeIcon className="fontIconBox-icon" icon={this.Document.icon as any} size="sm" />
         </button>;
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From b7353705ee06292e570c9847d72287190f3f42ed Mon Sep 17 00:00:00 2001
From: Stanley Yip <stanley_yip@brown.edu>
Date: Tue, 29 Oct 2019 19:09:04 -0400
Subject: started implementing ink select with document decorations

---
 src/client/util/SelectionManager.ts                | 30 +++++++++++
 src/client/views/DocumentDecorations.tsx           | 57 ++++++++++++++-------
 src/client/views/InkSelectDecorations.scss         |  5 ++
 src/client/views/InkSelectDecorations.tsx          | 59 ++++++++++++++++++++++
 src/client/views/MainView.tsx                      |  2 +
 src/client/views/Touchable.tsx                     |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  5 +-
 .../collections/collectionFreeForm/MarqueeView.tsx | 18 +++++--
 8 files changed, 152 insertions(+), 26 deletions(-)
 create mode 100644 src/client/views/InkSelectDecorations.scss
 create mode 100644 src/client/views/InkSelectDecorations.tsx

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

diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 2d717ca57..3ae43e029 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -11,6 +11,7 @@ export namespace SelectionManager {
 
         @observable IsDragging: boolean = false;
         @observable SelectedDocuments: Array<DocumentView> = [];
+        @observable SelectedInk: Array<Map<any, any>> = [];
 
 
         @action
@@ -41,6 +42,20 @@ export namespace SelectionManager {
         DeselectAll(): void {
             manager.SelectedDocuments.map(dv => dv.props.whenActiveChanged(false));
             manager.SelectedDocuments = [];
+            manager.SelectedInk = [];
+        }
+
+        @action
+        SelectInk(ink: Map<any, any>, ctrlPressed: boolean): void {
+            if (manager.SelectedInk.indexOf(ink) === -1) {
+                if (!ctrlPressed) {
+                    this.DeselectAll();
+                }
+
+                manager.SelectedInk.push(ink);
+            } else if (!ctrlPressed && manager.SelectedDocuments.length > 1) {
+                manager.SelectedInk = [ink];
+            }
         }
     }
 
@@ -53,6 +68,10 @@ export namespace SelectionManager {
         manager.SelectDoc(docView, ctrlPressed);
     }
 
+    export function SelectInk(ink: Map<any, any>, ctrlPressed: boolean): void {
+        manager.SelectInk(ink, ctrlPressed);
+    }
+
     export function IsSelected(doc: DocumentView): boolean {
         return manager.SelectedDocuments.indexOf(doc) !== -1;
     }
@@ -75,4 +94,15 @@ export namespace SelectionManager {
     export function SelectedDocuments(): Array<DocumentView> {
         return manager.SelectedDocuments.slice();
     }
+
+    export function SelectedInk(): Array<Map<any, any>> {
+        return manager.SelectedInk.slice();
+    }
+
+    export function AllSelected(): Array<DocumentView | Map<any, any>> {
+        let arr: Array<DocumentView | Map<any, any>> = [];
+        arr = SelectionManager.SelectedDocuments();
+        arr.push(...SelectionManager.SelectedInk());
+        return arr;
+    }
 }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 3d73f048d..90d6e1e8d 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -25,6 +25,8 @@ import { FormattedTextBox } from "./nodes/FormattedTextBox";
 import { IconBox } from "./nodes/IconBox";
 import React = require("react");
 import { TooltipTextMenu } from '../util/TooltipTextMenu';
+import { InkingCanvas } from './InkingCanvas';
+import { StrokeData } from '../../new_fields/InkField';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -166,23 +168,42 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @computed
     get Bounds(): { x: number, y: number, b: number, r: number } {
         let x = this._forceUpdate;
-        this._lastBox = SelectionManager.SelectedDocuments().reduce((bounds, documentView) => {
-            if (documentView.props.renderDepth === 0 ||
-                Doc.AreProtosEqual(documentView.props.Document, CurrentUserUtils.UserDocument)) {
-                return bounds;
+        this._lastBox = SelectionManager.AllSelected().reduce((bounds, docViewOrInk) => {
+            if (docViewOrInk instanceof DocumentView) {
+                if (docViewOrInk.props.renderDepth === 0 ||
+                    Doc.AreProtosEqual(docViewOrInk.props.Document, CurrentUserUtils.UserDocument)) {
+                    return bounds;
+                }
+                let transform = (docViewOrInk.props.ScreenToLocalTransform().scale(docViewOrInk.props.ContentScaling())).inverse();
+                if (transform.TranslateX === 0 && transform.TranslateY === 0) {
+                    setTimeout(action(() => this._forceUpdate++), 0); // bcz: fix CollectionStackingView's getTransform() somehow...without this, resizing things in the library view, for instance, show the wrong bounds
+                    return this._lastBox;
+                }
+
+                var [sptX, sptY] = transform.transformPoint(0, 0);
+                let [bptX, bptY] = transform.transformPoint(docViewOrInk.props.PanelWidth(), docViewOrInk.props.PanelHeight());
+                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)
+                };
             }
-            let transform = (documentView.props.ScreenToLocalTransform().scale(documentView.props.ContentScaling())).inverse();
-            if (transform.TranslateX === 0 && transform.TranslateY === 0) {
-                setTimeout(action(() => this._forceUpdate++), 0); // bcz: fix CollectionStackingView's getTransform() somehow...without this, resizing things in the library view, for instance, show the wrong bounds
-                return this._lastBox;
+            else {
+                let left = bounds.x;
+                let top = bounds.y;
+                let right = bounds.r;
+                let bottom = bounds.b;
+                docViewOrInk.forEach((value: StrokeData, key: string) => {
+                    value.pathData.map(val => {
+                        left = Math.min(val.x, left);
+                        top = Math.min(val.y, top);
+                        right = Math.max(val.x, right);
+                        bottom = Math.max(val.y, bottom);
+                    });
+                });
+                return {
+                    x: left, y: top, r: right, b: bottom
+                };
             }
-
-            var [sptX, sptY] = transform.transformPoint(0, 0);
-            let [bptX, bptY] = transform.transformPoint(documentView.props.PanelWidth(), documentView.props.PanelHeight());
-            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 });
         return this._lastBox;
     }
@@ -559,7 +580,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     }
     render() {
         var bounds = this.Bounds;
-        let seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
+        let seldoc = SelectionManager.AllSelected().length ? SelectionManager.AllSelected()[0] : undefined;
         if (bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
             return (null);
         }
@@ -586,7 +607,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 left: bounds.x - this._resizeBorderWidth / 2,
                 top: bounds.y - this._resizeBorderWidth / 2,
                 pointerEvents: this.Interacting ? "none" : "all",
-                zIndex: SelectionManager.SelectedDocuments().length > 1 ? 900 : 0,
+                zIndex: SelectionManager.AllSelected().length > 1 ? 900 : 0,
             }} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); }} >
             </div>
             <div className="documentDecorations-container" ref={this.setTextBar} style={{
@@ -615,7 +636,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
                 <div id="documentDecorations-borderRadius" className="documentDecorations-radius" onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}><span className="borderRadiusTooltip" title="Drag Corner Radius"></span></div>
                 <div className="link-button-container">
-                    <DocumentButtonBar views={SelectionManager.SelectedDocuments()} />
+                    {(SelectionManager.SelectedDocuments.length && SelectionManager.SelectedDocuments()[0]) ? <DocumentButtonBar views={SelectionManager.SelectedDocuments()} /> : (null)}
                 </div>
             </div >
         </div>
diff --git a/src/client/views/InkSelectDecorations.scss b/src/client/views/InkSelectDecorations.scss
new file mode 100644
index 000000000..daff58fd6
--- /dev/null
+++ b/src/client/views/InkSelectDecorations.scss
@@ -0,0 +1,5 @@
+.inkSelectDecorations {
+    position: absolute;
+    border: black 1px solid;
+    z-index: 9001;
+}
\ No newline at end of file
diff --git a/src/client/views/InkSelectDecorations.tsx b/src/client/views/InkSelectDecorations.tsx
new file mode 100644
index 000000000..a8eef3305
--- /dev/null
+++ b/src/client/views/InkSelectDecorations.tsx
@@ -0,0 +1,59 @@
+import React = require("react");
+import { Touchable } from "./Touchable";
+import { StrokeData } from "../../new_fields/InkField";
+import { observer } from "mobx-react";
+import { computed, observable, action, runInAction } from "mobx";
+import "./InkSelectDecorations.scss"
+
+@observer
+export default class InkSelectDecorations extends Touchable {
+    static Instance: InkSelectDecorations;
+
+    @observable private _selectedInkNodes: Map<any, any> = new Map();
+
+    constructor(props: Readonly<{}>) {
+        super(props);
+
+        InkSelectDecorations.Instance = this;
+    }
+
+    @action
+    public SetSelected = (inkNodes: Map<any, any>, keepOld: boolean = false) => {
+        if (!keepOld) {
+            this._selectedInkNodes = new Map();
+        }
+        inkNodes.forEach((value: any, key: any) => {
+            runInAction(() => this._selectedInkNodes.set(key, value));
+        });
+    }
+
+    @computed
+    get Bounds(): { x: number, y: number, b: number, r: number } {
+        let left = Number.MAX_VALUE;
+        let top = Number.MAX_VALUE;
+        let right = -Number.MAX_VALUE;
+        let bottom = -Number.MAX_VALUE;
+        this._selectedInkNodes.forEach((value: StrokeData, key: string) => {
+            value.pathData.map(val => {
+                left = Math.min(val.x, left);
+                top = Math.min(val.y, top);
+                right = Math.max(val.x, right);
+                bottom = Math.max(val.y, bottom);
+            });
+        });
+        return { x: left, y: top, b: bottom, r: right };
+    }
+
+    render() {
+        let bounds = this.Bounds;
+        return (
+            <div style={{
+                top: bounds.y, left: bounds.x,
+                height: bounds.b - bounds.y,
+                width: bounds.r - bounds.x
+            }}>
+
+            </div>
+        )
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 0ede0b770..41de32f1f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -37,6 +37,7 @@ import { OverlayView } from './OverlayView';
 import PDFMenu from './pdf/PDFMenu';
 import { PreviewCursor } from './PreviewCursor';
 import MarqueeOptionsMenu from './collections/collectionFreeForm/MarqueeOptionsMenu';
+import InkSelectDecorations from './InkSelectDecorations';
 
 @observer
 export class MainView extends React.Component {
@@ -512,6 +513,7 @@ export class MainView extends React.Component {
             <SharingManager />
             <GoogleAuthenticationManager />
             <DocumentDecorations />
+            <InkSelectDecorations />
             {this.mainContent}
             <PreviewCursor />
             <ContextMenu />
diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx
index 4955129ba..26779b168 100644
--- a/src/client/views/Touchable.tsx
+++ b/src/client/views/Touchable.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
 import { action } from 'mobx';
 import { InteractionUtils } from '../util/InteractionUtils';
 
-export abstract class Touchable<T> extends React.Component<T> {
+export abstract class Touchable<T = {}> extends React.Component<T> {
     protected _touchDrag: boolean = false;
     protected prevPoints: Map<number, React.Touch> = new Map<number, React.Touch>();
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 123941b03..c24e52aba 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -103,9 +103,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         added && this.updateCluster(newBox);
         return added;
     }
-    private selectDocuments = (docs: Doc[]) => {
+    private selectDocuments = (docs: Doc[], ink: Map<any, any>[]) => {
         SelectionManager.DeselectAll();
         docs.map(doc => DocumentManager.Instance.getDocumentView(doc)).map(dv => dv && SelectionManager.SelectDoc(dv, true));
+        ink.forEach(i => SelectionManager.SelectInk(i, true));
     }
     public isCurrent(doc: Doc) { return !doc.isMinimized && (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
 
@@ -190,7 +191,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
             // hacky way to get a list of DocumentViews in the current view given a list of Documents in the current view
             let prevSelected = SelectionManager.SelectedDocuments();
-            this.selectDocuments(eles);
+            this.selectDocuments(eles, []);
             let clusterDocs = SelectionManager.SelectedDocuments();
             SelectionManager.DeselectAll();
             prevSelected.map(dv => SelectionManager.SelectDoc(dv, true));
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 07db6354f..cd9ac7ecc 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -20,6 +20,7 @@ import { CollectionFreeFormView } from "./CollectionFreeFormView";
 import "./MarqueeView.scss";
 import React = require("react");
 import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
+import InkSelectDecorations from "../../InkSelectDecorations";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -27,7 +28,7 @@ interface MarqueeViewProps {
     container: CollectionFreeFormView;
     addDocument: (doc: Doc) => boolean;
     activeDocuments: () => Doc[];
-    selectDocuments: (docs: Doc[]) => void;
+    selectDocuments: (docs: Doc[], ink: Map<any, any>[]) => void;
     removeDocument: (doc: Doc) => boolean;
     addLiveTextDocument: (doc: Doc) => void;
     isSelected: () => boolean;
@@ -190,13 +191,14 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
 
     @action
     onPointerUp = (e: PointerEvent): void => {
-        if (!this.props.container.props.active()) this.props.selectDocuments([this.props.container.props.Document]);
+        if (!this.props.container.props.active()) this.props.selectDocuments([this.props.container.props.Document], []);
         if (this._visible) {
             let mselect = this.marqueeSelect();
             if (!e.shiftKey) {
                 SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document);
             }
-            this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document]);
+            this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document],
+                this.ink ? [this.marqueeInkSelect(this.ink.inkData)] : []);
         }
         if (!this._commandExecuted && (Math.abs(this.Bounds.height * this.Bounds.width) > 100)) {
             MarqueeOptionsMenu.Instance.createCollection = this.collection;
@@ -350,7 +352,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         }
         let newCollection = this.getCollection(selected);
         this.props.addDocument(newCollection);
-        this.props.selectDocuments([newCollection]);
+        this.props.selectDocuments([newCollection], []);
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
     }
@@ -422,9 +424,14 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         let centerShiftY = 0 - (this.Bounds.top + this.Bounds.height / 2);
         ink.forEach((value: StrokeData, key: string, map: any) => {
             if (InkingCanvas.IntersectStrokeRect(value, this.Bounds)) {
+                // let transform = this.props.container.props.ScreenToLocalTransform().scale(this.props.container.props.ContentScaling());
                 idata.set(key,
                     {
-                        pathData: value.pathData.map(val => ({ x: val.x + centerShiftX, y: val.y + centerShiftY })),
+                        pathData: value.pathData.map(val => {
+                            let tVal = this.props.getTransform().inverse().transformPoint(val.x, val.y);
+                            return { x: tVal[0], y: tVal[1] };
+                            // return { x: val.x + centerShiftX, y: val.y + centerShiftY }
+                        }),
                         color: value.color,
                         width: value.width,
                         tool: value.tool,
@@ -432,6 +439,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                     });
             }
         });
+        // InkSelectDecorations.Instance.SetSelected(idata);
         return idata;
     }
 
-- 
cgit v1.2.3-70-g09d2


From 36ad83493d2bd58dc6fe62df6002789ccc1b06a1 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Sun, 10 Nov 2019 14:56:58 -0500
Subject: no more RouteStore

---
 src/Utils.ts                                       |  3 +-
 src/client/apis/GoogleAuthenticationManager.tsx    |  5 +--
 .../apis/google_docs/GoogleApiClientUtils.ts       |  7 ++--
 .../apis/google_docs/GooglePhotosClientUtils.ts    |  5 +--
 src/client/cognitive_services/CognitiveServices.ts |  3 +-
 src/client/util/History.ts                         |  3 +-
 .../util/Import & Export/DirectoryImportBox.tsx    |  3 +-
 src/client/util/Import & Export/ImageUtils.ts      |  7 ++--
 src/client/util/SharingManager.tsx                 |  3 +-
 src/client/views/MainView.tsx                      |  5 +--
 src/client/views/collections/CollectionSubView.tsx |  4 +-
 src/client/views/nodes/ImageBox.tsx                |  3 +-
 src/client/views/nodes/VideoBox.tsx                |  3 +-
 src/client/views/search/SearchBox.tsx              |  3 +-
 src/mobile/ImageUpload.tsx                         |  3 +-
 src/new_fields/RichTextUtils.ts                    |  3 +-
 src/server/ApiManagers/DeleteManager.ts            | 13 +++----
 src/server/ApiManagers/ExportManager.ts            |  5 +--
 src/server/ApiManagers/PDFManager.ts               |  2 +-
 src/server/ApiManagers/UploadManager.ts            | 15 +++-----
 src/server/ApiManagers/UserManager.ts              |  7 ++--
 src/server/ApiManagers/UtilManager.ts              |  7 ++++
 src/server/Initialization.ts                       | 28 +++++++-------
 src/server/RouteManager.ts                         |  5 +--
 src/server/RouteStore.ts                           | 45 ----------------------
 src/server/RouteSubscriber.ts                      |  2 +-
 src/server/authentication/config/passport.ts       |  5 +--
 .../authentication/controllers/user_controller.ts  | 32 +++++++--------
 .../authentication/models/current_user_utils.ts    |  7 ++--
 src/server/index.ts                                | 26 ++++++-------
 30 files changed, 96 insertions(+), 166 deletions(-)
 delete mode 100644 src/server/RouteStore.ts

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 9a2f01f80..abff2eaba 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -2,7 +2,6 @@ import v4 = require('uuid/v4');
 import v5 = require("uuid/v5");
 import { Socket } from 'socket.io';
 import { Message } from './server/Message';
-import { RouteStore } from './server/RouteStore';
 
 export namespace Utils {
 
@@ -46,7 +45,7 @@ export namespace Utils {
     }
 
     export function CorsProxy(url: string): string {
-        return prepend(RouteStore.corsProxy + "/") + encodeURIComponent(url);
+        return prepend("/corsProxy/") + encodeURIComponent(url);
     }
 
     export function CopyText(text: string) {
diff --git a/src/client/apis/GoogleAuthenticationManager.tsx b/src/client/apis/GoogleAuthenticationManager.tsx
index 1ec9d8412..ae77c4b7b 100644
--- a/src/client/apis/GoogleAuthenticationManager.tsx
+++ b/src/client/apis/GoogleAuthenticationManager.tsx
@@ -4,7 +4,6 @@ import * as React from "react";
 import MainViewModal from "../views/MainViewModal";
 import { Opt } from "../../new_fields/Doc";
 import { Networking } from "../Network";
-import { RouteStore } from "../../server/RouteStore";
 import "./GoogleAuthenticationManager.scss";
 
 const AuthenticationUrl = "https://accounts.google.com/o/oauth2/v2/auth";
@@ -31,7 +30,7 @@ export default class GoogleAuthenticationManager extends React.Component<{}> {
     }
 
     public fetchOrGenerateAccessToken = async () => {
-        let response = await Networking.FetchFromServer(RouteStore.readGoogleAccessToken);
+        let response = await Networking.FetchFromServer("/readGoogleAccessToken");
         // if this is an authentication url, activate the UI to register the new access token
         if (new RegExp(AuthenticationUrl).test(response)) {
             this.isOpen = true;
@@ -44,7 +43,7 @@ export default class GoogleAuthenticationManager extends React.Component<{}> {
                             return;
                         }
                         const { access_token, avatar, name } = await Networking.PostToServer(
-                            RouteStore.writeGoogleAccessToken,
+                            "/writeGoogleAccessToken",
                             { authenticationCode }
                         );
                         runInAction(() => {
diff --git a/src/client/apis/google_docs/GoogleApiClientUtils.ts b/src/client/apis/google_docs/GoogleApiClientUtils.ts
index 183679317..26c7f8d2e 100644
--- a/src/client/apis/google_docs/GoogleApiClientUtils.ts
+++ b/src/client/apis/google_docs/GoogleApiClientUtils.ts
@@ -1,5 +1,4 @@
 import { docs_v1, slides_v1 } from "googleapis";
-import { RouteStore } from "../../../server/RouteStore";
 import { Opt } from "../../../new_fields/Doc";
 import { isArray } from "util";
 import { EditorState } from "prosemirror-state";
@@ -77,7 +76,7 @@ export namespace GoogleApiClientUtils {
         * @returns the documentId of the newly generated document, or undefined if the creation process fails.
         */
         export const create = async (options: CreateOptions): Promise<CreationResult> => {
-            const path = `${RouteStore.googleDocs}/Documents/${Actions.Create}`;
+            const path = `/googleDocs/Documents/${Actions.Create}`;
             const parameters = {
                 requestBody: {
                     title: options.title || `Dash Export (${new Date().toDateString()})`
@@ -154,7 +153,7 @@ export namespace GoogleApiClientUtils {
         }
 
         export const retrieve = async (options: RetrieveOptions): Promise<RetrievalResult> => {
-            const path = `${RouteStore.googleDocs}/Documents/${Actions.Retrieve}`;
+            const path = `/googleDocs/Documents/${Actions.Retrieve}`;
             try {
                 const parameters = { documentId: options.documentId };
                 const schema: RetrievalResult = await Networking.PostToServer(path, parameters);
@@ -165,7 +164,7 @@ export namespace GoogleApiClientUtils {
         };
 
         export const update = async (options: UpdateOptions): Promise<UpdateResult> => {
-            const path = `${RouteStore.googleDocs}/Documents/${Actions.Update}`;
+            const path = `/googleDocs/Documents/${Actions.Update}`;
             const parameters = {
                 documentId: options.documentId,
                 requestBody: {
diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index 402fc64b5..bf8897061 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -1,5 +1,4 @@
 import { Utils } from "../../../Utils";
-import { RouteStore } from "../../../server/RouteStore";
 import { ImageField } from "../../../new_fields/URLField";
 import { Cast, StrCast } from "../../../new_fields/Types";
 import { Doc, Opt, DocListCastAsync } from "../../../new_fields/Doc";
@@ -307,7 +306,7 @@ export namespace GooglePhotos {
         };
 
         export const WriteMediaItemsToServer = async (body: { mediaItems: any[] }): Promise<UploadInformation[]> => {
-            const uploads = await Networking.PostToServer(RouteStore.googlePhotosMediaDownload, body);
+            const uploads = await Networking.PostToServer("/googlePhotosMediaDownload", body);
             return uploads;
         };
 
@@ -345,7 +344,7 @@ export namespace GooglePhotos {
                 media.push({ url, description });
             }
             if (media.length) {
-                const results = await Networking.PostToServer(RouteStore.googlePhotosMediaUpload, { media, album });
+                const results = await Networking.PostToServer("/googlePhotosMediaUpload", { media, album });
                 return results;
             }
         };
diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts
index 08fcb4883..af5fb39fc 100644
--- a/src/client/cognitive_services/CognitiveServices.ts
+++ b/src/client/cognitive_services/CognitiveServices.ts
@@ -2,7 +2,6 @@ import * as request from "request-promise";
 import { Doc, Field, Opt } from "../../new_fields/Doc";
 import { Cast } from "../../new_fields/Types";
 import { Docs } from "../documents/Documents";
-import { RouteStore } from "../../server/RouteStore";
 import { Utils } from "../../Utils";
 import { InkData } from "../../new_fields/InkField";
 import { UndoManager } from "../util/UndoManager";
@@ -39,7 +38,7 @@ export enum Confidence {
 export namespace CognitiveServices {
 
     const ExecuteQuery = async <D>(service: Service, manager: APIManager<D>, data: D): Promise<any> => {
-        return fetch(Utils.prepend(`${RouteStore.cognitiveServices}/${service}`)).then(async response => {
+        return fetch(Utils.prepend(`cognitiveServices/${service}`)).then(async response => {
             let apiKey = await response.text();
             if (!apiKey) {
                 console.log(`No API key found for ${service}: ensure index.ts has access to a .env file in your root directory`);
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 899abbe40..1c51236cb 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -1,6 +1,5 @@
 import { Doc, Opt, Field } from "../../new_fields/Doc";
 import { DocServer } from "../DocServer";
-import { RouteStore } from "../../server/RouteStore";
 import { MainView } from "../views/MainView";
 import * as qs from 'query-string';
 import { Utils, OmitKeys } from "../../Utils";
@@ -26,7 +25,7 @@ export namespace HistoryUtil {
 
     // const handlers: ((state: ParsedUrl | null) => void)[] = [];
     function onHistory(e: PopStateEvent) {
-        if (window.location.pathname !== RouteStore.home) {
+        if (window.location.pathname !== "/home") {
             const url = e.state as ParsedUrl || parseUrl(window.location);
             if (url) {
                 switch (url.type) {
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 2e0ba25eb..437e7766b 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx	
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -1,7 +1,6 @@
 import "fs";
 import React = require("react");
 import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc";
-import { RouteStore } from "../../../server/RouteStore";
 import { action, observable, autorun, runInAction, computed, reaction, IReactionDisposer } from "mobx";
 import { FieldViewProps, FieldView } from "../../views/nodes/FieldView";
 import Measure, { ContentRect } from "react-measure";
@@ -124,7 +123,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
                 formData.append(Utils.GenerateGuid(), file);
             });
 
-            collector.push(...(await Networking.PostFormDataToServer(RouteStore.upload, formData)));
+            collector.push(...(await Networking.PostFormDataToServer("/upload", formData)));
             runInAction(() => this.completed += batch.length);
         });
 
diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts
index 914f4870a..ca80f3bca 100644
--- a/src/client/util/Import & Export/ImageUtils.ts	
+++ b/src/client/util/Import & Export/ImageUtils.ts	
@@ -1,7 +1,6 @@
-import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc";
+import { Doc } from "../../../new_fields/Doc";
 import { ImageField } from "../../../new_fields/URLField";
 import { Cast, StrCast } from "../../../new_fields/Types";
-import { RouteStore } from "../../../server/RouteStore";
 import { Docs } from "../../documents/Documents";
 import { Networking } from "../../Network";
 import { Id } from "../../../new_fields/FieldSymbols";
@@ -15,7 +14,7 @@ export namespace ImageUtils {
             return false;
         }
         const source = field.url.href;
-        const response = await Networking.PostToServer(RouteStore.inspectImage, { source });
+        const response = await Networking.PostToServer("/inspectImage", { source });
         const { error, data } = response.exifData;
         document.exif = error || Docs.Get.DocumentHierarchyFromJson(data);
         return data !== undefined;
@@ -23,7 +22,7 @@ export namespace ImageUtils {
 
     export const ExportHierarchyToFileSystem = async (collection: Doc): Promise<void> => {
         const a = document.createElement("a");
-        a.href = Utils.prepend(`${RouteStore.imageHierarchyExport}/${collection[Id]}`);
+        a.href = Utils.prepend(`imageHierarchyExport/${collection[Id]}`);
         a.download = `Dash Export [${StrCast(collection.title)}].zip`;
         a.click();
     };
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 2082d6324..cc1d628b1 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -4,7 +4,6 @@ import MainViewModal from "../views/MainViewModal";
 import { Doc, Opt, DocCastAsync } from "../../new_fields/Doc";
 import { DocServer } from "../DocServer";
 import { Cast, StrCast } from "../../new_fields/Types";
-import { RouteStore } from "../../server/RouteStore";
 import * as RequestPromise from "request-promise";
 import { Utils } from "../../Utils";
 import "./SharingManager.scss";
@@ -104,7 +103,7 @@ export default class SharingManager extends React.Component<{}> {
     }
 
     populateUsers = async () => {
-        let userList = await RequestPromise.get(Utils.prepend(RouteStore.getUsers));
+        let userList = await RequestPromise.get(Utils.prepend("/getUsers"));
         const raw = JSON.parse(userList) as User[];
         const evaluating = raw.map(async user => {
             let isCandidate = user.email !== Doc.CurrentUserEmail;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 39585113b..0c5a1003b 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -12,7 +12,6 @@ import { List } from '../../new_fields/List';
 import { listSpec } from '../../new_fields/Schema';
 import { Cast, FieldValue, StrCast } from '../../new_fields/Types';
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
-import { RouteStore } from '../../server/RouteStore';
 import { emptyFunction, returnEmptyString, returnFalse, returnOne, returnTrue, Utils } from '../../Utils';
 import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
@@ -74,7 +73,7 @@ export class MainView extends React.Component {
         this._urlState = HistoryUtil.parseUrl(window.location) || {} as any;
         // causes errors to be generated when modifying an observable outside of an action
         configure({ enforceActions: "observed" });
-        if (window.location.pathname !== RouteStore.home) {
+        if (window.location.pathname !== "/home") {
             let pathname = window.location.pathname.substr(1).split("/");
             if (pathname.length > 1) {
                 let type = pathname[0];
@@ -395,7 +394,7 @@ export class MainView extends React.Component {
                     zoomToScale={emptyFunction}
                     getScale={returnOne}>
                 </DocumentView>
-                <button className="mainView-logout" key="logout" onClick={() => window.location.assign(Utils.prepend(RouteStore.logout))}>
+                <button className="mainView-logout" key="logout" onClick={() => window.location.assign(Utils.prepend("/logout"))}>
                     {CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}
                 </button>
             </div></div>;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 6e8e4fa12..306f8e052 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -8,7 +8,6 @@ import { listSpec } from "../../../new_fields/Schema";
 import { ScriptField } from "../../../new_fields/ScriptField";
 import { Cast } from "../../../new_fields/Types";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
-import { RouteStore } from "../../../server/RouteStore";
 import { Utils } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { DocumentType } from "../../documents/DocumentTypes";
@@ -243,7 +242,6 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
             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;
@@ -268,7 +266,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                     }
                     let dropFileName = file ? file.name : "-empty-";
 
-                    let prom = fetch(upload, {
+                    let prom = fetch(Utils.prepend("/upload"), {
                         method: 'POST',
                         body: formData
                     }).then(async (res: Response) => {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 9f39eccea..07fd832be 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -12,7 +12,6 @@ import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schem
 import { ComputedField } from '../../../new_fields/ScriptField';
 import { BoolCast, Cast, FieldValue, NumCast, StrCast } from '../../../new_fields/Types';
 import { AudioField, ImageField } from '../../../new_fields/URLField';
-import { RouteStore } from '../../../server/RouteStore';
 import { Utils, returnOne, emptyFunction } from '../../../Utils';
 import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
 import { Docs } from '../../documents/Documents';
@@ -152,7 +151,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
             recorder.ondataavailable = async function (e: any) {
                 const formData = new FormData();
                 formData.append("file", e.data);
-                const res = await fetch(Utils.prepend(RouteStore.upload), {
+                const res = await fetch(Utils.prepend("/upload"), {
                     method: 'POST',
                     body: formData
                 });
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index aa9b28118..4884fa2bf 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -6,7 +6,6 @@ import { InkTool } from "../../../new_fields/InkField";
 import { makeInterface, createSchema, listSpec } from "../../../new_fields/Schema";
 import { Cast, FieldValue, NumCast, BoolCast, StrCast } from "../../../new_fields/Types";
 import { VideoField } from "../../../new_fields/URLField";
-import { RouteStore } from "../../../server/RouteStore";
 import { Utils, emptyFunction, returnOne } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { ContextMenu } from "../ContextMenu";
@@ -183,7 +182,7 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
 
     public static async convertDataUri(imageUri: string, returnedFilename: string) {
         try {
-            let posting = Utils.prepend(RouteStore.dataUriToImage);
+            let posting = Utils.prepend("/uploadURI");
             const returnedUri = await rp.post(posting, {
                 body: {
                     uri: imageUri,
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index b728ffaa9..6100c74df 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -8,7 +8,6 @@ import * as rp from 'request-promise';
 import { Doc } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { Cast, NumCast } from '../../../new_fields/Types';
-import { RouteStore } from '../../../server/RouteStore';
 import { Utils } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
 import { SetupDrag } from '../../util/DragManager';
@@ -90,7 +89,7 @@ export class SearchBox extends React.Component {
 
     public static async convertDataUri(imageUri: string, returnedFilename: string) {
         try {
-            let posting = Utils.prepend(RouteStore.dataUriToImage);
+            let posting = Utils.prepend("uploadURI");
             const returnedUri = await rp.post(posting, {
                 body: {
                     uri: imageUri,
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
index 33a615cbf..9fdaac66e 100644
--- a/src/mobile/ImageUpload.tsx
+++ b/src/mobile/ImageUpload.tsx
@@ -1,7 +1,6 @@
 import * as ReactDOM from 'react-dom';
 import * as rp from 'request-promise';
 import { Docs } from '../client/documents/Documents';
-import { RouteStore } from '../server/RouteStore';
 import "./ImageUpload.scss";
 import React = require('react');
 import { DocServer } from '../client/DocServer';
@@ -58,7 +57,7 @@ class Uploader extends React.Component {
 
                         this.status = "getting user document";
 
-                        const res = await rp.get(Utils.prepend(RouteStore.getUserDocumentId));
+                        const res = await rp.get(Utils.prepend("/getUserDocumentId"));
                         if (!res) {
                             throw new Error("No user id returned");
                         }
diff --git a/src/new_fields/RichTextUtils.ts b/src/new_fields/RichTextUtils.ts
index 63d718ce8..238b27439 100644
--- a/src/new_fields/RichTextUtils.ts
+++ b/src/new_fields/RichTextUtils.ts
@@ -8,7 +8,6 @@ import { Opt, Doc } from "./Doc";
 import Color = require('color');
 import { sinkListItem } from "prosemirror-schema-list";
 import { Utils } from "../Utils";
-import { RouteStore } from "../server/RouteStore";
 import { Docs } from "../client/documents/Documents";
 import { schema } from "../client/util/RichTextSchema";
 import { GooglePhotos } from "../client/apis/google_docs/GooglePhotosClientUtils";
@@ -129,7 +128,7 @@ export namespace RichTextUtils {
                     return { baseUrl, filename };
                 });
 
-                const uploads = await Networking.PostToServer(RouteStore.googlePhotosMediaDownload, { mediaItems });
+                const uploads = await Networking.PostToServer("/googlePhotosMediaDownload", { mediaItems });
 
                 if (uploads.length !== mediaItems.length) {
                     throw new AssertionError({ expected: mediaItems.length, actual: uploads.length, message: "Error with internally uploading inlineObjects!" });
diff --git a/src/server/ApiManagers/DeleteManager.ts b/src/server/ApiManagers/DeleteManager.ts
index bbf1d0425..f58a28ce5 100644
--- a/src/server/ApiManagers/DeleteManager.ts
+++ b/src/server/ApiManagers/DeleteManager.ts
@@ -1,6 +1,5 @@
 import ApiManager, { Registration } from "./ApiManager";
 import { Method, _permission_denied } from "../RouteManager";
-import { RouteStore } from "../RouteStore";
 import { WebSocket } from "../Websocket/Websocket";
 import { Database } from "../database";
 
@@ -10,25 +9,25 @@ export default class DeleteManager extends ApiManager {
 
         register({
             method: Method.GET,
-            subscription: RouteStore.delete,
+            subscription: "/delete",
             onValidation: async ({ res, isRelease }) => {
                 if (isRelease) {
                     return _permission_denied(res, deletionPermissionError);
                 }
                 await WebSocket.deleteFields();
-                res.redirect(RouteStore.home);
+                res.redirect("/home");
             }
         });
 
         register({
             method: Method.GET,
-            subscription: RouteStore.deleteAll,
+            subscription: "/deleteAll",
             onValidation: async ({ res, isRelease }) => {
                 if (isRelease) {
                     return _permission_denied(res, deletionPermissionError);
                 }
                 await WebSocket.deleteAll();
-                res.redirect(RouteStore.home);
+                res.redirect("/home");
             }
         });
 
@@ -41,7 +40,7 @@ export default class DeleteManager extends ApiManager {
                     return _permission_denied(res, deletionPermissionError);
                 }
                 await Database.Auxiliary.DeleteAll();
-                res.redirect(RouteStore.delete);
+                res.redirect("/delete");
             }
         });
 
@@ -53,7 +52,7 @@ export default class DeleteManager extends ApiManager {
                     return _permission_denied(res, deletionPermissionError);
                 }
                 await Database.Auxiliary.GoogleAuthenticationToken.DeleteAll();
-                res.redirect(RouteStore.delete);
+                res.redirect("/delete");
             }
         });
 
diff --git a/src/server/ApiManagers/ExportManager.ts b/src/server/ApiManagers/ExportManager.ts
index d42db1056..fc6ba0d22 100644
--- a/src/server/ApiManagers/ExportManager.ts
+++ b/src/server/ApiManagers/ExportManager.ts
@@ -1,7 +1,6 @@
 import ApiManager, { Registration } from "./ApiManager";
 import { Method } from "../RouteManager";
 import RouteSubscriber from "../RouteSubscriber";
-import { RouteStore } from "../RouteStore";
 import * as Archiver from 'archiver';
 import * as express from 'express';
 import { Database } from "../database";
@@ -32,7 +31,7 @@ export default class DownloadManager extends ApiManager {
          */
         register({
             method: Method.GET,
-            subscription: new RouteSubscriber(RouteStore.imageHierarchyExport).add('docId'),
+            subscription: new RouteSubscriber("imageHierarchyExport").add('docId'),
             onValidation: async ({ req, res }) => {
                 const id = req.params.docId;
                 const hierarchy: Hierarchy = {};
@@ -43,7 +42,7 @@ export default class DownloadManager extends ApiManager {
 
         register({
             method: Method.GET,
-            subscription: new RouteSubscriber("/downloadId").add("docId"),
+            subscription: new RouteSubscriber("downloadId").add("docId"),
             onValidation: async ({ req, res }) => {
                 return BuildAndDispatchZip(res, async zip => {
                     const { id, docs, files } = await getDocs(req.params.docId);
diff --git a/src/server/ApiManagers/PDFManager.ts b/src/server/ApiManagers/PDFManager.ts
index f328557b4..632b4965a 100644
--- a/src/server/ApiManagers/PDFManager.ts
+++ b/src/server/ApiManagers/PDFManager.ts
@@ -15,7 +15,7 @@ export default class PDFManager extends ApiManager {
 
         register({
             method: Method.GET,
-            subscription: new RouteSubscriber("/thumbnail").add("filename"),
+            subscription: new RouteSubscriber("thumbnail").add("filename"),
             onValidation: ({ req, res }) => {
                 let filename = req.params.filename;
                 let noExt = filename.substring(0, filename.length - ".png".length);
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 38635eda5..01abdab54 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -6,7 +6,6 @@ var AdmZip = require('adm-zip');
 import * as path from 'path';
 import { createReadStream, createWriteStream, unlink, readFileSync } from "fs";
 import { publicDirectory, filesDirectory, Partitions } from "..";
-import { RouteStore } from "../RouteStore";
 import { Database } from "../database";
 import { DashUploadUtils } from "../DashUploadUtils";
 import { Opt } from "../../new_fields/Doc";
@@ -85,12 +84,8 @@ export default class UploadManager extends ApiManager {
                                     let dirname = path.dirname(entry.entryName) + "/";
                                     let extname = path.extname(entry.entryName);
                                     let basename = path.basename(entry.entryName).split(".")[0];
-                                    // zip.extractEntryTo(dirname + basename + "_o" + extname, __dirname + RouteStore.public, true, false);
-                                    // zip.extractEntryTo(dirname + basename + "_s" + extname, __dirname + RouteStore.public, true, false);
-                                    // zip.extractEntryTo(dirname + basename + "_m" + extname, __dirname + RouteStore.public, true, false);
-                                    // zip.extractEntryTo(dirname + basename + "_l" + extname, __dirname + RouteStore.public, true, false);
                                     try {
-                                        zip.extractEntryTo(entry.entryName, __dirname + RouteStore.public, true, false);
+                                        zip.extractEntryTo(entry.entryName, publicDirectory, true, false);
                                         dirname = "/" + dirname;
 
                                         createReadStream(publicDirectory + dirname + basename + extname).pipe(createWriteStream(publicDirectory + dirname + basename + "_o" + extname));
@@ -131,7 +126,7 @@ export default class UploadManager extends ApiManager {
 
         register({
             method: Method.POST,
-            subscription: RouteStore.upload,
+            subscription: "/upload",
             onValidation: async ({ req, res }) => {
                 let form = new formidable.IncomingForm();
                 form.uploadDir = filesDirectory;
@@ -147,7 +142,7 @@ export default class UploadManager extends ApiManager {
                                 let dataBuffer = readFileSync(filesDirectory + filename);
                                 const result: ParsedPDF = await pdf(dataBuffer);
                                 await new Promise<void>((resolve, reject) => {
-                                    const path = filesDirectory + Partitions.PdfText + "/" + filename.substring(0, filename.length - ".pdf".length) + ".txt";
+                                    const path = filesDirectory + Partitions.pdf_text + "/" + filename.substring(0, filename.length - ".pdf".length) + ".txt";
                                     createWriteStream(path).write(result.text, error => {
                                         if (!error) {
                                             resolve();
@@ -171,7 +166,7 @@ export default class UploadManager extends ApiManager {
 
         register({
             method: Method.POST,
-            subscription: RouteStore.inspectImage,
+            subscription: "/inspectImage",
             onValidation: async ({ req, res }) => {
                 const { source } = req.body;
                 if (typeof source === "string") {
@@ -184,7 +179,7 @@ export default class UploadManager extends ApiManager {
 
         register({
             method: Method.POST,
-            subscription: RouteStore.dataUriToImage,
+            subscription: "/uploadURI",
             onValidation: ({ req, res }) => {
                 const uri = req.body.uri;
                 const filename = req.body.name;
diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts
index fe1ce7f2b..51a434fcf 100644
--- a/src/server/ApiManagers/UserManager.ts
+++ b/src/server/ApiManagers/UserManager.ts
@@ -1,7 +1,6 @@
 import ApiManager, { Registration } from "./ApiManager";
 import { Method } from "../RouteManager";
 import { WebSocket } from "../Websocket/Websocket";
-import { RouteStore } from "../RouteStore";
 import { Database } from "../database";
 
 export default class UserManager extends ApiManager {
@@ -10,7 +9,7 @@ export default class UserManager extends ApiManager {
 
         register({
             method: Method.GET,
-            subscription: RouteStore.getUsers,
+            subscription: "/getUsers",
             onValidation: async ({ res }) => {
                 const cursor = await Database.Instance.query({}, { email: 1, userDocumentId: 1 }, "users");
                 const results = await cursor.toArray();
@@ -20,13 +19,13 @@ export default class UserManager extends ApiManager {
 
         register({
             method: Method.GET,
-            subscription: RouteStore.getUserDocumentId,
+            subscription: "/getUserDocumentId",
             onValidation: ({ res, user }) => res.send(user.userDocumentId)
         });
 
         register({
             method: Method.GET,
-            subscription: RouteStore.getCurrUser,
+            subscription: "/getCurrentUser",
             onValidation: ({ res, user }) => res.send(JSON.stringify(user)),
             onUnauthenticated: ({ res }) => res.send(JSON.stringify({ id: "__guest__", email: "" }))
         });
diff --git a/src/server/ApiManagers/UtilManager.ts b/src/server/ApiManagers/UtilManager.ts
index 61cda2e9b..c1234be6c 100644
--- a/src/server/ApiManagers/UtilManager.ts
+++ b/src/server/ApiManagers/UtilManager.ts
@@ -2,11 +2,18 @@ import ApiManager, { Registration } from "./ApiManager";
 import { Method } from "../RouteManager";
 import { exec } from 'child_process';
 import { command_line } from "../ActionUtilities";
+import RouteSubscriber from "../RouteSubscriber";
 
 export default class UtilManager extends ApiManager {
 
     protected initialize(register: Registration): void {
 
+        register({
+            method: Method.GET,
+            subscription: new RouteSubscriber("environment").add("key"),
+            onValidation: ({ req, res }) => res.send(process.env[req.params.key])
+        });
+
         register({
             method: Method.GET,
             subscription: "/pull",
diff --git a/src/server/Initialization.ts b/src/server/Initialization.ts
index fbb5ae7a6..306058d81 100644
--- a/src/server/Initialization.ts
+++ b/src/server/Initialization.ts
@@ -9,7 +9,6 @@ import flash = require('connect-flash');
 import { Database } from './database';
 import { getForgot, getLogin, getLogout, getReset, getSignup, postForgot, postLogin, postReset, postSignup } from './authentication/controllers/user_controller';
 const MongoStore = require('connect-mongo')(session);
-import { RouteStore } from './RouteStore';
 import RouteManager from './RouteManager';
 import * as webpack from 'webpack';
 const config = require('../../webpack.config');
@@ -18,6 +17,8 @@ import * as wdm from 'webpack-dev-middleware';
 import * as whm from 'webpack-hot-middleware';
 import * as fs from 'fs';
 import * as request from 'request';
+import RouteSubscriber from './RouteSubscriber';
+import { publicDirectory } from '.';
 
 export type RouteSetter = (server: RouteManager) => void;
 export interface InitializationOptions {
@@ -29,8 +30,8 @@ export default async function InitializeServer(options: InitializationOptions) {
     const { listenAtPort, routeSetter } = options;
     const server = buildWithMiddleware(express());
 
-    server.use(express.static(__dirname + RouteStore.public));
-    server.use(RouteStore.images, express.static(__dirname + RouteStore.public));
+    server.use(express.static(publicDirectory));
+    server.use("/images", express.static(publicDirectory));
 
     server.use(wdm(compiler, { publicPath: config.output.publicPath }));
     server.use(whm(compiler));
@@ -87,24 +88,25 @@ function determineEnvironment() {
 }
 
 function registerAuthenticationRoutes(server: express.Express) {
-    server.get(RouteStore.signup, getSignup);
-    server.post(RouteStore.signup, postSignup);
+    server.get("/signup", getSignup);
+    server.post("/signup", postSignup);
 
-    server.get(RouteStore.login, getLogin);
-    server.post(RouteStore.login, postLogin);
+    server.get("/login", getLogin);
+    server.post("/login", postLogin);
 
-    server.get(RouteStore.logout, getLogout);
+    server.get("/logout", getLogout);
 
-    server.get(RouteStore.forgot, getForgot);
-    server.post(RouteStore.forgot, postForgot);
+    server.get("/forgotPassword", getForgot);
+    server.post("/forgotPassword", postForgot);
 
-    server.get(RouteStore.reset, getReset);
-    server.post(RouteStore.reset, postReset);
+    const reset = new RouteSubscriber("resetPassword").add("token").build;
+    server.get(reset, getReset);
+    server.post(reset, postReset);
 }
 
 function registerCorsProxy(server: express.Express) {
     const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/;
-    server.use(RouteStore.corsProxy, (req, res) => {
+    server.use("/corsProxy", (req, res) => {
         req.pipe(request(decodeURIComponent(req.url.substring(1)))).on("response", res => {
             const headers = Object.keys(res.headers);
             headers.forEach(headerName => {
diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts
index c1d38327f..3aae5734a 100644
--- a/src/server/RouteManager.ts
+++ b/src/server/RouteManager.ts
@@ -1,5 +1,4 @@
 import RouteSubscriber from "./RouteSubscriber";
-import { RouteStore } from "./RouteStore";
 import { DashUserModel } from "./authentication/models/user_model";
 import * as express from 'express';
 
@@ -67,10 +66,10 @@ export default class RouteManager {
                 if (onUnauthenticated) {
                     await tryExecute(onUnauthenticated, core);
                     if (!res.headersSent) {
-                        res.redirect(RouteStore.login);
+                        res.redirect("/login");
                     }
                 } else {
-                    res.redirect(RouteStore.login);
+                    res.redirect("/login");
                 }
             }
             setTimeout(() => {
diff --git a/src/server/RouteStore.ts b/src/server/RouteStore.ts
deleted file mode 100644
index a310d0c95..000000000
--- a/src/server/RouteStore.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-// PREPEND ALL ROUTES WITH FORWARD SLASHES!
-
-export enum RouteStore {
-    // GENERAL
-    root = "/",
-    home = "/home",
-    corsProxy = "/corsProxy",
-    delete = "/delete",
-    deleteAll = "/deleteAll",
-    pull = "/pull",
-
-    // UPLOAD AND STATIC FILE SERVING
-    public = "/public",
-    upload = "/upload",
-    dataUriToImage = "/uploadURI",
-    images = "/images",
-    inspectImage = "/inspectImage",
-    imageHierarchyExport = "/imageHierarchyExport",
-
-    // USER AND WORKSPACES
-    getCurrUser = "/getCurrentUser",
-    getUsers = "/getUsers",
-    getUserDocumentId = "/getUserDocumentId",
-    updateCursor = "/updateCursor",
-
-    openDocumentWithId = "/doc/:docId",
-
-    // AUTHENTICATION
-    signup = "/signup",
-    login = "/login",
-    logout = "/logout",
-    forgot = "/forgotpassword",
-    reset = "/reset/:token",
-
-    // APIS
-    cognitiveServices = "/cognitiveservices",
-    googleDocs = "/googleDocs",
-    readGoogleAccessToken = "/readGoogleAccessToken",
-    writeGoogleAccessToken = "/writeGoogleAccessToken",
-    googlePhotosMediaUpload = "/googlePhotosMediaUpload",
-    googlePhotosMediaDownload = "/googlePhotosMediaDownload",
-    googleDocsGet = "/googleDocsGet",
-    checkGoogle = "/checkGoogleAuthentication"
-
-}
\ No newline at end of file
diff --git a/src/server/RouteSubscriber.ts b/src/server/RouteSubscriber.ts
index e49be8af5..a1cf7c1c4 100644
--- a/src/server/RouteSubscriber.ts
+++ b/src/server/RouteSubscriber.ts
@@ -3,7 +3,7 @@ export default class RouteSubscriber {
     private requestParameters: string[] = [];
 
     constructor(root: string) {
-        this._root = root;
+        this._root = `/${root}`;
     }
 
     add(...parameters: string[]) {
diff --git a/src/server/authentication/config/passport.ts b/src/server/authentication/config/passport.ts
index 8915a4abf..0b15c3a36 100644
--- a/src/server/authentication/config/passport.ts
+++ b/src/server/authentication/config/passport.ts
@@ -3,7 +3,6 @@ import * as passportLocal from 'passport-local';
 import _ from "lodash";
 import { default as User } from '../models/user_model';
 import { Request, Response, NextFunction } from "express";
-import { RouteStore } from '../../RouteStore';
 
 const LocalStrategy = passportLocal.Strategy;
 
@@ -35,13 +34,13 @@ export let isAuthenticated = (req: Request, res: Response, next: NextFunction) =
     if (req.isAuthenticated()) {
         return next();
     }
-    return res.redirect(RouteStore.login);
+    return res.redirect("/login");
 };
 
 export let isAuthorized = (req: Request, res: Response, next: NextFunction) => {
     const provider = req.path.split("/").slice(-1)[0];
 
-    if (_.find((req.user as any).tokens, { kind: provider })) {
+    if (_.find((req.user).tokens, { kind: provider })) {
         next();
     } else {
         res.redirect(`/auth/${provider}`);
diff --git a/src/server/authentication/controllers/user_controller.ts b/src/server/authentication/controllers/user_controller.ts
index f5c6e1610..b2b9d33f6 100644
--- a/src/server/authentication/controllers/user_controller.ts
+++ b/src/server/authentication/controllers/user_controller.ts
@@ -10,10 +10,7 @@ import * as pug from 'pug';
 import * as async from 'async';
 import * as nodemailer from 'nodemailer';
 import c = require("crypto");
-import { RouteStore } from "../../RouteStore";
 import { Utils } from "../../../Utils";
-import { Schema } from "mongoose";
-import { Opt } from "../../../new_fields/Doc";
 import { MailOptions } from "nodemailer/lib/stream-transport";
 
 /**
@@ -23,8 +20,7 @@ import { MailOptions } from "nodemailer/lib/stream-transport";
  */
 export let getSignup = (req: Request, res: Response) => {
     if (req.user) {
-        let user = req.user;
-        return res.redirect(RouteStore.home);
+        return res.redirect("/home");
     }
     res.render("signup.pug", {
         title: "Sign Up",
@@ -45,7 +41,7 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
     const errors = req.validationErrors();
 
     if (errors) {
-        return res.redirect(RouteStore.signup);
+        return res.redirect("/signup");
     }
 
     const email = req.body.email as String;
@@ -62,7 +58,7 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
     User.findOne({ email }, (err, existingUser) => {
         if (err) { return next(err); }
         if (existingUser) {
-            return res.redirect(RouteStore.login);
+            return res.redirect("/login");
         }
         user.save((err: any) => {
             if (err) { return next(err); }
@@ -81,7 +77,7 @@ let tryRedirectToTarget = (req: Request, res: Response) => {
         req.session.target = undefined;
         res.redirect(target);
     } else {
-        res.redirect(RouteStore.home);
+        res.redirect("/home");
     }
 };
 
@@ -93,7 +89,7 @@ let tryRedirectToTarget = (req: Request, res: Response) => {
 export let getLogin = (req: Request, res: Response) => {
     if (req.user) {
         req.session!.target = undefined;
-        return res.redirect(RouteStore.home);
+        return res.redirect("/home");
     }
     res.render("login.pug", {
         title: "Log In",
@@ -115,13 +111,13 @@ export let postLogin = (req: Request, res: Response, next: NextFunction) => {
 
     if (errors) {
         req.flash("errors", "Unable to login at this time. Please try again.");
-        return res.redirect(RouteStore.signup);
+        return res.redirect("/signup");
     }
 
     passport.authenticate("local", (err: Error, user: DashUserModel, info: IVerifyOptions) => {
         if (err) { next(err); return; }
         if (!user) {
-            return res.redirect(RouteStore.signup);
+            return res.redirect("/signup");
         }
         req.logIn(user, (err) => {
             if (err) { next(err); return; }
@@ -141,7 +137,7 @@ export let getLogout = (req: Request, res: Response) => {
     if (sess) {
         sess.destroy((err) => { if (err) { console.log(err); } });
     }
-    res.redirect(RouteStore.login);
+    res.redirect("/login");
 };
 
 export let getForgot = function (req: Request, res: Response) {
@@ -168,7 +164,7 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio
             User.findOne({ email }, function (err, user: DashUserModel) {
                 if (!user) {
                     // NO ACCOUNT WITH SUBMITTED EMAIL
-                    res.redirect(RouteStore.forgot);
+                    res.redirect("/forgotPassword");
                     return;
                 }
                 user.passwordResetToken = token;
@@ -192,7 +188,7 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio
                 subject: 'Dash Password Reset',
                 text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
                     'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
-                    'http://' + req.headers.host + '/reset/' + token + '\n\n' +
+                    'http://' + req.headers.host + '/resetPassword/' + token + '\n\n' +
                     'If you did not request this, please ignore this email and your password will remain unchanged.\n'
             } as MailOptions;
             smtpTransport.sendMail(mailOptions, function (err: Error | null) {
@@ -202,14 +198,14 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio
         }
     ], function (err) {
         if (err) return next(err);
-        res.redirect(RouteStore.forgot);
+        res.redirect("/forgotPassword");
     });
 };
 
 export let getReset = function (req: Request, res: Response) {
     User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err, user: DashUserModel) {
         if (!user || err) {
-            return res.redirect(RouteStore.forgot);
+            return res.redirect("/forgotPassword");
         }
         res.render("reset.pug", {
             title: "Reset Password",
@@ -239,7 +235,7 @@ export let postReset = function (req: Request, res: Response) {
 
                 user.save(function (err) {
                     if (err) {
-                        res.redirect(RouteStore.login);
+                        res.redirect("/login");
                         return;
                     }
                     req.logIn(user, function (err) {
@@ -271,6 +267,6 @@ export let postReset = function (req: Request, res: Response) {
             });
         }
     ], function (err) {
-        res.redirect(RouteStore.login);
+        res.redirect("/login");
     });
 };
\ 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 73cac879e..5a8815983 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -11,7 +11,6 @@ import { List } from "../../../new_fields/List";
 import { listSpec } from "../../../new_fields/Schema";
 import { Cast, StrCast, PromiseValue } from "../../../new_fields/Types";
 import { Utils } from "../../../Utils";
-import { RouteStore } from "../../RouteStore";
 import { ScriptField } from "../../../new_fields/ScriptField";
 import { ButtonBox } from "../../../client/views/nodes/ButtonBox";
 import { UndoManager } from "../../../client/util/UndoManager";
@@ -198,8 +197,8 @@ export class CurrentUserUtils {
         return doc;
     }
 
-    public static loadCurrentUser() {
-        return rp.get(Utils.prepend(RouteStore.getCurrUser)).then(response => {
+    public static async loadCurrentUser() {
+        return rp.get(Utils.prepend("/getCurrentUser")).then(response => {
             if (response) {
                 const result: { id: string, email: string } = JSON.parse(response);
                 return result;
@@ -212,7 +211,7 @@ export class CurrentUserUtils {
     public static async loadUserDocument({ id, email }: { id: string, email: string }) {
         this.curr_id = id;
         Doc.CurrentUserEmail = email;
-        await rp.get(Utils.prepend(RouteStore.getUserDocumentId)).then(id => {
+        await rp.get(Utils.prepend("/getUserDocumentId")).then(id => {
             if (id && id !== "guest") {
                 return DocServer.GetRefField(id).then(async field => {
                     if (field instanceof Doc) {
diff --git a/src/server/index.ts b/src/server/index.ts
index aec301a74..8fc402cc9 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -3,7 +3,6 @@ import { GoogleApiServerUtils } from "./apis/google/GoogleApiServerUtils";
 import * as mobileDetect from 'mobile-detect';
 import * as path from 'path';
 import { Database } from './database';
-import { RouteStore } from './RouteStore';
 const serverPort = 4321;
 import { GooglePhotosUploadUtils } from './apis/google/GooglePhotosUploadUtils';
 import { Opt } from '../new_fields/Doc';
@@ -23,7 +22,7 @@ import DeleteManager from "./ApiManagers/DeleteManager";
 import PDFManager from "./ApiManagers/PDFManager";
 import UploadManager from "./ApiManagers/UploadManager";
 
-export const publicDirectory = __dirname + RouteStore.public;
+export const publicDirectory = __dirname + "/public";
 export const filesDirectory = publicDirectory + "/files/";
 export enum Partitions {
     pdf_text,
@@ -73,13 +72,12 @@ function routeSetter(router: RouteManager) {
     WebSocket.initialize(serverPort, router.isRelease);
 
     /**
-     * Anyone attempting to navigate to localhost at this port will
-     * first have to log in.
+     * Accessing root index redirects to home
      */
     router.addSupervisedRoute({
         method: Method.GET,
-        subscription: RouteStore.root,
-        onValidation: ({ res }) => res.redirect(RouteStore.home)
+        subscription: "/",
+        onValidation: ({ res }) => res.redirect("/home")
     });
 
     const serve: OnUnauthenticated = ({ req, res }) => {
@@ -90,7 +88,7 @@ function routeSetter(router: RouteManager) {
 
     router.addSupervisedRoute({
         method: Method.GET,
-        subscription: [RouteStore.home, new RouteSubscriber("/doc").add("docId")],
+        subscription: ["/home", new RouteSubscriber("doc").add("docId")],
         onValidation: serve,
         onUnauthenticated: ({ req, ...remaining }) => {
             const { originalUrl: target } = req;
@@ -110,9 +108,9 @@ function routeSetter(router: RouteManager) {
 
     router.addSupervisedRoute({
         method: Method.GET,
-        subscription: new RouteSubscriber(RouteStore.cognitiveServices).add('requestedservice'),
+        subscription: new RouteSubscriber("cognitiveServices").add('requestedService'),
         onValidation: ({ req, res }) => {
-            let service = req.params.requestedservice;
+            let service = req.params.requestedService;
             res.send(ServicesApiKeyMap.get(service));
         }
     });
@@ -125,7 +123,7 @@ function routeSetter(router: RouteManager) {
 
     router.addSupervisedRoute({
         method: Method.POST,
-        subscription: new RouteSubscriber(RouteStore.googleDocs).add("sector", "action"),
+        subscription: new RouteSubscriber("googleDocs").add("sector", "action"),
         onValidation: async ({ req, res, user }) => {
             let sector: GoogleApiServerUtils.Service = req.params.sector as GoogleApiServerUtils.Service;
             let action: GoogleApiServerUtils.Action = req.params.action as GoogleApiServerUtils.Action;
@@ -143,7 +141,7 @@ function routeSetter(router: RouteManager) {
 
     router.addSupervisedRoute({
         method: Method.GET,
-        subscription: RouteStore.readGoogleAccessToken,
+        subscription: "/readGoogleAccessToken",
         onValidation: async ({ user, res }) => {
             const userId = user.id;
             const token = await GoogleApiServerUtils.retrieveAccessToken(userId);
@@ -156,7 +154,7 @@ function routeSetter(router: RouteManager) {
 
     router.addSupervisedRoute({
         method: Method.POST,
-        subscription: RouteStore.writeGoogleAccessToken,
+        subscription: "/writeGoogleAccessToken",
         onValidation: async ({ user, req, res }) => {
             res.send(await GoogleApiServerUtils.processNewUser(user.id, req.body.authenticationCode));
         }
@@ -173,7 +171,7 @@ function routeSetter(router: RouteManager) {
 
     router.addSupervisedRoute({
         method: Method.POST,
-        subscription: RouteStore.googlePhotosMediaUpload,
+        subscription: "/googlePhotosMediaUpload",
         onValidation: async ({ user, req, res }) => {
             const { media } = req.body;
 
@@ -228,7 +226,7 @@ function routeSetter(router: RouteManager) {
     const UploadError = (count: number) => `Unable to upload ${count} images to Dash's server`;
     router.addSupervisedRoute({
         method: Method.POST,
-        subscription: RouteStore.googlePhotosMediaDownload,
+        subscription: "/googlePhotosMediaDownload",
         onValidation: async ({ req, res }) => {
             const contents: { mediaItems: MediaItem[] } = req.body;
             let failed = 0;
-- 
cgit v1.2.3-70-g09d2


From d7150995d62c498ab8435de986b90d98bdca020c Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 12 Nov 2019 15:07:03 -0500
Subject: a bunch of cleanup and bug fixes to text links and doculink buttons

---
 src/client/documents/Documents.ts                  |  5 +-
 src/client/util/LinkManager.ts                     |  2 -
 src/client/util/RichTextSchema.tsx                 |  4 +-
 src/client/util/TooltipTextMenu.tsx                | 23 +++---
 src/client/views/DocumentDecorations.tsx           | 10 ++-
 src/client/views/GlobalKeyHandler.ts               |  2 +-
 src/client/views/Main.tsx                          | 24 -------
 src/client/views/MainView.scss                     |  2 +-
 src/client/views/MainView.tsx                      |  8 +--
 src/client/views/linking/LinkMenuItem.tsx          |  1 -
 src/client/views/nodes/FormattedTextBox.tsx        |  9 ++-
 src/client/views/nodes/FormattedTextBoxComment.tsx | 83 ++++++++++++----------
 src/client/views/nodes/PDFBox.scss                 |  2 +
 src/client/views/nodes/PDFBox.tsx                  | 76 +++++++++++---------
 src/client/views/pdf/Annotation.tsx                |  6 +-
 src/client/views/pdf/PDFViewer.tsx                 | 14 +---
 src/new_fields/Doc.ts                              |  2 +-
 .../authentication/models/current_user_utils.ts    |  1 -
 18 files changed, 127 insertions(+), 147 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index d1fcabc4a..ba9f87025 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -257,6 +257,9 @@ export namespace Docs {
             return PrototypeMap.get(type)!;
         }
 
+        /**
+         * A collection of all links in the database.  Ideally, this would be a search, but for now all links are cached here.
+         */
         export function MainLinkDocument() {
             return Prototypes.get(DocumentType.LINKDOC);
         }
@@ -703,6 +706,7 @@ export namespace DocUtils {
 
             linkDocProto.title = title === "" ? source.doc.title + " to " + target.doc.title : title;
             linkDocProto.linkDescription = description;
+            linkDocProto.isPrototype = true;
 
             linkDocProto.anchor1 = source.doc;
             linkDocProto.anchor2 = target.doc;
@@ -714,7 +718,6 @@ export namespace DocUtils {
             linkDocProto.anchor2Timecode = target.doc.currentTimecode;
             linkDocProto.layoutKey1 = DocuLinkBox.LayoutString("anchor1");
             linkDocProto.layoutKey2 = DocuLinkBox.LayoutString("anchor2");
-            linkDocProto.borderRounding = "20";
             linkDocProto.width = linkDocProto.height = 0;
             linkDocProto.isBackground = true;
             linkDocProto.isButton = true;
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index ee2f2dadc..eedc4967d 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -34,8 +34,6 @@ export class LinkManager {
     // the linkmanagerdoc stores a list of docs representing all linkdocs in 'allLinks' and a list of strings representing all group types in 'allGroupTypes'
     // lists of strings representing the metadata keys for each group type is stored under a key that is the same as the group type 
     public get LinkManagerDoc(): Doc | undefined {
-        // return FieldValue(Cast(CurrentUserUtils.UserDocument.linkManagerDoc, Doc));
-
         return Docs.Prototypes.MainLinkDocument();
     }
 
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index a5e0ca720..76b8aeaa1 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -305,8 +305,8 @@ export const marks: { [index: string]: MarkSpec } = {
         }],
         toDOM(node: any) {
             return node.attrs.docref && node.attrs.title ?
-                ["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, class: "prosemirror-attribution" }, node.attrs.title], ["br"]] :
-                ["a", { ...node.attrs }, 0];
+                ["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, class: "prosemirror-attribution", title: `${node.attrs.title}` }, node.attrs.title], ["br"]] :
+                ["a", { ...node.attrs, title: `${node.attrs.title}` }, 0];
         }
     },
 
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 9ce7acec8..38471a955 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -17,7 +17,7 @@ import { DragManager } from "./DragManager";
 import { LinkManager } from "./LinkManager";
 import { schema } from "./RichTextSchema";
 import "./TooltipTextMenu.scss";
-import { Cast, NumCast } from '../../new_fields/Types';
+import { Cast, NumCast, StrCast } from '../../new_fields/Types';
 import { updateBullets } from './ProsemirrorExampleTransfer';
 import { DocumentDecorations } from '../views/DocumentDecorations';
 const { toggleMark, setBlockType } = require("prosemirror-commands");
@@ -284,7 +284,7 @@ export class TooltipTextMenu {
                                     if (proto && docView) {
                                         proto.sourceContext = docView.props.ContainingCollectionDoc;
                                     }
-                                    let text = this.makeLink(linkDoc, ctrlKey ? "onRight" : "inTab");
+                                    let text = this.makeLink(linkDoc, StrCast(linkDoc.anchor2.title), ctrlKey ? "onRight" : "inTab");
                                     if (linkDoc instanceof Doc && linkDoc.anchor2 instanceof Doc) {
                                         proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODODO open to more descriptive descriptions of following in text link
                                     }
@@ -374,25 +374,20 @@ export class TooltipTextMenu {
     //     let link = state.schema.mark(state.schema.marks.link, { href: target, location: location });
     // }
 
-    makeLink = (targetDoc: Doc, location: string): string => {
-        let target = Utils.prepend("/doc/" + targetDoc[Id]);
+    makeLink = (targetDoc: Doc, title: string, location: string): string => {
+        let link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + targetDoc[Id]), title: title, location: location });
+        this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link).
+            addMark(this.view.state.selection.from, this.view.state.selection.to, link));
         let node = this.view.state.selection.$from.nodeAfter;
-        let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: target, location: location, guid: targetDoc[Id] });
-        this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link));
-        this.view.dispatch(this.view.state.tr.addMark(this.view.state.selection.from, this.view.state.selection.to, link));
-        node = this.view.state.selection.$from.nodeAfter;
-        link = node && node.marks.find(m => m.type.name === "link");
-        if (node) {
-            if (node.text) {
-                return node.text;
-            }
+        if (node && node.text) {
+            return node.text;
         }
         return "";
     }
 
     deleteLink = () => {
         let node = this.view.state.selection.$from.nodeAfter;
-        let link = node && node.marks.find(m => m.type.name === "link");
+        let link = node && node.marks.find(m => m.type === this.view.state.schema.marks.link);
         let href = link!.attrs.href;
         if (href) {
             if (href.indexOf(Utils.prepend("/doc/")) === 0) {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 0336440d5..55c211d1d 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -24,6 +24,7 @@ import { DocumentView } from "./nodes/DocumentView";
 import { FieldView } from "./nodes/FieldView";
 import { IconBox } from "./nodes/IconBox";
 import React = require("react");
+import { DocumentType } from '../documents/DocumentTypes';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -112,7 +113,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             }
             else {
                 if (SelectionManager.SelectedDocuments().length > 0) {
-                    SelectionManager.SelectedDocuments()[0].props.Document.customTitle = true;
+                    SelectionManager.SelectedDocuments()[0].props.Document.customTitle = !this._title.startsWith("-");
                     let field = SelectionManager.SelectedDocuments()[0].props.Document[this._fieldKey];
                     if (typeof field === "number") {
                         SelectionManager.SelectedDocuments().forEach(d => {
@@ -174,6 +175,13 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
             var [sptX, sptY] = transform.transformPoint(0, 0);
             let [bptX, bptY] = transform.transformPoint(documentView.props.PanelWidth(), documentView.props.PanelHeight());
+            if (documentView.props.Document.type === DocumentType.LINK) {
+                let rect = documentView.ContentDiv!.getElementsByClassName("docuLinkBox-cont")[0].getBoundingClientRect();
+                sptX = rect.left;
+                sptY = rect.top;
+                bptX = rect.right;
+                bptY = rect.bottom;
+            }
             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)
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 9ca9fc163..8f397e331 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -165,7 +165,7 @@ export default class KeyManager {
                     }
                 }
                 break;
-            case "c":
+            case "t":
                 PromiseValue(Cast(CurrentUserUtils.UserDocument.Create, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
                 if (MainView.Instance.flyoutWidth === 240) {
                     MainView.Instance.flyoutWidth = 0;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index a91a2b69e..b21eb9c8f 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -3,34 +3,11 @@ import { Docs } from "../documents/Documents";
 import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
 import * as ReactDOM from 'react-dom';
 import * as React from 'react';
-import { Cast } from "../../new_fields/Types";
-import { Doc, DocListCastAsync } from "../../new_fields/Doc";
-import { List } from "../../new_fields/List";
 import { DocServer } from "../DocServer";
 import { AssignAllExtensions } from "../../extensions/General/Extensions";
 
 AssignAllExtensions();
 
-let swapDocs = async () => {
-    let oldDoc = await Cast(CurrentUserUtils.UserDocument.linkManagerDoc, Doc);
-    // Docs.Prototypes.MainLinkDocument().allLinks = new List<Doc>();
-    if (oldDoc) {
-        let links = await DocListCastAsync(oldDoc.allLinks);
-        // if (links && DocListCast(links)) {
-        if (links && links.length) {
-            let data = await DocListCastAsync(Docs.Prototypes.MainLinkDocument().allLinks);
-            if (data) {
-                data.push(...links.filter(i => data!.indexOf(i) === -1));
-                Docs.Prototypes.MainLinkDocument().allLinks = new List<Doc>(data.filter((i, idx) => data!.indexOf(i) === idx));
-            }
-            else {
-                Docs.Prototypes.MainLinkDocument().allLinks = new List<Doc>(links);
-            }
-        }
-        CurrentUserUtils.UserDocument.linkManagerDoc = undefined;
-    }
-};
-
 (async () => {
     const info = await CurrentUserUtils.loadCurrentUser();
     DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email);
@@ -38,7 +15,6 @@ let swapDocs = async () => {
     if (info.id !== "__guest__") {
         // a guest will not have an id registered
         await CurrentUserUtils.loadUserDocument(info);
-        await swapDocs();
     }
     document.getElementById('root')!.addEventListener('wheel', event => {
         if (event.ctrlKey) {
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index 21b135c49..a858a73c7 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -13,7 +13,7 @@
     left: 250px;
 }
 
-.mainView-container {
+#mainView-container {
     width: 100%;
     height: 100%;
     position: absolute;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 773da05df..83dbb433b 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -37,7 +37,6 @@ import { OverlayView } from './OverlayView';
 import PDFMenu from './pdf/PDFMenu';
 import { PreviewCursor } from './PreviewCursor';
 import { Scripting } from '../util/Scripting';
-import { LinkManager } from '../util/LinkManager';
 import { AudioBox } from './nodes/AudioBox';
 
 @observer
@@ -197,11 +196,6 @@ export class MainView extends React.Component {
         var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] };
         let mainDoc = Docs.Create.DockDocument([freeformDoc], JSON.stringify(dockingLayout), {}, id);
         if (this.userDoc && ((workspaces = Cast(this.userDoc.workspaces, Doc)) instanceof Doc)) {
-            if (!this.userDoc.linkManagerDoc) {
-                let linkManagerDoc = new Doc();
-                linkManagerDoc.allLinks = new List<Doc>([]);
-                this.userDoc.linkManagerDoc = linkManagerDoc;
-            }
             Doc.AddDocToList(workspaces, "data", mainDoc);
             mainDoc.title = `Workspace ${DocListCast(workspaces.data).length}`;
         }
@@ -504,7 +498,7 @@ export class MainView extends React.Component {
     }
 
     render() {
-        return (<div className="mainView-container">
+        return (<div id="mainView-container">
             <DictationOverlay />
             <SharingManager />
             <GoogleAuthenticationManager />
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index a6ee9c2c6..238660de3 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -8,7 +8,6 @@ import { Cast, StrCast } from '../../../new_fields/Types';
 import { DragLinkAsDocument } from '../../util/DragManager';
 import { LinkManager } from '../../util/LinkManager';
 import { ContextMenu } from '../ContextMenu';
-import { MainView } from '../MainView';
 import { LinkFollowBox } from './LinkFollowBox';
 import './LinkMenu.scss';
 import React = require("react");
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index abf26826c..a0f8523a2 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -842,6 +842,7 @@ export class FormattedTextBox extends DocExtendableComponent<(FieldViewProps & F
         this._editorView && this._editorView.destroy();
     }
     onPointerDown = (e: React.PointerEvent): void => {
+        FormattedTextBoxComment.textBox = this;
         let pos = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY });
         pos && (this._nodeClicked = this._editorView!.state.doc.nodeAt(pos.pos));
         if (this.props.onClick && e.button === 0) {
@@ -1018,11 +1019,13 @@ export class FormattedTextBox extends DocExtendableComponent<(FieldViewProps & F
     }
 
     render() {
+        trace();
         let rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
-        let interactive: "all" | "none" = InkingControl.Instance.selectedTool || this.layoutDoc.isBackground
-            ? "none" : "all";
+        let interactive = InkingControl.Instance.selectedTool || this.layoutDoc.isBackground;
         if (this.props.isSelected()) {
             FormattedTextBox._toolTipTextMenu!.updateFromDash(this._editorView!, undefined, this.props);
+        } else if (FormattedTextBoxComment.textBox === this) {
+            FormattedTextBoxComment.Hide();
         }
         return (
             <div className={`formattedTextBox-cont`} ref={this._ref}
@@ -1031,7 +1034,7 @@ export class FormattedTextBox extends DocExtendableComponent<(FieldViewProps & F
                     background: this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : undefined,
                     opacity: this.props.hideOnLeave ? (this._entered ? 1 : 0.1) : 1,
                     color: this.props.color ? this.props.color : this.props.hideOnLeave ? "white" : "inherit",
-                    pointerEvents: interactive,
+                    pointerEvents: interactive ? "none" : "all",
                     fontSize: this._ruleFontSize ? this._ruleFontSize : NumCast(this.layoutDoc.fontSize, 13),
                     fontFamily: this._ruleFontFamily ? this._ruleFontFamily : StrCast(this.layoutDoc.fontFamily, "Crimson Text"),
                 }}
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index bde278be3..2a7467108 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -6,8 +6,9 @@ import { Doc } from "../../../new_fields/Doc";
 import { schema } from "../../util/RichTextSchema";
 import { DocServer } from "../../DocServer";
 import { Utils } from "../../../Utils";
-import { StrCast } from "../../../new_fields/Types";
+import { StrCast, Cast, FieldValue } from "../../../new_fields/Types";
 import { FormattedTextBox } from "./FormattedTextBox";
+import { DocUtils } from "../../documents/Documents";
 
 export let formattedTextBoxCommentPlugin = new Plugin({
     view(editorView) { return new FormattedTextBoxComment(editorView); }
@@ -61,6 +62,7 @@ export class FormattedTextBoxComment {
             FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText);
             FormattedTextBoxComment.tooltip.className = "FormattedTextBox-tooltip";
             FormattedTextBoxComment.tooltip.style.pointerEvents = "all";
+            FormattedTextBoxComment.tooltip.style.maxWidth = "350px";
             FormattedTextBoxComment.tooltip.appendChild(input);
             FormattedTextBoxComment.tooltip.onpointerdown = (e: PointerEvent) => {
                 let keep = e.target && (e.target as any).type === "checkbox" ? true : false;
@@ -91,62 +93,67 @@ export class FormattedTextBoxComment {
         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;
+        }
 
-        if (!FormattedTextBoxComment.textBox || !FormattedTextBoxComment.textBox.props || !FormattedTextBoxComment.textBox.props.isSelected()) return;
+        const textBox = FormattedTextBoxComment.textBox;
+        if (!textBox || !textBox.props) {
+            return;
+        }
         let set = "none";
-        if (FormattedTextBoxComment.textBox && state.selection.$from) {
-            let nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark);
+        let nbef = 0;
+        // this section checks to see if the insertion point is over text entered by a different user.  If so, it sets ths comment text to indicate the user and the modification date
+        if (state.selection.$from) {
+            nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark);
             let naft = findEndOfMark(state.selection.$from, view, findOtherUserMark);
-            const spos = state.selection.$from.pos - nbef;
-            const epos = state.selection.$from.pos + naft;
-            let child = state.selection.$from.nodeBefore;
-            let mark = child && findOtherUserMark(child.marks);
             let noselection = view.state.selection.$from === view.state.selection.$to;
+            let child: any = null;
+            state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node));
+            let mark = child && findOtherUserMark(child.marks);
             if (mark && child && (nbef || naft) && (!mark.attrs.opened || noselection)) {
-                FormattedTextBoxComment.SetState(this, mark.attrs.opened, spos, epos, mark);
+                FormattedTextBoxComment.SetState(FormattedTextBoxComment.textBox, mark.attrs.opened, state.selection.$from.pos - nbef, state.selection.$from.pos + naft, mark);
             }
-            if (mark && child && nbef && naft) {
-                FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " " + mark.attrs.modified;
-                // These are in screen coordinates
-                // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
-                let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
-                // The box in which the tooltip is positioned, to use as base
-                let box = (document.getElementById("main-div") as any).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);
-                FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px";
-                FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px";
+            if (mark && child && ((nbef && naft) || !noselection)) {
+                FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " date=" + (new Date(mark.attrs.modified * 5000)).toDateString();
                 set = "";
             }
         }
+        // this checks if the selection is a hyperlink.  If so, it displays the target doc's text for internal links, and the url of the target for external links. 
         if (set === "none" && state.selection.$from) {
-            FormattedTextBoxComment.textBox = undefined;
-            let nbef = findStartOfMark(state.selection.$from, view, findLinkMark);
+            nbef = findStartOfMark(state.selection.$from, view, findLinkMark);
             let naft = findEndOfMark(state.selection.$from, view, findLinkMark);
-            let child = state.selection.$from.nodeBefore;
+            let child: any = null;
+            state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node));
             let mark = child && findLinkMark(child.marks);
             if (mark && child && nbef && naft) {
-                FormattedTextBoxComment.tooltipText.textContent = "link : " + (mark.attrs.title || mark.attrs.href);
+                FormattedTextBoxComment.tooltipText.textContent = "external => " + mark.attrs.href;
                 if (mark.attrs.href.indexOf(Utils.prepend("/doc/")) === 0) {
                     let docTarget = mark.attrs.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
-                    docTarget && DocServer.GetRefField(docTarget).then(linkDoc =>
-                        (linkDoc as Doc) && (FormattedTextBoxComment.tooltipText.textContent = "link :" + StrCast((linkDoc as Doc).title)));
+                    docTarget && DocServer.GetRefField(docTarget).then(linkDoc => {
+                        if (linkDoc instanceof Doc) {
+                            let target = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.props.Document) ? Cast(linkDoc.anchor2, Doc) : Cast(linkDoc.anchor1, Doc));
+                            let ext = (target && Doc.fieldExtensionDoc(target, "data")) || target; // try guessing that the target doc's data is in the 'data' field.  probably need an 'overviewLayout' and then just display the target Document ....
+                            let text = ext && StrCast(ext.text);
+                            ext && (FormattedTextBoxComment.tooltipText.textContent = "=> " + (text || StrCast(ext.title)));
+                        }
+                    });
                 }
-                // These are in screen coordinates
-                // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
-                let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
-                // The box in which the tooltip is positioned, to use as base
-                let box = (document.getElementById("main-div") as any).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);
-                FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px";
-                FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px";
                 set = "";
             }
         }
+        if (set !== "none") {
+            // These are in screen coordinates
+            // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
+            let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
+            // The box in which the tooltip is positioned, to use as base
+            let box = (document.getElementById("mainView-container") as any).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);
+            FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px";
+            FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px";
+        }
         FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = set);
     }
 
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 963205206..2d92c9581 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -48,6 +48,7 @@
     }
     .pdfViewer-text {
         .textLayer {
+            will-change: transform;
             span {
                 user-select: none;
             }
@@ -59,6 +60,7 @@
     pointer-events: all;
     .pdfViewer-text {
         .textLayer {
+            will-change: transform;
             span {
                 user-select: text;
             }
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 3baa6eb09..8e0515f8a 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, observable, runInAction, reaction, IReactionDisposer, trace } from 'mobx';
+import { action, observable, runInAction, reaction, IReactionDisposer, trace, untracked, computed } from 'mobx';
 import { observer } from "mobx-react";
 import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
@@ -32,7 +32,7 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
     private _valueValue: string = "";
     private _scriptValue: string = "";
     private _searchString: string = "";
-    private _initialScale: number | undefined;  // the initial scale of the PDF when first rendered which determines whether the document will be live on startup or not.  Getting bigger after startup won't make it automatically be live.
+    private _initialScale: number = 0;  // the initial scale of the PDF when first rendered which determines whether the document will be live on startup or not.  Getting bigger after startup won't make it automatically be live.
     private _everActive = false; // has this box ever had its contents activated -- if so, stop drawing the overlay title
     private _pdfViewer: PDFViewer | undefined;
     private _searchRef = React.createRef<HTMLInputElement>();
@@ -46,6 +46,11 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
     @observable private _pdf: Opt<Pdfjs.PDFDocumentProxy>;
     @observable private _pageControls = false;
 
+    constructor(props: any) {
+        super(props);
+        this._initialScale = this.props.ScreenToLocalTransform().Scale;
+    }
+
     componentWillUnmount() {
         this._selectReactionDisposer && this._selectReactionDisposer();
     }
@@ -190,39 +195,46 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
         ContextMenu.Instance.addItem({ description: "Pdf Funcs...", subitems: funcs, icon: "asterisk" });
     }
 
-    render() {
+    @computed get renderTitleBox() {
+        let classname = "pdfBox-cont" + (this.active() ? "-interactive" : "");
+        return <div className="pdfBox-title-outer" >
+            <div className={classname} >
+                <strong className="pdfBox-title" >{` ${this.props.Document.title}`}</strong>
+            </div>
+        </div>;
+    }
+
+    @computed get renderPdfView() {
         trace();
         const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
         let classname = "pdfBox-cont" + (this.active() ? "-interactive" : "");
-        let noPdf = !(pdfUrl instanceof PdfField) || !this._pdf;
-        if (this._initialScale === undefined) this._initialScale = this.props.ScreenToLocalTransform().Scale;
+        return <div className={classname} style={{
+            width: this.props.Document.fitWidth ? `${100 / this.props.ContentScaling()}%` : undefined,
+            height: this.props.Document.fitWidth ? `${100 / this.props.ContentScaling()}%` : undefined,
+            transform: `scale(${this.props.Document.fitWidth ? this.props.ContentScaling() : 1})`
+        }} onContextMenu={this.specificContextMenu} onPointerDown={e => {
+            let hit = document.elementFromPoint(e.clientX, e.clientY);
+            if (hit && hit.localName === "span" && this.props.isSelected()) {  // drag selecting text stops propagation
+                e.button === 0 && e.stopPropagation();
+            }
+        }}>
+            <PDFViewer {...this.props} pdf={this._pdf!} url={pdfUrl!.url.pathname} active={this.props.active} loaded={this.loaded}
+                setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView}
+                renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth}
+                Document={this.props.Document} DataDoc={this.dataDoc} ContentScaling={this.props.ContentScaling}
+                addDocTab={this.props.addDocTab} focus={this.props.focus}
+                pinToPres={this.props.pinToPres} addDocument={this.addDocument}
+                ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select}
+                isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged}
+                fieldKey={this.props.fieldKey} startupLive={this._initialScale < 2.5 ? true : false} />
+            {this.settingsPanel()}
+        </div>;
+    }
+
+    render() {
+        const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField, null);
         if (this.props.isSelected() || this.props.Document.scrollY !== undefined) this._everActive = true;
-        return (!this.extensionDoc || noPdf || (!this._everActive && this.props.ScreenToLocalTransform().Scale > 2.5) ?
-            <div className="pdfBox-title-outer" >
-                <div className={classname} >
-                    <strong className="pdfBox-title" >{` ${this.props.Document.title}`}</strong>
-                </div>
-            </div> :
-            <div className={classname} style={{
-                width: this.props.Document.fitWidth ? `${100 / this.props.ContentScaling()}%` : undefined,
-                height: this.props.Document.fitWidth ? `${100 / this.props.ContentScaling()}%` : undefined,
-                transform: `scale(${this.props.Document.fitWidth ? this.props.ContentScaling() : 1})`
-            }} onContextMenu={this.specificContextMenu} onPointerDown={e => {
-                let hit = document.elementFromPoint(e.clientX, e.clientY);
-                if (hit && hit.localName === "span" && this.props.isSelected()) {  // drag selecting text stops propagation
-                    e.button === 0 && e.stopPropagation();
-                }
-            }}>
-                <PDFViewer {...this.props} pdf={this._pdf!} url={pdfUrl!.url.pathname} active={this.props.active} loaded={this.loaded}
-                    setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView}
-                    renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth}
-                    Document={this.props.Document} DataDoc={this.dataDoc} ContentScaling={this.props.ContentScaling}
-                    addDocTab={this.props.addDocTab} focus={this.props.focus}
-                    pinToPres={this.props.pinToPres} addDocument={this.addDocument}
-                    ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select}
-                    isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged}
-                    fieldKey={this.props.fieldKey} startupLive={this._initialScale < 2.5 ? true : false} />
-                {this.settingsPanel()}
-            </div>);
+        return !pdfUrl || !this._pdf || !this.extensionDoc || (!this._everActive && this.props.ScreenToLocalTransform().Scale > 2.5) ?
+            this.renderTitleBox : this.renderPdfView;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 2d8f47666..936af9ab8 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -52,11 +52,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
 
         this._brushDisposer = reaction(
             () => FieldValue(Cast(this.props.document.group, Doc)) && Doc.isBrushedHighlightedDegree(FieldValue(Cast(this.props.document.group, Doc))!),
-            (brushed) => {
-                if (brushed !== undefined) {
-                    runInAction(() => this._brushed = brushed !== 0);
-                }
-            }
+            brushed => brushed !== undefined && runInAction(() => this._brushed = brushed !== 0)
         );
     }
 
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 060ba8613..0cb671156 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -199,7 +199,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
 
         this._annotationReactionDisposer = reaction(
             () => this.extensionDoc && DocListCast(this.extensionDoc.annotations),
-            annotations => annotations && annotations.length && this.renderAnnotations(annotations, true),
+            annotations => annotations && annotations.length && (this._annotations = annotations),
             { fireImmediately: true });
 
         this._filterReactionDisposer = reaction(
@@ -297,18 +297,6 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
         this.Index = -1;
         return mainAnnoDoc;
     }
-
-    @action
-    renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => {
-        if (removeOldAnnotations) {
-            this._annotations = annotations;
-        }
-        else {
-            this._annotations.push(...annotations);
-            this._annotations = new Array<Doc>(...this._annotations);
-        }
-    }
-
     @action
     prevAnnotation = () => {
         this.Index = Math.max(this.Index - 1, 0);
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 6aad4a6be..3bf1129b5 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -669,8 +669,8 @@ export namespace Doc {
         return doc;
     }
 
-    export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? "layoutKey1" : "layoutKey2"; }
 
+    export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? "layoutKey1" : "layoutKey2"; }
     export function linkFollowUnhighlight() {
         Doc.UnhighlightAll();
         document.removeEventListener("pointerdown", linkFollowUnhighlight);
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 56ea5bfe1..833e44bf6 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -15,7 +15,6 @@ import { RouteStore } from "../../RouteStore";
 import { InkingControl } from "../../../client/views/InkingControl";
 import { DragManager } from "../../../client/util/DragManager";
 import { nullAudio } from "../../../new_fields/URLField";
-import { LinkManager } from "../../../client/util/LinkManager";
 
 export class CurrentUserUtils {
     private static curr_id: string;
-- 
cgit v1.2.3-70-g09d2


From 92491285a0b394f433a018810606bc6a543e7e93 Mon Sep 17 00:00:00 2001
From: Stanley Yip <stanley_yip@brown.edu>
Date: Sat, 23 Nov 2019 16:17:28 -0500
Subject: a lot of fixes!

---
 src/client/views/DocComponent.tsx           | 4 ++--
 src/client/views/InkingStroke.tsx           | 5 +++--
 src/client/views/MainView.tsx               | 2 +-
 src/client/views/Touchable.tsx              | 2 +-
 src/client/views/nodes/DocumentView.tsx     | 8 ++++++--
 src/client/views/nodes/FormattedTextBox.tsx | 1 +
 6 files changed, 14 insertions(+), 8 deletions(-)

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

diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 1bd1006a8..b3a130b33 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -31,7 +31,7 @@ interface DocExtendableProps {
     renderDepth: number;
 }
 export function DocExtendableComponent<P extends DocExtendableProps, T>(schemaCtor: (doc: Doc) => T) {
-    class Component extends React.Component<P> {
+    class Component extends Touchable<P> {
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
         @computed get Document(): T { return schemaCtor(this.props.Document); }
         @computed get layoutDoc() { return Doc.Layout(this.props.Document); }
@@ -53,7 +53,7 @@ interface DocAnnotatableProps {
     renderDepth: number;
 }
 export function DocAnnotatableComponent<P extends DocAnnotatableProps, T>(schemaCtor: (doc: Doc) => T) {
-    class Component extends React.Component<P> {
+    class Component extends Touchable<P> {
         @observable _isChildActive = false;
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
         @computed get Document(): T { return schemaCtor(this.props.Document); }
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 74211cc90..e00ba91a4 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -53,8 +53,9 @@ export class InkingStroke extends DocExtendableComponent<FieldViewProps, InkDocu
             <svg width={width} height={height} style={{
                 transformOrigin: "top left",
                 transform: `translate(${left}px, ${top}px) scale(${scaleX}, ${scaleY})`,
-                mixBlendMode: this.Document.tool === InkTool.Highlighter ? "multiply" : "unset"
-            }}>
+                mixBlendMode: this.Document.tool === InkTool.Highlighter ? "multiply" : "unset",
+                pointerEvents: "all"
+            }} onTouchStart={this.onTouchStart}>
                 {points}
             </svg>
         );
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 162d0c08a..e6dd2fcad 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -468,7 +468,7 @@ export class MainView extends React.Component {
         return new Transform(-translateX, -translateY, 1 / scale);
     }
     @computed get docButtons() {
-        if (CurrentUserUtils.UserDocument.expandingButtons instanceof Doc) {
+        if (CurrentUserUtils.UserDocument?.expandingButtons instanceof Doc) {
             return <div className="mainView-docButtons" ref={this._docBtnRef}
                 style={{ left: (this._flyoutTranslate ? this.flyoutWidth : 0) + 20, height: !CurrentUserUtils.UserDocument.expandingButtons.isExpanded ? "42px" : undefined }} >
                 <MainViewNotifs />
diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx
index ba87025c4..326e8e578 100644
--- a/src/client/views/Touchable.tsx
+++ b/src/client/views/Touchable.tsx
@@ -41,7 +41,7 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
     @action
     protected onTouch = (e: TouchEvent): void => {
         // if we're not actually moving a lot, don't consider it as dragging yet
-        if (!InteractionUtils.IsDragging(this.prevPoints, e.targetTouches, 5) && !this._touchDrag) return;
+        // if (!InteractionUtils.IsDragging(this.prevPoints, e.targetTouches, 5) && !this._touchDrag) return;
         this._touchDrag = true;
         switch (e.targetTouches.length) {
             case 1:
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index cf5a94f01..3a600e0fd 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -41,6 +41,8 @@ import "./DocumentView.scss";
 import { FormattedTextBox } from './FormattedTextBox';
 import React = require("react");
 import { InteractionUtils } from '../../util/InteractionUtils';
+import { InkingControl } from '../InkingControl';
+import { InkTool } from '../../../new_fields/InkField';
 
 library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight,
     fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale,
@@ -191,7 +193,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
 
     onPointerDown = (e: React.PointerEvent): void => {
-        if (e.nativeEvent.cancelBubble && e.button === 0) return;
+        if ((e.nativeEvent.cancelBubble && (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCH)))
+            // return if we're inking, and not selecting a button document
+            || (InkingControl.Instance.selectedTool !== InkTool.None && !this.Document.onClick)) return;
         this._downX = e.clientX;
         this._downY = e.clientY;
         this._hitTemplateDrag = false;
@@ -202,7 +206,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 this._hitTemplateDrag = true;
             }
         }
-        if ((this.active || this.Document.onDragStart || this.Document.onClick) && !e.ctrlKey && e.button === 0 && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation(); // events stop at the lowest document that is active.  if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag);
+        if ((this.active || this.Document.onDragStart || this.Document.onClick) && !e.ctrlKey && (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCH)) && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation(); // events stop at the lowest document that is active.  if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag);
         document.removeEventListener("pointermove", this.onPointerMove);
         document.removeEventListener("pointerup", this.onPointerUp);
         document.addEventListener("pointermove", this.onPointerMove);
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 5201f9bdc..f30422b66 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1043,6 +1043,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                 onPointerUp={this.onPointerUp}
                 onPointerDown={this.onPointerDown}
                 onMouseUp={this.onMouseUp}
+                onTouchStart={this.onTouchStart}
                 onWheel={this.onPointerWheel}
                 onPointerEnter={action(() => this._entered = true)}
                 onPointerLeave={action(() => this._entered = false)}
-- 
cgit v1.2.3-70-g09d2


From e324248724a130a84b459a072dc846f500f8d9b0 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Sat, 23 Nov 2019 18:19:27 -0500
Subject: heartbeats

---
 src/client/DocServer.ts                     |  2 ++
 src/client/util/ClientDiagnostics.ts        | 29 +++++++++++++++++++++++++++++
 src/client/views/Main.tsx                   |  2 ++
 src/client/views/MainView.tsx               |  6 ++++--
 src/server/ApiManagers/DiagnosticManager.ts | 26 ++++++++++++++++++++++++++
 src/server/ApiManagers/UserManager.ts       |  2 +-
 src/server/Initialization.ts                | 13 +++++++------
 src/server/index.ts                         |  2 ++
 8 files changed, 73 insertions(+), 9 deletions(-)
 create mode 100644 src/client/util/ClientDiagnostics.ts
 create mode 100644 src/server/ApiManagers/DiagnosticManager.ts

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

diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 2cec1046b..14479694c 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -64,6 +64,8 @@ export namespace DocServer {
         }
     }
 
+    let connection_error = false;
+
     export function init(protocol: string, hostname: string, port: number, identifier: string) {
         _cache = {};
         GUID = identifier;
diff --git a/src/client/util/ClientDiagnostics.ts b/src/client/util/ClientDiagnostics.ts
new file mode 100644
index 000000000..e454cdecb
--- /dev/null
+++ b/src/client/util/ClientDiagnostics.ts
@@ -0,0 +1,29 @@
+import { observable, runInAction } from "mobx";
+import { MainView } from "../views/MainView";
+
+export namespace ClientDiagnostics {
+
+    export function start() {
+
+        let serverPolls = 0;
+        const serverHandle = setInterval(async () => {
+            if (++serverPolls === 20) {
+                alert("Your connection to the server has been terminated.");
+                clearInterval(serverHandle);
+            }
+            await fetch("/serverHeartbeat");
+            serverPolls--;
+        }, 100);
+
+
+        const solrHandle = setInterval(async () => {
+            const response = await fetch("/solrHeartbeat");
+            if (!response) {
+                alert("Looks like SOLR is not running on your machine.");
+                clearInterval(solrHandle);
+            }
+        }, 100);
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index b21eb9c8f..dec4a24e4 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -5,10 +5,12 @@ import * as ReactDOM from 'react-dom';
 import * as React from 'react';
 import { DocServer } from "../DocServer";
 import { AssignAllExtensions } from "../../extensions/General/Extensions";
+import { ClientDiagnostics } from "../util/ClientDiagnostics";
 
 AssignAllExtensions();
 
 (async () => {
+    ClientDiagnostics.start();
     const info = await CurrentUserUtils.loadCurrentUser();
     DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email);
     await Docs.Prototypes.initialize();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 031926604..d352ad776 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -4,7 +4,7 @@ import {
     faMusic, faObjectGroup, faPause, faMousePointer, faPenNib, faFileAudio, faPen, faEraser, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt, faHighlighter, faMicrophone, faCompressArrowsAlt
 } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
+import { action, computed, configure, observable, reaction, runInAction, autorun } from 'mobx';
 import { observer } from 'mobx-react';
 import "normalize.css";
 import * as React from 'react';
@@ -414,7 +414,8 @@ export class MainView extends React.Component {
                 <button className="mainView-logout" key="logout" onClick={() => window.location.assign(Utils.prepend("/logout"))}>
                     {CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}
                 </button>
-            </div></div>;
+            </div>
+        </div>;
     }
 
     @computed get mainContent() {
@@ -422,6 +423,7 @@ export class MainView extends React.Component {
         return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
             <div className="mainView-mainContent" >
                 <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger}>
+                    <div>HEY!</div>
                     <div className="mainView-libraryHandle"
                         style={{ cursor: "ew-resize", left: `${(this.flyoutWidth * (this._flyoutTranslate ? 1 : 0)) - 10}px`, backgroundColor: `${StrCast(sidebar.backgroundColor, "lightGray")}` }}
                         onPointerDown={this.onPointerDown} onPointerOver={this.pointerOverDragger}>
diff --git a/src/server/ApiManagers/DiagnosticManager.ts b/src/server/ApiManagers/DiagnosticManager.ts
new file mode 100644
index 000000000..b775167b6
--- /dev/null
+++ b/src/server/ApiManagers/DiagnosticManager.ts
@@ -0,0 +1,26 @@
+import ApiManager, { Registration } from "./ApiManager";
+import { Method } from "../RouteManager";
+import request = require('request-promise');
+
+export default class DiagnosticManager extends ApiManager {
+
+    protected initialize(register: Registration): void {
+
+        register({
+            method: Method.GET,
+            subscription: "/serverHeartbeat",
+            onValidation: ({ res }) => res.send(true)
+        });
+
+        register({
+            method: Method.GET,
+            subscription: "/solrHeartbeat",
+            onValidation: async ({ res }) => {
+                const response = await request("http://localhost:8983");
+                res.send(response !== undefined);
+            }
+        });
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts
index 4ee5a2b85..8edeab16d 100644
--- a/src/server/ApiManagers/UserManager.ts
+++ b/src/server/ApiManagers/UserManager.ts
@@ -56,7 +56,7 @@ export default class UserManager extends ApiManager {
                 const process = (target: { user: string, duration: number }[]) => {
                     const comparator = (first: ActivityUnit, second: ActivityUnit) => first.duration - second.duration;
                     const sorted = target.sort(comparator);
-                    return sorted.map(({ user, duration }) => `${user} (last active ${msToTime(duration)} ago)`);
+                    return sorted.map(({ user, duration }) => `${user} (${msToTime(duration)})`);
                 };
 
                 res.render("user_activity.pug", {
diff --git a/src/server/Initialization.ts b/src/server/Initialization.ts
index 7fad5556d..76acb4363 100644
--- a/src/server/Initialization.ts
+++ b/src/server/Initialization.ts
@@ -20,7 +20,6 @@ import * as request from 'request';
 import RouteSubscriber from './RouteSubscriber';
 import { publicDirectory } from '.';
 import { ConsoleColors, logPort } from './ActionUtilities';
-import { WebSocket } from './Websocket/Websocket';
 import { timeMap } from './ApiManagers/UserManager';
 
 /* RouteSetter is a wrapper around the server that prevents the server
@@ -38,11 +37,13 @@ export default async function InitializeServer(options: InitializationOptions) {
     server.use(express.static(publicDirectory));
     server.use("/images", express.static(publicDirectory));
 
-    server.use("*", (req, _res, next) => {
-        const userEmail = req.user?.email;
-        console.log(ConsoleColors.Cyan, req.originalUrl, userEmail ?? "<user logged out>");
-        if (userEmail) {
-            timeMap[userEmail] = Date.now();
+    server.use("*", ({ user, originalUrl }, _res, next) => {
+        if (!originalUrl.includes("Heartbeat")) {
+            const userEmail = user?.email;
+            console.log(ConsoleColors.Cyan, originalUrl, userEmail ?? "<user logged out>");
+            if (userEmail) {
+                timeMap[userEmail] = Date.now();
+            }
         }
         next();
     });
diff --git a/src/server/index.ts b/src/server/index.ts
index 9c48aca45..d02a6005e 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -21,6 +21,7 @@ import UploadManager from "./ApiManagers/UploadManager";
 import { log_execution } from "./ActionUtilities";
 import GeneralGoogleManager from "./ApiManagers/GeneralGoogleManager";
 import GooglePhotosManager from "./ApiManagers/GooglePhotosManager";
+import DiagnosticManager from "./ApiManagers/DiagnosticManager";
 
 export const publicDirectory = path.resolve(__dirname, "public");
 export const filesDirectory = path.resolve(publicDirectory, "files") + "/";
@@ -55,6 +56,7 @@ function routeSetter({ isRelease, addSupervisedRoute }: RouteManager) {
         new UserManager(),
         new UploadManager(),
         new DownloadManager(),
+        new DiagnosticManager(),
         new SearchManager(),
         new PDFManager(),
         new DeleteManager(),
-- 
cgit v1.2.3-70-g09d2


From 780240515a06d9d71a4b58a2559d8661478a560f Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Tue, 26 Nov 2019 20:34:33 -0500
Subject: cleanup after looking at changed files

---
 src/client/DocServer.ts                       | 2 --
 src/client/util/ClientDiagnostics.ts          | 4 ++--
 src/client/util/Import & Export/ImageUtils.ts | 2 +-
 src/client/views/MainView.tsx                 | 1 -
 src/client/views/search/SearchBox.tsx         | 2 +-
 src/server/ApiManagers/UserManager.ts         | 1 -
 src/server/Initialization.ts                  | 1 -
 src/server/RouteManager.ts                    | 2 ++
 8 files changed, 6 insertions(+), 9 deletions(-)

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

diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 14479694c..2cec1046b 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -64,8 +64,6 @@ export namespace DocServer {
         }
     }
 
-    let connection_error = false;
-
     export function init(protocol: string, hostname: string, port: number, identifier: string) {
         _cache = {};
         GUID = identifier;
diff --git a/src/client/util/ClientDiagnostics.ts b/src/client/util/ClientDiagnostics.ts
index 6f82a47db..24f196252 100644
--- a/src/client/util/ClientDiagnostics.ts
+++ b/src/client/util/ClientDiagnostics.ts
@@ -13,7 +13,7 @@ export namespace ClientDiagnostics {
             }
             await fetch("/serverHeartbeat");
             serverPolls--;
-        }, 100);
+        }, 1000 * 15);
 
 
         let executed = false;
@@ -24,7 +24,7 @@ export namespace ClientDiagnostics {
                 executed = true;
                 clearInterval(solrHandle);
             }
-        }, 100);
+        }, 1000 * 15);
 
     }
 
diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts
index ca80f3bca..6a9486f83 100644
--- a/src/client/util/Import & Export/ImageUtils.ts	
+++ b/src/client/util/Import & Export/ImageUtils.ts	
@@ -22,7 +22,7 @@ export namespace ImageUtils {
 
     export const ExportHierarchyToFileSystem = async (collection: Doc): Promise<void> => {
         const a = document.createElement("a");
-        a.href = Utils.prepend(`imageHierarchyExport/${collection[Id]}`);
+        a.href = Utils.prepend(`/imageHierarchyExport/${collection[Id]}`);
         a.download = `Dash Export [${StrCast(collection.title)}].zip`;
         a.click();
     };
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index f59da3cde..5231075a1 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -423,7 +423,6 @@ export class MainView extends React.Component {
         return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
             <div className="mainView-mainContent" >
                 <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger}>
-                    <div>HEY!</div>
                     <div className="mainView-libraryHandle"
                         style={{ cursor: "ew-resize", left: `${(this.flyoutWidth * (this._flyoutTranslate ? 1 : 0)) - 10}px`, backgroundColor: `${StrCast(sidebar.backgroundColor, "lightGray")}` }}
                         onPointerDown={this.onPointerDown} onPointerOver={this.pointerOverDragger}>
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 1337d3f95..ff35542ed 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -89,7 +89,7 @@ export class SearchBox extends React.Component {
 
     public static async convertDataUri(imageUri: string, returnedFilename: string) {
         try {
-            let posting = Utils.prepend("uploadURI");
+            let posting = Utils.prepend("/uploadURI");
             const returnedUri = await rp.post(posting, {
                 body: {
                     uri: imageUri,
diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts
index 8edeab16d..0f7d14320 100644
--- a/src/server/ApiManagers/UserManager.ts
+++ b/src/server/ApiManagers/UserManager.ts
@@ -9,7 +9,6 @@ interface ActivityUnit {
     duration: number;
 }
 
-
 export default class UserManager extends ApiManager {
 
     protected initialize(register: Registration): void {
diff --git a/src/server/Initialization.ts b/src/server/Initialization.ts
index 76acb4363..8b633a7cd 100644
--- a/src/server/Initialization.ts
+++ b/src/server/Initialization.ts
@@ -40,7 +40,6 @@ export default async function InitializeServer(options: InitializationOptions) {
     server.use("*", ({ user, originalUrl }, _res, next) => {
         if (!originalUrl.includes("Heartbeat")) {
             const userEmail = user?.email;
-            console.log(ConsoleColors.Cyan, originalUrl, userEmail ?? "<user logged out>");
             if (userEmail) {
                 timeMap[userEmail] = Date.now();
             }
diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts
index 3aae5734a..3a20d5af5 100644
--- a/src/server/RouteManager.ts
+++ b/src/server/RouteManager.ts
@@ -1,6 +1,7 @@
 import RouteSubscriber from "./RouteSubscriber";
 import { DashUserModel } from "./authentication/models/user_model";
 import * as express from 'express';
+import { ConsoleColors } from "./ActionUtilities";
 
 export enum Method {
     GET,
@@ -52,6 +53,7 @@ export default class RouteManager {
                 try {
                     await toExecute(args);
                 } catch (e) {
+                    console.log(ConsoleColors.Red, target, user?.email ?? "<user logged out>");
                     if (onError) {
                         onError({ ...core, error: e });
                     } else {
-- 
cgit v1.2.3-70-g09d2


From b9147cee91307242558d3a277601b1357a83f2e6 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 27 Nov 2019 12:55:49 -0500
Subject: fixing templates & workspace/document sidebar treeviews

---
 src/client/documents/Documents.ts                  |  2 +-
 src/client/views/MainView.tsx                      | 13 +++++------
 src/client/views/collections/CollectionSubView.tsx |  2 +-
 .../views/collections/CollectionTreeView.tsx       |  5 +++--
 src/client/views/nodes/FormattedTextBox.tsx        | 26 +++++++++++-----------
 src/client/views/search/FilterBox.tsx              |  2 +-
 src/new_fields/Doc.ts                              | 18 +++++++--------
 7 files changed, 34 insertions(+), 34 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index dea057b93..0befec1da 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -299,7 +299,7 @@ export namespace Docs {
             // whatever options pertain to this specific prototype
             let options = { title, type, baseProto: true, ...defaultOptions, ...(template.options || {}) };
             options.layout = layout.view.LayoutString(layout.dataField);
-            return Doc.assign(new Doc(prototypeId, true), { ...options, baseLayout: options.layout });
+            return Doc.assign(new Doc(prototypeId, true), { ...options });
         }
 
     }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index e6dd2fcad..f3a1e799c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -187,22 +187,21 @@ export class MainView extends React.Component {
 
     @action
     createNewWorkspace = async (id?: string) => {
+        let workspaces = Cast(this.userDoc.workspaces, Doc) as Doc;
+        let workspaceCount = DocListCast(workspaces.data).length + 1;
         let freeformOptions: DocumentOptions = {
             x: 0,
             y: 400,
             width: this._panelWidth * .7,
             height: this._panelHeight,
-            title: "My Blank Collection",
+            title: "Collection " + workspaceCount,
             backgroundColor: "white"
         };
-        let workspaces: FieldResult<Doc>;
         let freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
+        Doc.AddDocToList(Doc.GetProto(CurrentUserUtils.UserDocument.documents as Doc), "data", freeformDoc);
         var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] };
-        let mainDoc = Docs.Create.DockDocument([freeformDoc], JSON.stringify(dockingLayout), {}, id);
-        if (this.userDoc && ((workspaces = Cast(this.userDoc.workspaces, Doc)) instanceof Doc)) {
-            Doc.AddDocToList(workspaces, "data", mainDoc);
-            mainDoc.title = `Workspace ${DocListCast(workspaces.data).length}`;
-        }
+        let mainDoc = Docs.Create.DockDocument([freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${workspaceCount}` }, id);
+        Doc.AddDocToList(workspaces, "data", mainDoc);
         // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
         setTimeout(() => this.openWorkspace(mainDoc), 0);
     }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d7e9494a3..368e988d4 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -218,7 +218,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                             }
                         });
                     } else {
-                        let htmlDoc = Docs.Create.HtmlDocument(html, { ...options, width: 300, height: 300, documentText: text });
+                        let htmlDoc = Docs.Create.HtmlDocument(html, { ...options, title: "-web page-", width: 300, height: 300, documentText: text });
                         this.props.addDocument(htmlDoc);
                     }
                     return;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 42cdd1455..83f09b8e9 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -588,7 +588,8 @@ export class CollectionTreeView extends CollectionSubView(Document) {
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
         } else {
             let layoutItems: ContextMenuProps[] = [];
-            layoutItems.push({ description: this.props.Document.preventTreeViewOpen ? "Persist Treeview State" : "Abandon Treeview State", event: () => this.props.Document.preventTreeViewOpen = !this.props.Document.preventTreeViewOpen, icon: "paint-brush" });
+            layoutItems.push({ description: (this.props.Document.preventTreeViewOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.props.Document.preventTreeViewOpen = !this.props.Document.preventTreeViewOpen, icon: "paint-brush" });
+            layoutItems.push({ description: (this.props.Document.hideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.props.Document.hideHeaderFields = !this.props.Document.hideHeaderFields, icon: "paint-brush" });
             ContextMenu.Instance.addItem({ description: "Treeview Options ...", subitems: layoutItems, icon: "eye" });
         }
     }
@@ -634,7 +635,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
                     {
                         TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
                             moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
-                            this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => !this.props.Document.hideHeaderFields,
+                            this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => BoolCast(this.props.Document.hideHeaderFields),
                             BoolCast(this.props.Document.preventTreeViewOpen), [])
                     }
                 </ul>
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index d601e188d..657b4b406 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -267,19 +267,19 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                 // embed document when dragging with a userDropAction or an embedDoc flag set
             } else if (de.data.userDropAction || de.data.embedDoc) {
                 let target = de.data.droppedDocuments[0];
-                const link = DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: target }, "Embedded Doc:" + target.title);
-                if (link) {
-                    target.fitToBox = true;
-                    let node = schema.nodes.dashDoc.create({
-                        width: target[WidthSym](), height: target[HeightSym](),
-                        title: "dashDoc", docid: target[Id],
-                        float: "right"
-                    });
-                    let view = this._editorView!;
-                    view.dispatch(view.state.tr.insert(view.posAtCoords({ left: de.x, top: de.y })!.pos, node));
-                    this.tryUpdateHeight();
-                    e.stopPropagation();
-                }
+                // const link = DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: target }, "Embedded Doc:" + target.title);
+                // if (link) {
+                target.fitToBox = true;
+                let node = schema.nodes.dashDoc.create({
+                    width: target[WidthSym](), height: target[HeightSym](),
+                    title: "dashDoc", docid: target[Id],
+                    float: "right"
+                });
+                let view = this._editorView!;
+                view.dispatch(view.state.tr.insert(view.posAtCoords({ left: de.x, top: de.y })!.pos, node));
+                this.tryUpdateHeight();
+                e.stopPropagation();
+                // }
             } // otherwise, fall through to outer collection to handle drop
         }
     }
diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx
index 62f3aba4c..7812c9d97 100644
--- a/src/client/views/search/FilterBox.tsx
+++ b/src/client/views/search/FilterBox.tsx
@@ -187,7 +187,7 @@ export class FilterBox extends React.Component {
         let collections: Doc[] = [];
 
         selectedDocs.forEach(async element => {
-            let layout: string = StrCast(element.props.Document.baseLayout);
+            let layout: string = StrCast(element.props.Document.layout);
             //checks if selected view (element) is a collection. if it is, adds to list to search through
             if (layout.indexOf("Collection") > -1) {
                 //makes sure collections aren't added more than once
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 271b7cfd3..a811322d7 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -602,15 +602,15 @@ export namespace Doc {
         fieldTemplate.scale = 1;
         fieldTemplate.showTitle = suppressTitle ? undefined : "title";
         let data = fieldTemplate.data;
-        setTimeout(action(() => {
-            !templateDataDoc[metadataFieldName] && data instanceof ObjectField && (Doc.GetProto(templateDataDoc)[metadataFieldName] = ObjectField.MakeCopy(data));
-            let layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metadataFieldName}"}`);
-            let layoutDelegate = Doc.Layout(fieldTemplate);
-            layoutDelegate.layout = layout;
-            fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout;
-            if (fieldTemplate.backgroundColor !== templateDataDoc.defaultBackgroundColor) fieldTemplate.defaultBackgroundColor = fieldTemplate.backgroundColor;
-            fieldTemplate.proto = templateDataDoc;
-        }), 0);
+        // setTimeout(action(() => {
+        !templateDataDoc[metadataFieldName] && data instanceof ObjectField && (Doc.GetProto(templateDataDoc)[metadataFieldName] = ObjectField.MakeCopy(data));
+        let layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metadataFieldName}"}`);
+        let layoutDelegate = Doc.Layout(fieldTemplate);
+        layoutDelegate.layout = layout;
+        fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout;
+        if (fieldTemplate.backgroundColor !== templateDataDoc.defaultBackgroundColor) fieldTemplate.defaultBackgroundColor = fieldTemplate.backgroundColor;
+        fieldTemplate.proto = templateDataDoc;
+        // }), 0);
         return true;
     }
 
-- 
cgit v1.2.3-70-g09d2


From 77ee66de66a411f79bbbc036d379d09be38d172f Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Mon, 2 Dec 2019 12:12:58 -0500
Subject: further cleanup

---
 src/Utils.ts                                       |   2 -
 src/client/util/ClientDiagnostics.ts               |  18 +--
 .../util/Import & Export/DirectoryImportBox.tsx    |   3 +-
 src/client/views/MainView.tsx                      |   2 +-
 src/client/views/collections/CollectionSubView.tsx |   6 +-
 src/server/ApiManagers/DeleteManager.ts            |   1 -
 src/server/ApiManagers/GeneralGoogleManager.ts     |  15 +--
 src/server/ApiManagers/PDFManager.ts               | 134 ++++++++++-----------
 src/server/ApiManagers/UploadManager.ts            |  43 ++++---
 src/server/DashUploadUtils.ts                      |  18 ++-
 src/server/RouteManager.ts                         |  14 +++
 .../authentication/models/current_user_utils.ts    |   3 +-
 src/server/credentials/test.json                   |  14 ---
 13 files changed, 125 insertions(+), 148 deletions(-)
 delete mode 100644 src/server/credentials/test.json

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

diff --git a/src/Utils.ts b/src/Utils.ts
index b60e9e023..2543743a4 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -2,8 +2,6 @@ import v4 = require('uuid/v4');
 import v5 = require("uuid/v5");
 import { Socket } from 'socket.io';
 import { Message } from './server/Message';
-import { EventEmitter } from 'events';
-import { ConsoleColors } from './server/ActionUtilities';
 
 export namespace Utils {
 
diff --git a/src/client/util/ClientDiagnostics.ts b/src/client/util/ClientDiagnostics.ts
index 7eef935fd..0a213aa1c 100644
--- a/src/client/util/ClientDiagnostics.ts
+++ b/src/client/util/ClientDiagnostics.ts
@@ -12,18 +12,22 @@ export namespace ClientDiagnostics {
             serverPolls--;
         }, 1000 * 15);
 
-
         let executed = false;
-        const handle = async () => {
+        let solrHandle: NodeJS.Timeout | undefined;
+        const handler = async () => {
             const response = await fetch("/solrHeartbeat");
             if (!(await response.json()).running) {
-                !executed && alert("Looks like SOLR is not running on your machine.");
-                executed = true;
-                clearInterval(solrHandle);
+                if (!executed) {
+                    alert("Looks like SOLR is not running on your machine.");
+                    executed = true;
+                    solrHandle && clearInterval(solrHandle);
+                }
             }
         };
-        await handle();
-        const solrHandle = setInterval(handle, 1000 * 15);
+        await handler();
+        if (!executed) {
+            solrHandle = setInterval(handler, 1000 * 15);
+        }
 
     }
 
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index b5e806a97..104d9e099 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx	
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -106,7 +106,8 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
 
         runInAction(() => this.phase = `Internal: uploading ${this.quota - this.completed} files to Dash...`);
 
-        const uploads = await BatchedArray.from(validated, { batchSize: 15 }).batchedMapAsync<any>(async (batch, collector) => {
+        const batched = BatchedArray.from(validated, { batchSize: 15 });
+        const uploads = await batched.batchedMapAsync<any>(async (batch, collector) => {
             const formData = new FormData();
 
             batch.forEach(file => {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 5231075a1..85dfd8be2 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -4,7 +4,7 @@ import {
     faMusic, faObjectGroup, faPause, faMousePointer, faPenNib, faFileAudio, faPen, faEraser, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt, faHighlighter, faMicrophone, faCompressArrowsAlt
 } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, configure, observable, reaction, runInAction, autorun } from 'mobx';
+import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import "normalize.css";
 import * as React from 'react';
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index a1bd1527e..a1ae77fef 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -6,7 +6,7 @@ import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
 import { listSpec } from "../../../new_fields/Schema";
 import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, StrCast } from "../../../new_fields/Types";
+import { Cast } from "../../../new_fields/Types";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { Utils } from "../../../Utils";
 import { DocServer } from "../../DocServer";
@@ -279,9 +279,9 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                     formData.append('file', file);
                     let dropFileName = file ? file.name : "-empty-";
                     promises.push(Networking.PostFormDataToServer("/upload", formData).then(results => {
-                        results.map(action((file: any) => {
+                        results.map(action(({ clientAccessPath }: any) => {
                             let full = { ...options, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300, width: 300, title: dropFileName };
-                            let pathname = Utils.prepend(file.clientAccessPath);
+                            let pathname = Utils.prepend(clientAccessPath);
                             Docs.Get.DocumentFromType(type, pathname, full).then(doc => {
                                 doc && (Doc.GetProto(doc).fileUpload = path.basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, ""));
                                 doc && this.props.addDocument(doc);
diff --git a/src/server/ApiManagers/DeleteManager.ts b/src/server/ApiManagers/DeleteManager.ts
index f58a28ce5..71818c673 100644
--- a/src/server/ApiManagers/DeleteManager.ts
+++ b/src/server/ApiManagers/DeleteManager.ts
@@ -56,7 +56,6 @@ export default class DeleteManager extends ApiManager {
             }
         });
 
-
     }
 
 }
diff --git a/src/server/ApiManagers/GeneralGoogleManager.ts b/src/server/ApiManagers/GeneralGoogleManager.ts
index 171912185..629684e0c 100644
--- a/src/server/ApiManagers/GeneralGoogleManager.ts
+++ b/src/server/ApiManagers/GeneralGoogleManager.ts
@@ -20,8 +20,7 @@ export default class GeneralGoogleManager extends ApiManager {
             method: Method.GET,
             subscription: "/readGoogleAccessToken",
             onValidation: async ({ user, res }) => {
-                const userId = user.id;
-                const token = await GoogleApiServerUtils.retrieveAccessToken(userId);
+                const token = await GoogleApiServerUtils.retrieveAccessToken(user.id);
                 if (!token) {
                     return res.send(GoogleApiServerUtils.generateAuthenticationUrl());
                 }
@@ -37,18 +36,6 @@ export default class GeneralGoogleManager extends ApiManager {
             }
         });
 
-        register({
-            method: Method.GET,
-            subscription: "/deleteWithGoogleCredentials",
-            onValidation: async ({ res, isRelease }) => {
-                if (isRelease) {
-                    return _permission_denied(res, deletionPermissionError);
-                }
-                await Database.Auxiliary.GoogleAuthenticationToken.DeleteAll();
-                res.redirect("/delete");
-            }
-        });
-
         register({
             method: Method.POST,
             subscription: new RouteSubscriber("/googleDocs").add("sector", "action"),
diff --git a/src/server/ApiManagers/PDFManager.ts b/src/server/ApiManagers/PDFManager.ts
index 4bd750aaf..151b48dd9 100644
--- a/src/server/ApiManagers/PDFManager.ts
+++ b/src/server/ApiManagers/PDFManager.ts
@@ -4,10 +4,11 @@ import RouteSubscriber from "../RouteSubscriber";
 import { exists, createReadStream, createWriteStream } from "fs";
 import * as Pdfjs from 'pdfjs-dist';
 import { createCanvas } from "canvas";
-const probe = require("probe-image-size");
+const imageSize = require("probe-image-size");
 import * as express from "express";
 import * as path from "path";
 import { Directory, serverPathToFile, clientPathToFile } from "./UploadManager";
+import { ConsoleColors } from "../ActionUtilities";
 
 export default class PDFManager extends ApiManager {
 
@@ -16,84 +17,77 @@ export default class PDFManager extends ApiManager {
         register({
             method: Method.GET,
             subscription: new RouteSubscriber("thumbnail").add("filename"),
-            onValidation: ({ req, res }) => {
-                let filename = req.params.filename;
-                let noExt = filename.substring(0, filename.length - ".png".length);
-                let pagenumber = parseInt(noExt.split('-')[1]);
-                return new Promise<void>(resolve => {
-                    const path = serverPathToFile(Directory.pdf_thumbnails, filename);
-                    exists(path, (exists: boolean) => {
-                        console.log(`${path} ${exists ? "exists" : "does not exist"}`);
-                        if (exists) {
-                            let input = createReadStream(path);
-                            probe(input, (err: any, { width, height }: any) => {
-                                if (err) {
-                                    console.log(err);
-                                    console.log(`error on ${filename}`);
-                                    return;
-                                }
-                                res.send({
-                                    path: clientPathToFile(Directory.pdf_thumbnails, filename),
-                                    width,
-                                    height
-                                });
-                            });
-                        }
-                        else {
-                            const name = filename.substring(0, filename.length - noExt.split('-')[1].length - ".PNG".length - 1) + ".pdf";
-                            LoadPage(serverPathToFile(Directory.pdfs, name), pagenumber, res);
-                        }
-                        resolve();
-                    });
-                });
-            }
+            onValidation: ({ req, res }) => getOrCreateThumbnail(req.params.filename, res)
         });
 
-        function LoadPage(file: string, pageNumber: number, res: express.Response) {
-            console.log(file);
-            Pdfjs.getDocument(file).promise
-                .then((pdf: Pdfjs.PDFDocumentProxy) => {
-                    let factory = new NodeCanvasFactory();
-                    console.log(pageNumber);
-                    pdf.getPage(pageNumber).then((page: Pdfjs.PDFPageProxy) => {
-                        console.log("reading " + page);
-                        let viewport = page.getViewport(1 as any);
-                        let canvasAndContext = factory.create(viewport.width, viewport.height);
-                        let renderContext = {
-                            canvasContext: canvasAndContext.context,
-                            canvasFactory: factory,
-                            viewport
-                        };
-                        console.log("read " + pageNumber);
+    }
 
-                        page.render(renderContext).promise
-                            .then(() => {
-                                console.log("saving " + pageNumber);
-                                let stream = canvasAndContext.canvas.createPNGStream();
-                                let filenames = path.basename(file).split(".");
-                                const pngFile = serverPathToFile(Directory.pdf_thumbnails, `${filenames[0]}-${pageNumber}.png`);
-                                let out = createWriteStream(pngFile);
-                                stream.pipe(out);
-                                out.on("finish", () => {
-                                    console.log(`Success! Saved to ${pngFile}`);
-                                    res.send({
-                                        path: pngFile,
-                                        width: viewport.width,
-                                        height: viewport.height
-                                    });
-                                });
-                            }, (reason: string) => {
-                                console.error(reason + ` ${pageNumber}`);
-                            });
+}
+
+function getOrCreateThumbnail(thumbnailName: string, res: express.Response) {
+    const noExtension = thumbnailName.substring(0, thumbnailName.length - ".png".length);
+    const pageString = noExtension.split('-')[1];
+    const pageNumber = parseInt(pageString);
+    return new Promise<void>(resolve => {
+        const path = serverPathToFile(Directory.pdf_thumbnails, thumbnailName);
+        exists(path, (exists: boolean) => {
+            if (exists) {
+                let existingThumbnail = createReadStream(path);
+                imageSize(existingThumbnail, (err: any, { width, height }: any) => {
+                    if (err) {
+                        console.log(ConsoleColors.Red, `In PDF thumbnail response, unable to determine dimensions of ${thumbnailName}:`);
+                        console.log(err);
+                        return;
+                    }
+                    res.send({
+                        path: clientPathToFile(Directory.pdf_thumbnails, thumbnailName),
+                        width,
+                        height
                     });
                 });
-        }
-
-    }
+            } else {
+                const offset = thumbnailName.length - pageString.length - 5;
+                const name = thumbnailName.substring(0, offset) + ".pdf";
+                const path = serverPathToFile(Directory.pdfs, name);
+                CreateThumbnail(path, pageNumber, res);
+            }
+            resolve();
+        });
+    });
+}
 
+async function CreateThumbnail(file: string, pageNumber: number, res: express.Response) {
+    const documentProxy = await Pdfjs.getDocument(file).promise;
+    const factory = new NodeCanvasFactory();
+    const page = await documentProxy.getPage(pageNumber);
+    const viewport = page.getViewport(1 as any);
+    const { canvas, context } = factory.create(viewport.width, viewport.height);
+    const renderContext = {
+        canvasContext: context,
+        canvasFactory: factory,
+        viewport
+    };
+    await page.render(renderContext).promise;
+    const pngStream = canvas.createPNGStream();
+    const filenames = path.basename(file).split(".");
+    const pngFile = serverPathToFile(Directory.pdf_thumbnails, `${filenames[0]}-${pageNumber}.png`);
+    const out = createWriteStream(pngFile);
+    pngStream.pipe(out);
+    out.on("finish", () => {
+        res.send({
+            path: pngFile,
+            width: viewport.width,
+            height: viewport.height
+        });
+    });
+    out.on("error", error => {
+        console.log(ConsoleColors.Red, `In PDF thumbnail creation, encountered the following error when piping ${pngFile}:`);
+        console.log(error);
+    });
 }
 
 class NodeCanvasFactory {
+
     create = (width: number, height: number) => {
         var canvas = createCanvas(width, height);
         var context = canvas.getContext('2d');
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 2f76871a6..80ae0ad61 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -38,6 +38,27 @@ export default class UploadManager extends ApiManager {
 
     protected initialize(register: Registration): void {
 
+        register({
+            method: Method.POST,
+            subscription: "/upload",
+            onValidation: async ({ req, res }) => {
+                let form = new formidable.IncomingForm();
+                form.uploadDir = pathToDirectory(Directory.parsed_files);
+                form.keepExtensions = true;
+                return new Promise<void>(resolve => {
+                    form.parse(req, async (_err, _fields, files) => {
+                        let results: any[] = [];
+                        for (const key in files) {
+                            const result = await DashUploadUtils.upload(files[key]);
+                            result && results.push(result);
+                        }
+                        _success(res, results);
+                        resolve();
+                    });
+                });
+            }
+        });
+
         register({
             method: Method.POST,
             subscription: "/uploadDoc",
@@ -142,28 +163,6 @@ export default class UploadManager extends ApiManager {
             }
         });
 
-
-        register({
-            method: Method.POST,
-            subscription: "/upload",
-            onValidation: async ({ req, res }) => {
-                let form = new formidable.IncomingForm();
-                form.uploadDir = pathToDirectory(Directory.parsed_files);
-                form.keepExtensions = true;
-                return new Promise<void>(resolve => {
-                    form.parse(req, async (_err, _fields, files) => {
-                        let results: any[] = [];
-                        for (const key in files) {
-                            const result = await DashUploadUtils.upload(files[key]);
-                            result && results.push(result);
-                        }
-                        _success(res, results);
-                        resolve();
-                    });
-                });
-            }
-        });
-
         register({
             method: Method.POST,
             subscription: "/inspectImage",
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index c831eb072..9ccc72e35 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -85,7 +85,8 @@ export namespace DashUploadUtils {
                     return UploadPdf(path);
                 }
         }
-        console.log(ConsoleColors.Red, `Ignoring unsupported file ${name} with upload type (${type}).`);
+
+        console.log(ConsoleColors.Red, `Ignoring unsupported file (${name}) with upload type (${type}).`);
         return { clientAccessPath: undefined };
     }
 
@@ -169,17 +170,12 @@ export namespace DashUploadUtils {
         if (isLocal) {
             return results;
         }
-        const metadata = (await new Promise<any>((resolve, reject) => {
-            request.head(source, async (error, res) => {
-                if (error) {
-                    return reject(error);
-                }
-                resolve(res);
-            });
-        })).headers;
+        const { headers } = (await new Promise<any>((resolve, reject) => {
+            request.head(source, (error, res) => error ? reject(error) : resolve(res));
+        }));
         return {
-            contentSize: parseInt(metadata[size]),
-            contentType: metadata[type],
+            contentSize: parseInt(headers[size]),
+            contentType: headers[type],
             ...results
         };
     };
diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts
index 3a20d5af5..7c49485f1 100644
--- a/src/server/RouteManager.ts
+++ b/src/server/RouteManager.ts
@@ -26,6 +26,8 @@ export interface RouteInitializer {
     onError?: OnError;
 }
 
+const registered = new Map<string, Set<Method>>();
+
 export default class RouteManager {
     private server: express.Express;
     private _isRelease: boolean;
@@ -89,6 +91,18 @@ export default class RouteManager {
             } else {
                 route = subscriber.build;
             }
+            const existing = registered.get(route);
+            if (existing) {
+                if (existing.has(method)) {
+                    console.log(ConsoleColors.Red, `\nDuplicate registration error: already registered ${route} with Method[${method}]`);
+                    console.log('Please remove duplicate registrations before continuing...\n');
+                    process.exit(0);
+                }
+            } else {
+                const specific = new Set<Method>();
+                specific.add(method);
+                registered.set(route, specific);
+            }
             switch (method) {
                 case Method.GET:
                     this.server.get(route, supervised);
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 052aa54a6..ac4462f78 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -1,4 +1,4 @@
-import { action, computed, observable, reaction, runInAction } from "mobx";
+import { action, computed, observable, reaction } from "mobx";
 import * as rp from 'request-promise';
 import { DocServer } from "../../../client/DocServer";
 import { Docs } from "../../../client/documents/Documents";
@@ -11,7 +11,6 @@ import { listSpec } from "../../../new_fields/Schema";
 import { ScriptField, ComputedField } from "../../../new_fields/ScriptField";
 import { Cast, PromiseValue } from "../../../new_fields/Types";
 import { Utils } from "../../../Utils";
-import { ButtonBox } from "../../../client/views/nodes/ButtonBox";
 import { nullAudio } from "../../../new_fields/URLField";
 import { DragManager } from "../../../client/util/DragManager";
 import { InkingControl } from "../../../client/views/InkingControl";
diff --git a/src/server/credentials/test.json b/src/server/credentials/test.json
deleted file mode 100644
index 0a032cc2d..000000000
--- a/src/server/credentials/test.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-    "installed": {
-        "client_id": "343179513178-ud6tvmh275r2fq93u9eesrnc66t6akh9.apps.googleusercontent.com",
-        "project_id": "quickstart-1565056383187",
-        "auth_uri": "https://accounts.google.com/o/oauth2/auth",
-        "token_uri": "https://oauth2.googleapis.com/token",
-        "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
-        "client_secret": "w8KIFSc0MQpmUYHed4qEzn8b",
-        "redirect_uris": [
-            "urn:ietf:wg:oauth:2.0:oob",
-            "http://localhost"
-        ]
-    }
-}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From ef94ad7df2a087141ddb8d347d3e3c484ff7609b Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Tue, 3 Dec 2019 01:46:36 -0500
Subject: const linter rule and restored google docs push, fixed routing

---
 package.json                                       |   3 +-
 src/Utils.ts                                       |  91 +++---
 src/client/DocServer.ts                            |  10 +-
 src/client/Network.ts                              |   2 +-
 src/client/apis/GoogleAuthenticationManager.tsx    |   2 +-
 .../apis/google_docs/GoogleApiClientUtils.ts       |  20 +-
 .../apis/google_docs/GooglePhotosClientUtils.ts    |  16 +-
 src/client/apis/youtube/YoutubeBox.tsx             | 102 +++----
 src/client/cognitive_services/CognitiveServices.ts |  50 ++--
 src/client/documents/Documents.ts                  |  80 ++---
 src/client/northstar/dash-fields/HistogramField.ts |   8 +-
 .../model/binRanges/QuantitativeVisualBinRange.ts  |  16 +-
 src/client/northstar/operations/BaseOperation.ts   |  14 +-
 src/client/northstar/utils/MathUtil.ts             |  44 +--
 src/client/util/DictationManager.ts                |  75 +++--
 src/client/util/DocumentManager.ts                 |  28 +-
 src/client/util/DragManager.ts                     |  91 +++---
 src/client/util/DropConverter.ts                   |  10 +-
 src/client/util/History.ts                         |   6 +-
 .../util/Import & Export/DirectoryImportBox.tsx    |  58 ++--
 .../util/Import & Export/ImportMetadataEntry.tsx   |   6 +-
 src/client/util/InteractionUtils.ts                |  18 +-
 src/client/util/LinkManager.ts                     |  62 ++--
 src/client/util/ProsemirrorExampleTransfer.ts      |  50 ++--
 src/client/util/RichTextRules.ts                   |  78 ++---
 src/client/util/RichTextSchema.tsx                 |  86 +++---
 src/client/util/Scripting.ts                       |  38 +--
 src/client/util/SearchUtil.ts                      |  24 +-
 src/client/util/SerializationHelper.ts             |   9 +-
 src/client/util/SharingManager.tsx                 |  13 +-
 src/client/util/TooltipLinkingMenu.tsx             |  22 +-
 src/client/util/TooltipTextMenu.tsx                | 332 ++++++++++-----------
 src/client/util/TypedEvent.ts                      |  62 ++--
 src/client/util/UndoManager.ts                     |  16 +-
 src/client/views/CollectionLinearView.tsx          |  12 +-
 src/client/views/ContextMenu.tsx                   |   6 +-
 src/client/views/ContextMenuItem.tsx               |   2 +-
 src/client/views/DictationOverlay.tsx              |  10 +-
 src/client/views/DocComponent.tsx                  |   5 +-
 src/client/views/DocumentButtonBar.scss            |  32 +-
 src/client/views/DocumentButtonBar.tsx             |  57 ++--
 src/client/views/DocumentDecorations.tsx           |  97 +++---
 src/client/views/GlobalKeyHandler.ts               |  30 +-
 src/client/views/InkSelectDecorations.tsx          |  10 +-
 src/client/views/InkingControl.tsx                 |  26 +-
 src/client/views/InkingStroke.tsx                  |  26 +-
 src/client/views/MainView.tsx                      |  36 +--
 src/client/views/MainViewModal.tsx                 |   6 +-
 src/client/views/MetadataEntryMenu.tsx             |   4 +-
 src/client/views/OverlayView.tsx                   |   6 +-
 src/client/views/PreviewCursor.tsx                 |  12 +-
 src/client/views/ScriptBox.tsx                     |   8 +-
 src/client/views/TemplateMenu.tsx                  |  32 +-
 src/client/views/Touchable.tsx                     |   6 +-
 .../views/collections/CollectionDockingView.tsx    |  92 +++---
 .../collections/CollectionMasonryViewFieldRow.tsx  |  82 ++---
 .../views/collections/CollectionSchemaCells.tsx    |  42 +--
 .../views/collections/CollectionSchemaHeaders.tsx  |  36 ++-
 .../CollectionSchemaMovableTableHOC.tsx            |  62 ++--
 .../views/collections/CollectionSchemaView.tsx     | 118 ++++----
 .../views/collections/CollectionStackingView.tsx   | 120 ++++----
 .../CollectionStackingViewFieldColumn.tsx          |  83 +++---
 .../views/collections/CollectionStaffView.tsx      |   6 +-
 src/client/views/collections/CollectionSubView.tsx |  66 ++--
 .../views/collections/CollectionTreeView.tsx       | 144 ++++-----
 src/client/views/collections/CollectionView.tsx    |  28 +-
 .../views/collections/CollectionViewChromes.tsx    |  67 ++---
 src/client/views/collections/KeyRestrictionRow.tsx |   6 +-
 .../views/collections/ParentDocumentSelector.tsx   |   4 +-
 .../CollectionFreeFormLayoutEngines.tsx            |  10 +-
 .../CollectionFreeFormLinkView.tsx                 |  45 ++-
 .../CollectionFreeFormLinksView.tsx                |   8 +-
 .../CollectionFreeFormRemoteCursors.tsx            |  12 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 231 +++++++-------
 .../collectionFreeForm/MarqueeOptionsMenu.tsx      |   2 +-
 .../collections/collectionFreeForm/MarqueeView.tsx | 137 +++++----
 .../caption_toggle/DetailedCaptionToggle.tsx       |   6 +-
 src/client/views/linking/LinkEditor.tsx            |  82 ++---
 src/client/views/linking/LinkFollowBox.tsx         |  48 +--
 src/client/views/linking/LinkMenu.tsx              |   6 +-
 src/client/views/linking/LinkMenuGroup.tsx         |  22 +-
 src/client/views/linking/LinkMenuItem.tsx          |  14 +-
 src/client/views/nodes/AudioBox.tsx                |  24 +-
 src/client/views/nodes/ButtonBox.tsx               |   8 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  20 +-
 .../views/nodes/ContentFittingDocumentView.tsx     |   4 +-
 src/client/views/nodes/DocuLinkBox.tsx             |  26 +-
 src/client/views/nodes/DocumentContentsView.tsx    |   2 +-
 src/client/views/nodes/DocumentView.tsx            |  72 ++---
 src/client/views/nodes/FaceRectangle.tsx           |   2 +-
 src/client/views/nodes/FaceRectangles.tsx          |   8 +-
 src/client/views/nodes/FontIconBox.tsx             |   8 +-
 src/client/views/nodes/FormattedTextBox.tsx        | 197 ++++++------
 src/client/views/nodes/FormattedTextBoxComment.tsx |  22 +-
 src/client/views/nodes/IconBox.tsx                 |   6 +-
 src/client/views/nodes/ImageBox.tsx                |  86 +++---
 src/client/views/nodes/KeyValueBox.tsx             |  40 +--
 src/client/views/nodes/KeyValuePair.tsx            |   8 +-
 src/client/views/nodes/PDFBox.tsx                  |   8 +-
 src/client/views/nodes/PresBox.tsx                 |  38 +--
 src/client/views/nodes/VideoBox.tsx                |  66 ++--
 src/client/views/nodes/WebBox.tsx                  |  22 +-
 src/client/views/pdf/Annotation.tsx                |  14 +-
 src/client/views/pdf/PDFMenu.tsx                   |   2 +-
 src/client/views/pdf/PDFViewer.tsx                 |  66 ++--
 .../views/presentationview/PresElementBox.tsx      |  12 +-
 src/client/views/search/FilterBox.tsx              |  44 +--
 src/client/views/search/IconButton.tsx             |   2 +-
 src/client/views/search/NaviconButton.tsx          |  22 +-
 src/client/views/search/SearchBox.tsx              |  20 +-
 src/client/views/search/SearchItem.tsx             |  26 +-
 src/client/views/search/ToggleBar.tsx              |   3 +-
 src/debug/Viewer.tsx                               |   4 +-
 src/mobile/ImageUpload.tsx                         |  10 +-
 src/new_fields/Doc.ts                              |  94 +++---
 src/new_fields/List.ts                             |   4 +-
 src/new_fields/RichTextUtils.ts                    |  76 ++---
 src/new_fields/Schema.ts                           |   2 +-
 src/new_fields/ScriptField.ts                      |  10 +-
 src/server/ActionUtilities.ts                      |  29 +-
 src/server/ApiManagers/DownloadManager.ts          |   3 +-
 src/server/ApiManagers/GeneralGoogleManager.ts     |  17 +-
 src/server/ApiManagers/GooglePhotosManager.ts      |   4 +-
 src/server/ApiManagers/PDFManager.ts               |  12 +-
 src/server/ApiManagers/SearchManager.ts            |  12 +-
 src/server/ApiManagers/UploadManager.ts            |  16 +-
 src/server/ApiManagers/UtilManager.ts              |   8 +-
 src/server/DashUploadUtils.ts                      |  12 +-
 src/server/Initialization.ts                       |  15 +-
 src/server/RouteManager.ts                         |  16 +-
 src/server/Websocket/Websocket.ts                  |  15 +-
 src/server/apis/google/GoogleApiServerUtils.ts     |   8 +-
 src/server/authentication/config/passport.ts       |   2 +-
 .../authentication/controllers/user_controller.ts  |   5 +-
 .../authentication/models/current_user_utils.ts    |   6 +-
 src/server/database.ts                             |   4 +-
 src/server/downsize.ts                             |   2 +-
 src/server/index.ts                                |  11 +-
 src/typings/index.d.ts                             |   1 +
 test/test.ts                                       |  18 +-
 tslint.json                                        |  58 ++--
 141 files changed, 2467 insertions(+), 2429 deletions(-)

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

diff --git a/package.json b/package.json
index 3725d76eb..499aefdb5 100644
--- a/package.json
+++ b/package.json
@@ -127,6 +127,7 @@
         "child_process": "^1.0.2",
         "class-transformer": "^0.2.0",
         "color": "^3.1.2",
+        "colors": "^1.4.0",
         "connect-flash": "^0.1.1",
         "connect-mongo": "^2.0.3",
         "cookie-parser": "^1.4.4",
@@ -228,4 +229,4 @@
         "xoauth2": "^1.2.0",
         "youtube": "^0.1.0"
     }
-}
+}
\ No newline at end of file
diff --git a/src/Utils.ts b/src/Utils.ts
index 2543743a4..7401ef981 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -54,7 +54,7 @@ export namespace Utils {
     }
 
     export function CopyText(text: string) {
-        var textArea = document.createElement("textarea");
+        const textArea = document.createElement("textarea");
         textArea.value = text;
         document.body.appendChild(textArea);
         textArea.focus();
@@ -66,14 +66,14 @@ export namespace Utils {
     }
 
     export function fromRGBAstr(rgba: string) {
-        let rm = rgba.match(/rgb[a]?\(([ 0-9]+)/);
-        let r = rm ? Number(rm[1]) : 0;
-        let gm = rgba.match(/rgb[a]?\([ 0-9]+,([ 0-9]+)/);
-        let g = gm ? Number(gm[1]) : 0;
-        let bm = rgba.match(/rgb[a]?\([ 0-9]+,[ 0-9]+,([ 0-9]+)/);
-        let b = bm ? Number(bm[1]) : 0;
-        let am = rgba.match(/rgba?\([ 0-9]+,[ 0-9]+,[ 0-9]+,([ .0-9]+)/);
-        let a = am ? Number(am[1]) : 1;
+        const rm = rgba.match(/rgb[a]?\(([ 0-9]+)/);
+        const r = rm ? Number(rm[1]) : 0;
+        const gm = rgba.match(/rgb[a]?\([ 0-9]+,([ 0-9]+)/);
+        const g = gm ? Number(gm[1]) : 0;
+        const bm = rgba.match(/rgb[a]?\([ 0-9]+,[ 0-9]+,([ 0-9]+)/);
+        const b = bm ? Number(bm[1]) : 0;
+        const am = rgba.match(/rgba?\([ 0-9]+,[ 0-9]+,[ 0-9]+,([ .0-9]+)/);
+        const a = am ? Number(am[1]) : 1;
         return { r: r, g: g, b: b, a: a };
     }
 
@@ -86,10 +86,10 @@ export namespace Utils {
         // s /= 100;
         // l /= 100;
 
-        let c = (1 - Math.abs(2 * l - 1)) * s,
+        const c = (1 - Math.abs(2 * l - 1)) * s,
             x = c * (1 - Math.abs((h / 60) % 2 - 1)),
-            m = l - c / 2,
-            r = 0,
+            m = l - c / 2;
+        let r = 0,
             g = 0,
             b = 0;
         if (0 <= h && h < 60) {
@@ -118,10 +118,10 @@ export namespace Utils {
         b /= 255;
 
         // Find greatest and smallest channel values
-        let cmin = Math.min(r, g, b),
+        const cmin = Math.min(r, g, b),
             cmax = Math.max(r, g, b),
-            delta = cmax - cmin,
-            h = 0,
+            delta = cmax - cmin;
+        let h = 0,
             s = 0,
             l = 0;
         // Calculate hue
@@ -173,11 +173,11 @@ export namespace Utils {
     function project(px: number, py: number, ax: number, ay: number, bx: number, by: number) {
 
         if (ax === bx && ay === by) return { point: { x: ax, y: ay }, left: false, dot: 0, t: 0 };
-        var atob = { x: bx - ax, y: by - ay };
-        var atop = { x: px - ax, y: py - ay };
-        var len = atob.x * atob.x + atob.y * atob.y;
+        const atob = { x: bx - ax, y: by - ay };
+        const atop = { x: px - ax, y: py - ay };
+        const len = atob.x * atob.x + atob.y * atob.y;
         var dot = atop.x * atob.x + atop.y * atob.y;
-        var t = Math.min(1, Math.max(0, dot / len));
+        const t = Math.min(1, Math.max(0, dot / len));
 
         dot = (bx - ax) * (py - ay) - (by - ay) * (px - ax);
 
@@ -195,38 +195,38 @@ export namespace Utils {
     export function closestPtBetweenRectangles(l: number, t: number, w: number, h: number,
         l1: number, t1: number, w1: number, h1: number,
         x: number, y: number) {
-        var r = l + w,
+        const r = l + w,
             b = t + h;
-        var r1 = l1 + w1,
+        const r1 = l1 + w1,
             b1 = t1 + h1;
-        let hsegs = [[l, r, t, l1, r1, t1], [l, r, b, l1, r1, t1], [l, r, t, l1, r1, b1], [l, r, b, l1, r1, b1]];
-        let vsegs = [[l, t, b, l1, t1, b1], [r, t, b, l1, t1, b1], [l, t, b, r1, t1, b1], [r, t, b, r1, t1, b1]];
-        let res = hsegs.reduce((closest, seg) => {
-            let res = distanceBetweenHorizontalLines(seg[0], seg[1], seg[2], seg[3], seg[4], seg[5]);
+        const hsegs = [[l, r, t, l1, r1, t1], [l, r, b, l1, r1, t1], [l, r, t, l1, r1, b1], [l, r, b, l1, r1, b1]];
+        const vsegs = [[l, t, b, l1, t1, b1], [r, t, b, l1, t1, b1], [l, t, b, r1, t1, b1], [r, t, b, r1, t1, b1]];
+        const res = hsegs.reduce((closest, seg) => {
+            const res = distanceBetweenHorizontalLines(seg[0], seg[1], seg[2], seg[3], seg[4], seg[5]);
             return (res[0] < closest[0]) ? res : closest;
         }, [Number.MAX_VALUE, []] as [number, number[]]);
-        let fres = vsegs.reduce((closest, seg) => {
-            let res = distanceBetweenVerticalLines(seg[0], seg[1], seg[2], seg[3], seg[4], seg[5]);
+        const fres = vsegs.reduce((closest, seg) => {
+            const res = distanceBetweenVerticalLines(seg[0], seg[1], seg[2], seg[3], seg[4], seg[5]);
             return (res[0] < closest[0]) ? res : closest;
         }, res);
 
-        let near = project(x, y, fres[1][0], fres[1][1], fres[1][2], fres[1][3]);
+        const near = project(x, y, fres[1][0], fres[1][1], fres[1][2], fres[1][3]);
         return project(near.point.x, near.point.y, fres[1][0], fres[1][1], fres[1][2], fres[1][3]);
     }
 
     export function getNearestPointInPerimeter(l: number, t: number, w: number, h: number, x: number, y: number) {
-        var r = l + w,
+        const r = l + w,
             b = t + h;
 
-        var x = clamp(x, l, r),
+        x = clamp(x, l, r),
             y = clamp(y, t, b);
 
-        var dl = Math.abs(x - l),
+        const dl = Math.abs(x - l),
             dr = Math.abs(x - r),
             dt = Math.abs(y - t),
             db = Math.abs(y - b);
 
-        var m = Math.min(dl, dr, dt, db);
+        const m = Math.min(dl, dr, dt, db);
 
         return (m === dt) ? [x, t] :
             (m === db) ? [x, b] :
@@ -234,7 +234,7 @@ export namespace Utils {
     }
 
     export function GetClipboardText(): string {
-        var textArea = document.createElement("textarea");
+        const textArea = document.createElement("textarea");
         document.body.appendChild(textArea);
         textArea.focus();
         textArea.select();
@@ -257,7 +257,7 @@ export namespace Utils {
         if (logFilter !== undefined && logFilter !== message.type) {
             return;
         }
-        let idString = (message.id || "").padStart(36, ' ');
+        const idString = (message.id || "").padStart(36, ' ');
         prefix = prefix.padEnd(16, ' ');
         console.log(`${prefix}: ${idString}, ${receiving ? 'receiving' : 'sending'} ${messageName} with data ${JSON.stringify(message)} `);
     }
@@ -309,18 +309,18 @@ export function OmitKeys(obj: any, keys: string[], addKeyFunc?: (dup: any) => vo
 }
 
 export function WithKeys(obj: any, keys: string[], addKeyFunc?: (dup: any) => void) {
-    var dup: any = {};
+    const dup: any = {};
     keys.forEach(key => dup[key] = obj[key]);
     addKeyFunc && addKeyFunc(dup);
     return dup;
 }
 
 export function timenow() {
-    var now = new Date();
+    const now = new Date();
     let ampm = 'am';
     let h = now.getHours();
     let m: any = now.getMinutes();
-    let s: any = now.getSeconds();
+    const s: any = now.getSeconds();
     if (h >= 12) {
         if (h > 12) h -= 12;
         ampm = 'pm';
@@ -331,8 +331,8 @@ export function timenow() {
 
 export function aggregateBounds(boundsList: { x: number, y: number, width: number, height: number }[]) {
     return boundsList.reduce((bounds, b) => {
-        var [sptX, sptY] = [b.x, b.y];
-        let [bptX, bptY] = [sptX + b.width, sptY + b.height];
+        const [sptX, sptY] = [b.x, b.y];
+        const [bptX, bptY] = [sptX + b.width, sptY + b.height];
         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)
@@ -371,10 +371,11 @@ export type Without<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
 export type Predicate<K, V> = (entry: [K, V]) => boolean;
 
 export function DeepCopy<K, V>(source: Map<K, V>, predicate?: Predicate<K, V>) {
-    let deepCopy = new Map<K, V>();
-    let entries = source.entries(), next = entries.next();
+    const deepCopy = new Map<K, V>();
+    const entries = source.entries();
+    let next = entries.next();
     while (!next.done) {
-        let entry = next.value;
+        const entry = next.value;
         if (!predicate || predicate(entry)) {
             deepCopy.set(entry[0], entry[1]);
         }
@@ -427,13 +428,13 @@ export function smoothScroll(duration: number, element: HTMLElement, to: number)
     animateScroll();
 }
 export function addStyleSheet(styleType: string = "text/css") {
-    let style = document.createElement("style");
+    const style = document.createElement("style");
     style.type = styleType;
-    var sheets = document.head.appendChild(style);
+    const sheets = document.head.appendChild(style);
     return (sheets as any).sheet;
 }
 export function addStyleSheetRule(sheet: any, selector: any, css: any) {
-    var propText = typeof css === "string" ? css : Object.keys(css).map(p => p + ":" + (p === "content" ? "'" + css[p] + "'" : css[p])).join(";");
+    const propText = typeof css === "string" ? css : Object.keys(css).map(p => p + ":" + (p === "content" ? "'" + css[p] + "'" : css[p])).join(";");
     return sheet.insertRule("." + selector + "{" + propText + "}", sheet.cssRules.length);
 }
 export function removeStyleSheetRule(sheet: any, rule: number) {
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 2cec1046b..e4b183715 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,5 +1,5 @@
 import * as OpenSocket from 'socket.io-client';
-import { MessageStore, Diff, YoutubeQueryTypes } from "./../server/Message";
+import { MessageStore, YoutubeQueryTypes } from "./../server/Message";
 import { Opt, Doc } from '../new_fields/Doc';
 import { Utils, emptyFunction } from '../Utils';
 import { SerializationHelper } from './util/SerializationHelper';
@@ -148,7 +148,7 @@ export namespace DocServer {
         // an initial pass through the cache to determine whether the document needs to be fetched,
         // is already in the process of being fetched or already exists in the
         // cache
-        let cached = _cache[id];
+        const cached = _cache[id];
         if (cached === undefined) {
             // NOT CACHED => we'll have to send a request to the server
 
@@ -195,7 +195,7 @@ export namespace DocServer {
     }
 
     export async function getYoutubeChannels() {
-        let apiKey = await Utils.EmitCallback(_socket, MessageStore.YoutubeApiQuery, { type: YoutubeQueryTypes.Channels });
+        const apiKey = await Utils.EmitCallback(_socket, MessageStore.YoutubeApiQuery, { type: YoutubeQueryTypes.Channels });
         return apiKey;
     }
 
@@ -255,7 +255,7 @@ export namespace DocServer {
             for (const field of fields) {
                 if (field !== undefined) {
                     // deserialize
-                    let prom = SerializationHelper.Deserialize(field).then(deserialized => {
+                    const prom = SerializationHelper.Deserialize(field).then(deserialized => {
                         fieldMap[field.id] = deserialized;
 
                         //overwrite or delete any promises (that we inserted as flags
@@ -411,7 +411,7 @@ export namespace DocServer {
     }
 
     let _RespondToUpdate = _respondToUpdateImpl;
-    let _respondToDelete = _respondToDeleteImpl;
+    const _respondToDelete = _respondToDeleteImpl;
 
     function respondToUpdate(diff: any) {
         _RespondToUpdate(diff);
diff --git a/src/client/Network.ts b/src/client/Network.ts
index f9ef27267..ccf60f199 100644
--- a/src/client/Network.ts
+++ b/src/client/Network.ts
@@ -8,7 +8,7 @@ export namespace Networking {
     }
 
     export async function PostToServer(relativeRoute: string, body?: any) {
-        let options = {
+        const options = {
             uri: Utils.prepend(relativeRoute),
             method: "POST",
             body,
diff --git a/src/client/apis/GoogleAuthenticationManager.tsx b/src/client/apis/GoogleAuthenticationManager.tsx
index ae77c4b7b..ce1277667 100644
--- a/src/client/apis/GoogleAuthenticationManager.tsx
+++ b/src/client/apis/GoogleAuthenticationManager.tsx
@@ -30,7 +30,7 @@ export default class GoogleAuthenticationManager extends React.Component<{}> {
     }
 
     public fetchOrGenerateAccessToken = async () => {
-        let response = await Networking.FetchFromServer("/readGoogleAccessToken");
+        const response = await Networking.FetchFromServer("/readGoogleAccessToken");
         // if this is an authentication url, activate the UI to register the new access token
         if (new RegExp(AuthenticationUrl).test(response)) {
             this.isOpen = true;
diff --git a/src/client/apis/google_docs/GoogleApiClientUtils.ts b/src/client/apis/google_docs/GoogleApiClientUtils.ts
index 26c7f8d2e..d2a79f189 100644
--- a/src/client/apis/google_docs/GoogleApiClientUtils.ts
+++ b/src/client/apis/google_docs/GoogleApiClientUtils.ts
@@ -1,4 +1,4 @@
-import { docs_v1, slides_v1 } from "googleapis";
+import { docs_v1 } from "googleapis";
 import { Opt } from "../../../new_fields/Doc";
 import { isArray } from "util";
 import { EditorState } from "prosemirror-state";
@@ -94,7 +94,7 @@ export namespace GoogleApiClientUtils {
 
             export type ExtractResult = { text: string, paragraphs: DeconstructedParagraph[] };
             export const extractText = (document: docs_v1.Schema$Document, removeNewlines = false): ExtractResult => {
-                let paragraphs = extractParagraphs(document);
+                const paragraphs = extractParagraphs(document);
                 let text = paragraphs.map(paragraph => paragraph.contents.filter(content => !("inlineObjectId" in content)).map(run => run as docs_v1.Schema$TextRun).join("")).join("");
                 text = text.substring(0, text.length - 1);
                 removeNewlines && text.ReplaceAll("\n", "");
@@ -107,14 +107,14 @@ export namespace GoogleApiClientUtils {
                 const fragments: DeconstructedParagraph[] = [];
                 if (document.body && document.body.content) {
                     for (const element of document.body.content) {
-                        let runs: ContentArray = [];
+                        const runs: ContentArray = [];
                         let bullet: Opt<number>;
                         if (element.paragraph) {
                             if (element.paragraph.elements) {
                                 for (const inner of element.paragraph.elements) {
                                     if (inner) {
                                         if (inner.textRun) {
-                                            let run = inner.textRun;
+                                            const run = inner.textRun;
                                             (run.content || !filterEmpty) && runs.push(inner.textRun);
                                         } else if (inner.inlineObjectElement) {
                                             runs.push(inner.inlineObjectElement);
@@ -182,8 +182,8 @@ export namespace GoogleApiClientUtils {
         export const read = async (options: ReadOptions): Promise<Opt<ReadResult>> => {
             return retrieve({ documentId: options.documentId }).then(document => {
                 if (document) {
-                    let title = document.title!;
-                    let body = Utils.extractText(document, options.removeNewlines).text;
+                    const title = document.title!;
+                    const body = Utils.extractText(document, options.removeNewlines).text;
                     return { title, body };
                 }
             });
@@ -192,7 +192,7 @@ export namespace GoogleApiClientUtils {
         export const readLines = async (options: ReadOptions): Promise<Opt<ReadLinesResult>> => {
             return retrieve({ documentId: options.documentId }).then(document => {
                 if (document) {
-                    let title = document.title;
+                    const title = document.title;
                     let bodyLines = Utils.extractText(document).text.split("\n");
                     options.removeNewlines && (bodyLines = bodyLines.filter(line => line.length));
                     return { title, bodyLines };
@@ -201,7 +201,7 @@ export namespace GoogleApiClientUtils {
         };
 
         export const setStyle = async (options: UpdateOptions) => {
-            let replies: any = await update({
+            const replies: any = await update({
                 documentId: options.documentId,
                 requests: options.requests
             });
@@ -221,7 +221,7 @@ export namespace GoogleApiClientUtils {
             let index = options.index;
             const mode = options.mode;
             if (!(index && mode === WriteMode.Insert)) {
-                let schema = await retrieve({ documentId });
+                const schema = await retrieve({ documentId });
                 if (!schema || !(index = Utils.endOf(schema))) {
                     return undefined;
                 }
@@ -248,7 +248,7 @@ export namespace GoogleApiClientUtils {
                 return undefined;
             }
             requests.push(...options.content.requests);
-            let replies: any = await update({ documentId: documentId, requests });
+            const replies: any = await update({ documentId: documentId, requests });
             if ("errors" in replies) {
                 console.log("Write operation failed:");
                 console.log(replies.errors.map((error: any) => error.message));
diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index bf8897061..966d8053a 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -128,10 +128,10 @@ export namespace GooglePhotos {
 
         export const CollectionFromSearch = async (constructor: CollectionConstructor, requested: Opt<Partial<Query.SearchOptions>>): Promise<Doc> => {
             await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
-            let response = await Query.ContentSearch(requested);
-            let uploads = await Transactions.WriteMediaItemsToServer(response);
+            const response = await Query.ContentSearch(requested);
+            const uploads = await Transactions.WriteMediaItemsToServer(response);
             const children = uploads.map((upload: Transactions.UploadInformation) => {
-                let document = Docs.Create.ImageDocument(Utils.fileUrl(upload.fileNames.clean));
+                const document = Docs.Create.ImageDocument(Utils.fileUrl(upload.fileNames.clean));
                 document.fillColumn = true;
                 document.contentSize = upload.contentSize;
                 return document;
@@ -157,12 +157,12 @@ export namespace GooglePhotos {
             const images = (await DocListCastAsync(collection.data))!.map(Doc.GetProto);
             images && images.forEach(image => tagMapping.set(image[Id], ContentCategories.NONE));
             const values = Object.values(ContentCategories);
-            for (let value of values) {
+            for (const value of values) {
                 if (value !== ContentCategories.NONE) {
                     const results = await ContentSearch({ included: [value] });
                     if (results.mediaItems) {
                         const ids = results.mediaItems.map(item => item.id);
-                        for (let id of ids) {
+                        for (const id of ids) {
                             const image = await Cast(idMapping[id], Doc);
                             if (image) {
                                 const key = image[Id];
@@ -220,9 +220,9 @@ export namespace GooglePhotos {
 
         export const AlbumSearch = async (albumId: string, pageSize = 100): Promise<MediaItem[]> => {
             const photos = await endpoint();
-            let mediaItems: MediaItem[] = [];
+            const mediaItems: MediaItem[] = [];
             let nextPageTokenStored: Opt<string> = undefined;
-            let found = 0;
+            const found = 0;
             do {
                 const response: any = await photos.mediaItems.search(albumId, pageSize, nextPageTokenStored);
                 mediaItems.push(...response.mediaItems);
@@ -332,7 +332,7 @@ export namespace GooglePhotos {
                 album = await Create.Album(album.title);
             }
             const media: MediaInput[] = [];
-            for (let source of sources) {
+            for (const source of sources) {
                 const data = Cast(Doc.GetProto(source).data, ImageField);
                 if (!data) {
                     return;
diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx
index bed812852..fd3d9e2f1 100644
--- a/src/client/apis/youtube/YoutubeBox.tsx
+++ b/src/client/apis/youtube/YoutubeBox.tsx
@@ -48,44 +48,44 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
      */
     async componentWillMount() {
         //DocServer.getYoutubeChannels();
-        let castedSearchBackUp = Cast(this.props.Document.cachedSearchResults, Doc);
-        let awaitedBackUp = await castedSearchBackUp;
-        let castedDetailBackUp = Cast(this.props.Document.cachedDetails, Doc);
-        let awaitedDetails = await castedDetailBackUp;
+        const castedSearchBackUp = Cast(this.props.Document.cachedSearchResults, Doc);
+        const awaitedBackUp = await castedSearchBackUp;
+        const castedDetailBackUp = Cast(this.props.Document.cachedDetails, Doc);
+        const awaitedDetails = await castedDetailBackUp;
 
 
         if (awaitedBackUp) {
 
 
-            let jsonList = await DocListCastAsync(awaitedBackUp.json);
-            let jsonDetailList = await DocListCastAsync(awaitedDetails!.json);
+            const jsonList = await DocListCastAsync(awaitedBackUp.json);
+            const jsonDetailList = await DocListCastAsync(awaitedDetails!.json);
 
             if (jsonList!.length !== 0) {
                 runInAction(() => this.searchResultsFound = true);
                 let index = 0;
                 //getting the necessary information from backUps and building templates that will be used to map in render
-                for (let video of jsonList!) {
-
-                    let videoId = await Cast(video.id, Doc);
-                    let id = StrCast(videoId!.videoId);
-                    let snippet = await Cast(video.snippet, Doc);
-                    let videoTitle = this.filterYoutubeTitleResult(StrCast(snippet!.title));
-                    let thumbnail = await Cast(snippet!.thumbnails, Doc);
-                    let thumbnailMedium = await Cast(thumbnail!.medium, Doc);
-                    let thumbnailUrl = StrCast(thumbnailMedium!.url);
-                    let videoDescription = StrCast(snippet!.description);
-                    let pusblishDate = (this.roundPublishTime(StrCast(snippet!.publishedAt)))!;
-                    let channelTitle = StrCast(snippet!.channelTitle);
+                for (const video of jsonList!) {
+
+                    const videoId = await Cast(video.id, Doc);
+                    const id = StrCast(videoId!.videoId);
+                    const snippet = await Cast(video.snippet, Doc);
+                    const videoTitle = this.filterYoutubeTitleResult(StrCast(snippet!.title));
+                    const thumbnail = await Cast(snippet!.thumbnails, Doc);
+                    const thumbnailMedium = await Cast(thumbnail!.medium, Doc);
+                    const thumbnailUrl = StrCast(thumbnailMedium!.url);
+                    const videoDescription = StrCast(snippet!.description);
+                    const pusblishDate = (this.roundPublishTime(StrCast(snippet!.publishedAt)))!;
+                    const channelTitle = StrCast(snippet!.channelTitle);
                     let duration: string = "";
                     let viewCount: string = "";
                     if (jsonDetailList!.length !== 0) {
-                        let contentDetails = await Cast(jsonDetailList![index].contentDetails, Doc);
-                        let statistics = await Cast(jsonDetailList![index].statistics, Doc);
+                        const contentDetails = await Cast(jsonDetailList![index].contentDetails, Doc);
+                        const statistics = await Cast(jsonDetailList![index].statistics, Doc);
                         duration = this.convertIsoTimeToDuration(StrCast(contentDetails!.duration));
                         viewCount = this.abbreviateViewCount(parseInt(StrCast(statistics!.viewCount)))!;
                     }
                     index = index + 1;
-                    let newTemplate: VideoTemplate = { videoId: id, videoTitle: videoTitle, thumbnailUrl: thumbnailUrl, publishDate: pusblishDate, channelTitle: channelTitle, videoDescription: videoDescription, duration: duration, viewCount: viewCount };
+                    const newTemplate: VideoTemplate = { videoId: id, videoTitle: videoTitle, thumbnailUrl: thumbnailUrl, publishDate: pusblishDate, channelTitle: channelTitle, videoDescription: videoDescription, duration: duration, viewCount: viewCount };
                     runInAction(() => this.curVideoTemplates.push(newTemplate));
                 }
             }
@@ -115,7 +115,7 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
      */
     onEnterKeyDown = (e: React.KeyboardEvent) => {
         if (e.keyCode === 13) {
-            let submittedTitle = this.YoutubeSearchElement!.value;
+            const submittedTitle = this.YoutubeSearchElement!.value;
             this.YoutubeSearchElement!.value = "";
             this.YoutubeSearchElement!.blur();
             DocServer.getYoutubeVideos(submittedTitle, this.processesVideoResults);
@@ -184,23 +184,23 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
      * difference between today's date and that date, in terms of "ago" to imitate youtube.
      */
     roundPublishTime = (publishTime: string) => {
-        let date = new Date(publishTime).getTime();
-        let curDate = new Date().getTime();
-        let timeDif = curDate - date;
-        let totalSeconds = timeDif / 1000;
-        let totalMin = totalSeconds / 60;
-        let totalHours = totalMin / 60;
-        let totalDays = totalHours / 24;
-        let totalMonths = totalDays / 30.417;
-        let totalYears = totalMonths / 12;
-
-
-        let truncYears = Math.trunc(totalYears);
-        let truncMonths = Math.trunc(totalMonths);
-        let truncDays = Math.trunc(totalDays);
-        let truncHours = Math.trunc(totalHours);
-        let truncMin = Math.trunc(totalMin);
-        let truncSec = Math.trunc(totalSeconds);
+        const date = new Date(publishTime).getTime();
+        const curDate = new Date().getTime();
+        const timeDif = curDate - date;
+        const totalSeconds = timeDif / 1000;
+        const totalMin = totalSeconds / 60;
+        const totalHours = totalMin / 60;
+        const totalDays = totalHours / 24;
+        const totalMonths = totalDays / 30.417;
+        const totalYears = totalMonths / 12;
+
+
+        const truncYears = Math.trunc(totalYears);
+        const truncMonths = Math.trunc(totalMonths);
+        const truncDays = Math.trunc(totalDays);
+        const truncHours = Math.trunc(totalHours);
+        const truncMin = Math.trunc(totalMin);
+        const truncSec = Math.trunc(totalSeconds);
 
         let pluralCase = "";
 
@@ -230,7 +230,7 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
      */
     convertIsoTimeToDuration = (isoDur: string) => {
 
-        let convertedTime = isoDur.replace(/D|H|M/g, ":").replace(/P|T|S/g, "").split(":");
+        const convertedTime = isoDur.replace(/D|H|M/g, ":").replace(/P|T|S/g, "").split(":");
 
         if (1 === convertedTime.length) {
             2 !== convertedTime[0].length && (convertedTime[0] = "0" + convertedTime[0]), convertedTime[0] = "0:" + convertedTime[0];
@@ -269,10 +269,10 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
             if (this.searchResults.length !== 0) {
                 return <ul>
                     {this.searchResults.map((video, index) => {
-                        let filteredTitle = this.filterYoutubeTitleResult(video.snippet.title);
-                        let channelTitle = video.snippet.channelTitle;
-                        let videoDescription = video.snippet.description;
-                        let pusblishDate = this.roundPublishTime(video.snippet.publishedAt);
+                        const filteredTitle = this.filterYoutubeTitleResult(video.snippet.title);
+                        const channelTitle = video.snippet.channelTitle;
+                        const videoDescription = video.snippet.description;
+                        const pusblishDate = this.roundPublishTime(video.snippet.publishedAt);
                         let duration;
                         let viewCount;
                         if (this.videoDetails.length !== 0) {
@@ -331,26 +331,26 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
      */
     @action
     embedVideoOnClick = (videoId: string, filteredTitle: string) => {
-        let embeddedUrl = "https://www.youtube.com/embed/" + videoId;
+        const embeddedUrl = "https://www.youtube.com/embed/" + videoId;
         this.selectedVideoUrl = embeddedUrl;
-        let addFunction = this.props.addDocument!;
-        let newVideoX = NumCast(this.props.Document.x);
-        let newVideoY = NumCast(this.props.Document.y) + NumCast(this.props.Document.height);
+        const addFunction = this.props.addDocument!;
+        const newVideoX = NumCast(this.props.Document.x);
+        const newVideoY = NumCast(this.props.Document.y) + NumCast(this.props.Document.height);
 
         addFunction(Docs.Create.VideoDocument(embeddedUrl, { title: filteredTitle, width: 400, height: 315, x: newVideoX, y: newVideoY }));
         this.videoClicked = true;
     }
 
     render() {
-        let content =
+        const content =
             <div className="youtubeBox-cont" style={{ width: "100%", height: "100%", position: "absolute" }} onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
                 <input type="text" placeholder="Search for a video" onKeyDown={this.onEnterKeyDown} style={{ height: 40, width: "100%", border: "1px solid black", padding: 5, textAlign: "center" }} ref={(e) => this.YoutubeSearchElement = e!} />
                 {this.renderSearchResultsOrVideo()}
             </div>;
 
-        let frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
+        const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
 
-        let classname = "webBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+        const classname = "webBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
         return (
             <>
                 <div className={classname}  >
diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts
index 5a7f5e991..02eff3b25 100644
--- a/src/client/cognitive_services/CognitiveServices.ts
+++ b/src/client/cognitive_services/CognitiveServices.ts
@@ -1,5 +1,5 @@
 import * as request from "request-promise";
-import { Doc, Field, Opt } from "../../new_fields/Doc";
+import { Doc, Field } from "../../new_fields/Doc";
 import { Cast } from "../../new_fields/Types";
 import { Docs } from "../documents/Documents";
 import { Utils } from "../../Utils";
@@ -101,14 +101,14 @@ export namespace CognitiveServices {
         export namespace Appliers {
 
             export const ProcessImage: AnalysisApplier<string> = async (target: Doc, keys: string[], url: string, service: Service, converter: Converter) => {
-                let batch = UndoManager.StartBatch("Image Analysis");
+                const batch = UndoManager.StartBatch("Image Analysis");
 
-                let storageKey = keys[0];
+                const storageKey = keys[0];
                 if (!url || await Cast(target[storageKey], Doc)) {
                     return;
                 }
                 let toStore: any;
-                let results = await ExecuteQuery(service, Manager, url);
+                const results = await ExecuteQuery(service, Manager, url);
                 if (!results) {
                     toStore = "Cognitive Services could not process the given image URL.";
                 } else {
@@ -131,36 +131,32 @@ export namespace CognitiveServices {
 
     export namespace Inking {
 
-        export const Manager: APIManager<InkData> = {
-
-            converter: (inkData: InkData): string => {
-                let entries = inkData.entries(), next = entries.next();
-                let strokes: AzureStrokeData[] = [], id = 0;
-                while (!next.done) {
-                    strokes.push({
-                        id: id++,
-                        points: next.value[1].pathData.map(point => `${point.x},${point.y}`).join(","),
-                        language: "en-US"
-                    });
-                    next = entries.next();
-                }
+        export const Manager: APIManager<InkData[]> = {
+
+            converter: (inkData: InkData[]): string => {
+                let id = 0;
+                const strokes: AzureStrokeData[] = inkData.map(points => ({
+                    id: id++,
+                    points: points.map(({ x, y }) => `${x},${y}`).join(","),
+                    language: "en-US"
+                }));
                 return JSON.stringify({
                     version: 1,
                     language: "en-US",
                     unit: "mm",
-                    strokes: strokes
+                    strokes
                 });
             },
 
             requester: async (apiKey: string, body: string) => {
-                let xhttp = new XMLHttpRequest();
-                let serverAddress = "https://api.cognitive.microsoft.com";
-                let endpoint = serverAddress + "/inkrecognizer/v1.0-preview/recognize";
+                const xhttp = new XMLHttpRequest();
+                const serverAddress = "https://api.cognitive.microsoft.com";
+                const endpoint = serverAddress + "/inkrecognizer/v1.0-preview/recognize";
 
-                let promisified = (resolve: any, reject: any) => {
+                const promisified = (resolve: any, reject: any) => {
                     xhttp.onreadystatechange = function () {
                         if (this.readyState === 4) {
-                            let result = xhttp.responseText;
+                            const result = xhttp.responseText;
                             switch (this.status) {
                                 case 200:
                                     return resolve(result);
@@ -184,15 +180,15 @@ export namespace CognitiveServices {
 
         export namespace Appliers {
 
-            export const ConcatenateHandwriting: AnalysisApplier<InkData> = async (target: Doc, keys: string[], inkData: InkData) => {
-                let batch = UndoManager.StartBatch("Ink Analysis");
+            export const ConcatenateHandwriting: AnalysisApplier<InkData[]> = async (target: Doc, keys: string[], inkData: InkData[]) => {
+                const batch = UndoManager.StartBatch("Ink Analysis");
 
                 let results = await ExecuteQuery(Service.Handwriting, Manager, inkData);
                 if (results) {
                     results.recognitionUnits && (results = results.recognitionUnits);
                     target[keys[0]] = Docs.Get.DocumentHierarchyFromJson(results, "Ink Analysis");
-                    let recognizedText = results.map((item: any) => item.recognizedText);
-                    let individualWords = recognizedText.filter((text: string) => text && text.split(" ").length === 1);
+                    const recognizedText = results.map((item: any) => item.recognizedText);
+                    const individualWords = recognizedText.filter((text: string) => text && text.split(" ").length === 1);
                     target[keys[1]] = individualWords.join(" ");
                 }
 
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index d1e3ea708..e0f2858ba 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -35,10 +35,10 @@ import { CollectionDockingView } from "../views/collections/CollectionDockingVie
 import { LinkManager } from "../util/LinkManager";
 import { DocumentManager } from "../util/DocumentManager";
 import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox";
-import { Scripting, CompileScript } from "../util/Scripting";
+import { Scripting } from "../util/Scripting";
 import { ButtonBox } from "../views/nodes/ButtonBox";
 import { FontIconBox } from "../views/nodes/FontIconBox";
-import { SchemaHeaderField, RandomPastel } from "../../new_fields/SchemaHeaderField";
+import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField";
 import { PresBox } from "../views/nodes/PresBox";
 import { ComputedField, ScriptField } from "../../new_fields/ScriptField";
 import { ProxyField } from "../../new_fields/Proxy";
@@ -50,8 +50,8 @@ import { ColorBox } from "../views/nodes/ColorBox";
 import { DocuLinkBox } from "../views/nodes/DocuLinkBox";
 import { InkingStroke } from "../views/InkingStroke";
 import { InkField } from "../../new_fields/InkField";
-var requestImageSize = require('../util/request-image-size');
-var path = require('path');
+const requestImageSize = require('../util/request-image-size');
+const path = require('path');
 
 export interface DocumentOptions {
     x?: number;
@@ -239,16 +239,16 @@ export namespace Docs {
             ProxyField.initPlugin();
             ComputedField.initPlugin();
             // non-guid string ids for each document prototype
-            let prototypeIds = Object.values(DocumentType).filter(type => type !== DocumentType.NONE).map(type => type + suffix);
+            const prototypeIds = Object.values(DocumentType).filter(type => type !== DocumentType.NONE).map(type => type + suffix);
             // fetch the actual prototype documents from the server
-            let actualProtos = await DocServer.GetRefFields(prototypeIds);
+            const actualProtos = await DocServer.GetRefFields(prototypeIds);
 
             // update this object to include any default values: DocumentOptions for all prototypes
             prototypeIds.map(id => {
-                let existing = actualProtos[id] as Doc;
-                let type = id.replace(suffix, "") as DocumentType;
+                const existing = actualProtos[id] as Doc;
+                const type = id.replace(suffix, "") as DocumentType;
                 // get or create prototype of the specified type...
-                let target = existing || buildPrototype(type, id);
+                const target = existing || buildPrototype(type, id);
                 // ...and set it if not undefined (can be undefined only if TemplateMap does not contain
                 // an entry dedicated to the given DocumentType)
                 target && PrototypeMap.set(type, target);
@@ -287,17 +287,17 @@ export namespace Docs {
          */
         function buildPrototype(type: DocumentType, prototypeId: string): Opt<Doc> {
             // load template from type
-            let template = TemplateMap.get(type);
+            const template = TemplateMap.get(type);
             if (!template) {
                 return undefined;
             }
-            let layout = template.layout;
+            const layout = template.layout;
             // create title
-            let upper = suffix.toUpperCase();
-            let title = prototypeId.toUpperCase().replace(upper, `_${upper}`);
+            const upper = suffix.toUpperCase();
+            const title = prototypeId.toUpperCase().replace(upper, `_${upper}`);
             // synthesize the default options, the type and title from computed values and
             // whatever options pertain to this specific prototype
-            let options = { title, type, baseProto: true, ...defaultOptions, ...(template.options || {}) };
+            const options = { title, type, baseProto: true, ...defaultOptions, ...(template.options || {}) };
             options.layout = layout.view.LayoutString(layout.dataField);
             return Doc.assign(new Doc(prototypeId, true), { ...options });
         }
@@ -343,8 +343,8 @@ export namespace Docs {
 
             protoProps.isPrototype = true;
 
-            let dataDoc = MakeDataDelegate(proto, protoProps, data);
-            let viewDoc = Doc.MakeDelegate(dataDoc, delegId);
+            const dataDoc = MakeDataDelegate(proto, protoProps, data);
+            const viewDoc = Doc.MakeDelegate(dataDoc, delegId);
 
             AudioBox.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: viewDoc }, { doc: d }, "audio link", "link to audio: " + d.title));
 
@@ -370,16 +370,16 @@ export namespace Docs {
         }
 
         export function ImageDocument(url: string, options: DocumentOptions = {}) {
-            let imgField = new ImageField(new URL(url));
-            let inst = InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: path.basename(url), ...options });
+            const imgField = new ImageField(new URL(url));
+            const inst = InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: path.basename(url), ...options });
             let target = imgField.url.href;
             if (new RegExp(window.location.origin).test(target)) {
-                let extension = path.extname(target);
+                const extension = path.extname(target);
                 target = `${target.substring(0, target.length - extension.length)}_o${extension}`;
             }
             requestImageSize(target)
                 .then((size: any) => {
-                    let aspect = size.height / size.width;
+                    const aspect = size.height / size.width;
                     if (!inst.nativeWidth) {
                         inst.nativeWidth = size.width;
                     }
@@ -423,7 +423,7 @@ export namespace Docs {
         }
 
         export function InkDocument(color: string, tool: number, strokeWidth: number, points: { x: number, y: number }[], options: DocumentOptions = {}) {
-            let doc = InstanceFromProto(Prototypes.get(DocumentType.INK), new InkField(points), options);
+            const doc = InstanceFromProto(Prototypes.get(DocumentType.INK), new InkField(points), options);
             doc.color = color;
             doc.strokeWidth = strokeWidth;
             doc.tool = tool;
@@ -439,12 +439,12 @@ export namespace Docs {
         }
 
         export async function DBDocument(url: string, options: DocumentOptions = {}, columnOptions: DocumentOptions = {}) {
-            let schemaName = options.title ? options.title : "-no schema-";
-            let ctlog = await Gateway.Instance.GetSchema(url, schemaName);
+            const schemaName = options.title ? options.title : "-no schema-";
+            const ctlog = await Gateway.Instance.GetSchema(url, schemaName);
             if (ctlog && ctlog.schemas) {
-                let schema = ctlog.schemas[0];
-                let schemaDoc = Docs.Create.TreeDocument([], { ...options, nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: schema.displayName! });
-                let schemaDocuments = Cast(schemaDoc.data, listSpec(Doc), []);
+                const schema = ctlog.schemas[0];
+                const schemaDoc = Docs.Create.TreeDocument([], { ...options, nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: schema.displayName! });
+                const schemaDocuments = Cast(schemaDoc.data, listSpec(Doc), []);
                 if (!schemaDocuments) {
                     return;
                 }
@@ -455,8 +455,8 @@ export namespace Docs {
                         if (field instanceof Doc) {
                             docs.push(field);
                         } else {
-                            var atmod = new ColumnAttributeModel(attr);
-                            let histoOp = new HistogramOperation(schema.displayName!,
+                            const atmod = new ColumnAttributeModel(attr);
+                            const histoOp = new HistogramOperation(schema.displayName!,
                                 new AttributeTransformationModel(atmod, AggregateFunction.None),
                                 new AttributeTransformationModel(atmod, AggregateFunction.Count),
                                 new AttributeTransformationModel(atmod, AggregateFunction.Count));
@@ -523,7 +523,7 @@ export namespace Docs {
         }
 
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
-            let inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id);
+            const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id);
             Doc.GetProto(inst).data = new List<Doc>(documents);
             return inst;
         }
@@ -538,7 +538,7 @@ export namespace Docs {
         };
 
         export function StandardCollectionDockingDocument(configs: Array<DocConfig>, options: DocumentOptions, id?: string, type: string = "row") {
-            let layoutConfig = {
+            const layoutConfig = {
                 content: [
                     {
                         type: type,
@@ -603,7 +603,8 @@ export namespace Docs {
          * might involve arbitrary recursion (since toField might itself call convertObject)
          */
         const convertObject = (object: any, title?: string): Doc => {
-            let target = new Doc(), result: Opt<Field>;
+            const target = new Doc();
+            let result: Opt<Field>;
             Object.keys(object).map(key => (result = toField(object[key], key)) && (target[key] = result));
             title && !target.title && (target.title = title);
             return target;
@@ -617,7 +618,8 @@ export namespace Docs {
          * might involve arbitrary recursion (since toField might itself call convertList)
          */
         const convertList = (list: Array<any>): List<Field> => {
-            let target = new List(), result: Opt<Field>;
+            const target = new List();
+            let result: Opt<Field>;
             list.map(item => (result = toField(item)) && target.push(result));
             return target;
         };
@@ -658,11 +660,11 @@ export namespace Docs {
             }
             if (type.indexOf("html") !== -1) {
                 if (path.includes(window.location.hostname)) {
-                    let s = path.split('/');
-                    let id = s[s.length - 1];
+                    const s = path.split('/');
+                    const id = s[s.length - 1];
                     return DocServer.GetRefField(id).then(field => {
                         if (field instanceof Doc) {
-                            let alias = Doc.MakeAlias(field);
+                            const alias = Doc.MakeAlias(field);
                             alias.x = options.x || 0;
                             alias.y = options.y || 0;
                             alias.width = options.width || 300;
@@ -699,9 +701,9 @@ export namespace DocUtils {
                     DocListCastAsync(promoteDoc.links).then(links => {
                         links && links.map(async link => {
                             if (link) {
-                                let a1 = await Cast(link.anchor1, Doc);
+                                const a1 = await Cast(link.anchor1, Doc);
                                 if (a1 && Doc.AreProtosEqual(a1, promoteDoc)) link.anchor1 = copy;
-                                let a2 = await Cast(link.anchor2, Doc);
+                                const a2 = await Cast(link.anchor2, Doc);
                                 if (a2 && Doc.AreProtosEqual(a2, promoteDoc)) link.anchor2 = copy;
                                 LinkManager.Instance.deleteLink(link);
                                 LinkManager.Instance.addLink(link);
@@ -714,11 +716,11 @@ export namespace DocUtils {
     }
 
     export function MakeLink(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, title: string = "", description: string = "", id?: string) {
-        let sv = DocumentManager.Instance.getDocumentView(source.doc);
+        const sv = DocumentManager.Instance.getDocumentView(source.doc);
         if (sv && sv.props.ContainingCollectionDoc === target.doc) return;
         if (target.doc === CurrentUserUtils.UserDocument) return undefined;
 
-        let linkDocProto = new Doc(id, true);
+        const linkDocProto = new Doc(id, true);
         UndoManager.RunInBatch(() => {
             linkDocProto.type = DocumentType.LINK;
 
diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts
index e6f32272e..f3365e73d 100644
--- a/src/client/northstar/dash-fields/HistogramField.ts
+++ b/src/client/northstar/dash-fields/HistogramField.ts
@@ -10,7 +10,7 @@ import { Deserializable } from "../../util/SerializationHelper";
 import { Copy, ToScriptString } from "../../../new_fields/FieldSymbols";
 
 function serialize(field: HistogramField) {
-    let obj = OmitKeys(field, ['Links', 'BrushLinks', 'Result', 'BrushColors', 'FilterModels', 'FilterOperand']).omit;
+    const obj = OmitKeys(field, ['Links', 'BrushLinks', 'Result', 'BrushColors', 'FilterModels', 'FilterOperand']).omit;
     return obj;
 }
 
@@ -19,7 +19,7 @@ function deserialize(jp: any) {
     let Y: AttributeTransformationModel | undefined;
     let V: AttributeTransformationModel | undefined;
 
-    let schema = CurrentUserUtils.GetNorthstarSchema(jp.SchemaName);
+    const schema = CurrentUserUtils.GetNorthstarSchema(jp.SchemaName);
     if (schema) {
         CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => {
             if (attr.displayName === jp.X.AttributeModel.Attribute.DisplayName) {
@@ -52,8 +52,8 @@ export class HistogramField extends ObjectField {
     }
 
     [Copy]() {
-        let y = this.HistoOp;
-        let z = this.HistoOp.Copy;
+        // const y = this.HistoOp;
+        // const z = this.HistoOp.Copy;
         return new HistogramField(HistogramOperation.Duplicate(this.HistoOp));
     }
 
diff --git a/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts b/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts
index c579c8e5f..7bc097e1d 100644
--- a/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts
+++ b/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts
@@ -37,7 +37,7 @@ export class QuantitativeVisualBinRange extends VisualBinRange {
     }
 
     public GetBins(): number[] {
-        let bins = new Array<number>();
+        const bins = new Array<number>();
 
         for (let v: number = this.DataBinRange.minValue!; v < this.DataBinRange.maxValue!; v += this.DataBinRange.step!) {
             bins.push(v);
@@ -46,8 +46,8 @@ export class QuantitativeVisualBinRange extends VisualBinRange {
     }
 
     public static Initialize(dataMinValue: number, dataMaxValue: number, targetBinNumber: number, isIntegerRange: boolean): QuantitativeVisualBinRange {
-        let extent = QuantitativeVisualBinRange.getExtent(dataMinValue, dataMaxValue, targetBinNumber, isIntegerRange);
-        let dataBinRange = new QuantitativeBinRange();
+        const extent = QuantitativeVisualBinRange.getExtent(dataMinValue, dataMaxValue, targetBinNumber, isIntegerRange);
+        const dataBinRange = new QuantitativeBinRange();
         dataBinRange.minValue = extent[0];
         dataBinRange.maxValue = extent[1];
         dataBinRange.step = extent[2];
@@ -60,10 +60,10 @@ export class QuantitativeVisualBinRange extends VisualBinRange {
             // dataMin -= 0.1;
             dataMax += 0.1;
         }
-        let span = dataMax - dataMin;
+        const span = dataMax - dataMin;
 
         let step = Math.pow(10, Math.floor(Math.log10(span / m)));
-        let err = m / span * step;
+        const err = m / span * step;
 
         if (err <= .15) {
             step *= 10;
@@ -78,9 +78,9 @@ export class QuantitativeVisualBinRange extends VisualBinRange {
         if (isIntegerRange) {
             step = Math.ceil(step);
         }
-        let ret: number[] = new Array<number>(3);
-        let minDivStep = Math.floor(dataMin / step);
-        let maxDivStep = Math.floor(dataMax / step);
+        const ret: number[] = new Array<number>(3);
+        const minDivStep = Math.floor(dataMin / step);
+        const maxDivStep = Math.floor(dataMax / step);
         ret[0] = minDivStep * step; // Math.floor(Math.Round(dataMin, 8)/step)*step;
         ret[1] = maxDivStep * step + step; // Math.floor(Math.Round(dataMax, 8)/step)*step + step;
         ret[2] = step;
diff --git a/src/client/northstar/operations/BaseOperation.ts b/src/client/northstar/operations/BaseOperation.ts
index 0d1361ebf..013f2244e 100644
--- a/src/client/northstar/operations/BaseOperation.ts
+++ b/src/client/northstar/operations/BaseOperation.ts
@@ -44,12 +44,12 @@ export abstract class BaseOperation {
                 }
             }
 
-            let operationParameters = this.CreateOperationParameters();
+            const operationParameters = this.CreateOperationParameters();
             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();
+            const salt = Math.random().toString();
             this.RequestSalt = salt;
 
             if (!operationParameters) {
@@ -59,27 +59,27 @@ export abstract class BaseOperation {
 
             this.ComputationStarted = true;
             //let start = performance.now();
-            let promise = Gateway.Instance.StartOperation(operationParameters.toJSON());
+            const promise = Gateway.Instance.StartOperation(operationParameters.toJSON());
             promise.catch(err => {
                 action(() => {
                     this.Error = err;
                     console.error(err);
                 });
             });
-            let operationReference = await promise;
+            const operationReference = await promise;
 
 
             if (operationReference) {
                 this.OperationReference = operationReference;
 
-                let resultParameters = new ResultParameters();
+                const resultParameters = new ResultParameters();
                 resultParameters.operationReference = operationReference;
 
-                let pollPromise = new PollPromise(salt, operationReference);
+                const pollPromise = new PollPromise(salt, operationReference);
                 BaseOperation._currentOperations.set(this.Id, pollPromise);
 
                 pollPromise.Start(async () => {
-                    let result = await Gateway.Instance.GetResult(resultParameters.toJSON());
+                    const result = await Gateway.Instance.GetResult(resultParameters.toJSON());
                     if (result instanceof ErrorResult) {
                         throw new Error((result).message);
                     }
diff --git a/src/client/northstar/utils/MathUtil.ts b/src/client/northstar/utils/MathUtil.ts
index 4b44f40c3..5def5e704 100644
--- a/src/client/northstar/utils/MathUtil.ts
+++ b/src/client/northstar/utils/MathUtil.ts
@@ -92,37 +92,37 @@ 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
+        const 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
         // 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
         // We clamp t from [0,1] to handle points outside the segment vw.
-        var dot = MathUtil.Dot(
+        const dot = MathUtil.Dot(
             MathUtil.SubtractPoint(p, v),
             MathUtil.SubtractPoint(w, v)) / l2;
-        var t = Math.max(0, Math.min(1, dot));
+        const t = Math.max(0, Math.min(1, dot));
         // Projection falls on the segment
-        var projection = MathUtil.AddPoint(v,
+        const projection = MathUtil.AddPoint(v,
             MathUtil.MultiplyConstant(
                 MathUtil.SubtractPoint(w, v), t));
         return MathUtil.Dist(p, projection);
     }
 
     public static LineSegmentIntersection(ps1: PIXIPoint, pe1: PIXIPoint, ps2: PIXIPoint, pe2: PIXIPoint): PIXIPoint | undefined {
-        var a1 = pe1.y - ps1.y;
-        var b1 = ps1.x - pe1.x;
+        const a1 = pe1.y - ps1.y;
+        const b1 = ps1.x - pe1.x;
 
-        var a2 = pe2.y - ps2.y;
-        var b2 = ps2.x - pe2.x;
+        const a2 = pe2.y - ps2.y;
+        const b2 = ps2.x - pe2.x;
 
-        var delta = a1 * b2 - a2 * b1;
+        const delta = a1 * b2 - a2 * b1;
         if (delta === 0) {
             return undefined;
         }
-        var c2 = a2 * ps2.x + b2 * ps2.y;
-        var c1 = a1 * ps1.x + b1 * ps1.y;
-        var invdelta = 1 / delta;
+        const c2 = a2 * ps2.x + b2 * ps2.y;
+        const c1 = a1 * ps1.x + b1 * ps1.y;
+        const invdelta = 1 / delta;
         return new PIXIPoint((b2 * c1 - b1 * c2) * invdelta, (a1 * c2 - a2 * c1) * invdelta);
     }
 
@@ -144,13 +144,13 @@ export class MathUtil {
     }
 
     public static LinePIXIRectangleIntersection(lineFrom: PIXIPoint, lineTo: PIXIPoint, rect: PIXIRectangle): Array<PIXIPoint> {
-        var r1 = new PIXIPoint(rect.left, rect.top);
-        var r2 = new PIXIPoint(rect.right, rect.top);
-        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 inter = this.LineSegmentIntersection(lineFrom, lineTo, r1, r2);
+        const r1 = new PIXIPoint(rect.left, rect.top);
+        const r2 = new PIXIPoint(rect.right, rect.top);
+        const r3 = new PIXIPoint(rect.right, rect.bottom);
+        const r4 = new PIXIPoint(rect.left, rect.bottom);
+        const ret = new Array<PIXIPoint>();
+        const dist = this.Dist(lineFrom, lineTo);
+        let inter = this.LineSegmentIntersection(lineFrom, lineTo, r1, r2);
         if (inter && this.PointInPIXIRectangle(inter, rect) &&
             this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) {
             ret.push(inter);
@@ -190,7 +190,7 @@ export class MathUtil {
     }
 
     public static Normalize(p1: PIXIPoint) {
-        var d = this.Length(p1);
+        const d = this.Length(p1);
         return new PIXIPoint(p1.x / d, p1.y / d);
     }
 
@@ -236,8 +236,8 @@ export class MathUtil {
     }
 
     public static Combinations<T>(chars: T[]) {
-        let result = new Array<T>();
-        let f = (prefix: any, chars: any) => {
+        const result = new Array<T>();
+        const f = (prefix: any, chars: any) => {
             for (let i = 0; i < chars.length; i++) {
                 result.push(prefix.concat(chars[i]));
                 f(prefix.concat(chars[i]), chars.slice(i + 1));
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 6bbd3d0ed..3d8f2d234 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -11,7 +11,6 @@ import { Cast, CastCtor } from "../../new_fields/Types";
 import { listSpec } from "../../new_fields/Schema";
 import { AudioField, ImageField } from "../../new_fields/URLField";
 import { HistogramField } from "../northstar/dash-fields/HistogramField";
-import { MainView } from "../views/MainView";
 import { Utils } from "../../Utils";
 import { RichTextField } from "../../new_fields/RichTextField";
 import { DictationOverlay } from "../views/DictationOverlay";
@@ -48,7 +47,7 @@ export namespace DictationManager {
 
         export const Infringed = "unable to process: dictation manager still involved in previous session";
         const browser = (() => {
-            let identifier = navigator.userAgent.toLowerCase();
+            const identifier = navigator.userAgent.toLowerCase();
             if (identifier.indexOf("safari") >= 0) {
                 return "Safari";
             }
@@ -90,7 +89,7 @@ export namespace DictationManager {
         export const listen = async (options?: Partial<ListeningOptions>) => {
             let results: string | undefined;
 
-            let overlay = options !== undefined && options.useOverlay;
+            const overlay = options !== undefined && options.useOverlay;
             if (overlay) {
                 DictationOverlay.Instance.dictationOverlayVisible = true;
                 DictationOverlay.Instance.isListening = { interim: false };
@@ -102,7 +101,7 @@ export namespace DictationManager {
                     Utils.CopyText(results);
                     if (overlay) {
                         DictationOverlay.Instance.isListening = false;
-                        let execute = options && options.tryExecute;
+                        const execute = options && options.tryExecute;
                         DictationOverlay.Instance.dictatedPhrase = execute ? results.toLowerCase() : results;
                         DictationOverlay.Instance.dictationSuccess = execute ? await DictationManager.Commands.execute(results) : true;
                     }
@@ -131,12 +130,12 @@ export namespace DictationManager {
             }
             isListening = true;
 
-            let handler = options ? options.interimHandler : undefined;
-            let continuous = options ? options.continuous : undefined;
-            let indefinite = continuous && continuous.indefinite;
-            let language = options ? options.language : undefined;
-            let intra = options && options.delimiters ? options.delimiters.intra : undefined;
-            let inter = options && options.delimiters ? options.delimiters.inter : undefined;
+            const handler = options ? options.interimHandler : undefined;
+            const continuous = options ? options.continuous : undefined;
+            const indefinite = continuous && continuous.indefinite;
+            const language = options ? options.language : undefined;
+            const intra = options && options.delimiters ? options.delimiters.intra : undefined;
+            const inter = options && options.delimiters ? options.delimiters.inter : undefined;
 
             recognizer.onstart = () => console.log("initiating speech recognition session...");
             recognizer.interimResults = handler !== undefined;
@@ -177,7 +176,7 @@ export namespace DictationManager {
                     recognizer.start();
                 };
 
-                let complete = () => {
+                const complete = () => {
                     if (indefinite) {
                         current && sessionResults.push(current);
                         sessionResults.length && resolve(sessionResults.join(inter || interSession));
@@ -213,8 +212,8 @@ export namespace DictationManager {
         };
 
         const synthesize = (e: SpeechRecognitionEvent, delimiter?: string) => {
-            let results = e.results;
-            let transcripts: string[] = [];
+            const results = e.results;
+            const transcripts: string[] = [];
             for (let i = 0; i < results.length; i++) {
                 transcripts.push(results.item(i).item(0).transcript.trim());
             }
@@ -238,18 +237,18 @@ export namespace DictationManager {
 
         export const execute = async (phrase: string) => {
             return UndoManager.RunInBatch(async () => {
-                let targets = SelectionManager.SelectedDocuments();
+                const targets = SelectionManager.SelectedDocuments();
                 if (!targets || !targets.length) {
                     return;
                 }
 
                 phrase = phrase.toLowerCase();
-                let entry = Independent.get(phrase);
+                const entry = Independent.get(phrase);
 
                 if (entry) {
                     let success = false;
-                    let restrictTo = entry.restrictTo;
-                    for (let target of targets) {
+                    const restrictTo = entry.restrictTo;
+                    for (const target of targets) {
                         if (!restrictTo || validate(target, restrictTo)) {
                             await entry.action(target);
                             success = true;
@@ -258,14 +257,14 @@ export namespace DictationManager {
                     return success;
                 }
 
-                for (let entry of Dependent) {
-                    let regex = entry.expression;
-                    let matches = regex.exec(phrase);
+                for (const entry of Dependent) {
+                    const regex = entry.expression;
+                    const matches = regex.exec(phrase);
                     regex.lastIndex = 0;
                     if (matches !== null) {
                         let success = false;
-                        let restrictTo = entry.restrictTo;
-                        for (let target of targets) {
+                        const restrictTo = entry.restrictTo;
+                        for (const target of targets) {
                             if (!restrictTo || validate(target, restrictTo)) {
                                 await entry.action(target, matches);
                                 success = true;
@@ -289,7 +288,7 @@ export namespace DictationManager {
         ]);
 
         const tryCast = (view: DocumentView, type: DocumentType) => {
-            let ctor = ConstructorMap.get(type);
+            const ctor = ConstructorMap.get(type);
             if (!ctor) {
                 return false;
             }
@@ -297,7 +296,7 @@ export namespace DictationManager {
         };
 
         const validate = (target: DocumentView, types: DocumentType[]) => {
-            for (let type of types) {
+            for (const type of types) {
                 if (tryCast(target, type)) {
                     return true;
                 }
@@ -306,11 +305,11 @@ export namespace DictationManager {
         };
 
         const interpretNumber = (number: string) => {
-            let initial = parseInt(number);
+            const initial = parseInt(number);
             if (!isNaN(initial)) {
                 return initial;
             }
-            let converted = interpreter.wordsToNumbers(number, { fuzzy: true });
+            const converted = interpreter.wordsToNumbers(number, { fuzzy: true });
             if (converted === null) {
                 return NaN;
             }
@@ -326,20 +325,20 @@ export namespace DictationManager {
 
             ["open fields", {
                 action: (target: DocumentView) => {
-                    let kvp = Docs.Create.KVPDocument(target.props.Document, { width: 300, height: 300 });
+                    const kvp = Docs.Create.KVPDocument(target.props.Document, { width: 300, height: 300 });
                     target.props.addDocTab(kvp, target.props.DataDoc, "onRight");
                 }
             }],
 
             ["new outline", {
                 action: (target: DocumentView) => {
-                    let newBox = Docs.Create.TextDocument({ width: 400, height: 200, title: "My Outline" });
+                    const newBox = Docs.Create.TextDocument({ width: 400, height: 200, title: "My Outline" });
                     newBox.autoHeight = true;
-                    let proto = newBox.proto!;
-                    let prompt = "Press alt + r to start dictating here...";
-                    let head = 3;
-                    let anchor = head + prompt.length;
-                    let proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`;
+                    const proto = newBox.proto!;
+                    const prompt = "Press alt + r to start dictating here...";
+                    const head = 3;
+                    const anchor = head + prompt.length;
+                    const proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`;
                     proto.data = new RichTextField(proseMirrorState);
                     proto.backgroundColor = "#eeffff";
                     target.props.addDocTab(newBox, proto, "onRight");
@@ -353,10 +352,10 @@ export namespace DictationManager {
             {
                 expression: /create (\w+) documents of type (image|nested collection)/g,
                 action: (target: DocumentView, matches: RegExpExecArray) => {
-                    let count = interpretNumber(matches[1]);
-                    let what = matches[2];
-                    let dataDoc = Doc.GetProto(target.props.Document);
-                    let fieldKey = "data";
+                    const count = interpretNumber(matches[1]);
+                    const what = matches[2];
+                    const dataDoc = Doc.GetProto(target.props.Document);
+                    const fieldKey = "data";
                     if (isNaN(count)) {
                         return;
                     }
@@ -379,7 +378,7 @@ export namespace DictationManager {
             {
                 expression: /view as (freeform|stacking|masonry|schema|tree)/g,
                 action: (target: DocumentView, matches: RegExpExecArray) => {
-                    let mode = CollectionViewType.valueOf(matches[1]);
+                    const mode = CollectionViewType.valueOf(matches[1]);
                     mode && (target.props.Document.viewType = mode);
                 },
                 restrictTo: [DocumentType.COL]
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 346e88f40..d491cd1b1 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -33,7 +33,7 @@ export class DocumentManager {
 
     //gets all views
     public getDocumentViewsById(id: string) {
-        let toReturn: DocumentView[] = [];
+        const toReturn: DocumentView[] = [];
         DocumentManager.Instance.DocumentViews.map(view => {
             if (view.props.Document[Id] === id) {
                 toReturn.push(view);
@@ -41,7 +41,7 @@ export class DocumentManager {
         });
         if (toReturn.length === 0) {
             DocumentManager.Instance.DocumentViews.map(view => {
-                let doc = view.props.Document.proto;
+                const doc = view.props.Document.proto;
                 if (doc && doc[Id] && doc[Id] === id) {
                     toReturn.push(view);
                 }
@@ -57,9 +57,9 @@ export class DocumentManager {
     public getDocumentViewById(id: string, preferredCollection?: CollectionView): DocumentView | undefined {
 
         let toReturn: DocumentView | undefined;
-        let passes = preferredCollection ? [preferredCollection, undefined] : [undefined];
+        const passes = preferredCollection ? [preferredCollection, undefined] : [undefined];
 
-        for (let pass of passes) {
+        for (const pass of passes) {
             DocumentManager.Instance.DocumentViews.map(view => {
                 if (view.props.Document[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) {
                     toReturn = view;
@@ -68,7 +68,7 @@ export class DocumentManager {
             });
             if (!toReturn) {
                 DocumentManager.Instance.DocumentViews.map(view => {
-                    let doc = view.props.Document.proto;
+                    const doc = view.props.Document.proto;
                     if (doc && doc[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) {
                         toReturn = view;
                     }
@@ -90,7 +90,7 @@ export class DocumentManager {
         return views.length ? views[0] : undefined;
     }
     public getDocumentViews(toFind: Doc): DocumentView[] {
-        let toReturn: DocumentView[] = [];
+        const toReturn: DocumentView[] = [];
 
         DocumentManager.Instance.DocumentViews.map(view =>
             Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view));
@@ -100,17 +100,17 @@ export class DocumentManager {
 
     @computed
     public get LinkedDocumentViews() {
-        let pairs = DocumentManager.Instance.DocumentViews.filter(dv =>
+        const pairs = DocumentManager.Instance.DocumentViews.filter(dv =>
             (dv.isSelected() || Doc.IsBrushed(dv.props.Document)) // draw links from DocumentViews that are selected or brushed OR
             || DocumentManager.Instance.DocumentViews.some(dv2 => {                                                  // Documentviews which
-                let rest = DocListCast(dv2.props.Document.links).some(l => Doc.AreProtosEqual(l, dv.props.Document));// are link doc anchors 
-                let init = (dv2.isSelected() || Doc.IsBrushed(dv2.props.Document)) && dv2.Document.type !== DocumentType.AUDIO;  // on a view that is selected or brushed
+                const rest = DocListCast(dv2.props.Document.links).some(l => Doc.AreProtosEqual(l, dv.props.Document));// are link doc anchors 
+                const init = (dv2.isSelected() || Doc.IsBrushed(dv2.props.Document)) && dv2.Document.type !== DocumentType.AUDIO;  // on a view that is selected or brushed
                 return init && rest;
             })
         ).reduce((pairs, dv) => {
-            let linksList = LinkManager.Instance.getAllRelatedLinks(dv.props.Document);
+            const linksList = LinkManager.Instance.getAllRelatedLinks(dv.props.Document);
             pairs.push(...linksList.reduce((pairs, link) => {
-                let linkToDoc = link && LinkManager.Instance.getOppositeAnchor(link, dv.props.Document);
+                const linkToDoc = link && LinkManager.Instance.getOppositeAnchor(link, dv.props.Document);
                 linkToDoc && DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
                     if (dv.props.Document.type !== DocumentType.LINK || dv.props.layoutKey !== docView1.props.layoutKey) {
                         pairs.push({ a: dv, b: docView1, l: link });
@@ -125,7 +125,7 @@ export class DocumentManager {
     }
 
     public jumpToDocument = async (targetDoc: Doc, willZoom: boolean, dockFunc?: (doc: Doc) => void, docContext?: Doc, linkId?: string, closeContextIfNotFound: boolean = false): Promise<void> => {
-        let highlight = () => {
+        const highlight = () => {
             const finalDocView = DocumentManager.Instance.getFirstDocumentView(targetDoc);
             finalDocView && (finalDocView.Document.scrollToLinkID = linkId);
             finalDocView && Doc.linkFollowHighlight(finalDocView.props.Document);
@@ -199,12 +199,12 @@ export class DocumentManager {
 
     @action
     zoomIntoScale = (docDelegate: Doc, scale: number) => {
-        let docView = DocumentManager.Instance.getDocumentView(Doc.GetProto(docDelegate));
+        const docView = DocumentManager.Instance.getDocumentView(Doc.GetProto(docDelegate));
         docView && docView.props.zoomToScale(scale);
     }
 
     getScaleOfDocView = (docDelegate: Doc) => {
-        let doc = Doc.GetProto(docDelegate);
+        const doc = Doc.GetProto(docDelegate);
 
         const docView = DocumentManager.Instance.getDocumentView(doc);
         if (docView) {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index bbc29585c..b681387d1 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,7 +1,6 @@
 import { action, runInAction } from "mobx";
 import { Doc, Field } from "../../new_fields/Doc";
-import { Cast, StrCast, ScriptCast } from "../../new_fields/Types";
-import { URLField } from "../../new_fields/URLField";
+import { Cast, ScriptCast } from "../../new_fields/Types";
 import { emptyFunction } from "../../Utils";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import * as globalCssVariables from "../views/globalCssVariables.scss";
@@ -27,14 +26,14 @@ export function SetupDrag(
     dontHideOnDrop?: boolean,
     dragStarted?: () => void
 ) {
-    let onRowMove = async (e: PointerEvent) => {
+    const onRowMove = async (e: PointerEvent) => {
         e.stopPropagation();
         e.preventDefault();
 
         document.removeEventListener("pointermove", onRowMove);
         document.removeEventListener('pointerup', onRowUp);
-        let doc = await docFunc();
-        var dragData = new DragManager.DocumentDragData([doc]);
+        const doc = await docFunc();
+        const dragData = new DragManager.DocumentDragData([doc]);
         dragData.dropAction = dropAction;
         dragData.moveDocument = moveFunc;
         dragData.options = options;
@@ -42,11 +41,11 @@ export function SetupDrag(
         DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y);
         dragStarted && dragStarted();
     };
-    let onRowUp = (): void => {
+    const onRowUp = (): void => {
         document.removeEventListener("pointermove", onRowMove);
         document.removeEventListener('pointerup', onRowUp);
     };
-    let onItemDown = async (e: React.PointerEvent) => {
+    const onItemDown = async (e: React.PointerEvent) => {
         if (e.button === 0) {
             e.stopPropagation();
             if (e.shiftKey && CollectionDockingView.Instance) {
@@ -74,11 +73,11 @@ function moveLinkedDocument(doc: Doc, targetCollection: Doc, addDocument: (doc:
 }
 
 export async function DragLinkAsDocument(dragEle: HTMLElement, x: number, y: number, linkDoc: Doc, sourceDoc: Doc) {
-    let draggeddoc = LinkManager.Instance.getOppositeAnchor(linkDoc, sourceDoc);
+    const draggeddoc = LinkManager.Instance.getOppositeAnchor(linkDoc, sourceDoc);
     if (draggeddoc) {
-        let moddrag = await Cast(draggeddoc.annotationOn, Doc);
-        let dragdocs = moddrag ? [moddrag] : [draggeddoc];
-        let dragData = new DragManager.DocumentDragData(dragdocs);
+        const moddrag = await Cast(draggeddoc.annotationOn, Doc);
+        const dragdocs = moddrag ? [moddrag] : [draggeddoc];
+        const dragData = new DragManager.DocumentDragData(dragdocs);
         dragData.moveDocument = moveLinkedDocument;
         DragManager.StartLinkedDocumentDrag([dragEle], dragData, x, y, {
             handlers: {
@@ -90,26 +89,26 @@ export async function DragLinkAsDocument(dragEle: HTMLElement, x: number, y: num
 }
 
 export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Doc, singleLink?: Doc) {
-    let srcTarg = sourceDoc.proto;
+    const srcTarg = sourceDoc.proto;
     let draggedDocs: Doc[] = [];
 
     if (srcTarg) {
-        let linkDocs = singleLink ? [singleLink] : LinkManager.Instance.getAllRelatedLinks(srcTarg);
+        const linkDocs = singleLink ? [singleLink] : LinkManager.Instance.getAllRelatedLinks(srcTarg);
         if (linkDocs) {
             draggedDocs = linkDocs.map(link => {
-                let opp = LinkManager.Instance.getOppositeAnchor(link, sourceDoc);
+                const opp = LinkManager.Instance.getOppositeAnchor(link, sourceDoc);
                 if (opp) return opp;
             }) as Doc[];
         }
     }
     if (draggedDocs.length) {
-        let moddrag: Doc[] = [];
+        const moddrag: Doc[] = [];
         for (const draggedDoc of draggedDocs) {
-            let doc = await Cast(draggedDoc.annotationOn, Doc);
+            const doc = await Cast(draggedDoc.annotationOn, Doc);
             if (doc) moddrag.push(doc);
         }
-        let dragdocs = moddrag.length ? moddrag : draggedDocs;
-        let dragData = new DragManager.DocumentDragData(dragdocs);
+        const dragdocs = moddrag.length ? moddrag : draggedDocs;
+        const dragData = new DragManager.DocumentDragData(dragdocs);
         dragData.moveDocument = moveLinkedDocument;
         DragManager.StartLinkedDocumentDrag([dragEle], dragData, x, y, {
             handlers: {
@@ -254,11 +253,11 @@ export namespace DragManager {
     }
 
     export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) {
-        let dragData = new DragManager.DocumentDragData([]);
+        const dragData = new DragManager.DocumentDragData([]);
         runInAction(() => StartDragFunctions.map(func => func()));
         StartDrag(eles, dragData, downX, downY, options, options && options.finishDrag ? options.finishDrag :
             (dropData: { [id: string]: any }) => {
-                let bd = Docs.Create.ButtonDocument({ width: 150, height: 50, title: title });
+                const bd = Docs.Create.ButtonDocument({ width: 150, height: 50, title: title });
                 bd.onClick = ScriptField.MakeScript(script);
                 params.map(p => Object.keys(vars).indexOf(p) !== -1 && (Doc.GetProto(bd)[p] = new PrefetchProxy(vars[p] as Doc)));
                 initialize && initialize(bd);
@@ -273,11 +272,11 @@ export namespace DragManager {
         runInAction(() => StartDragFunctions.map(func => func()));
         StartDrag(eles, dragData, downX, downY, options,
             (dropData: { [id: string]: any }) => {
-                let droppedDocuments: Doc[] = dragData.draggedDocuments.reduce((droppedDocs: Doc[], d) => {
-                    let dvs = DocumentManager.Instance.getDocumentViews(d);
+                const droppedDocuments: Doc[] = dragData.draggedDocuments.reduce((droppedDocs: Doc[], d) => {
+                    const dvs = DocumentManager.Instance.getDocumentViews(d);
                     if (dvs.length) {
-                        let containingView = SelectionManager.SelectedDocuments()[0] ? SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView : undefined;
-                        let inContext = dvs.filter(dv => dv.props.ContainingCollectionView === containingView);
+                        const containingView = SelectionManager.SelectedDocuments()[0] ? SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView : undefined;
+                        const inContext = dvs.filter(dv => dv.props.ContainingCollectionView === containingView);
                         if (inContext.length) {
                             inContext.forEach(dv => droppedDocs.push(dv.props.Document));
                         } else {
@@ -336,26 +335,26 @@ export namespace DragManager {
             DragManager.Root().appendChild(dragDiv);
         }
         SelectionManager.SetIsDragging(true);
-        let scaleXs: number[] = [];
-        let scaleYs: number[] = [];
-        let xs: number[] = [];
-        let ys: number[] = [];
+        const scaleXs: number[] = [];
+        const scaleYs: number[] = [];
+        const xs: number[] = [];
+        const ys: number[] = [];
 
         const docs = dragData instanceof DocumentDragData ? dragData.draggedDocuments :
             dragData instanceof AnnotationDragData ? [dragData.dragDocument] : [];
-        let dragElements = eles.map(ele => {
+        const dragElements = eles.map(ele => {
             const w = ele.offsetWidth,
                 h = ele.offsetHeight;
             const rect = ele.getBoundingClientRect();
             const scaleX = rect.width / w,
                 scaleY = rect.height / h;
-            let x = rect.left,
+            const x = rect.left,
                 y = rect.top;
             xs.push(x);
             ys.push(y);
             scaleXs.push(scaleX);
             scaleYs.push(scaleY);
-            let dragElement = ele.cloneNode(true) as HTMLElement;
+            const dragElement = ele.cloneNode(true) as HTMLElement;
             dragElement.style.opacity = "0.7";
             dragElement.style.borderRadius = getComputedStyle(ele).borderRadius;
             dragElement.style.position = "absolute";
@@ -372,25 +371,25 @@ export namespace DragManager {
             dragElement.style.height = `${rect.height / scaleY}px`;
 
             if (docs.length) {
-                var pdfBox = dragElement.getElementsByTagName("canvas");
-                var pdfBoxSrc = ele.getElementsByTagName("canvas");
+                const pdfBox = dragElement.getElementsByTagName("canvas");
+                const pdfBoxSrc = ele.getElementsByTagName("canvas");
                 Array.from(pdfBox).map((pb, i) => pb.getContext('2d')!.drawImage(pdfBoxSrc[i], 0, 0));
-                var pdfView = dragElement.getElementsByClassName("pdfViewer-viewer");
-                var pdfViewSrc = ele.getElementsByClassName("pdfViewer-viewer");
-                let tops = Array.from(pdfViewSrc).map(p => p.scrollTop);
-                let oldopacity = dragElement.style.opacity;
+                const pdfView = dragElement.getElementsByClassName("pdfViewer-viewer");
+                const pdfViewSrc = ele.getElementsByClassName("pdfViewer-viewer");
+                const tops = Array.from(pdfViewSrc).map(p => p.scrollTop);
+                const oldopacity = dragElement.style.opacity;
                 dragElement.style.opacity = "0";
                 setTimeout(() => {
                     dragElement.style.opacity = oldopacity;
                     Array.from(pdfView).map((v, i) => v.scrollTo({ top: tops[i] }));
                 }, 0);
             }
-            let set = dragElement.getElementsByTagName('*');
+            const set = dragElement.getElementsByTagName('*');
             if (dragElement.hasAttribute("style")) (dragElement as any).style.pointerEvents = "none";
             // tslint:disable-next-line: prefer-for-of
             for (let i = 0; i < set.length; i++) {
                 if (set[i].hasAttribute("style")) {
-                    let s = set[i];
+                    const s = set[i];
                     (s as any).style.pointerEvents = "none";
                 }
             }
@@ -429,8 +428,8 @@ export namespace DragManager {
                 }, dragData.droppedDocuments);
             }
             //TODO: Why can't we use e.movementX and e.movementY?
-            let moveX = e.pageX - lastX;
-            let moveY = e.pageY - lastY;
+            const moveX = e.pageX - lastX;
+            const moveY = e.pageY - lastY;
             lastX = e.pageX;
             lastY = e.pageY;
             dragElements.map((dragElement, i) => (dragElement.style.transform =
@@ -438,11 +437,11 @@ export namespace DragManager {
             );
         };
 
-        let hideDragShowOriginalElements = () => {
+        const hideDragShowOriginalElements = () => {
             dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
             eles.map(ele => ele.hidden = false);
         };
-        let endDrag = () => {
+        const endDrag = () => {
             document.removeEventListener("pointermove", moveHandler, true);
             document.removeEventListener("pointerup", upHandler);
             if (options) {
@@ -466,17 +465,17 @@ export namespace DragManager {
     }
 
     function dispatchDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (dragData: { [index: string]: any }) => void) {
-        let removed = dragData.dontHideOnDrop ? [] : dragEles.map(dragEle => {
+        const removed = dragData.dontHideOnDrop ? [] : dragEles.map(dragEle => {
             // let parent = dragEle.parentElement;
             // if (parent) parent.removeChild(dragEle);
-            let ret = [dragEle, dragEle.style.width, dragEle.style.height];
+            const 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] as HTMLElement;
+            const dragEle = r[0] as HTMLElement;
             dragEle.style.width = r[1] as string;
             dragEle.style.height = r[2] as string;
             // let parent = r[1];
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 6b53333d7..b2c720d5d 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -9,10 +9,10 @@ import { ScriptField } from "../../new_fields/ScriptField";
 
 
 function makeTemplate(doc: Doc): boolean {
-    let layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc;
-    let layout = StrCast(layoutDoc.layout).match(/fieldKey={"[^"]*"}/)![0];
-    let fieldKey = layout.replace('fieldKey={"', "").replace(/"}$/, "");
-    let docs = DocListCast(layoutDoc[fieldKey]);
+    const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc;
+    const layout = StrCast(layoutDoc.layout).match(/fieldKey={"[^"]*"}/)![0];
+    const fieldKey = layout.replace('fieldKey={"', "").replace(/"}$/, "");
+    const docs = DocListCast(layoutDoc[fieldKey]);
     let any = false;
     docs.map(d => {
         if (!StrCast(d.title).startsWith("-")) {
@@ -28,7 +28,7 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
     data && data.draggedDocuments.map((doc, i) => {
         let dbox = doc;
         if (!doc.onDragStart && !doc.onClick && doc.viewType !== CollectionViewType.Linear) {
-            let layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc;
+            const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc;
             if (layoutDoc.type === DocumentType.COL) {
                 layoutDoc.isTemplateDoc = makeTemplate(layoutDoc);
             } else {
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 1c51236cb..545e8acb4 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -1,4 +1,4 @@
-import { Doc, Opt, Field } from "../../new_fields/Doc";
+import { Doc } from "../../new_fields/Doc";
 import { DocServer } from "../DocServer";
 import { MainView } from "../views/MainView";
 import * as qs from 'query-string';
@@ -53,7 +53,7 @@ export namespace HistoryUtil {
     }
 
     export function getState(): ParsedUrl {
-        let state = copyState(history.state);
+        const state = copyState(history.state);
         state.initializers = state.initializers || {};
         return state;
     }
@@ -160,7 +160,7 @@ export namespace HistoryUtil {
         const pathname = location.pathname.substring(1);
         const search = location.search;
         const opts = search.length ? qs.parse(search, { sort: false }) : {};
-        let pathnameSplit = pathname.split("/");
+        const pathnameSplit = pathname.split("/");
 
         const type = pathnameSplit[0];
 
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 104d9e099..e6a215b2c 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx	
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -1,7 +1,7 @@
 import "fs";
 import React = require("react");
 import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc";
-import { action, observable, autorun, runInAction, computed, reaction, IReactionDisposer } from "mobx";
+import { action, observable, runInAction, computed, reaction, IReactionDisposer } from "mobx";
 import { FieldViewProps, FieldView } from "../../views/nodes/FieldView";
 import Measure, { ContentRect } from "react-measure";
 import { library } from '@fortawesome/fontawesome-svg-core';
@@ -48,7 +48,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
     constructor(props: FieldViewProps) {
         super(props);
         library.add(faTag, faPlus);
-        let doc = this.props.Document;
+        const doc = this.props.Document;
         this.editingMetadata = this.editingMetadata || false;
         this.persistent = this.persistent || false;
         !Cast(doc.data, listSpec(Doc)) && (doc.data = new List<Doc>());
@@ -78,16 +78,16 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
             this.phase = "Initializing download...";
         });
 
-        let docs: Doc[] = [];
+        const docs: Doc[] = [];
 
-        let files = e.target.files;
+        const files = e.target.files;
         if (!files || files.length === 0) return;
 
-        let directory = (files.item(0) as any).webkitRelativePath.split("/", 1)[0];
+        const directory = (files.item(0) as any).webkitRelativePath.split("/", 1)[0];
 
-        let validated: File[] = [];
+        const validated: File[] = [];
         for (let i = 0; i < files.length; i++) {
-            let file = files.item(i);
+            const file = files.item(i);
             if (file && !unsupported.includes(file.type)) {
                 const ext = path.extname(file.name).toLowerCase();
                 if (AcceptibleMedia.imageFormats.includes(ext)) {
@@ -101,8 +101,8 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
             this.completed = 0;
         });
 
-        let sizes: number[] = [];
-        let modifiedDates: number[] = [];
+        const sizes: number[] = [];
+        const modifiedDates: number[] = [];
 
         runInAction(() => this.phase = `Internal: uploading ${this.quota - this.completed} files to Dash...`);
 
@@ -136,26 +136,26 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
         }));
 
         for (let i = 0; i < docs.length; i++) {
-            let doc = docs[i];
+            const doc = docs[i];
             doc.size = sizes[i];
             doc.modified = modifiedDates[i];
             this.entries.forEach(entry => {
-                let target = entry.onDataDoc ? Doc.GetProto(doc) : doc;
+                const target = entry.onDataDoc ? Doc.GetProto(doc) : doc;
                 target[entry.key] = entry.value;
             });
         }
 
-        let doc = this.props.Document;
-        let height: number = NumCast(doc.height) || 0;
-        let offset: number = this.persistent ? (height === 0 ? 0 : height + 30) : 0;
-        let options: DocumentOptions = {
+        const doc = this.props.Document;
+        const height: number = NumCast(doc.height) || 0;
+        const offset: number = this.persistent ? (height === 0 ? 0 : height + 30) : 0;
+        const options: DocumentOptions = {
             title: `Import of ${directory}`,
             width: 1105,
             height: 500,
             x: NumCast(doc.x),
             y: NumCast(doc.y) + offset
         };
-        let parent = this.props.ContainingCollectionView;
+        const parent = this.props.ContainingCollectionView;
         if (parent) {
             let importContainer: Doc;
             if (docs.length < 50) {
@@ -194,18 +194,18 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
 
     @action
     preserveCentering = (rect: ContentRect) => {
-        let bounds = rect.offset!;
+        const bounds = rect.offset!;
         if (bounds.width === 0 || bounds.height === 0) {
             return;
         }
-        let offset = this.dimensions / 2;
+        const offset = this.dimensions / 2;
         this.left = bounds.width / 2 - offset;
         this.top = bounds.height / 2 - offset;
     }
 
     @action
     addMetadataEntry = async () => {
-        let entryDoc = new Doc();
+        const entryDoc = new Doc();
         entryDoc.checked = false;
         entryDoc.key = keyPlaceholder;
         entryDoc.value = valuePlaceholder;
@@ -214,7 +214,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
 
     @action
     remove = async (entry: ImportMetadataEntry) => {
-        let metadata = await DocListCastAsync(this.props.Document.data);
+        const metadata = await DocListCastAsync(this.props.Document.data);
         if (metadata) {
             let index = this.entries.indexOf(entry);
             if (index !== -1) {
@@ -228,18 +228,18 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
     }
 
     render() {
-        let dimensions = 50;
-        let entries = DocListCast(this.props.Document.data);
-        let isEditing = this.editingMetadata;
-        let completed = this.completed;
-        let quota = this.quota;
-        let uploading = this.uploading;
-        let showRemoveLabel = this.removeHover;
-        let persistent = this.persistent;
+        const dimensions = 50;
+        const entries = DocListCast(this.props.Document.data);
+        const isEditing = this.editingMetadata;
+        const completed = this.completed;
+        const quota = this.quota;
+        const uploading = this.uploading;
+        const showRemoveLabel = this.removeHover;
+        const persistent = this.persistent;
         let percent = `${completed / quota * 100}`;
         percent = percent.split(".")[0];
         percent = percent.startsWith("100") ? "99" : percent;
-        let marginOffset = (percent.length === 1 ? 5 : 0) - 1.6;
+        const marginOffset = (percent.length === 1 ? 5 : 0) - 1.6;
         const message = <span className={"phase"}>{this.phase}</span>;
         const centerPiece = this.phase.includes("Google Photos") ?
             <img src={"/assets/google_photos.png"} style={{
diff --git a/src/client/util/Import & Export/ImportMetadataEntry.tsx b/src/client/util/Import & Export/ImportMetadataEntry.tsx
index f5198c39b..8e1c50bea 100644
--- a/src/client/util/Import & Export/ImportMetadataEntry.tsx	
+++ b/src/client/util/Import & Export/ImportMetadataEntry.tsx	
@@ -1,11 +1,11 @@
 import React = require("react");
 import { observer } from "mobx-react";
 import { EditableView } from "../../views/EditableView";
-import { observable, action, computed } from "mobx";
+import { action, computed } from "mobx";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { faPlus } from "@fortawesome/free-solid-svg-icons";
 import { library } from '@fortawesome/fontawesome-svg-core';
-import { Opt, Doc } from "../../../new_fields/Doc";
+import { Doc } from "../../../new_fields/Doc";
 import { StrCast, BoolCast } from "../../../new_fields/Types";
 
 interface KeyValueProps {
@@ -85,7 +85,7 @@ export default class ImportMetadataEntry extends React.Component<KeyValueProps>
     }
 
     render() {
-        let keyValueStyle: React.CSSProperties = {
+        const keyValueStyle: React.CSSProperties = {
             paddingLeft: 10,
             width: "50%",
             opacity: this.valid ? 1 : 0.5,
diff --git a/src/client/util/InteractionUtils.ts b/src/client/util/InteractionUtils.ts
index b7738e862..0c3de66ed 100644
--- a/src/client/util/InteractionUtils.ts
+++ b/src/client/util/InteractionUtils.ts
@@ -29,8 +29,8 @@ export namespace InteractionUtils {
      * @param pts - n-arbitrary long list of points
      */
     export function CenterPoint(pts: React.Touch[]): { X: number, Y: number } {
-        let centerX = pts.map(pt => pt.clientX).reduce((a, b) => a + b, 0) / pts.length;
-        let centerY = pts.map(pt => pt.clientY).reduce((a, b) => a + b, 0) / pts.length;
+        const centerX = pts.map(pt => pt.clientX).reduce((a, b) => a + b, 0) / pts.length;
+        const centerY = pts.map(pt => pt.clientY).reduce((a, b) => a + b, 0) / pts.length;
         return { X: centerX, Y: centerY };
     }
 
@@ -42,9 +42,9 @@ export namespace InteractionUtils {
      * @param oldPoint2 - previous point 2
      */
     export function Pinching(pt1: React.Touch, pt2: React.Touch, oldPoint1: React.Touch, oldPoint2: React.Touch): number {
-        let threshold = 4;
-        let oldDist = TwoPointEuclidist(oldPoint1, oldPoint2);
-        let newDist = TwoPointEuclidist(pt1, pt2);
+        const threshold = 4;
+        const oldDist = TwoPointEuclidist(oldPoint1, oldPoint2);
+        const newDist = TwoPointEuclidist(pt1, pt2);
 
         /** if they have the same sign, then we are either pinching in or out.
           * threshold it by 10 (it has to be pinching by at least threshold to be a valid pinch)
@@ -64,12 +64,12 @@ export namespace InteractionUtils {
      * @param oldPoint2 - previous point 2
      */
     export function Pinning(pt1: React.Touch, pt2: React.Touch, oldPoint1: React.Touch, oldPoint2: React.Touch): number {
-        let threshold = 4;
+        const threshold = 4;
 
-        let pt1Dist = TwoPointEuclidist(oldPoint1, pt1);
-        let pt2Dist = TwoPointEuclidist(oldPoint2, pt2);
+        const pt1Dist = TwoPointEuclidist(oldPoint1, pt1);
+        const pt2Dist = TwoPointEuclidist(oldPoint2, pt2);
 
-        let pinching = Pinching(pt1, pt2, oldPoint1, oldPoint2);
+        const pinching = Pinching(pt1, pt2, oldPoint1, oldPoint2);
 
         if (pinching !== 0) {
             if ((pt1Dist < threshold && pt2Dist > threshold) || (pt1Dist > threshold && pt2Dist < threshold)) {
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index eedc4967d..fb6f27478 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -38,16 +38,16 @@ export class LinkManager {
     }
 
     public getAllLinks(): Doc[] {
-        let ldoc = LinkManager.Instance.LinkManagerDoc;
+        const ldoc = LinkManager.Instance.LinkManagerDoc;
         if (ldoc) {
-            let docs = DocListCast(ldoc.allLinks);
+            const docs = DocListCast(ldoc.allLinks);
             return docs;
         }
         return [];
     }
 
     public addLink(linkDoc: Doc): boolean {
-        let linkList = LinkManager.Instance.getAllLinks();
+        const linkList = LinkManager.Instance.getAllLinks();
         linkList.push(linkDoc);
         if (LinkManager.Instance.LinkManagerDoc) {
             LinkManager.Instance.LinkManagerDoc.allLinks = new List<Doc>(linkList);
@@ -57,8 +57,8 @@ export class LinkManager {
     }
 
     public deleteLink(linkDoc: Doc): boolean {
-        let linkList = LinkManager.Instance.getAllLinks();
-        let index = LinkManager.Instance.getAllLinks().indexOf(linkDoc);
+        const linkList = LinkManager.Instance.getAllLinks();
+        const index = LinkManager.Instance.getAllLinks().indexOf(linkDoc);
         if (index > -1) {
             linkList.splice(index, 1);
             if (LinkManager.Instance.LinkManagerDoc) {
@@ -71,23 +71,23 @@ export class LinkManager {
 
     // finds all links that contain the given anchor
     public getAllRelatedLinks(anchor: Doc): Doc[] {//List<Doc> {
-        let related = LinkManager.Instance.getAllLinks().filter(link => {
-            let protomatch1 = Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, null));
-            let protomatch2 = Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, null));
+        const related = LinkManager.Instance.getAllLinks().filter(link => {
+            const protomatch1 = Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, null));
+            const protomatch2 = Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, null));
             return protomatch1 || protomatch2 || Doc.AreProtosEqual(link, anchor);
         });
         return related;
     }
 
     public deleteAllLinksOnAnchor(anchor: Doc) {
-        let related = LinkManager.Instance.getAllRelatedLinks(anchor);
+        const related = LinkManager.Instance.getAllRelatedLinks(anchor);
         related.forEach(linkDoc => LinkManager.Instance.deleteLink(linkDoc));
     }
 
     public addGroupType(groupType: string): boolean {
         if (LinkManager.Instance.LinkManagerDoc) {
             LinkManager.Instance.LinkManagerDoc[groupType] = new List<string>([]);
-            let groupTypes = LinkManager.Instance.getAllGroupTypes();
+            const groupTypes = LinkManager.Instance.getAllGroupTypes();
             groupTypes.push(groupType);
             LinkManager.Instance.LinkManagerDoc.allGroupTypes = new List<string>(groupTypes);
             return true;
@@ -99,8 +99,8 @@ export class LinkManager {
     public deleteGroupType(groupType: string): boolean {
         if (LinkManager.Instance.LinkManagerDoc) {
             if (LinkManager.Instance.LinkManagerDoc[groupType]) {
-                let groupTypes = LinkManager.Instance.getAllGroupTypes();
-                let index = groupTypes.findIndex(type => type.toUpperCase() === groupType.toUpperCase());
+                const groupTypes = LinkManager.Instance.getAllGroupTypes();
+                const index = groupTypes.findIndex(type => type.toUpperCase() === groupType.toUpperCase());
                 if (index > -1) groupTypes.splice(index, 1);
                 LinkManager.Instance.LinkManagerDoc.allGroupTypes = new List<string>(groupTypes);
                 LinkManager.Instance.LinkManagerDoc[groupType] = undefined;
@@ -146,8 +146,8 @@ export class LinkManager {
     }
 
     public addGroupToAnchor(linkDoc: Doc, anchor: Doc, groupDoc: Doc, replace: boolean = false) {
-        let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor);
-        let index = groups.findIndex(gDoc => {
+        const groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor);
+        const index = groups.findIndex(gDoc => {
             return StrCast(groupDoc.type).toUpperCase() === StrCast(gDoc.type).toUpperCase();
         });
         if (index > -1 && replace) {
@@ -161,32 +161,32 @@ export class LinkManager {
 
     // removes group doc of given group type only from given anchor on given link
     public removeGroupFromAnchor(linkDoc: Doc, anchor: Doc, groupType: string) {
-        let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor);
-        let newGroups = groups.filter(groupDoc => StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase());
+        const groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor);
+        const newGroups = groups.filter(groupDoc => StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase());
         LinkManager.Instance.setAnchorGroups(linkDoc, anchor, newGroups);
     }
 
     // returns map of group type to anchor's links in that group type
     public getRelatedGroupedLinks(anchor: Doc): Map<string, Array<Doc>> {
-        let related = this.getAllRelatedLinks(anchor);
-        let anchorGroups = new Map<string, Array<Doc>>();
+        const related = this.getAllRelatedLinks(anchor);
+        const anchorGroups = new Map<string, Array<Doc>>();
         related.forEach(link => {
-            let groups = LinkManager.Instance.getAnchorGroups(link, anchor);
+            const groups = LinkManager.Instance.getAnchorGroups(link, anchor);
 
             if (groups.length > 0) {
                 groups.forEach(groupDoc => {
-                    let groupType = StrCast(groupDoc.type);
+                    const groupType = StrCast(groupDoc.type);
                     if (groupType === "") {
-                        let group = anchorGroups.get("*");
+                        const group = anchorGroups.get("*");
                         anchorGroups.set("*", group ? [...group, link] : [link]);
                     } else {
-                        let group = anchorGroups.get(groupType);
+                        const group = anchorGroups.get(groupType);
                         anchorGroups.set(groupType, group ? [...group, link] : [link]);
                     }
                 });
             } else {
                 // if link is in no groups then put it in default group
-                let group = anchorGroups.get("*");
+                const group = anchorGroups.get("*");
                 anchorGroups.set("*", group ? [...group, link] : [link]);
             }
 
@@ -212,11 +212,11 @@ export class LinkManager {
 
     // returns a list of all metadata docs associated with the given group type
     public getAllMetadataDocsInGroup(groupType: string): Array<Doc> {
-        let md: Doc[] = [];
-        let allLinks = LinkManager.Instance.getAllLinks();
+        const md: Doc[] = [];
+        const allLinks = LinkManager.Instance.getAllLinks();
         allLinks.forEach(linkDoc => {
-            let anchor1Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor1, Doc, null));
-            let anchor2Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor2, Doc, null));
+            const anchor1Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor1, Doc, null));
+            const anchor2Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor2, Doc, null));
             anchor1Groups.forEach(groupDoc => { if (StrCast(groupDoc.type).toUpperCase() === groupType.toUpperCase()) { const meta = Cast(groupDoc.metadata, Doc, null); meta && md.push(meta); } });
             anchor2Groups.forEach(groupDoc => { if (StrCast(groupDoc.type).toUpperCase() === groupType.toUpperCase()) { const meta = Cast(groupDoc.metadata, Doc, null); meta && md.push(meta); } });
         });
@@ -225,8 +225,8 @@ export class LinkManager {
 
     // checks if a link with the given anchors exists
     public doesLinkExist(anchor1: Doc, anchor2: Doc): boolean {
-        let allLinks = LinkManager.Instance.getAllLinks();
-        let index = allLinks.findIndex(linkDoc => {
+        const allLinks = LinkManager.Instance.getAllLinks();
+        const index = allLinks.findIndex(linkDoc => {
             return (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor1) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor2)) ||
                 (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor2) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor1));
         });
@@ -237,8 +237,8 @@ export class LinkManager {
     //TODO This should probably return undefined if there isn't an opposite anchor
     //TODO This should also await the return value of the anchor so we don't filter out promises
     public getOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc | undefined {
-        let a1 = Cast(linkDoc.anchor1, Doc, null);
-        let a2 = Cast(linkDoc.anchor2, Doc, null);
+        const a1 = Cast(linkDoc.anchor1, Doc, null);
+        const a2 = Cast(linkDoc.anchor2, Doc, null);
         if (Doc.AreProtosEqual(anchor, a1)) return a2;
         if (Doc.AreProtosEqual(anchor, a2)) return a1;
         if (Doc.AreProtosEqual(anchor, linkDoc)) return linkDoc;
diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts
index 003ff6272..f1fa6f11d 100644
--- a/src/client/util/ProsemirrorExampleTransfer.ts
+++ b/src/client/util/ProsemirrorExampleTransfer.ts
@@ -4,7 +4,7 @@ import { undoInputRule } from "prosemirror-inputrules";
 import { Schema } from "prosemirror-model";
 import { liftListItem, sinkListItem } from "./prosemirrorPatches.js";
 import { splitListItem, wrapInList, } from "prosemirror-schema-list";
-import { EditorState, Transaction, TextSelection, NodeSelection } from "prosemirror-state";
+import { EditorState, Transaction, TextSelection } from "prosemirror-state";
 import { TooltipTextMenu } from "./TooltipTextMenu";
 
 const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
@@ -15,22 +15,22 @@ export let updateBullets = (tx2: Transaction, schema: Schema, mapStyle?: string)
     let fontSize: number | undefined = undefined;
     tx2.doc.descendants((node: any, offset: any, index: any) => {
         if (node.type === schema.nodes.ordered_list || node.type === schema.nodes.list_item) {
-            let path = (tx2.doc.resolve(offset) as any).path;
+            const path = (tx2.doc.resolve(offset) as any).path;
             let depth = Array.from(path).reduce((p: number, c: any) => p + (c.hasOwnProperty("type") && c.type === schema.nodes.ordered_list ? 1 : 0), 0);
             if (node.type === schema.nodes.ordered_list) depth++;
             fontSize = depth === 1 && node.attrs.setFontSize ? Number(node.attrs.setFontSize) : fontSize;
-            let fsize = fontSize && node.type === schema.nodes.ordered_list ? Math.max(6, fontSize - (depth - 1) * 4) : undefined;
+            const fsize = fontSize && node.type === schema.nodes.ordered_list ? Math.max(6, fontSize - (depth - 1) * 4) : undefined;
             tx2.setNodeMarkup(offset, node.type, { ...node.attrs, mapStyle: mapStyle ? mapStyle : node.attrs.mapStyle, bulletStyle: depth, inheritedFontSize: fsize }, node.marks);
         }
     });
     return tx2;
 };
 export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?: KeyMap): KeyMap {
-    let keys: { [key: string]: any } = {}, type;
+    const keys: { [key: string]: any } = {};
 
     function bind(key: string, cmd: any) {
         if (mapKeys) {
-            let mapped = mapKeys[key];
+            const mapped = mapKeys[key];
             if (mapped === false) return;
             if (mapped) key = mapped;
         }
@@ -79,7 +79,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
     // });
 
 
-    let cmd = chainCommands(exitCode, (state, dispatch) => {
+    const cmd = chainCommands(exitCode, (state, dispatch) => {
         if (dispatch) {
             dispatch(state.tr.replaceSelectionWith(schema.nodes.hard_break.create()).scrollIntoView());
             return true;
@@ -99,7 +99,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
         bind("Shift-Ctrl-" + i, setBlockType(schema.nodes.heading, { level: i }));
     }
 
-    let hr = schema.nodes.horizontal_rule;
+    const hr = schema.nodes.horizontal_rule;
     bind("Mod-_", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
         dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView());
         return true;
@@ -108,18 +108,18 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
     bind("Mod-s", TooltipTextMenu.insertStar);
 
     bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
-        var ref = state.selection;
-        var range = ref.$from.blockRange(ref.$to);
-        var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
+        const ref = state.selection;
+        const range = ref.$from.blockRange(ref.$to);
+        const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
         if (!sinkListItem(schema.nodes.list_item)(state, (tx2: Transaction) => {
-            let tx3 = updateBullets(tx2, schema);
+            const tx3 = updateBullets(tx2, schema);
             marks && tx3.ensureMarks([...marks]);
             marks && tx3.setStoredMarks([...marks]);
             dispatch(tx3);
         })) { // couldn't sink into an existing list, so wrap in a new one
-            let newstate = state.applyTransaction(state.tr.setSelection(TextSelection.create(state.doc, range!.start, range!.end)));
+            const newstate = state.applyTransaction(state.tr.setSelection(TextSelection.create(state.doc, range!.start, range!.end)));
             if (!wrapInList(schema.nodes.ordered_list)(newstate.state, (tx2: Transaction) => {
-                let tx3 = updateBullets(tx2, schema);
+                const tx3 = updateBullets(tx2, schema);
                 // when promoting to a list, assume list will format things so don't copy the stored marks.
                 marks && tx3.ensureMarks([...marks]);
                 marks && tx3.setStoredMarks([...marks]);
@@ -131,10 +131,10 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
     });
 
     bind("Shift-Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
-        var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
+        const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
 
         if (!liftListItem(schema.nodes.list_item)(state.tr, (tx2: Transaction) => {
-            let tx3 = updateBullets(tx2, schema);
+            const tx3 = updateBullets(tx2, schema);
             marks && tx3.ensureMarks([...marks]);
             marks && tx3.setStoredMarks([...marks]);
             dispatch(tx3);
@@ -143,14 +143,14 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
         }
     });
 
-    let splitMetadata = (marks: any, tx: Transaction) => {
+    const splitMetadata = (marks: any, tx: Transaction) => {
         marks && tx.ensureMarks(marks.filter((val: any) => val.type !== schema.marks.metadata && val.type !== schema.marks.metadataKey && val.type !== schema.marks.metadataVal));
         marks && tx.setStoredMarks(marks.filter((val: any) => val.type !== schema.marks.metadata && val.type !== schema.marks.metadataKey && val.type !== schema.marks.metadataVal));
         return tx;
     };
-    bind("Enter", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
-        var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
-        if (!splitListItem(schema.nodes.list_item)(state, (tx3: Transaction) => dispatch(tx3))) {
+    bind("Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
+        const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
+        if (!splitListItem(schema.nodes.list_item)(state, dispatch)) {
             if (!splitBlockKeepMarks(state, (tx3: Transaction) => {
                 splitMetadata(marks, tx3);
                 if (!liftListItem(schema.nodes.list_item)(tx3, dispatch as ((tx: Transaction<Schema<any, any>>) => void))) {
@@ -163,18 +163,18 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
         return true;
     });
     bind("Space", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
-        var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
+        const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
         dispatch(splitMetadata(marks, state.tr));
         return false;
     });
     bind(":", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
-        let range = state.selection.$from.blockRange(state.selection.$to, (node: any) => {
+        const range = state.selection.$from.blockRange(state.selection.$to, (node: any) => {
             return !node.marks || !node.marks.find((m: any) => m.type === schema.marks.metadata);
         });
-        let path = (state.doc.resolve(state.selection.from - 1) as any).path;
-        let spaceSeparator = path[path.length - 3].childCount > 1 ? 0 : -1;
-        let textsel = TextSelection.create(state.doc, range!.end - path[path.length - 3].lastChild.nodeSize + spaceSeparator, range!.end);
-        let text = range ? state.doc.textBetween(textsel.from, textsel.to) : "";
+        const path = (state.doc.resolve(state.selection.from - 1) as any).path;
+        const spaceSeparator = path[path.length - 3].childCount > 1 ? 0 : -1;
+        const textsel = TextSelection.create(state.doc, range!.end - path[path.length - 3].lastChild.nodeSize + spaceSeparator, range!.end);
+        const text = range ? state.doc.textBetween(textsel.from, textsel.to) : "";
         let whitespace = text.length - 1;
         for (; whitespace >= 0 && text[whitespace] !== " "; whitespace--) { }
         if (text.endsWith(":")) {
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index f4c44e5ce..1a637df32 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -62,109 +62,109 @@ export const inpRules = {
         new InputRule(
             new RegExp(/^#([0-9]+)\s$/),
             (state, match, start, end) => {
-                let size = Number(match[1]);
-                let ruleProvider = FormattedTextBox.InputBoxOverlay!.props.ruleProvider;
-                let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
+                const size = Number(match[1]);
+                const ruleProvider = FormattedTextBox.InputBoxOverlay!.props.ruleProvider;
+                const heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
                 if (ruleProvider && heading) {
-                    (Cast(FormattedTextBox.InputBoxOverlay!.props.Document, Doc) as Doc).heading = Number(match[1]);
+                    (Cast(FormattedTextBox.InputBoxOverlay!.props.Document, Doc) as Doc).heading = size;
                     return state.tr.deleteRange(start, end);
                 }
-                return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontSize.create({ fontSize: Number(match[1]) }));
+                return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontSize.create({ fontSize: size }));
             }),
         new InputRule(
             new RegExp(/t/),
             (state, match, start, end) => {
                 if (state.selection.to === state.selection.from) return null;
-                let node = (state.doc.resolve(start) as any).nodeAfter;
+                const node = (state.doc.resolve(start) as any).nodeAfter;
                 return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: "todo", modified: Math.round(Date.now() / 1000 / 60) })) : state.tr;
             }),
         new InputRule(
             new RegExp(/i/),
             (state, match, start, end) => {
                 if (state.selection.to === state.selection.from) return null;
-                let node = (state.doc.resolve(start) as any).nodeAfter;
+                const node = (state.doc.resolve(start) as any).nodeAfter;
                 return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: "ignore", modified: Math.round(Date.now() / 1000 / 60) })) : state.tr;
             }),
         new InputRule(
             new RegExp(/\!/),
             (state, match, start, end) => {
                 if (state.selection.to === state.selection.from) return null;
-                let node = (state.doc.resolve(start) as any).nodeAfter;
+                const node = (state.doc.resolve(start) as any).nodeAfter;
                 return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: "important", modified: Math.round(Date.now() / 1000 / 60) })) : state.tr;
             }),
         new InputRule(
             new RegExp(/\x/),
             (state, match, start, end) => {
                 if (state.selection.to === state.selection.from) return null;
-                let node = (state.doc.resolve(start) as any).nodeAfter;
+                const node = (state.doc.resolve(start) as any).nodeAfter;
                 return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: "disagree", modified: Math.round(Date.now() / 1000 / 60) })) : state.tr;
             }),
         new InputRule(
             new RegExp(/^\^\^\s$/),
             (state, match, start, end) => {
-                let node = (state.doc.resolve(start) as any).nodeAfter;
-                let sm = state.storedMarks || undefined;
-                let ruleProvider = FormattedTextBox.InputBoxOverlay!.props.ruleProvider;
-                let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
+                const node = (state.doc.resolve(start) as any).nodeAfter;
+                const sm = state.storedMarks || undefined;
+                const ruleProvider = FormattedTextBox.InputBoxOverlay!.props.ruleProvider;
+                const heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
                 if (ruleProvider && heading) {
                     ruleProvider["ruleAlign_" + heading] = "center";
                     return node ? state.tr.deleteRange(start, end).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
                 }
-                let replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "center" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
+                const replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "center" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
                     state.tr;
                 return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
             }),
         new InputRule(
             new RegExp(/^\[\[\s$/),
             (state, match, start, end) => {
-                let node = (state.doc.resolve(start) as any).nodeAfter;
-                let sm = state.storedMarks || undefined;
-                let ruleProvider = FormattedTextBox.InputBoxOverlay!.props.ruleProvider;
-                let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
+                const node = (state.doc.resolve(start) as any).nodeAfter;
+                const sm = state.storedMarks || undefined;
+                const ruleProvider = FormattedTextBox.InputBoxOverlay!.props.ruleProvider;
+                const heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
                 if (ruleProvider && heading) {
                     ruleProvider["ruleAlign_" + heading] = "left";
                     return node ? state.tr.deleteRange(start, end).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
                 }
-                let replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "left" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
+                const replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "left" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
                     state.tr;
                 return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
             }),
         new InputRule(
             new RegExp(/^\]\]\s$/),
             (state, match, start, end) => {
-                let node = (state.doc.resolve(start) as any).nodeAfter;
-                let sm = state.storedMarks || undefined;
-                let ruleProvider = FormattedTextBox.InputBoxOverlay!.props.ruleProvider;
-                let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
+                const node = (state.doc.resolve(start) as any).nodeAfter;
+                const sm = state.storedMarks || undefined;
+                const ruleProvider = FormattedTextBox.InputBoxOverlay!.props.ruleProvider;
+                const heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
                 if (ruleProvider && heading) {
                     ruleProvider["ruleAlign_" + heading] = "right";
                     return node ? state.tr.deleteRange(start, end).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
                 }
-                let replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "right" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
+                const replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "right" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
                     state.tr;
                 return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
             }),
         new InputRule(
             new RegExp(/##\s$/),
             (state, match, start, end) => {
-                let target = Docs.Create.TextDocument({ width: 75, height: 35, autoHeight: true, fontSize: 9, title: "inline comment" });
-                let node = (state.doc.resolve(start) as any).nodeAfter;
-                let newNode = schema.nodes.dashComment.create({ docid: target[Id] });
-                let dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: "dashDoc", docid: target[Id], float: "right" });
-                let sm = state.storedMarks || undefined;
-                let replaced = node ? state.tr.insert(start, newNode).replaceRangeWith(start + 1, end + 1, dashDoc).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
+                const target = Docs.Create.TextDocument({ width: 75, height: 35, autoHeight: true, fontSize: 9, title: "inline comment" });
+                const node = (state.doc.resolve(start) as any).nodeAfter;
+                const newNode = schema.nodes.dashComment.create({ docid: target[Id] });
+                const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: "dashDoc", docid: target[Id], float: "right" });
+                const sm = state.storedMarks || undefined;
+                const replaced = node ? state.tr.insert(start, newNode).replaceRangeWith(start + 1, end + 1, dashDoc).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
                     state.tr;
                 return replaced;//.setSelection(new NodeSelection(replaced.doc.resolve(end)));
             }),
         new InputRule(
             new RegExp(/\(\(/),
             (state, match, start, end) => {
-                let node = (state.doc.resolve(start) as any).nodeAfter;
-                let sm = state.storedMarks || undefined;
-                let mark = state.schema.marks.highlight.create();
-                let selected = state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).addMark(start, end, mark);
-                let content = selected.selection.content();
-                let replaced = node ? selected.replaceRangeWith(start, start,
+                const node = (state.doc.resolve(start) as any).nodeAfter;
+                const sm = state.storedMarks || undefined;
+                const mark = state.schema.marks.highlight.create();
+                const selected = state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).addMark(start, end, mark);
+                const content = selected.selection.content();
+                const replaced = node ? selected.replaceRangeWith(start, start,
                     schema.nodes.star.create({ visibility: true, text: content, textslice: content.toJSON() })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
                     state.tr;
                 return replaced.setSelection(new TextSelection(replaced.doc.resolve(end + 1)));
@@ -172,14 +172,14 @@ export const inpRules = {
         new InputRule(
             new RegExp(/\)\)/),
             (state, match, start, end) => {
-                let mark = state.schema.marks.highlight.create();
+                const mark = state.schema.marks.highlight.create();
                 return state.tr.removeStoredMark(mark);
             }),
         new InputRule(
             new RegExp(/\^f\s$/),
             (state, match, start, end) => {
-                let newNode = schema.nodes.footnote.create({});
-                let tr = state.tr;
+                const newNode = schema.nodes.footnote.create({});
+                const tr = state.tr;
                 tr.deleteRange(start, end).replaceSelectionWith(newNode); // replace insertion with a footnote.
                 return tr.setSelection(new NodeSelection( // select the footnote node to open its display
                     tr.doc.resolve(  // get the location of the footnote node by subtracting the nodesize of the footnote from the current insertion point anchor (which will be immediately after the footnote node)
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 522232e9f..cb03892f3 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -1,4 +1,4 @@
-import { action, observable, runInAction, reaction, IReactionDisposer } from "mobx";
+import { reaction, IReactionDisposer } from "mobx";
 import { baseKeymap, toggleMark } from "prosemirror-commands";
 import { redo, undo } from "prosemirror-history";
 import { keymap } from "prosemirror-keymap";
@@ -257,9 +257,9 @@ export const nodes: { [index: string]: NodeSpec } = {
             if (node.attrs.mapStyle === "bullet") return ['ul', 0];
             const decMap = bs ? "decimal" + bs : "";
             const multiMap = bs === 1 ? "decimal1" : bs === 2 ? "upper-alpha" : bs === 3 ? "lower-roman" : bs === 4 ? "lower-alpha" : "";
-            let map = node.attrs.mapStyle === "decimal" ? decMap : multiMap;
-            let fsize = node.attrs.setFontSize ? node.attrs.setFontSize : node.attrs.inheritedFontSize;
-            let ffam = node.attrs.setFontFamily;
+            const map = node.attrs.mapStyle === "decimal" ? decMap : multiMap;
+            const fsize = node.attrs.setFontSize ? node.attrs.setFontSize : node.attrs.inheritedFontSize;
+            const ffam = node.attrs.setFontFamily;
             return node.attrs.visibility ? ['ol', { class: `${map}-ol`, style: `list-style: none; font-size: ${fsize}; font-family: ${ffam}` }, 0] :
                 ['ol', { class: `${map}-ol`, style: `list-style: none; font-size: ${fsize}; font-family: ${ffam}` }];
         }
@@ -287,7 +287,7 @@ export const nodes: { [index: string]: NodeSpec } = {
             const bs = node.attrs.bulletStyle;
             const decMap = bs ? "decimal" + bs : "";
             const multiMap = bs === 1 ? "decimal1" : bs === 2 ? "upper-alpha" : bs === 3 ? "lower-roman" : bs === 4 ? "lower-alpha" : "";
-            let map = node.attrs.mapStyle === "decimal" ? decMap : node.attrs.mapStyle === "multi" ? multiMap : "";
+            const map = node.attrs.mapStyle === "decimal" ? decMap : node.attrs.mapStyle === "multi" ? multiMap : "";
             return node.attrs.visibility ? ["li", { class: `${map}` }, 0] : ["li", { class: `${map}` }, "..."];
             //return ["li", { class: `${map}` }, 0];
         }
@@ -432,7 +432,7 @@ export const marks: { [index: string]: MarkSpec } = {
                 tag: "span",
                 getAttrs: (p: any) => {
                     if (typeof (p) !== "string") {
-                        let style = getComputedStyle(p);
+                        const style = getComputedStyle(p);
                         if (style.textDecoration === "underline") return null;
                         if (p.parentElement.outerHTML.indexOf("text-decoration: underline") !== -1 &&
                             p.parentElement.outerHTML.indexOf("text-decoration-style: dotted") !== -1) {
@@ -457,7 +457,7 @@ export const marks: { [index: string]: MarkSpec } = {
                 tag: "span",
                 getAttrs: (p: any) => {
                     if (typeof (p) !== "string") {
-                        let style = getComputedStyle(p);
+                        const style = getComputedStyle(p);
                         if (style.textDecoration === "underline" || p.parentElement.outerHTML.indexOf("text-decoration-style:line") !== -1) {
                             return null;
                         }
@@ -493,11 +493,11 @@ export const marks: { [index: string]: MarkSpec } = {
         },
         group: "inline",
         toDOM(node: any) {
-            let uid = node.attrs.userid.replace(".", "").replace("@", "");
-            let min = Math.round(node.attrs.modified / 12);
-            let hr = Math.round(min / 60);
-            let day = Math.round(hr / 60 / 24);
-            let remote = node.attrs.userid !== Doc.CurrentUserEmail ? " userMark-remote" : "";
+            const uid = node.attrs.userid.replace(".", "").replace("@", "");
+            const min = Math.round(node.attrs.modified / 12);
+            const hr = Math.round(min / 60);
+            const day = Math.round(hr / 60 / 24);
+            const remote = node.attrs.userid !== Doc.CurrentUserEmail ? " userMark-remote" : "";
             return node.attrs.opened ?
                 ['span', { class: "userMark-" + uid + remote + " userMark-min-" + min + " userMark-hr-" + hr + " userMark-day-" + day }, 0] :
                 ['span', { class: "userMark-" + uid + remote + " userMark-min-" + min + " userMark-hr-" + hr + " userMark-day-" + day }, ['span', 0]];
@@ -513,7 +513,7 @@ export const marks: { [index: string]: MarkSpec } = {
         },
         group: "inline",
         toDOM(node: any) {
-            let uid = node.attrs.userid.replace(".", "").replace("@", "");
+            const uid = node.attrs.userid.replace(".", "").replace("@", "");
             return node.attrs.opened ?
                 ['span', { class: "userTag-" + uid + " userTag-" + node.attrs.tag }, 0] :
                 ['span', { class: "userTag-" + uid + " userTag-" + node.attrs.tag }, ['span', 0]];
@@ -534,7 +534,7 @@ export const marks: { [index: string]: MarkSpec } = {
         },
         parseDOM: [{
             tag: "span", getAttrs(dom: any) {
-                let cstyle = getComputedStyle(dom);
+                const cstyle = getComputedStyle(dom);
                 if (cstyle.font) {
                     if (cstyle.font.indexOf("Times New Roman") !== -1) return { family: "Times New Roman" };
                     if (cstyle.font.indexOf("Arial") !== -1) return { family: "Arial" };
@@ -599,7 +599,7 @@ export class ImageResizeView {
         this._handle.style.display = "none";
         this._handle.style.bottom = "-10px";
         this._handle.style.right = "-10px";
-        let self = this;
+        const self = this;
         this._img.onclick = function (e: any) {
             e.stopPropagation();
             e.preventDefault();
@@ -620,8 +620,8 @@ export class ImageResizeView {
         this._handle.onpointerdown = function (e: any) {
             e.preventDefault();
             e.stopPropagation();
-            let wid = Number(getComputedStyle(self._img).width.replace(/px/, ""));
-            let hgt = Number(getComputedStyle(self._img).height.replace(/px/, ""));
+            const wid = Number(getComputedStyle(self._img).width.replace(/px/, ""));
+            const hgt = Number(getComputedStyle(self._img).height.replace(/px/, ""));
             const startX = e.pageX;
             const startWidth = parseFloat(node.attrs.width);
             const onpointermove = (e: any) => {
@@ -634,7 +634,7 @@ export class ImageResizeView {
             const onpointerup = () => {
                 document.removeEventListener("pointermove", onpointermove);
                 document.removeEventListener("pointerup", onpointerup);
-                let pos = view.state.selection.from;
+                const pos = view.state.selection.from;
                 view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, { ...node.attrs, width: self._outer.style.width, height: self._outer.style.height }));
                 view.dispatch(view.state.tr.setSelection(new NodeSelection(view.state.doc.resolve(pos))));
             };
@@ -671,19 +671,19 @@ export class DashDocCommentView {
         this._collapsed.id = "DashDocCommentView-" + node.attrs.docid;
         this._view = view;
         this._collapsed.onpointerdown = (e: any) => {
-            let node = view.state.doc.nodeAt(getPos() + 1);
+            const node = view.state.doc.nodeAt(getPos() + 1);
             view.dispatch(view.state.tr.
                 setNodeMarkup(getPos() + 1, undefined, { ...node.attrs, hidden: node.attrs.hidden ? false : true })); // update the attrs 
             setTimeout(() => node.attrs.hidden && DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc)), 100);
-        }
+        };
         this._collapsed.onpointerenter = (e: any) => {
-            let node = view.state.doc.nodeAt(getPos() + 1);
+            const node = view.state.doc.nodeAt(getPos() + 1);
             DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc));
             e.preventDefault();
             e.stopPropagation();
         };
         this._collapsed.onpointerleave = (e: any) => {
-            let node = view.state.doc.nodeAt(getPos() + 1);
+            const node = view.state.doc.nodeAt(getPos() + 1);
             DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowUnhighlight());
             e.preventDefault();
             e.stopPropagation();
@@ -701,7 +701,7 @@ export class DashDocView {
     _textBox: FormattedTextBox;
 
     getDocTransform = () => {
-        let { scale, translateX, translateY } = Utils.GetScreenTransform(this._outer);
+        const { scale, translateX, translateY } = Utils.GetScreenTransform(this._outer);
         return new Transform(-translateX, -translateY, 1).scale(1 / this.contentScaling() / scale);
     }
     contentScaling = () => NumCast(this._dashDoc!.nativeWidth) > 0 && !this._dashDoc!.ignoreAspect ? this._dashDoc![WidthSym]() / NumCast(this._dashDoc!.nativeWidth) : 1;
@@ -721,24 +721,24 @@ export class DashDocView {
         this._dashSpan.style.position = "absolute";
         this._dashSpan.style.display = "inline-block";
         this._dashSpan.style.borderWidth = "4";
-        let removeDoc = () => {
-            let pos = getPos();
-            let ns = new NodeSelection(view.state.doc.resolve(pos));
+        const removeDoc = () => {
+            const pos = getPos();
+            const ns = new NodeSelection(view.state.doc.resolve(pos));
             view.dispatch(view.state.tr.setSelection(ns).deleteSelection());
             return true;
         };
         this._dashSpan.onpointerleave = () => {
-            let ele = document.getElementById("DashDocCommentView-" + node.attrs.docid);
+            const ele = document.getElementById("DashDocCommentView-" + node.attrs.docid);
             if (ele) {
                 (ele as HTMLDivElement).style.backgroundColor = "";
             }
-        }
+        };
         this._dashSpan.onpointerenter = () => {
-            let ele = document.getElementById("DashDocCommentView-" + node.attrs.docid);
+            const ele = document.getElementById("DashDocCommentView-" + node.attrs.docid);
             if (ele) {
                 (ele as HTMLDivElement).style.backgroundColor = "orange";
             }
-        }
+        };
         DocServer.GetRefField(node.attrs.docid).then(async dashDoc => {
             if (dashDoc instanceof Doc) {
                 self._dashDoc = dashDoc;
@@ -777,7 +777,7 @@ export class DashDocView {
                 />, this._dashSpan);
             }
         });
-        let self = this;
+        const self = this;
         this._dashSpan.onkeydown = function (e: any) { e.stopPropagation(); };
         this._dashSpan.onkeypress = function (e: any) { e.stopPropagation(); };
         this._dashSpan.onwheel = function (e: any) { e.preventDefault(); };
@@ -830,7 +830,7 @@ export class FootnoteView {
     }
     open() {
         // Append a tooltip to the outer node
-        let tooltip = this.dom.appendChild(document.createElement("div"));
+        const tooltip = this.dom.appendChild(document.createElement("div"));
         tooltip.className = "footnote-tooltip";
         // And put a sub-ProseMirror into that
         this.innerView = new EditorView(tooltip, {
@@ -885,14 +885,14 @@ export class FootnoteView {
         this.dom.textContent = "";
     }
     dispatchInner(tr: any) {
-        let { state, transactions } = this.innerView.state.applyTransaction(tr);
+        const { state, transactions } = this.innerView.state.applyTransaction(tr);
         this.innerView.updateState(state);
 
         if (!tr.getMeta("fromOutside")) {
-            let outerTr = this.outerView.state.tr, offsetMap = StepMap.offset(this.getPos() + 1);
-            for (let transaction of transactions) {
-                let steps = transaction.steps;
-                for (let step of steps) {
+            const outerTr = this.outerView.state.tr, offsetMap = StepMap.offset(this.getPos() + 1);
+            for (const transaction of transactions) {
+                const steps = transaction.steps;
+                for (const step of steps) {
                     outerTr.step(step.map(offsetMap));
                 }
             }
@@ -903,11 +903,11 @@ export class FootnoteView {
         if (!node.sameMarkup(this.node)) return false;
         this.node = node;
         if (this.innerView) {
-            let state = this.innerView.state;
-            let start = node.content.findDiffStart(state.doc.content);
+            const state = this.innerView.state;
+            const start = node.content.findDiffStart(state.doc.content);
             if (start !== null) {
                 let { a: endA, b: endB } = node.content.findDiffEnd(state.doc.content);
-                let overlap = start - Math.min(endA, endB);
+                const overlap = start - Math.min(endA, endB);
                 if (overlap > 0) { endA += overlap; endB += overlap; }
                 this.innerView.dispatch(
                     state.tr
@@ -967,10 +967,10 @@ export class SummarizedView {
     className = (visible: boolean) => "formattedTextBox-summarizer" + (visible ? "" : "-collapsed");
 
     updateSummarizedText(start?: any) {
-        let mark = this._view.state.schema.marks.highlight.create();
+        const mark = this._view.state.schema.marks.highlight.create();
         let endPos = start;
 
-        let visited = new Set();
+        const visited = new Set();
         for (let i: number = start + 1; i < this._view.state.doc.nodeSize - 1; i++) {
             let skip = false;
             this._view.state.doc.nodesBetween(start, i, (node: Node, pos: number, parent: Node, index: number) => {
@@ -999,7 +999,7 @@ export const schema = new Schema({ nodes, marks });
 const fromJson = schema.nodeFromJSON;
 
 schema.nodeFromJSON = (json: any) => {
-    let node = fromJson(json);
+    const node = fromJson(json);
     if (json.type === "star") {
         node.attrs.text = Slice.fromJSON(schema, node.attrs.textslice);
     }
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index ff4451824..0fa96963e 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -94,16 +94,16 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
         return { compiled: false, errors: diagnostics };
     }
 
-    let paramNames = Object.keys(scriptingGlobals);
-    let params = paramNames.map(key => scriptingGlobals[key]);
+    const paramNames = Object.keys(scriptingGlobals);
+    const params = paramNames.map(key => scriptingGlobals[key]);
     // let fieldTypes = [Doc, ImageField, PdfField, VideoField, AudioField, List, RichTextField, ScriptField, ComputedField, CompileScript];
     // let paramNames = ["Docs", ...fieldTypes.map(fn => fn.name)];
     // let params: any[] = [Docs, ...fieldTypes];
-    let compiledFunction = new Function(...paramNames, `return ${script}`);
-    let { capturedVariables = {} } = options;
-    let run = (args: { [name: string]: any } = {}, onError?: (e: any) => void, errorVal?: any): ScriptResult => {
-        let argsArray: any[] = [];
-        for (let name of customParams) {
+    const compiledFunction = new Function(...paramNames, `return ${script}`);
+    const { capturedVariables = {} } = options;
+    const run = (args: { [name: string]: any } = {}, onError?: (e: any) => void, errorVal?: any): ScriptResult => {
+        const argsArray: any[] = [];
+        for (const name of customParams) {
             if (name === "this") {
                 continue;
             }
@@ -113,7 +113,7 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
                 argsArray.push(capturedVariables[name]);
             }
         }
-        let thisParam = args.this || capturedVariables.this;
+        const thisParam = args.this || capturedVariables.this;
         let batch: { end(): void } | undefined = undefined;
         try {
             if (!options.editable) {
@@ -146,7 +146,7 @@ class ScriptingCompilerHost {
 
     // getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, onError?: ((message: string) => void) | undefined, shouldCreateNewSourceFile?: boolean | undefined): ts.SourceFile | undefined {
     getSourceFile(fileName: string, languageVersion: any, onError?: ((message: string) => void) | undefined, shouldCreateNewSourceFile?: boolean | undefined): any | undefined {
-        let contents = this.readFile(fileName);
+        const contents = this.readFile(fileName);
         if (contents !== undefined) {
             return ts.createSourceFile(fileName, contents, languageVersion, true);
         }
@@ -180,7 +180,7 @@ class ScriptingCompilerHost {
         return this.files.some(file => file.fileName === fileName);
     }
     readFile(fileName: string): string | undefined {
-        let file = this.files.find(file => file.fileName === fileName);
+        const file = this.files.find(file => file.fileName === fileName);
         if (file) {
             return file.content;
         }
@@ -218,7 +218,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
     if (options.globals) {
         Scripting.setScriptingGlobals(options.globals);
     }
-    let host = new ScriptingCompilerHost;
+    const host = new ScriptingCompilerHost;
     if (options.traverser) {
         const sourceFile = ts.createSourceFile('script.ts', script, ts.ScriptTarget.ES2015, true);
         const onEnter = typeof options.traverser === "object" ? options.traverser.onEnter : options.traverser;
@@ -240,7 +240,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
         script = printer.printFile(transformed[0]);
         result.dispose();
     }
-    let paramNames: string[] = [];
+    const paramNames: string[] = [];
     if ("this" in params || "this" in capturedVariables) {
         paramNames.push("this");
     }
@@ -248,7 +248,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
         if (key === "this") continue;
         paramNames.push(key);
     }
-    let paramList = paramNames.map(key => {
+    const paramList = paramNames.map(key => {
         const val = params[key];
         return `${key}: ${val}`;
     });
@@ -258,18 +258,18 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
         paramNames.push(key);
         paramList.push(`${key}: ${typeof val === "object" ? Object.getPrototypeOf(val).constructor.name : typeof val}`);
     }
-    let paramString = paramList.join(", ");
-    let funcScript = `(function(${paramString})${requiredType ? `: ${requiredType}` : ''} {
+    const paramString = paramList.join(", ");
+    const funcScript = `(function(${paramString})${requiredType ? `: ${requiredType}` : ''} {
         ${addReturn ? `return ${script};` : script}
     })`;
     host.writeFile("file.ts", funcScript);
 
     if (typecheck) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib);
-    let program = ts.createProgram(["file.ts"], {}, host);
-    let testResult = program.emit();
-    let outputText = host.readFile("file.js");
+    const program = ts.createProgram(["file.ts"], {}, host);
+    const testResult = program.emit();
+    const outputText = host.readFile("file.js");
 
-    let diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics);
+    const diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics);
 
     const result = Run(outputText, paramNames, diagnostics, script, options);
 
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 2cf13680a..7a9176bec 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -34,36 +34,36 @@ export namespace SearchUtil {
     export function Search(query: string, returnDocs: false, options?: SearchParams): Promise<IdSearchResult>;
     export async function Search(query: string, returnDocs: boolean, options: SearchParams = {}) {
         query = query || "*"; //If we just have a filter query, search for * as the query
-        let result: IdSearchResult = JSON.parse(await rp.get(Utils.prepend("/search"), {
+        const result: IdSearchResult = JSON.parse(await rp.get(Utils.prepend("/search"), {
             qs: { ...options, q: query },
         }));
         if (!returnDocs) {
             return result;
         }
 
-        let { ids, numFound, highlighting } = result;
+        const { ids, highlighting } = result;
 
-        let txtresult = query !== "*" && JSON.parse(await rp.get(Utils.prepend("/textsearch"), {
+        const txtresult = query !== "*" && JSON.parse(await rp.get(Utils.prepend("/textsearch"), {
             qs: { ...options, q: query },
         }));
 
-        let fileids = txtresult ? txtresult.ids : [];
-        let newIds: string[] = [];
-        let newLines: string[][] = [];
+        const fileids = txtresult ? txtresult.ids : [];
+        const newIds: string[] = [];
+        const newLines: string[][] = [];
         await Promise.all(fileids.map(async (tr: string, i: number) => {
-            let docQuery = "fileUpload_t:" + tr.substr(0, 7); //If we just have a filter query, search for * as the query
-            let docResult = JSON.parse(await rp.get(Utils.prepend("/search"), { qs: { ...options, q: docQuery } }));
+            const docQuery = "fileUpload_t:" + tr.substr(0, 7); //If we just have a filter query, search for * as the query
+            const docResult = JSON.parse(await rp.get(Utils.prepend("/search"), { qs: { ...options, q: docQuery } }));
             newIds.push(...docResult.ids);
             newLines.push(...docResult.ids.map((dr: any) => txtresult.lines[i]));
         }));
 
 
-        let theDocs: Doc[] = [];
-        let theLines: string[][] = [];
+        const theDocs: Doc[] = [];
+        const theLines: string[][] = [];
         const textDocMap = await DocServer.GetRefFields(newIds);
         const textDocs = newIds.map((id: string) => textDocMap[id]).map(doc => doc as Doc);
         for (let i = 0; i < textDocs.length; i++) {
-            let testDoc = textDocs[i];
+            const testDoc = textDocs[i];
             if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1) {
                 theDocs.push(Doc.GetProto(testDoc));
                 theLines.push(newLines[i].map(line => line.replace(query, query.toUpperCase())));
@@ -73,7 +73,7 @@ export namespace SearchUtil {
         const docMap = await DocServer.GetRefFields(ids);
         const docs = ids.map((id: string) => docMap[id]).map(doc => doc as Doc);
         for (let i = 0; i < ids.length; i++) {
-            let testDoc = docs[i];
+            const testDoc = docs[i];
             if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && (options.allowAliases || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) {
                 theDocs.push(testDoc);
                 theLines.push([]);
diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts
index ff048f647..1f6b939d3 100644
--- a/src/client/util/SerializationHelper.ts
+++ b/src/client/util/SerializationHelper.ts
@@ -1,7 +1,6 @@
-import { PropSchema, serialize, deserialize, custom, setDefaultModelSchema, getDefaultModelSchema, primitive, SKIP } from "serializr";
-import { Field, Doc } from "../../new_fields/Doc";
+import { PropSchema, serialize, deserialize, custom, setDefaultModelSchema, getDefaultModelSchema } from "serializr";
+import { Field } from "../../new_fields/Doc";
 import { ClientUtils } from "./ClientUtils";
-import { emptyFunction } from "../../Utils";
 
 let serializing = 0;
 export function afterDocDeserialize(cb: (err: any, val: any) => void, err: any, newValue: any) {
@@ -65,8 +64,8 @@ export namespace SerializationHelper {
     }
 }
 
-let serializationTypes: { [name: string]: { ctor: { new(): any }, afterDeserialize?: (obj: any) => void | Promise<any> } } = {};
-let reverseMap: { [ctor: string]: string } = {};
+const serializationTypes: { [name: string]: { ctor: { new(): any }, afterDeserialize?: (obj: any) => void | Promise<any> } } = {};
+const reverseMap: { [ctor: string]: string } = {};
 
 export interface DeserializableOpts {
     (constructor: { new(...args: any[]): any }): void;
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index cc1d628b1..7496ac73c 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -9,7 +9,6 @@ import { Utils } from "../../Utils";
 import "./SharingManager.scss";
 import { Id } from "../../new_fields/FieldSymbols";
 import { observer } from "mobx-react";
-import { MainView } from "../views/MainView";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { library } from '@fortawesome/fontawesome-svg-core';
 import * as fa from '@fortawesome/free-solid-svg-icons';
@@ -103,10 +102,10 @@ export default class SharingManager extends React.Component<{}> {
     }
 
     populateUsers = async () => {
-        let userList = await RequestPromise.get(Utils.prepend("/getUsers"));
+        const userList = await RequestPromise.get(Utils.prepend("/getUsers"));
         const raw = JSON.parse(userList) as User[];
         const evaluating = raw.map(async user => {
-            let isCandidate = user.email !== Doc.CurrentUserEmail;
+            const isCandidate = user.email !== Doc.CurrentUserEmail;
             if (isCandidate) {
                 const userDocument = await DocServer.GetRefField(user.userDocumentId);
                 if (userDocument instanceof Doc) {
@@ -130,7 +129,7 @@ export default class SharingManager extends React.Component<{}> {
         if (state === SharingPermissions.None) {
             const metadata = (await DocCastAsync(manager[key]));
             if (metadata) {
-                let sharedAlias = (await DocCastAsync(metadata.sharedAlias))!;
+                const sharedAlias = (await DocCastAsync(metadata.sharedAlias))!;
                 Doc.RemoveDocFromList(notificationDoc, storage, sharedAlias);
                 manager[key] = undefined;
             }
@@ -145,7 +144,7 @@ export default class SharingManager extends React.Component<{}> {
     }
 
     private setExternalSharing = (state: string) => {
-        let sharingDoc = this.sharingDoc;
+        const sharingDoc = this.sharingDoc;
         if (!sharingDoc) {
             return;
         }
@@ -156,7 +155,7 @@ export default class SharingManager extends React.Component<{}> {
         if (!this.targetDoc) {
             return undefined;
         }
-        let baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]);
+        const baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]);
         return `${baseUrl}?sharing=true`;
     }
 
@@ -178,7 +177,7 @@ export default class SharingManager extends React.Component<{}> {
     }
 
     private focusOn = (contents: string) => {
-        let title = this.targetDoc ? StrCast(this.targetDoc.title) : "";
+        const title = this.targetDoc ? StrCast(this.targetDoc.title) : "";
         return (
             <span
                 className={"focus-span"}
diff --git a/src/client/util/TooltipLinkingMenu.tsx b/src/client/util/TooltipLinkingMenu.tsx
index e6d6c471f..b46675a04 100644
--- a/src/client/util/TooltipLinkingMenu.tsx
+++ b/src/client/util/TooltipLinkingMenu.tsx
@@ -2,10 +2,6 @@ import { EditorState } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
 import { FieldViewProps } from "../views/nodes/FieldView";
 import "./TooltipTextMenu.scss";
-import React = require("react");
-const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands");
-
-const SVG = "http://www.w3.org/2000/svg";
 
 //appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc.
 export class TooltipLinkingMenu {
@@ -23,9 +19,9 @@ export class TooltipLinkingMenu {
         //add the div which is the tooltip
         view.dom.parentNode!.parentNode!.appendChild(this.tooltip);
 
-        let target = "https://www.google.com";
+        const target = "https://www.google.com";
 
-        let link = document.createElement("a");
+        const link = document.createElement("a");
         link.href = target;
         link.textContent = target;
         link.target = "_blank";
@@ -37,7 +33,7 @@ export class TooltipLinkingMenu {
 
     //updates the tooltip menu when the selection changes
     update(view: EditorView, lastState: EditorState | undefined) {
-        let state = view.state;
+        const 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;
@@ -53,16 +49,16 @@ export class TooltipLinkingMenu {
 
         // 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);
+        const { from, to } = state.selection;
+        const 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();
+        const 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);
+        const left = Math.max((start.left + end.left) / 2, start.left + 3);
         this.tooltip.style.left = (left - box.left) * this.editorProps.ScreenToLocalTransform().Scale + "px";
-        let width = Math.abs(start.left - end.left) / 2 * this.editorProps.ScreenToLocalTransform().Scale;
-        let mid = Math.min(start.left, end.left) + width;
+        const width = Math.abs(start.left - end.left) / 2 * this.editorProps.ScreenToLocalTransform().Scale;
+        const mid = Math.min(start.left, end.left) + width;
 
         this.tooltip.style.width = "auto";
         this.tooltip.style.bottom = (box.bottom - start.top) * this.editorProps.ScreenToLocalTransform().Scale + "px";
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 5136089b3..5e777cef9 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -2,7 +2,7 @@ import { action } from "mobx";
 import { Dropdown, icons, MenuItem } from "prosemirror-menu"; //no import css
 import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos, Schema } from "prosemirror-model";
 import { wrapInList } from 'prosemirror-schema-list';
-import { EditorState, NodeSelection, TextSelection, Transaction } from "prosemirror-state";
+import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
 import { Doc, Field, Opt } from "../../new_fields/Doc";
 import { Id } from "../../new_fields/FieldSymbols";
@@ -86,7 +86,7 @@ export class TooltipTextMenu {
         this.basicTools.className = "basic-tools";
 
         // init buttons to the tooltip -- paths to svgs are obtained from fontawesome
-        let items = [
+        const items = [
             { command: toggleMark(schema.marks.strong), dom: this.svgIcon("strong", "Bold", "M333.49 238a122 122 0 0 0 27-65.21C367.87 96.49 308 32 233.42 32H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h31.87v288H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h209.32c70.8 0 134.14-51.75 141-122.4 4.74-48.45-16.39-92.06-50.83-119.6zM145.66 112h87.76a48 48 0 0 1 0 96h-87.76zm87.76 288h-87.76V288h87.76a56 56 0 0 1 0 112z") },
             { command: toggleMark(schema.marks.em), dom: this.svgIcon("em", "Italic", "M320 48v32a16 16 0 0 1-16 16h-62.76l-80 320H208a16 16 0 0 1 16 16v32a16 16 0 0 1-16 16H16a16 16 0 0 1-16-16v-32a16 16 0 0 1 16-16h62.76l80-320H112a16 16 0 0 1-16-16V48a16 16 0 0 1 16-16h192a16 16 0 0 1 16 16z") },
             { command: toggleMark(schema.marks.underline), dom: this.svgIcon("underline", "Underline", "M32 64h32v160c0 88.22 71.78 160 160 160s160-71.78 160-160V64h32a16 16 0 0 0 16-16V16a16 16 0 0 0-16-16H272a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h32v160a80 80 0 0 1-160 0V64h32a16 16 0 0 0 16-16V16a16 16 0 0 0-16-16H32a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16zm400 384H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z") },
@@ -145,7 +145,7 @@ export class TooltipTextMenu {
 
         // link menu
         this.updateLinkMenu();
-        let dropdown = await this.createLinkDropdown();
+        const dropdown = await this.createLinkDropdown();
         this._linkDropdownDom = dropdown.render(this.view).dom;
         this.tooltip.appendChild(this._linkDropdownDom);
 
@@ -209,17 +209,17 @@ export class TooltipTextMenu {
     // creates dragger element that allows dragging and collapsing (on double click) 
     // of editor and appends it to the wrapper
     createDragger() {
-        let draggerWrapper = document.createElement("div");
+        const draggerWrapper = document.createElement("div");
         draggerWrapper.className = "dragger-wrapper";
 
-        let dragger = document.createElement("div");
+        const dragger = document.createElement("div");
         dragger.className = "dragger";
 
-        let line1 = document.createElement("span");
+        const line1 = document.createElement("span");
         line1.className = "dragger-line";
-        let line2 = document.createElement("span");
+        const line2 = document.createElement("span");
         line2.className = "dragger-line";
-        let line3 = document.createElement("span");
+        const line3 = document.createElement("span");
         line3.className = "dragger-line";
 
         dragger.appendChild(line1);
@@ -293,12 +293,12 @@ export class TooltipTextMenu {
     //label of dropdown will change to given label
     updateFontSizeDropdown(label: string) {
         //font SIZES
-        let fontSizeBtns: MenuItem[] = [];
+        const fontSizeBtns: MenuItem[] = [];
         this.fontSizes.forEach(mark => {
             fontSizeBtns.push(this.dropdownFontSizeBtn(String(mark.attrs.fontSize), "color: black; width: 50px;", mark, this.view, this.changeToFontSize));
         });
 
-        let newfontSizeDom = (new Dropdown(fontSizeBtns, {
+        const newfontSizeDom = (new Dropdown(fontSizeBtns, {
             label: label,
             css: "color:black; min-width: 60px;"
         }) as MenuItem).render(this.view).dom;
@@ -312,12 +312,12 @@ export class TooltipTextMenu {
     //label of dropdown will change to given label
     updateFontStyleDropdown(label: string) {
         //font STYLES
-        let fontBtns: MenuItem[] = [];
+        const fontBtns: MenuItem[] = [];
         this.fontStyles.forEach((mark) => {
             fontBtns.push(this.dropdownFontFamilyBtn(mark.attrs.family, "color: black; font-family: " + mark.attrs.family + ", sans-serif; width: 125px;", mark, this.view, this.changeToFontFamily));
         });
 
-        let newfontStyleDom = (new Dropdown(fontBtns, {
+        const newfontStyleDom = (new Dropdown(fontBtns, {
             label: label,
             css: "color:black; width: 125px;"
         }) as MenuItem).render(this.view).dom;
@@ -339,19 +339,19 @@ export class TooltipTextMenu {
             this.linkText.style.overflow = "hidden";
             this.linkText.style.color = "white";
             this.linkText.onpointerdown = (e: PointerEvent) => { e.stopPropagation(); };
-            let linkBtn = document.createElement("div");
+            const linkBtn = document.createElement("div");
             linkBtn.textContent = ">>";
             linkBtn.style.width = "10px";
             linkBtn.style.height = "10px";
             linkBtn.style.color = "white";
             linkBtn.style.cssFloat = "left";
             linkBtn.onpointerdown = (e: PointerEvent) => {
-                let node = this.view.state.selection.$from.nodeAfter;
-                let link = node && node.marks.find(m => m.type.name === "link");
+                const node = this.view.state.selection.$from.nodeAfter;
+                const link = node && node.marks.find(m => m.type.name === "link");
                 if (link) {
-                    let href: string = link.attrs.href;
+                    const href: string = link.attrs.href;
                     if (href.indexOf(Utils.prepend("/doc/")) === 0) {
-                        let docid = href.replace(Utils.prepend("/doc/"), "");
+                        const docid = href.replace(Utils.prepend("/doc/"), "");
                         DocServer.GetRefField(docid).then(action((f: Opt<Field>) => {
                             if (f instanceof Doc) {
                                 if (DocumentManager.Instance.getDocumentView(f)) {
@@ -374,23 +374,23 @@ export class TooltipTextMenu {
             this.linkDrag.id = "link-drag";
             this.linkDrag.onpointerdown = (e: PointerEvent) => {
                 if (!this.editorProps) return;
-                let dragData = new DragManager.LinkDragData(this.editorProps.Document);
+                const dragData = new DragManager.LinkDragData(this.editorProps.Document);
                 dragData.dontClearTextBox = true;
                 // hack to get source context -sy
-                let docView = DocumentManager.Instance.getDocumentView(this.editorProps.Document);
+                const docView = DocumentManager.Instance.getDocumentView(this.editorProps.Document);
                 e.stopPropagation();
-                let ctrlKey = e.ctrlKey;
+                const ctrlKey = e.ctrlKey;
                 DragManager.StartLinkDrag(this.linkDrag!, dragData, e.clientX, e.clientY,
                     {
                         handlers: {
                             dragComplete: action(() => {
                                 if (dragData.linkDocument) {
-                                    let linkDoc = dragData.linkDocument;
-                                    let proto = Doc.GetProto(linkDoc);
+                                    const linkDoc = dragData.linkDocument;
+                                    const proto = Doc.GetProto(linkDoc);
                                     if (proto && docView) {
                                         proto.sourceContext = docView.props.ContainingCollectionDoc;
                                     }
-                                    let text = this.makeLink(linkDoc, StrCast(linkDoc.anchor2.title), ctrlKey ? "onRight" : "inTab");
+                                    const text = this.makeLink(linkDoc, StrCast(linkDoc.anchor2.title), ctrlKey ? "onRight" : "inTab");
                                     if (linkDoc instanceof Doc && linkDoc.anchor2 instanceof Doc) {
                                         proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODODO open to more descriptive descriptions of following in text link
                                     }
@@ -406,8 +406,8 @@ export class TooltipTextMenu {
             this.tooltip.appendChild(this.linkEditor);
         }
 
-        let node = this.view.state.selection.$from.nodeAfter;
-        let link = node && node.marks.find(m => m.type.name === "link");
+        const node = this.view.state.selection.$from.nodeAfter;
+        const link = node && node.marks.find(m => m.type.name === "link");
         this.linkText.textContent = link ? link.attrs.href : "-empty-";
 
         this.linkText.onkeydown = (e: KeyboardEvent) => {
@@ -420,19 +420,19 @@ export class TooltipTextMenu {
     }
 
     async getTextLinkTargetTitle() {
-        let node = this.view.state.selection.$from.nodeAfter;
-        let link = node && node.marks.find(m => m.type.name === "link");
+        const node = this.view.state.selection.$from.nodeAfter;
+        const link = node && node.marks.find(m => m.type.name === "link");
         if (link) {
-            let href = link.attrs.href;
+            const href = link.attrs.href;
             if (href) {
                 if (href.indexOf(Utils.prepend("/doc/")) === 0) {
                     const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
                     if (linkclicked) {
-                        let linkDoc = await DocServer.GetRefField(linkclicked);
+                        const linkDoc = await DocServer.GetRefField(linkclicked);
                         if (linkDoc instanceof Doc) {
-                            let anchor1 = await Cast(linkDoc.anchor1, Doc);
-                            let anchor2 = await Cast(linkDoc.anchor2, Doc);
-                            let currentDoc = SelectionManager.SelectedDocuments().length && SelectionManager.SelectedDocuments()[0].props.Document;
+                            const anchor1 = await Cast(linkDoc.anchor1, Doc);
+                            const anchor2 = await Cast(linkDoc.anchor2, Doc);
+                            const currentDoc = SelectionManager.SelectedDocuments().length && SelectionManager.SelectedDocuments()[0].props.Document;
                             if (currentDoc && anchor1 && anchor2) {
                                 if (Doc.AreProtosEqual(currentDoc, anchor1)) {
                                     return StrCast(anchor2.title);
@@ -453,18 +453,18 @@ export class TooltipTextMenu {
     }
 
     async createLinkDropdown() {
-        let targetTitle = await this.getTextLinkTargetTitle();
-        let input = document.createElement("input");
+        const targetTitle = await this.getTextLinkTargetTitle();
+        const input = document.createElement("input");
 
         // menu item for input for hyperlink url 
         // TODO: integrate search to allow users to search for a doc to link to
-        let linkInfo = new MenuItem({
+        const linkInfo = new MenuItem({
             title: "",
             execEvent: "",
             class: "button-setting-disabled",
             css: "",
             render() {
-                let p = document.createElement("p");
+                const p = document.createElement("p");
                 p.textContent = "Linked to:";
 
                 input.type = "text";
@@ -475,7 +475,7 @@ export class TooltipTextMenu {
                     input.focus();
                 };
 
-                let div = document.createElement("div");
+                const div = document.createElement("div");
                 div.appendChild(p);
                 div.appendChild(input);
                 return div;
@@ -487,13 +487,13 @@ export class TooltipTextMenu {
         });
 
         // menu item to update/apply the hyperlink to the selected text
-        let linkApply = new MenuItem({
+        const linkApply = new MenuItem({
             title: "",
             execEvent: "",
             class: "",
             css: "",
             render() {
-                let button = document.createElement("button");
+                const button = document.createElement("button");
                 button.className = "link-url-button";
                 button.textContent = "Apply hyperlink";
                 return button;
@@ -507,17 +507,17 @@ export class TooltipTextMenu {
 
         // menu item to remove the link
         // TODO: allow this to be undoable
-        let self = this;
-        let deleteLink = new MenuItem({
+        const self = this;
+        const deleteLink = new MenuItem({
             title: "Delete link",
             execEvent: "",
             class: "separated-button",
             css: "",
             render() {
-                let button = document.createElement("button");
+                const button = document.createElement("button");
                 button.textContent = "Remove link";
 
-                let wrapper = document.createElement("div");
+                const wrapper = document.createElement("div");
                 wrapper.appendChild(button);
                 return wrapper;
             },
@@ -525,15 +525,15 @@ export class TooltipTextMenu {
             async run() {
                 self.deleteLink();
                 // update link dropdown
-                let dropdown = await self.createLinkDropdown();
-                let newLinkDropdowndom = dropdown.render(self.view).dom;
+                const dropdown = await self.createLinkDropdown();
+                const newLinkDropdowndom = dropdown.render(self.view).dom;
                 self._linkDropdownDom && self.tooltip.replaceChild(newLinkDropdowndom, self._linkDropdownDom);
                 self._linkDropdownDom = newLinkDropdowndom;
             }
         });
 
 
-        let linkDropdown = new Dropdown(targetTitle ? [linkInfo, linkApply, deleteLink] : [linkInfo, linkApply], { class: "buttonSettings-dropdown" }) as MenuItem;
+        const linkDropdown = new Dropdown(targetTitle ? [linkInfo, linkApply, deleteLink] : [linkInfo, linkApply], { class: "buttonSettings-dropdown" }) as MenuItem;
         return linkDropdown;
     }
 
@@ -542,10 +542,10 @@ export class TooltipTextMenu {
     // }
 
     makeLink = (targetDoc: Doc, title: string, location: string): string => {
-        let link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + targetDoc[Id]), title: title, location: location });
+        const link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + targetDoc[Id]), title: title, location: location });
         this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link).
             addMark(this.view.state.selection.from, this.view.state.selection.to, link));
-        let node = this.view.state.selection.$from.nodeAfter;
+        const node = this.view.state.selection.$from.nodeAfter;
         if (node && node.text) {
             return node.text;
         }
@@ -562,9 +562,9 @@ export class TooltipTextMenu {
     }
 
     deleteLink = () => {
-        let node = this.view.state.selection.$from.nodeAfter;
-        let link = node && node.marks.find(m => m.type === this.view.state.schema.marks.link);
-        let href = link!.attrs.href;
+        const node = this.view.state.selection.$from.nodeAfter;
+        const link = node && node.marks.find(m => m.type === this.view.state.schema.marks.link);
+        const href = link!.attrs.href;
         if (href) {
             if (href.indexOf(Utils.prepend("/doc/")) === 0) {
                 const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
@@ -599,7 +599,7 @@ export class TooltipTextMenu {
     }
 
     createLink() {
-        let markType = schema.marks.link;
+        const markType = schema.marks.link;
         return new MenuItem({
             title: "Add or remove link",
             label: "Add or remove link",
@@ -613,8 +613,8 @@ export class TooltipTextMenu {
                 let curLink = "";
                 if (this.markActive(state, markType)) {
 
-                    let { from, $from, to, empty } = state.selection;
-                    let node = state.doc.nodeAt(from);
+                    const { from, $from, to, empty } = state.selection;
+                    const node = state.doc.nodeAt(from);
                     node && node.marks.map(m => {
                         m.type === markType && (curLink = m.attrs.href);
                     });
@@ -649,7 +649,7 @@ export class TooltipTextMenu {
         if (listTypeBtn) { this.tooltip.removeChild(listTypeBtn); }
 
         //Make a dropdown of all list types
-        let toAdd: MenuItem[] = [];
+        const toAdd: MenuItem[] = [];
         this.listTypeToIcon.forEach((icon, type) => {
             toAdd.push(this.dropdownNodeBtn(icon, "color: black; width: 40px;", type, this.view, this.listTypes, this.changeToNodeType));
         });
@@ -683,22 +683,22 @@ export class TooltipTextMenu {
 
     public static insertStar(state: EditorState<any>, dispatch: any) {
         if (state.selection.empty) return false;
-        let mark = state.schema.marks.highlight.create();
-        let tr = state.tr;
+        const mark = state.schema.marks.highlight.create();
+        const tr = state.tr;
         tr.addMark(state.selection.from, state.selection.to, mark);
-        let content = tr.selection.content();
-        let newNode = state.schema.nodes.star.create({ visibility: false, text: content, textslice: content.toJSON() });
+        const content = tr.selection.content();
+        const newNode = state.schema.nodes.star.create({ visibility: false, text: content, textslice: content.toJSON() });
         dispatch && dispatch(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark));
         return true;
     }
 
     public static insertComment(state: EditorState<any>, dispatch: any) {
         if (state.selection.empty) return false;
-        let mark = state.schema.marks.highlight.create();
-        let tr = state.tr;
+        const mark = state.schema.marks.highlight.create();
+        const tr = state.tr;
         tr.addMark(state.selection.from, state.selection.to, mark);
-        let content = tr.selection.content();
-        let newNode = state.schema.nodes.star.create({ visibility: false, text: content, textslice: content.toJSON() });
+        const content = tr.selection.content();
+        const newNode = state.schema.nodes.star.create({ visibility: false, text: content, textslice: content.toJSON() });
         dispatch && dispatch(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark));
         return true;
     }
@@ -710,17 +710,17 @@ export class TooltipTextMenu {
             class: "menuicon",
             execEvent: "",
             render() {
-                let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+                const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
                 svg.setAttribute("viewBox", "-100 -100 650 650");
-                let path = document.createElementNS('http://www.w3.org/2000/svg', "path");
+                const path = document.createElementNS('http://www.w3.org/2000/svg', "path");
                 path.setAttributeNS(null, "d", "M0 479.98L99.92 512l35.45-35.45-67.04-67.04L0 479.98zm124.61-240.01a36.592 36.592 0 0 0-10.79 38.1l13.05 42.83-50.93 50.94 96.23 96.23 50.86-50.86 42.74 13.08c13.73 4.2 28.65-.01 38.15-10.78l35.55-41.64-173.34-173.34-41.52 35.44zm403.31-160.7l-63.2-63.2c-20.49-20.49-53.38-21.52-75.12-2.35L190.55 183.68l169.77 169.78L530.27 154.4c19.18-21.74 18.15-54.63-2.35-75.13z");
                 svg.appendChild(path);
 
-                let color = document.createElement("div");
+                const color = document.createElement("div");
                 color.className = "buttonColor";
                 color.style.backgroundColor = TooltipTextMenuManager.Instance.highlight.toString();
 
-                let wrapper = document.createElement("div");
+                const wrapper = document.createElement("div");
                 wrapper.id = "colorPicker";
                 wrapper.appendChild(svg);
                 wrapper.appendChild(color);
@@ -735,26 +735,26 @@ export class TooltipTextMenu {
     public static insertHighlight(color: String, state: EditorState<any>, dispatch: any) {
         if (state.selection.empty) return false;
 
-        let highlightMark = state.schema.mark(state.schema.marks.marker, { highlight: color });
+        const highlightMark = state.schema.mark(state.schema.marks.marker, { highlight: color });
         dispatch(state.tr.addMark(state.selection.from, state.selection.to, highlightMark));
     }
 
     createHighlightDropdown() {
         // menu item for color picker
-        let self = this;
-        let colors = new MenuItem({
+        const self = this;
+        const colors = new MenuItem({
             title: "",
             execEvent: "",
             class: "button-setting-disabled",
             css: "",
             render() {
-                let p = document.createElement("p");
+                const p = document.createElement("p");
                 p.textContent = "Change highlight:";
 
-                let colorsWrapper = document.createElement("div");
+                const colorsWrapper = document.createElement("div");
                 colorsWrapper.className = "colorPicker-wrapper";
 
-                let colors = [
+                const colors = [
                     PastelSchemaPalette.get("pink2"),
                     PastelSchemaPalette.get("purple4"),
                     PastelSchemaPalette.get("bluegreen1"),
@@ -768,7 +768,7 @@ export class TooltipTextMenu {
                 ];
 
                 colors.forEach(color => {
-                    let button = document.createElement("button");
+                    const button = document.createElement("button");
                     button.className = color === TooltipTextMenuManager.Instance.highlight ? "colorPicker active" : "colorPicker";
                     if (color) {
                         button.style.backgroundColor = color;
@@ -779,8 +779,8 @@ export class TooltipTextMenu {
                             TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlight, self.view.state, self.view.dispatch);
 
                             // update color menu
-                            let highlightDom = self.createHighlightTool().render(self.view).dom;
-                            let highlightDropdownDom = self.createHighlightDropdown().render(self.view).dom;
+                            const highlightDom = self.createHighlightTool().render(self.view).dom;
+                            const highlightDropdownDom = self.createHighlightDropdown().render(self.view).dom;
                             self.highlightDom && self.tooltip.replaceChild(highlightDom, self.highlightDom);
                             self.highlightDropdownDom && self.tooltip.replaceChild(highlightDropdownDom, self.highlightDropdownDom);
                             self.highlightDom = highlightDom;
@@ -790,7 +790,7 @@ export class TooltipTextMenu {
                     colorsWrapper.appendChild(button);
                 });
 
-                let div = document.createElement("div");
+                const div = document.createElement("div");
                 div.appendChild(p);
                 div.appendChild(colorsWrapper);
                 return div;
@@ -801,7 +801,7 @@ export class TooltipTextMenu {
             }
         });
 
-        let colorDropdown = new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem;
+        const colorDropdown = new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem;
         return colorDropdown;
     }
 
@@ -812,17 +812,17 @@ export class TooltipTextMenu {
             class: "menuicon",
             execEvent: "",
             render() {
-                let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+                const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
                 svg.setAttribute("viewBox", "-100 -100 650 650");
-                let path = document.createElementNS('http://www.w3.org/2000/svg', "path");
+                const path = document.createElementNS('http://www.w3.org/2000/svg', "path");
                 path.setAttributeNS(null, "d", "M204.3 5C104.9 24.4 24.8 104.3 5.2 203.4c-37 187 131.7 326.4 258.8 306.7 41.2-6.4 61.4-54.6 42.5-91.7-23.1-45.4 9.9-98.4 60.9-98.4h79.7c35.8 0 64.8-29.6 64.9-65.3C511.5 97.1 368.1-26.9 204.3 5zM96 320c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm32-128c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128-64c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128 64c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32z");
                 svg.appendChild(path);
 
-                let color = document.createElement("div");
+                const color = document.createElement("div");
                 color.className = "buttonColor";
                 color.style.backgroundColor = TooltipTextMenuManager.Instance.color.toString();
 
-                let wrapper = document.createElement("div");
+                const wrapper = document.createElement("div");
                 wrapper.id = "colorPicker";
                 wrapper.appendChild(svg);
                 wrapper.appendChild(color);
@@ -837,26 +837,26 @@ export class TooltipTextMenu {
     public static insertColor(color: String, state: EditorState<any>, dispatch: any) {
         if (state.selection.empty) return false;
 
-        let colorMark = state.schema.mark(state.schema.marks.color, { color: color });
+        const colorMark = state.schema.mark(state.schema.marks.color, { color: color });
         dispatch(state.tr.addMark(state.selection.from, state.selection.to, colorMark));
     }
 
     createColorDropdown() {
         // menu item for color picker
-        let self = this;
-        let colors = new MenuItem({
+        const self = this;
+        const colors = new MenuItem({
             title: "",
             execEvent: "",
             class: "button-setting-disabled",
             css: "",
             render() {
-                let p = document.createElement("p");
+                const p = document.createElement("p");
                 p.textContent = "Change color:";
 
-                let colorsWrapper = document.createElement("div");
+                const colorsWrapper = document.createElement("div");
                 colorsWrapper.className = "colorPicker-wrapper";
 
-                let colors = [
+                const colors = [
                     DarkPastelSchemaPalette.get("pink2"),
                     DarkPastelSchemaPalette.get("purple4"),
                     DarkPastelSchemaPalette.get("bluegreen1"),
@@ -870,7 +870,7 @@ export class TooltipTextMenu {
                 ];
 
                 colors.forEach(color => {
-                    let button = document.createElement("button");
+                    const button = document.createElement("button");
                     button.className = color === TooltipTextMenuManager.Instance.color ? "colorPicker active" : "colorPicker";
                     if (color) {
                         button.style.backgroundColor = color;
@@ -880,8 +880,8 @@ export class TooltipTextMenu {
                             TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, self.view.state, self.view.dispatch);
 
                             // update color menu
-                            let colorDom = self.createColorTool().render(self.view).dom;
-                            let colorDropdownDom = self.createColorDropdown().render(self.view).dom;
+                            const colorDom = self.createColorTool().render(self.view).dom;
+                            const colorDropdownDom = self.createColorDropdown().render(self.view).dom;
                             self.colorDom && self.tooltip.replaceChild(colorDom, self.colorDom);
                             self.colorDropdownDom && self.tooltip.replaceChild(colorDropdownDom, self.colorDropdownDom);
                             self.colorDom = colorDom;
@@ -891,7 +891,7 @@ export class TooltipTextMenu {
                     colorsWrapper.appendChild(button);
                 });
 
-                let div = document.createElement("div");
+                const div = document.createElement("div");
                 div.appendChild(p);
                 div.appendChild(colorsWrapper);
                 return div;
@@ -902,7 +902,7 @@ export class TooltipTextMenu {
             }
         });
 
-        let colorDropdown = new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem;
+        const colorDropdown = new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem;
         return colorDropdown;
     }
 
@@ -911,7 +911,7 @@ export class TooltipTextMenu {
             height: 32, width: 32,
             path: "M30.828 1.172c-1.562-1.562-4.095-1.562-5.657 0l-5.379 5.379-3.793-3.793-4.243 4.243 3.326 3.326-14.754 14.754c-0.252 0.252-0.358 0.592-0.322 0.921h-0.008v5c0 0.552 0.448 1 1 1h5c0 0 0.083 0 0.125 0 0.288 0 0.576-0.11 0.795-0.329l14.754-14.754 3.326 3.326 4.243-4.243-3.793-3.793 5.379-5.379c1.562-1.562 1.562-4.095 0-5.657zM5.409 30h-3.409v-3.409l14.674-14.674 3.409 3.409-14.674 14.674z"
         };
-        let self = this;
+        const self = this;
         return new MenuItem({
             title: "Brush tool",
             label: "Brush tool",
@@ -923,7 +923,7 @@ export class TooltipTextMenu {
                 this.brush_function(state, dispatch);
 
                 // update dropdown with marks
-                let newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom;
+                const newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom;
                 self._brushDropdownDom && self.tooltip.replaceChild(newBrushDropdowndom, self._brushDropdownDom);
                 self._brushDropdownDom = newBrushDropdowndom;
             },
@@ -947,7 +947,7 @@ export class TooltipTextMenu {
             }
         }
         else {
-            let { from, to, $from } = this.view.state.selection;
+            const { from, to, $from } = this.view.state.selection;
             if (this._brushdom) {
                 if (!this.view.state.selection.empty && $from && $from.nodeAfter) {
                     if (TooltipTextMenuManager.Instance._brushMarks && to - from > 0) {
@@ -982,7 +982,7 @@ export class TooltipTextMenu {
         }
 
 
-        let brushInfo = new MenuItem({
+        const brushInfo = new MenuItem({
             title: "",
             label: label,
             execEvent: "",
@@ -994,17 +994,17 @@ export class TooltipTextMenu {
             }
         });
 
-        let self = this;
-        let clearBrush = new MenuItem({
+        const self = this;
+        const clearBrush = new MenuItem({
             title: "Clear brush",
             execEvent: "",
             class: "separated-button",
             css: "",
             render() {
-                let button = document.createElement("button");
+                const button = document.createElement("button");
                 button.textContent = "Clear brush";
 
-                let wrapper = document.createElement("div");
+                const wrapper = document.createElement("div");
                 wrapper.appendChild(button);
                 return wrapper;
             },
@@ -1015,24 +1015,24 @@ export class TooltipTextMenu {
 
                 // update brush tool
                 // TODO: this probably isn't very clean
-                let newBrushdom = self.createBrush().render(self.view).dom;
+                const newBrushdom = self.createBrush().render(self.view).dom;
                 self._brushdom && self.tooltip.replaceChild(newBrushdom, self._brushdom);
                 self._brushdom = newBrushdom;
-                let newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom;
+                const newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom;
                 self._brushDropdownDom && self.tooltip.replaceChild(newBrushDropdowndom, self._brushDropdownDom);
                 self._brushDropdownDom = newBrushDropdowndom;
             }
         });
 
-        let hasMarks = TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMarks.size > 0;
-        let brushDom = new Dropdown(hasMarks ? [brushInfo, clearBrush] : [brushInfo], { class: "buttonSettings-dropdown" }) as MenuItem;
+        const hasMarks = TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMarks.size > 0;
+        const brushDom = new Dropdown(hasMarks ? [brushInfo, clearBrush] : [brushInfo], { class: "buttonSettings-dropdown" }) as MenuItem;
         return brushDom;
     }
     //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected textchangeToMarkInGroup = (markType: MarkType | undefined, view: EditorView, fontMarks: MarkType[]) => {
     changeToMarkInGroup = (markType: MarkType | undefined, view: EditorView, fontMarks: MarkType[]) => {
-        let { $cursor, ranges } = view.state.selection as TextSelection;
-        let state = view.state;
-        let dispatch = view.dispatch;
+        const { $cursor, ranges } = view.state.selection as TextSelection;
+        const state = view.state;
+        const dispatch = view.dispatch;
 
         //remove all other active font marks
         fontMarks.forEach((type) => {
@@ -1044,10 +1044,10 @@ export class TooltipTextMenu {
                 } else {
                     let has = false;
                     for (let i = 0; !has && i < ranges.length; i++) {
-                        let { $from, $to } = ranges[i];
+                        const { $from, $to } = ranges[i];
                         has = state.doc.rangeHasMark($from.pos, $to.pos, type);
                     }
-                    for (let i of ranges) {
+                    for (const i of ranges) {
                         if (has) {
                             toggleMark(type)(view.state, view.dispatch, view);
                         }
@@ -1059,7 +1059,7 @@ export class TooltipTextMenu {
         if (markType) {
             //actually apply font
             if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
-                let status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
+                const status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
                     { ...(view.state.selection as NodeSelection).node.attrs, setFontFamily: markType.name, setFontSize: Number(markType.name.replace(/p/, "")) }), view.state.schema);
                 view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
             }
@@ -1068,9 +1068,9 @@ export class TooltipTextMenu {
     }
 
     changeToFontFamily = (mark: Mark, view: EditorView) => {
-        let { $cursor, ranges } = view.state.selection as TextSelection;
-        let state = view.state;
-        let dispatch = view.dispatch;
+        const { $cursor, ranges } = view.state.selection as TextSelection;
+        const state = view.state;
+        const dispatch = view.dispatch;
 
         //remove all other active font marks
         if ($cursor) {
@@ -1080,28 +1080,28 @@ export class TooltipTextMenu {
         } else {
             let has = false;
             for (let i = 0; !has && i < ranges.length; i++) {
-                let { $from, $to } = ranges[i];
+                const { $from, $to } = ranges[i];
                 has = state.doc.rangeHasMark($from.pos, $to.pos, view.state.schema.marks.pFontFamily);
             }
-            for (let i of ranges) {
+            for (const i of ranges) {
                 if (has) {
                     toggleMark(view.state.schema.marks.pFontFamily)(view.state, view.dispatch, view);
                 }
             }
         }
 
-        let fontName = mark.attrs.family;
+        const fontName = mark.attrs.family;
         if (fontName) { this.updateFontStyleDropdown(fontName); }
         if (this.editorProps) {
-            let ruleProvider = this.editorProps.ruleProvider;
-            let heading = NumCast(this.editorProps.Document.heading);
+            const ruleProvider = this.editorProps.ruleProvider;
+            const heading = NumCast(this.editorProps.Document.heading);
             if (ruleProvider && heading) {
                 ruleProvider["ruleFont_" + heading] = fontName;
             }
         }
         //actually apply font
         if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
-            let status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
+            const status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
                 { ...(view.state.selection as NodeSelection).node.attrs, setFontFamily: fontName }), view.state.schema);
             view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
         }
@@ -1110,9 +1110,9 @@ export class TooltipTextMenu {
     }
 
     changeToFontSize = (mark: Mark, view: EditorView) => {
-        let { $cursor, ranges } = view.state.selection as TextSelection;
-        let state = view.state;
-        let dispatch = view.dispatch;
+        const { $cursor, ranges } = view.state.selection as TextSelection;
+        const state = view.state;
+        const dispatch = view.dispatch;
 
         //remove all other active font marks
         if ($cursor) {
@@ -1122,28 +1122,28 @@ export class TooltipTextMenu {
         } else {
             let has = false;
             for (let i = 0; !has && i < ranges.length; i++) {
-                let { $from, $to } = ranges[i];
+                const { $from, $to } = ranges[i];
                 has = state.doc.rangeHasMark($from.pos, $to.pos, view.state.schema.marks.pFontSize);
             }
-            for (let i of ranges) {
+            for (const i of ranges) {
                 if (has) {
                     toggleMark(view.state.schema.marks.pFontSize)(view.state, view.dispatch, view);
                 }
             }
         }
 
-        let size = mark.attrs.fontSize;
+        const size = mark.attrs.fontSize;
         if (size) { this.updateFontSizeDropdown(String(size) + " pt"); }
         if (this.editorProps) {
-            let ruleProvider = this.editorProps.ruleProvider;
-            let heading = NumCast(this.editorProps.Document.heading);
+            const ruleProvider = this.editorProps.ruleProvider;
+            const heading = NumCast(this.editorProps.Document.heading);
             if (ruleProvider && heading) {
                 ruleProvider["ruleSize_" + heading] = size;
             }
         }
         //actually apply font
         if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
-            let status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
+            const status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
                 { ...(view.state.selection as NodeSelection).node.attrs, setFontSize: size }), view.state.schema);
             view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
         }
@@ -1154,20 +1154,20 @@ export class TooltipTextMenu {
     //remove all node typeand apply the passed-in one to the selected text
     changeToNodeType = (nodeType: NodeType | undefined) => {
         //remove oldif (nodeType) { //add new
-        let view = this.view;
+        const view = this.view;
         if (nodeType === schema.nodes.bullet_list) {
             wrapInList(nodeType)(view.state, view.dispatch);
         } else {
-            var marks = view.state.storedMarks || (view.state.selection.$to.parentOffset && view.state.selection.$from.marks());
+            const marks = view.state.storedMarks || (view.state.selection.$to.parentOffset && view.state.selection.$from.marks());
             if (!wrapInList(schema.nodes.ordered_list)(view.state, (tx2: any) => {
-                let tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle);
+                const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle);
                 marks && tx3.ensureMarks([...marks]);
                 marks && tx3.setStoredMarks([...marks]);
 
                 view.dispatch(tx2);
             })) {
-                let tx2 = view.state.tr;
-                let tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle);
+                const tx2 = view.state.tr;
+                const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle);
                 marks && tx3.ensureMarks([...marks]);
                 marks && tx3.setStoredMarks([...marks]);
 
@@ -1223,15 +1223,15 @@ export class TooltipTextMenu {
         });
     }
 
-    markActive = function(state: EditorState<any>, type: MarkType<Schema<string, string>>) {
-        let { from, $from, to, empty } = state.selection;
+    markActive = function (state: EditorState<any>, type: MarkType<Schema<string, string>>) {
+        const { from, $from, to, empty } = state.selection;
         if (empty) return type.isInSet(state.storedMarks || $from.marks());
         else return state.doc.rangeHasMark(from, to, type);
     };
 
     // Helper function to create menu icons
     icon(text: string, name: string, title: string = name) {
-        let span = document.createElement("span");
+        const span = document.createElement("span");
         span.className = name + " menuicon";
         span.title = title;
         span.textContent = text;
@@ -1240,13 +1240,13 @@ export class TooltipTextMenu {
     }
 
     svgIcon(name: string, title: string = name, dpath: string) {
-        let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+        const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
         svg.setAttribute("viewBox", "-100 -100 650 650");
-        let path = document.createElementNS('http://www.w3.org/2000/svg', "path");
+        const path = document.createElementNS('http://www.w3.org/2000/svg', "path");
         path.setAttributeNS(null, "d", dpath);
         svg.appendChild(path);
 
-        let span = document.createElement("span");
+        const span = document.createElement("span");
         span.className = name + " menuicon";
         span.title = title;
         span.appendChild(svg);
@@ -1256,9 +1256,9 @@ export class TooltipTextMenu {
 
     //method for checking whether node can be inserted
     canInsert(state: EditorState, nodeType: NodeType<Schema<string, string>>) {
-        let $from = state.selection.$from;
+        const $from = state.selection.$from;
         for (let d = $from.depth; d >= 0; d--) {
-            let index = $from.index(d);
+            const index = $from.index(d);
             if ($from.node(d).canReplaceWith(index, index, nodeType)) return true;
         }
         return false;
@@ -1267,13 +1267,13 @@ export class TooltipTextMenu {
 
     //adapted this method - use it to check if block has a tag (ie bulleting)
     blockActive(type: NodeType<Schema<string, string>>, state: EditorState) {
-        let attrs = {};
+        const attrs = {};
 
         if (state.selection instanceof NodeSelection) {
             const sel: NodeSelection = state.selection;
-            let $from = sel.$from;
-            let to = sel.to;
-            let node = sel.node;
+            const $from = sel.$from;
+            const to = sel.to;
+            const node = sel.node;
 
             if (node) {
                 return node.hasMarkup(type, attrs);
@@ -1292,10 +1292,10 @@ export class TooltipTextMenu {
     }
 
     getMarksInSelection(state: EditorState<any>) {
-        let found = new Set<Mark>();
-        let { from, to } = state.selection as TextSelection;
+        const found = new Set<Mark>();
+        const { from, to } = state.selection as TextSelection;
         state.doc.nodesBetween(from, to, (node) => {
-            let marks = node.marks;
+            const marks = node.marks;
             if (marks) {
                 marks.forEach(m => {
                     found.add(m);
@@ -1306,7 +1306,7 @@ export class TooltipTextMenu {
     }
 
     reset_mark_doms() {
-        let iterator = this._marksToDoms.values();
+        const iterator = this._marksToDoms.values();
         let next = iterator.next();
         while (!next.done) {
             next.value.style.color = "white";
@@ -1322,7 +1322,7 @@ export class TooltipTextMenu {
             return;
         }
         this.view = view;
-        let state = view.state;
+        const state = view.state;
         DocumentDecorations.Instance.showTextBar();
         props && (this.editorProps = props);
         // Don't do anything if the document/selection didn't change
@@ -1338,13 +1338,13 @@ export class TooltipTextMenu {
         }
 
         // update link dropdown
-        let linkDropdown = await this.createLinkDropdown();
-        let newLinkDropdowndom = linkDropdown.render(this.view).dom;
+        const linkDropdown = await this.createLinkDropdown();
+        const newLinkDropdowndom = linkDropdown.render(this.view).dom;
         this._linkDropdownDom && this.tooltip.replaceChild(newLinkDropdowndom, this._linkDropdownDom);
         this._linkDropdownDom = newLinkDropdowndom;
 
         //UPDATE FONT STYLE DROPDOWN
-        let activeStyles = this.activeFontFamilyOnSelection();
+        const activeStyles = this.activeFontFamilyOnSelection();
         if (activeStyles !== undefined) {
             if (activeStyles.length === 1) {
                 console.log("updating font style dropdown", activeStyles[0]);
@@ -1353,7 +1353,7 @@ export class TooltipTextMenu {
         }
 
         //UPDATE FONT SIZE DROPDOWN
-        let activeSizes = this.activeFontSizeOnSelection();
+        const activeSizes = this.activeFontSizeOnSelection();
         if (activeSizes !== undefined) {
             if (activeSizes.length === 1) { //if there's only one active font size
                 activeSizes[0] && this.updateFontSizeDropdown(String(activeSizes[0]) + " pt");
@@ -1366,7 +1366,7 @@ export class TooltipTextMenu {
         this.reset_mark_doms();
         this._activeMarks.forEach((mark) => {
             if (this._marksToDoms.has(mark)) {
-                let dom = this._marksToDoms.get(mark);
+                const dom = this._marksToDoms.get(mark);
                 if (dom) dom.style.color = "greenyellow";
             }
         });
@@ -1385,8 +1385,8 @@ export class TooltipTextMenu {
     //finds fontSize at start of selection
     activeFontSizeOnSelection() {
         //current selection
-        let state = this.view.state;
-        let activeSizes: number[] = [];
+        const state = this.view.state;
+        const activeSizes: number[] = [];
         const pos = this.view.state.selection.$from;
         const ref_node: ProsNode = this.reference_node(pos);
         if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) {
@@ -1397,8 +1397,8 @@ export class TooltipTextMenu {
     //finds fontSize at start of selection
     activeFontFamilyOnSelection() {
         //current selection
-        let state = this.view.state;
-        let activeFamilies: string[] = [];
+        const state = this.view.state;
+        const activeFamilies: string[] = [];
         const pos = this.view.state.selection.$from;
         const ref_node: ProsNode = this.reference_node(pos);
         if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) {
@@ -1409,15 +1409,15 @@ export class TooltipTextMenu {
     //finds all active marks on selection in given group
     activeMarksOnSelection(markGroup: MarkType[]) {
         //current selection
-        let { empty, ranges, $to } = this.view.state.selection as TextSelection;
-        let state = this.view.state;
-        let dispatch = this.view.dispatch;
+        const { empty, ranges, $to } = this.view.state.selection as TextSelection;
+        const state = this.view.state;
+        const dispatch = this.view.dispatch;
         let activeMarks: MarkType[];
         if (!empty) {
             activeMarks = markGroup.filter(mark => {
-                let has = false;
+                const has = false;
                 for (let i = 0; !has && i < ranges.length; i++) {
-                    let { $from, $to } = ranges[i];
+                    const { $from, $to } = ranges[i];
                     return state.doc.rangeHasMark($from.pos, $to.pos, mark);
                 }
                 return false;
@@ -1440,7 +1440,7 @@ export class TooltipTextMenu {
                     if (mark_type === state.schema.marks.pFontSize) {
                         return ref_node.marks.some(m => m.type.name === state.schema.marks.pFontSize.name);
                     }
-                    let mark = state.schema.mark(mark_type);
+                    const mark = state.schema.mark(mark_type);
                     return ref_node.marks.includes(mark);
                     return false;
                 });
diff --git a/src/client/util/TypedEvent.ts b/src/client/util/TypedEvent.ts
index 532ba78eb..90fd299c1 100644
--- a/src/client/util/TypedEvent.ts
+++ b/src/client/util/TypedEvent.ts
@@ -1,40 +1,40 @@
 export interface Listener<T> {
-  (event: T): any;
+    (event: T): any;
 }
 
 export interface Disposable {
-  dispose(): void;
+    dispose(): void;
 }
 
 /** passes through events as they happen. You will not get events from before you start listening */
 export class TypedEvent<T> {
-  private listeners: Listener<T>[] = [];
-  private listenersOncer: Listener<T>[] = [];
-
-  on = (listener: Listener<T>): Disposable => {
-    this.listeners.push(listener);
-    return {
-      dispose: () => this.off(listener)
-    };
-  }
-
-  once = (listener: Listener<T>): void => {
-    this.listenersOncer.push(listener);
-  }
-
-  off = (listener: Listener<T>) => {
-    var callbackIndex = this.listeners.indexOf(listener);
-    if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1);
-  }
-
-  emit = (event: T) => {
-    /** Update any general listeners */
-    this.listeners.forEach((listener) => listener(event));
-
-    /** Clear the `once` queue */
-    this.listenersOncer.forEach((listener) => listener(event));
-    this.listenersOncer = [];
-  }
-
-  pipe = (te: TypedEvent<T>): Disposable => this.on((e) => te.emit(e));
+    private listeners: Listener<T>[] = [];
+    private listenersOncer: Listener<T>[] = [];
+
+    on = (listener: Listener<T>): Disposable => {
+        this.listeners.push(listener);
+        return {
+            dispose: () => this.off(listener)
+        };
+    }
+
+    once = (listener: Listener<T>): void => {
+        this.listenersOncer.push(listener);
+    }
+
+    off = (listener: Listener<T>) => {
+        const callbackIndex = this.listeners.indexOf(listener);
+        if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1);
+    }
+
+    emit = (event: T) => {
+        /** Update any general listeners */
+        this.listeners.forEach((listener) => listener(event));
+
+        /** Clear the `once` queue */
+        this.listenersOncer.forEach((listener) => listener(event));
+        this.listenersOncer = [];
+    }
+
+    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 472afac1d..314b52bf3 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -3,7 +3,7 @@ import 'source-map-support/register';
 import { Without } from "../../Utils";
 
 function getBatchName(target: any, key: string | symbol): string {
-    let keyName = key.toString();
+    const keyName = key.toString();
     if (target && target.constructor && target.constructor.name) {
         return `${target.constructor.name}.${keyName}`;
     }
@@ -23,7 +23,7 @@ function propertyDecorator(target: any, key: string | symbol) {
                 writable: true,
                 configurable: true,
                 value: function (...args: any[]) {
-                    let batch = UndoManager.StartBatch(getBatchName(target, key));
+                    const batch = UndoManager.StartBatch(getBatchName(target, key));
                     try {
                         return value.apply(this, args);
                     } finally {
@@ -40,7 +40,7 @@ export function undoBatch(fn: (...args: any[]) => any): (...args: any[]) => any;
 export function undoBatch(target: any, key?: string | symbol, descriptor?: TypedPropertyDescriptor<any>): any {
     if (!key) {
         return function () {
-            let batch = UndoManager.StartBatch("");
+            const batch = UndoManager.StartBatch("");
             try {
                 return target.apply(undefined, arguments);
             } finally {
@@ -55,7 +55,7 @@ export function undoBatch(target: any, key?: string | symbol, descriptor?: Typed
     const oldFunction = descriptor.value;
 
     descriptor.value = function (...args: any[]) {
-        let batch = UndoManager.StartBatch(getBatchName(target, key));
+        const batch = UndoManager.StartBatch(getBatchName(target, key));
         try {
             return oldFunction.apply(this, args);
         } finally {
@@ -98,7 +98,7 @@ export namespace UndoManager {
         GetOpenBatches().forEach(batch => console.log(batch.batchName));
     }
 
-    let openBatches: Batch[] = [];
+    const openBatches: Batch[] = [];
     export function GetOpenBatches(): Without<Batch, 'end'>[] {
         return openBatches;
     }
@@ -146,7 +146,7 @@ export namespace UndoManager {
 
     //TODO Make this return the return value
     export function RunInBatch<T>(fn: () => T, batchName: string) {
-        let batch = StartBatch(batchName);
+        const batch = StartBatch(batchName);
         try {
             return runInAction(fn);
         } finally {
@@ -159,7 +159,7 @@ export namespace UndoManager {
             return;
         }
 
-        let commands = undoStack.pop();
+        const commands = undoStack.pop();
         if (!commands) {
             return;
         }
@@ -178,7 +178,7 @@ export namespace UndoManager {
             return;
         }
 
-        let commands = redoStack.pop();
+        const commands = redoStack.pop();
         if (!commands) {
             return;
         }
diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx
index f718735a8..09e4ef99c 100644
--- a/src/client/views/CollectionLinearView.tsx
+++ b/src/client/views/CollectionLinearView.tsx
@@ -48,12 +48,12 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
     dimension = () => NumCast(this.props.Document.height); // 2 * the padding
     getTransform = (ele: React.RefObject<HTMLDivElement>) => () => {
         if (!ele.current) return Transform.Identity();
-        let { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current);
+        const { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current);
         return new Transform(-translateX, -translateY, 1 / scale);
     }
 
     render() {
-        let guid = Utils.GenerateGuid();
+        const guid = Utils.GenerateGuid();
         return <div className="collectionLinearView-outer">
             <div className="collectionLinearView" ref={this.createDropTarget} >
                 <input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.isExpanded)} ref={this.addMenuToggle}
@@ -62,10 +62,10 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
 
                 <div className="collectionLinearView-content" style={{ height: this.dimension(), width: NumCast(this.props.Document.width, 25) }}>
                     {this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => {
-                        let nested = pair.layout.viewType === CollectionViewType.Linear;
-                        let dref = React.createRef<HTMLDivElement>();
-                        let nativeWidth = NumCast(pair.layout.nativeWidth, this.dimension());
-                        let deltaSize = nativeWidth * .15 / 2;
+                        const nested = pair.layout.viewType === CollectionViewType.Linear;
+                        const dref = React.createRef<HTMLDivElement>();
+                        const nativeWidth = NumCast(pair.layout.nativeWidth, this.dimension());
+                        const deltaSize = nativeWidth * .15 / 2;
                         return <div className={`collectionLinearView-docBtn` + (pair.layout.onClick || pair.layout.onDragStart ? "-scalable" : "")} key={pair.layout[Id]} ref={dref}
                             style={{
                                 width: nested ? pair.layout[WidthSym]() : this.dimension() - deltaSize,
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 5d452e72e..937aff0d6 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -49,8 +49,8 @@ export class ContextMenu extends React.Component {
     @action
     onPointerUp = (e: PointerEvent) => {
         this._mouseDown = false;
-        let curX = e.clientX;
-        let curY = e.clientY;
+        const curX = e.clientX;
+        const curY = e.clientY;
         if (this._mouseX !== curX || this._mouseY !== curY) {
             this._shouldDisplay = false;
         }
@@ -208,7 +208,7 @@ export class ContextMenu extends React.Component {
         if (!this._display) {
             return null;
         }
-        let style = this._yRelativeToTop ? { left: this.pageX, top: this.pageY } :
+        const style = this._yRelativeToTop ? { left: this.pageX, top: this.pageY } :
             { left: this.pageX, bottom: this.pageY };
 
         const contents = (
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index 330b94afa..fef9e5f60 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -88,7 +88,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
                 </div>
             );
         } else if ("subitems" in this.props) {
-            let submenu = !this.overItem ? (null) :
+            const submenu = !this.overItem ? (null) :
                 <div className="contextMenu-subMenu-cont" style={{ marginLeft: "25%", left: "0px" }}>
                     {this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)}
                 </div>;
diff --git a/src/client/views/DictationOverlay.tsx b/src/client/views/DictationOverlay.tsx
index 2accf9bfd..65770c0bb 100644
--- a/src/client/views/DictationOverlay.tsx
+++ b/src/client/views/DictationOverlay.tsx
@@ -24,7 +24,7 @@ export class DictationOverlay extends React.Component {
     }
 
     public initiateDictationFade = () => {
-        let duration = DictationManager.Commands.dictationFadeDuration;
+        const duration = DictationManager.Commands.dictationFadeDuration;
         this.overlayTimeout = setTimeout(() => {
             this.dictationOverlayVisible = false;
             this.dictationSuccess = undefined;
@@ -50,14 +50,14 @@ export class DictationOverlay extends React.Component {
     public set isListening(value: DictationManager.Controls.ListeningUIStatus) { runInAction(() => this._dictationListeningState = value); }
 
     render() {
-        let success = this.dictationSuccess;
-        let result = this.isListening && !this.isListening.interim ? DictationManager.placeholder : `"${this.dictatedPhrase}"`;
-        let dialogueBoxStyle = {
+        const success = this.dictationSuccess;
+        const result = this.isListening && !this.isListening.interim ? DictationManager.placeholder : `"${this.dictatedPhrase}"`;
+        const dialogueBoxStyle = {
             background: success === undefined ? "gainsboro" : success ? "lawngreen" : "red",
             borderColor: this.isListening ? "red" : "black",
             fontStyle: "italic"
         };
-        let overlayStyle = {
+        const overlayStyle = {
             backgroundColor: this.isListening ? "red" : "darkslategrey"
         };
         return (<MainViewModal
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index b3a130b33..27ee9f122 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,4 +1,3 @@
-import * as React from 'react';
 import { Doc } from '../../new_fields/Doc';
 import { Touchable } from './Touchable';
 import { computed, action, observable } from 'mobx';
@@ -65,8 +64,8 @@ export function DocAnnotatableComponent<P extends DocAnnotatableProps, T>(schema
         @action.bound
         removeDocument(doc: Doc): boolean {
             Doc.GetProto(doc).annotationOn = undefined;
-            let value = this.extensionDoc && Cast(this.extensionDoc[this.annotationsKey], listSpec(Doc), []);
-            let index = value ? Doc.IndexOf(doc, value.map(d => d as Doc), true) : -1;
+            const value = this.extensionDoc && Cast(this.extensionDoc[this.annotationsKey], listSpec(Doc), []);
+            const index = value ? Doc.IndexOf(doc, value.map(d => d as Doc), true) : -1;
             return index !== -1 && value && value.splice(index, 1) ? true : false;
         }
         // if the moved document is already in this overlay collection nothing needs to be done.
diff --git a/src/client/views/DocumentButtonBar.scss b/src/client/views/DocumentButtonBar.scss
index db6bf2ba0..c2ca93900 100644
--- a/src/client/views/DocumentButtonBar.scss
+++ b/src/client/views/DocumentButtonBar.scss
@@ -17,6 +17,7 @@ $linkGap : 3px;
     transform: scale(1.05);
     cursor: pointer;
 }
+
 .documentButtonBar-linkButton-empty,
 .documentButtonBar-linkButton-nonempty {
     height: 20px;
@@ -74,6 +75,31 @@ $linkGap : 3px;
 }
 
 
-@-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } }
-@-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } }
-@keyframes spin { 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } }
\ No newline at end of file
+@-moz-keyframes spin {
+    100% {
+        -moz-transform: rotate(360deg);
+    }
+}
+
+@-webkit-keyframes spin {
+    100% {
+        -webkit-transform: rotate(360deg);
+    }
+}
+
+@keyframes spin {
+    100% {
+        -webkit-transform: rotate(360deg);
+        transform: rotate(360deg);
+    }
+}
+
+@keyframes shadow-pulse {
+    0% {
+        box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.8);
+    }
+
+    100% {
+        box-shadow: 0 0 0 10px rgba(0, 255, 0, 0);
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 1fefc70f1..15cbd2788 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -21,6 +21,7 @@ import React = require("react");
 import { DocumentView } from './nodes/DocumentView';
 import { ParentDocSelector } from './collections/ParentDocumentSelector';
 import { CollectionDockingView } from './collections/CollectionDockingView';
+import { DocumentDecorations } from './DocumentDecorations';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -51,7 +52,9 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[],
     @observable private pushIcon: IconProp = "arrow-alt-circle-up";
     @observable private pullIcon: IconProp = "arrow-alt-circle-down";
     @observable private pullColor: string = "white";
-    @observable private isAnimatingFetch = false;
+    @observable public isAnimatingFetch = false;
+    @observable public isAnimatingPulse = false;
+
     @observable private openHover = false;
 
     public static Instance: DocumentButtonBar;
@@ -75,6 +78,7 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[],
     });
 
     public startPushOutcome = action((success: boolean) => {
+        this.isAnimatingPulse = false;
         if (!this._pushAnimating) {
             this._pushAnimating = true;
             this.pushIcon = success ? "check-circle" : "stop-circle";
@@ -99,27 +103,26 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[],
         this._pullColorAnimating = false;
     });
 
-
     @action
     onLinkButtonMoved = (e: PointerEvent): void => {
         if (this._linkButton.current !== null && (Math.abs(e.clientX - this._downX) > 3 || Math.abs(e.clientY - this._downY) > 3)) {
             document.removeEventListener("pointermove", this.onLinkButtonMoved);
             document.removeEventListener("pointerup", this.onLinkButtonUp);
-            let docView = this.props.views[0];
-            let container = docView.props.ContainingCollectionDoc?.proto;
-            let dragData = new DragManager.LinkDragData(docView.props.Document, container ? [container] : []);
-            let linkDrag = UndoManager.StartBatch("Drag Link");
+            const docView = this.props.views[0];
+            const container = docView.props.ContainingCollectionDoc?.proto;
+            const dragData = new DragManager.LinkDragData(docView.props.Document, container ? [container] : []);
+            const linkDrag = UndoManager.StartBatch("Drag Link");
             DragManager.StartLinkDrag(this._linkButton.current, dragData, e.pageX, e.pageY, {
                 handlers: {
                     dragComplete: () => {
-                        let tooltipmenu = FormattedTextBox.ToolTipTextMenu;
-                        let linkDoc = dragData.linkDocument;
+                        const tooltipmenu = FormattedTextBox.ToolTipTextMenu;
+                        const linkDoc = dragData.linkDocument;
                         if (linkDoc && tooltipmenu) {
-                            let proto = Doc.GetProto(linkDoc);
+                            const proto = Doc.GetProto(linkDoc);
                             if (proto && docView) {
                                 proto.sourceContext = docView.props.ContainingCollectionDoc;
                             }
-                            let text = tooltipmenu.makeLink(linkDoc, StrCast(linkDoc.anchor2.title), e.ctrlKey ? "onRight" : "inTab");
+                            const text = tooltipmenu.makeLink(linkDoc, StrCast(linkDoc.anchor2.title), e.ctrlKey ? "onRight" : "inTab");
                             if (linkDoc instanceof Doc && linkDoc.anchor2 instanceof Doc) {
                                 proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODODO open to more descriptive descriptions of following in text link
                             }
@@ -152,21 +155,27 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[],
 
     @computed
     get considerGoogleDocsPush() {
-        let targetDoc = this.props.views[0].props.Document;
-        let published = Doc.GetProto(targetDoc)[GoogleRef] !== undefined;
-        return <div title={`${published ? "Push" : "Publish"} to Google Docs`} className="documentButtonBar-linker" onClick={() => {
-            DocumentButtonBar.hasPushedHack = false;
-            targetDoc[Pushes] = NumCast(targetDoc[Pushes]) + 1;
-        }}>
+        const targetDoc = this.props.views[0].props.Document;
+        const published = Doc.GetProto(targetDoc)[GoogleRef] !== undefined;
+        const animation = this.isAnimatingPulse ? "shadow-pulse 1s linear infinite" : "none";
+        return <div
+            title={`${published ? "Push" : "Publish"} to Google Docs`}
+            className="documentButtonBar-linker"
+            style={{ animation }}
+            onClick={() => {
+                !published && runInAction(() => this.isAnimatingPulse = true);
+                DocumentButtonBar.hasPushedHack = false;
+                targetDoc[Pushes] = NumCast(targetDoc[Pushes]) + 1;
+            }}>
             <FontAwesomeIcon className="documentdecorations-icon" icon={published ? (this.pushIcon as any) : cloud} size={published ? "sm" : "xs"} />
         </div>;
     }
 
     @computed
     get considerGoogleDocsPull() {
-        let targetDoc = this.props.views[0].props.Document;
-        let dataDoc = Doc.GetProto(targetDoc);
-        let animation = this.isAnimatingFetch ? "spin 0.5s linear infinite" : "none";
+        const targetDoc = this.props.views[0].props.Document;
+        const dataDoc = Doc.GetProto(targetDoc);
+        const animation = this.isAnimatingFetch ? "spin 0.5s linear infinite" : "none";
         return !dataDoc[GoogleRef] ? (null) : <div className="documentButtonBar-linker"
             title={`${!dataDoc.unchanged ? "Pull from" : "Fetch"} Google Docs`}
             style={{ backgroundColor: this.pullColor }}
@@ -192,7 +201,7 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[],
 
     @computed
     get linkButton() {
-        let linkCount = LinkManager.Instance.getAllRelatedLinks(this.props.views[0].props.Document).length;
+        const linkCount = LinkManager.Instance.getAllRelatedLinks(this.props.views[0].props.Document).length;
         return <div title="Drag(create link) Tap(view links)" className="documentButtonBar-linkFlyout" ref={this._linkButton}>
             <Flyout anchorPoint={anchorPoints.RIGHT_TOP}
                 content={<LinkMenu docView={this.props.views[0]} addDocTab={this.props.views[0].props.addDocTab} changeFlyout={emptyFunction} />}>
@@ -214,13 +223,13 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[],
     }
 
     render() {
-        let templates: Map<Template, boolean> = new Map();
+        const templates: Map<Template, boolean> = new Map();
         Array.from(Object.values(Templates.TemplateList)).map(template =>
             templates.set(template, this.props.views.reduce((checked, doc) => checked || doc.getLayoutPropStr("show" + template.Name) ? true : false, false as boolean)));
 
-        let isText = this.props.views[0].props.Document.data instanceof RichTextField; // bcz: Todo - can't assume layout is using the 'data' field.  need to add fieldKey to DocumentView
-        let considerPull = isText && this.considerGoogleDocsPull;
-        let considerPush = isText && this.considerGoogleDocsPush;
+        const isText = this.props.views[0].props.Document.data instanceof RichTextField; // bcz: Todo - can't assume layout is using the 'data' field.  need to add fieldKey to DocumentView
+        const considerPull = isText && this.considerGoogleDocsPull;
+        const considerPush = isText && this.considerGoogleDocsPush;
         return <div className="documentButtonBar">
             <div className="documentButtonBar-button">
                 {this.linkButton}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 66f47147f..7b0c31e37 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -24,7 +24,6 @@ import { DocumentView } from "./nodes/DocumentView";
 import { FieldView } from "./nodes/FieldView";
 import { IconBox } from "./nodes/IconBox";
 import React = require("react");
-import { PointData } from '../../new_fields/InkField';
 import { DocumentType } from '../documents/DocumentTypes';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
@@ -68,8 +67,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @observable public pushIcon: IconProp = "arrow-alt-circle-up";
     @observable public pullIcon: IconProp = "arrow-alt-circle-down";
     @observable public pullColor: string = "white";
-    @observable public isAnimatingFetch = false;
-    @observable public isAnimatingPulse = false;
     @observable public openHover = false;
 
     constructor(props: Readonly<{}>) {
@@ -82,25 +79,25 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @action titleChanged = (event: any) => { this._title = event.target.value; };
     @action titleBlur = () => { this._edtingTitle = false; };
     @action titleEntered = (e: any) => {
-        var key = e.keyCode || e.which;
+        const key = e.keyCode || e.which;
         // enter pressed
         if (key === 13) {
-            var text = e.target.value;
+            const text = e.target.value;
             if (text[0] === '#') {
                 this._fieldKey = text.slice(1, text.length);
                 this._title = this.selectionTitle;
             } else if (text.startsWith("::")) {
-                let targetID = text.slice(2, text.length);
-                let promoteDoc = SelectionManager.SelectedDocuments()[0];
+                const targetID = text.slice(2, text.length);
+                const promoteDoc = SelectionManager.SelectedDocuments()[0];
                 DocUtils.Publish(promoteDoc.props.Document, targetID, promoteDoc.props.addDocument, promoteDoc.props.removeDocument);
             } else if (text.startsWith(">")) {
-                let fieldTemplateView = SelectionManager.SelectedDocuments()[0];
+                const fieldTemplateView = SelectionManager.SelectedDocuments()[0];
                 SelectionManager.DeselectAll();
-                let fieldTemplate = fieldTemplateView.props.Document;
-                let containerView = fieldTemplateView.props.ContainingCollectionView;
-                let docTemplate = fieldTemplateView.props.ContainingCollectionDoc;
+                const fieldTemplate = fieldTemplateView.props.Document;
+                const containerView = fieldTemplateView.props.ContainingCollectionView;
+                const docTemplate = fieldTemplateView.props.ContainingCollectionDoc;
                 if (containerView && docTemplate) {
-                    let metaKey = text.startsWith(">>") ? text.slice(2, text.length) : text.slice(1, text.length);
+                    const metaKey = text.startsWith(">>") ? text.slice(2, text.length) : text.slice(1, text.length);
                     if (metaKey !== containerView.props.fieldKey && containerView.props.DataDoc) {
                         const fd = fieldTemplate.data;
                         fd instanceof ObjectField && (Doc.GetProto(containerView.props.DataDoc)[metaKey] = ObjectField.MakeCopy(fd));
@@ -115,15 +112,15 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             else {
                 if (SelectionManager.SelectedDocuments().length > 0) {
                     SelectionManager.SelectedDocuments()[0].props.Document.customTitle = !this._title.startsWith("-");
-                    let field = SelectionManager.SelectedDocuments()[0].props.Document[this._fieldKey];
+                    const field = SelectionManager.SelectedDocuments()[0].props.Document[this._fieldKey];
                     if (typeof field === "number") {
                         SelectionManager.SelectedDocuments().forEach(d => {
-                            let doc = d.props.Document.proto ? d.props.Document.proto : d.props.Document;
+                            const doc = d.props.Document.proto ? d.props.Document.proto : d.props.Document;
                             doc[this._fieldKey] = +this._title;
                         });
                     } else {
                         SelectionManager.SelectedDocuments().forEach(d => {
-                            let doc = d.props.Document.proto ? d.props.Document.proto : d.props.Document;
+                            const doc = d.props.Document.proto ? d.props.Document.proto : d.props.Document;
                             doc[this._fieldKey] = this._title;
                         });
                     }
@@ -165,11 +162,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 Doc.AreProtosEqual(documentView.props.Document, CurrentUserUtils.UserDocument)) {
                 return bounds;
             }
-            let transform = (documentView.props.ScreenToLocalTransform().scale(documentView.props.ContentScaling())).inverse();
+            const transform = (documentView.props.ScreenToLocalTransform().scale(documentView.props.ContentScaling())).inverse();
             var [sptX, sptY] = transform.transformPoint(0, 0);
             let [bptX, bptY] = transform.transformPoint(documentView.props.PanelWidth(), documentView.props.PanelHeight());
             if (documentView.props.Document.type === DocumentType.LINK) {
-                let rect = documentView.ContentDiv!.getElementsByClassName("docuLinkBox-cont")[0].getBoundingClientRect();
+                const rect = documentView.ContentDiv!.getElementsByClassName("docuLinkBox-cont")[0].getBoundingClientRect();
                 sptX = rect.left;
                 sptY = rect.top;
                 bptX = rect.right;
@@ -192,8 +189,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
     @action
     onBackgroundMove = (e: PointerEvent): void => {
-        let dragDocView = SelectionManager.SelectedDocuments()[0];
-        let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
+        const dragDocView = SelectionManager.SelectedDocuments()[0];
+        const dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
         const [left, top] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).inverse().transformPoint(0, 0);
         dragData.offset = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).transformDirection(e.x - left, e.y - top);
         dragData.moveDocument = SelectionManager.SelectedDocuments()[0].props.moveDocument;
@@ -256,8 +253,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             this._downX = e.pageX;
             this._downY = e.pageY;
             this._removeIcon = false;
-            let selDoc = SelectionManager.SelectedDocuments()[0];
-            let selDocPos = selDoc.props.ScreenToLocalTransform().scale(selDoc.props.ContentScaling()).inverse().transformPoint(0, 0);
+            const selDoc = SelectionManager.SelectedDocuments()[0];
+            const selDocPos = selDoc.props.ScreenToLocalTransform().scale(selDoc.props.ContentScaling()).inverse().transformPoint(0, 0);
             this._minimizedX = selDocPos[0] + 12;
             this._minimizedY = selDocPos[1] + 12;
             document.removeEventListener("pointermove", this.onMinimizeMove);
@@ -272,12 +269,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         e.stopPropagation();
         if (Math.abs(e.pageX - this._downX) > Utils.DRAG_THRESHOLD ||
             Math.abs(e.pageY - this._downY) > Utils.DRAG_THRESHOLD) {
-            let selDoc = SelectionManager.SelectedDocuments()[0];
-            let selDocPos = selDoc.props.ScreenToLocalTransform().scale(selDoc.props.ContentScaling()).inverse().transformPoint(0, 0);
-            let snapped = Math.abs(e.pageX - selDocPos[0]) < 20 && Math.abs(e.pageY - selDocPos[1]) < 20;
+            const selDoc = SelectionManager.SelectedDocuments()[0];
+            const selDocPos = selDoc.props.ScreenToLocalTransform().scale(selDoc.props.ContentScaling()).inverse().transformPoint(0, 0);
+            const snapped = Math.abs(e.pageX - selDocPos[0]) < 20 && Math.abs(e.pageY - selDocPos[1]) < 20;
             this._minimizedX = snapped ? selDocPos[0] + 4 : e.clientX;
             this._minimizedY = snapped ? selDocPos[1] - 18 : e.clientY;
-            let selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd);
+            const selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd);
 
             if (selectedDocs.length > 1) {
                 this._iconDoc = this._iconDoc ? this._iconDoc : this.createIcon(SelectionManager.SelectedDocuments(), CollectionView.LayoutString(""));
@@ -295,15 +292,15 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         if (e.button === 0) {
             document.removeEventListener("pointermove", this.onMinimizeMove);
             document.removeEventListener("pointerup", this.onMinimizeUp);
-            let selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd);
+            const selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd);
             if (this._iconDoc && selectedDocs.length === 1 && this._removeIcon) {
                 selectedDocs[0].props.removeDocument && selectedDocs[0].props.removeDocument(this._iconDoc);
             }
             if (!this._removeIcon && selectedDocs.length === 1) { // if you click on the top-left button when just 1 doc is selected, then collapse it.  not sure why we don't do it for multiple selections
                 this.getIconDoc(selectedDocs[0]).then(async icon => {
-                    let minimizedDoc = await Cast(selectedDocs[0].props.Document.minimizedDoc, Doc);
+                    const minimizedDoc = await Cast(selectedDocs[0].props.Document.minimizedDoc, Doc);
                     if (minimizedDoc) {
-                        let scrpt = selectedDocs[0].props.ScreenToLocalTransform().scale(selectedDocs[0].props.ContentScaling()).inverse().transformPoint(
+                        const scrpt = selectedDocs[0].props.ScreenToLocalTransform().scale(selectedDocs[0].props.ContentScaling()).inverse().transformPoint(
                             NumCast(minimizedDoc.x) - NumCast(selectedDocs[0].Document.x), NumCast(minimizedDoc.y) - NumCast(selectedDocs[0].Document.y));
                         SelectionManager.DeselectAll();
                         DocumentManager.Instance.animateBetweenPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs));
@@ -317,8 +314,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
     @undoBatch
     @action createIcon = (selected: DocumentView[], layoutString: string): Doc => {
-        let doc = selected[0].props.Document;
-        let iconDoc = Docs.Create.IconDocument(layoutString);
+        const doc = selected[0].props.Document;
+        const iconDoc = Docs.Create.IconDocument(layoutString);
         iconDoc.isButton = true;
 
         IconBox.AutomaticTitle(iconDoc);
@@ -334,7 +331,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     }
     @action
     public getIconDoc = async (docView: DocumentView): Promise<Doc | undefined> => {
-        let doc = docView.props.Document;
+        const doc = docView.props.Document;
         let iconDoc: Doc | undefined = await Cast(doc.minimizedDoc, Doc);
 
         if (!iconDoc || !DocumentManager.Instance.getDocumentView(iconDoc)) {
@@ -344,8 +341,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         return iconDoc;
     }
     moveIconDoc(iconDoc: Doc) {
-        let selView = SelectionManager.SelectedDocuments()[0];
-        let where = (selView.props.ScreenToLocalTransform()).scale(selView.props.ContentScaling()).
+        const selView = SelectionManager.SelectedDocuments()[0];
+        const where = (selView.props.ScreenToLocalTransform()).scale(selView.props.ContentScaling()).
             transformPoint(this._minimizedX - 12, this._minimizedY - 12);
         iconDoc.x = where[0] + NumCast(selView.props.Document.x);
         iconDoc.y = where[1] + NumCast(selView.props.Document.y);
@@ -370,8 +367,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         dist = dist < 3 ? 0 : dist;
         let usingRule = false;
         SelectionManager.SelectedDocuments().map(dv => {
-            let ruleProvider = dv.props.ruleProvider;
-            let heading = NumCast(dv.props.Document.heading);
+            const ruleProvider = dv.props.ruleProvider;
+            const heading = NumCast(dv.props.Document.heading);
             ruleProvider && heading && (Doc.GetProto(ruleProvider)["ruleRounding_" + heading] = `${Math.min(100, dist)}%`);
             usingRule = usingRule || (ruleProvider && heading ? true : false);
         });
@@ -419,8 +416,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
         let dX = 0, dY = 0, dW = 0, dH = 0;
 
-        let moveX = e.clientX - this._lastX; // e.movementX;
-        let moveY = e.clientY - this._lastY; // e.movementY;
+        const moveX = e.clientX - this._lastX; // e.movementX;
+        const moveY = e.clientY - this._lastY; // e.movementY;
         this._lastX = e.clientX;
         this._lastY = e.clientY;
 
@@ -465,18 +462,18 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
         SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
             if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
-                let doc = PositionDocument(element.props.Document);
-                let layoutDoc = PositionDocument(Doc.Layout(element.props.Document));
+                const doc = PositionDocument(element.props.Document);
+                const layoutDoc = PositionDocument(Doc.Layout(element.props.Document));
                 let nwidth = layoutDoc.nativeWidth || 0;
                 let nheight = layoutDoc.nativeHeight || 0;
-                let width = (layoutDoc.width || 0);
-                let height = (layoutDoc.height || (nheight / nwidth * width));
-                let scale = element.props.ScreenToLocalTransform().Scale * element.props.ContentScaling();
-                let actualdW = Math.max(width + (dW * scale), 20);
-                let actualdH = Math.max(height + (dH * scale), 20);
+                const width = (layoutDoc.width || 0);
+                const height = (layoutDoc.height || (nheight / nwidth * width));
+                const scale = element.props.ScreenToLocalTransform().Scale * element.props.ContentScaling();
+                const actualdW = Math.max(width + (dW * scale), 20);
+                const actualdH = Math.max(height + (dH * scale), 20);
                 doc.x = (doc.x || 0) + dX * (actualdW - width);
                 doc.y = (doc.y || 0) + dY * (actualdH - height);
-                let fixedAspect = e.ctrlKey || (!layoutDoc.ignoreAspect && nwidth && nheight);
+                const fixedAspect = e.ctrlKey || (!layoutDoc.ignoreAspect && nwidth && nheight);
                 if (fixedAspect && e.ctrlKey && layoutDoc.ignoreAspect) {
                     layoutDoc.ignoreAspect = false;
                     layoutDoc.nativeWidth = nwidth = layoutDoc.width || 0;
@@ -529,8 +526,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @computed
     get selectionTitle(): string {
         if (SelectionManager.SelectedDocuments().length === 1) {
-            let selected = SelectionManager.SelectedDocuments()[0];
-            let field = selected.props.Document[this._fieldKey];
+            const selected = SelectionManager.SelectedDocuments()[0];
+            const field = selected.props.Document[this._fieldKey];
             if (typeof field === "string") {
                 return field;
             }
@@ -555,12 +552,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         }
     }
     render() {
-        var bounds = this.Bounds;
-        let seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
+        const bounds = this.Bounds;
+        const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
         if (SelectionManager.GetIsDragging() || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
             return (null);
         }
-        let minimizeIcon = (
+        const minimizeIcon = (
             <div className="documentDecorations-minimizeButton" onPointerDown={this.onMinimizeDown}>
                 {/* Currently, this is set to be enabled if there is no ink selected. It might be interesting to think about minimizing ink if it's useful? -syip2*/}
                 {SelectionManager.SelectedDocuments().length === 1 ? IconBox.DocumentIcon(StrCast(SelectionManager.SelectedDocuments()[0].props.Document.layout, "...")) : "..."}
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 8f397e331..edc7df12a 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -25,7 +25,7 @@ export default class KeyManager {
     private router = new Map<string, KeyHandler>();
 
     constructor() {
-        let isMac = navigator.platform.toLowerCase().indexOf("mac") >= 0;
+        const isMac = navigator.platform.toLowerCase().indexOf("mac") >= 0;
 
         // SHIFT CONTROL ALT META
         this.router.set("0000", this.unmodified);
@@ -36,22 +36,22 @@ export default class KeyManager {
     }
 
     public handle = async (e: KeyboardEvent) => {
-        let keyname = e.key && e.key.toLowerCase();
+        const keyname = e.key && e.key.toLowerCase();
         this.handleGreedy(keyname);
 
         if (modifiers.includes(keyname)) {
             return;
         }
 
-        let bit = (value: boolean) => value ? "1" : "0";
-        let modifierIndex = bit(e.shiftKey) + bit(e.ctrlKey) + bit(e.altKey) + bit(e.metaKey);
+        const bit = (value: boolean) => value ? "1" : "0";
+        const modifierIndex = bit(e.shiftKey) + bit(e.ctrlKey) + bit(e.altKey) + bit(e.metaKey);
 
-        let handleConstrained = this.router.get(modifierIndex);
+        const handleConstrained = this.router.get(modifierIndex);
         if (!handleConstrained) {
             return;
         }
 
-        let control = await handleConstrained(keyname, e);
+        const control = await handleConstrained(keyname, e);
 
         control.stopPropagation && e.stopPropagation();
         control.preventDefault && e.preventDefault();
@@ -65,7 +65,7 @@ export default class KeyManager {
     private unmodified = action((keyname: string, e: KeyboardEvent) => {
         switch (keyname) {
             case "escape":
-                let main = MainView.Instance;
+                const main = MainView.Instance;
                 InkingControl.Instance.switchTool(InkTool.None);
                 if (main.isPointerDown) {
                     DragManager.AbortDrag();
@@ -89,8 +89,8 @@ export default class KeyManager {
                 }
                 UndoManager.RunInBatch(() => {
                     SelectionManager.SelectedDocuments().map(docView => {
-                        let doc = docView.props.Document;
-                        let remove = docView.props.removeDocument;
+                        const doc = docView.props.Document;
+                        const remove = docView.props.removeDocument;
                         remove && remove(doc);
                     });
                 }, "delete");
@@ -121,8 +121,8 @@ export default class KeyManager {
     }
 
     private alt = action((keyname: string) => {
-        let stopPropagation = true;
-        let preventDefault = true;
+        const stopPropagation = true;
+        const preventDefault = true;
 
         switch (keyname) {
             // case "n":
@@ -190,7 +190,7 @@ export default class KeyManager {
                 }
                 break;
             case "o":
-                let target = SelectionManager.SelectedDocuments()[0];
+                const target = SelectionManager.SelectedDocuments()[0];
                 target && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(target);
                 break;
             case "r":
@@ -220,12 +220,12 @@ export default class KeyManager {
     });
 
     async printClipboard() {
-        let text: string = await navigator.clipboard.readText();
+        const text: string = await navigator.clipboard.readText();
     }
 
     private ctrl_shift = action((keyname: string) => {
-        let stopPropagation = true;
-        let preventDefault = true;
+        const stopPropagation = true;
+        const preventDefault = true;
 
         switch (keyname) {
             case "z":
diff --git a/src/client/views/InkSelectDecorations.tsx b/src/client/views/InkSelectDecorations.tsx
index d40df9b75..3ad50762d 100644
--- a/src/client/views/InkSelectDecorations.tsx
+++ b/src/client/views/InkSelectDecorations.tsx
@@ -29,10 +29,10 @@ export default class InkSelectDecorations extends Touchable {
 
     @computed
     get Bounds(): { x: number, y: number, b: number, r: number } {
-        let left = Number.MAX_VALUE;
-        let top = Number.MAX_VALUE;
-        let right = -Number.MAX_VALUE;
-        let bottom = -Number.MAX_VALUE;
+        const left = Number.MAX_VALUE;
+        const top = Number.MAX_VALUE;
+        const right = -Number.MAX_VALUE;
+        const bottom = -Number.MAX_VALUE;
         this._selectedInkNodes.forEach((value: PointData, key: string) => {
             // value.pathData.map(val => {
             //     left = Math.min(val.x, left);
@@ -45,7 +45,7 @@ export default class InkSelectDecorations extends Touchable {
     }
 
     render() {
-        let bounds = this.Bounds;
+        const bounds = this.Bounds;
         return <div style={{
             top: bounds.y, left: bounds.x,
             height: bounds.b - bounds.y,
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 75faa9641..e33f193b8 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -1,5 +1,5 @@
 import { action, computed, observable } from "mobx";
-import { ColorResult } from 'react-color';
+import { ColorState } from 'react-color';
 import { Doc } from "../../new_fields/Doc";
 import { InkTool } from "../../new_fields/InkField";
 import { List } from "../../new_fields/List";
@@ -35,16 +35,16 @@ export class InkingControl {
     }
 
     @undoBatch
-    switchColor = action((color: ColorResult): void => {
+    switchColor = action((color: ColorState): void => {
         this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff");
 
         if (InkingControl.Instance.selectedTool === InkTool.None) {
-            let selected = SelectionManager.SelectedDocuments();
-            let oldColors = selected.map(view => {
-                let targetDoc = view.props.Document.dragFactory instanceof Doc ? view.props.Document.dragFactory :
+            const selected = SelectionManager.SelectedDocuments();
+            const oldColors = selected.map(view => {
+                const targetDoc = view.props.Document.dragFactory instanceof Doc ? view.props.Document.dragFactory :
                     view.props.Document.layout instanceof Doc ? view.props.Document.layout :
                         view.props.Document.isTemplateField ? view.props.Document : Doc.GetProto(view.props.Document);
-                let sel = window.getSelection();
+                const sel = window.getSelection();
                 if (StrCast(targetDoc.layout).indexOf("FormattedTextBox") !== -1 && (!sel || sel.toString() !== "")) {
                     targetDoc.color = this._selectedColor;
                     return {
@@ -52,24 +52,24 @@ export class InkingControl {
                         previous: StrCast(targetDoc.color)
                     };
                 }
-                let oldColor = StrCast(targetDoc.backgroundColor);
+                const oldColor = StrCast(targetDoc.backgroundColor);
                 let matchedColor = this._selectedColor;
                 const cvd = view.props.ContainingCollectionDoc;
                 let ruleProvider = view.props.ruleProvider;
                 if (cvd) {
                     if (!cvd.colorPalette) {
-                        let defaultPalette = ["rg(114,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)",
+                        const defaultPalette = ["rg(114,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)",
                             "rgb(209,150,226)", "rgb(127,235,144)", "rgb(252,188,189)", "rgb(247,175,81)",];
-                        let colorPalette = Cast(cvd.colorPalette, listSpec("string"));
+                        const colorPalette = Cast(cvd.colorPalette, listSpec("string"));
                         if (!colorPalette) cvd.colorPalette = new List<string>(defaultPalette);
                     }
-                    let cp = Cast(cvd.colorPalette, listSpec("string")) as string[];
+                    const cp = Cast(cvd.colorPalette, listSpec("string")) as string[];
                     let closest = 0;
                     let dist = 10000000;
-                    let ccol = Utils.fromRGBAstr(StrCast(targetDoc.backgroundColor));
+                    const ccol = Utils.fromRGBAstr(StrCast(targetDoc.backgroundColor));
                     for (let i = 0; i < cp.length; i++) {
-                        let cpcol = Utils.fromRGBAstr(cp[i]);
-                        let d = Math.sqrt((ccol.r - cpcol.r) * (ccol.r - cpcol.r) + (ccol.b - cpcol.b) * (ccol.b - cpcol.b) + (ccol.g - cpcol.g) * (ccol.g - cpcol.g));
+                        const cpcol = Utils.fromRGBAstr(cp[i]);
+                        const d = Math.sqrt((ccol.r - cpcol.r) * (ccol.r - cpcol.r) + (ccol.b - cpcol.b) * (ccol.b - cpcol.b) + (ccol.g - cpcol.g) * (ccol.g - cpcol.g));
                         if (d < dist) {
                             dist = d;
                             closest = i;
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index a27f106e3..7cee84fc5 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -14,7 +14,7 @@ type InkDocument = makeInterface<[typeof documentSchema]>;
 const InkDocument = makeInterface(documentSchema);
 
 export function CreatePolyline(points: { x: number, y: number }[], left: number, top: number, color?: string, width?: number) {
-    let pts = points.reduce((acc: string, pt: { x: number, y: number }) => acc + `${pt.x - left},${pt.y - top} `, "");
+    const pts = points.reduce((acc: string, pt: { x: number, y: number }) => acc + `${pt.x - left},${pt.y - top} `, "");
     return (
         <polyline
             points={pts}
@@ -35,18 +35,18 @@ export class InkingStroke extends DocExtendableComponent<FieldViewProps, InkDocu
     @computed get PanelHeight() { return this.props.PanelHeight(); }
 
     render() {
-        let data: InkData = Cast(this.Document.data, InkField)?.inkData ?? [];
-        let xs = data.map(p => p.x);
-        let ys = data.map(p => p.y);
-        let left = Math.min(...xs);
-        let top = Math.min(...ys);
-        let right = Math.max(...xs);
-        let bottom = Math.max(...ys);
-        let points = CreatePolyline(data, 0, 0, this.Document.color, this.Document.strokeWidth);
-        let width = right - left;
-        let height = bottom - top;
-        let scaleX = this.PanelWidth / width;
-        let scaleY = this.PanelHeight / height;
+        const data: InkData = Cast(this.Document.data, InkField)?.inkData ?? [];
+        const xs = data.map(p => p.x);
+        const ys = data.map(p => p.y);
+        const left = Math.min(...xs);
+        const top = Math.min(...ys);
+        const right = Math.max(...xs);
+        const bottom = Math.max(...ys);
+        const points = CreatePolyline(data, 0, 0, this.Document.color, this.Document.strokeWidth);
+        const width = right - left;
+        const height = bottom - top;
+        const scaleX = this.PanelWidth / width;
+        const scaleY = this.PanelHeight / height;
         return (
             <svg width={width} height={height} style={{
                 transformOrigin: "top left",
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 291781da1..bd20cbcf1 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -60,10 +60,10 @@ export class MainView extends React.Component {
     public isPointerDown = false;
 
     componentWillMount() {
-        var tag = document.createElement('script');
+        const tag = document.createElement('script');
 
         tag.src = "https://www.youtube.com/iframe_api";
-        var firstScriptTag = document.getElementsByTagName('script')[0];
+        const firstScriptTag = document.getElementsByTagName('script')[0];
         firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag);
         window.removeEventListener("keydown", KeyManager.Instance.handle);
         window.addEventListener("keydown", KeyManager.Instance.handle);
@@ -82,9 +82,9 @@ export class MainView extends React.Component {
         // causes errors to be generated when modifying an observable outside of an action
         configure({ enforceActions: "observed" });
         if (window.location.pathname !== "/home") {
-            let pathname = window.location.pathname.substr(1).split("/");
+            const pathname = window.location.pathname.substr(1).split("/");
             if (pathname.length > 1) {
-                let type = pathname[0];
+                const type = pathname[0];
                 if (type === "doc") {
                     CurrentUserUtils.MainDocId = pathname[1];
                     if (!this.userDoc) {
@@ -158,7 +158,7 @@ export class MainView extends React.Component {
 
     initAuthenticationRouters = async () => {
         // Load the user's active workspace, or create a new one if initial session after signup
-        let received = CurrentUserUtils.MainDocId;
+        const received = CurrentUserUtils.MainDocId;
         if (received && !this.userDoc) {
             reaction(
                 () => CurrentUserUtils.GuestTarget,
@@ -175,7 +175,7 @@ export class MainView extends React.Component {
                     }),
                 );
             }
-            let doc = this.userDoc && await Cast(this.userDoc.activeWorkspace, Doc);
+            const doc = this.userDoc && await Cast(this.userDoc.activeWorkspace, Doc);
             if (doc) {
                 this.openWorkspace(doc);
             } else {
@@ -186,9 +186,9 @@ export class MainView extends React.Component {
 
     @action
     createNewWorkspace = async (id?: string) => {
-        let workspaces = Cast(this.userDoc.workspaces, Doc) as Doc;
-        let workspaceCount = DocListCast(workspaces.data).length + 1;
-        let freeformOptions: DocumentOptions = {
+        const workspaces = Cast(this.userDoc.workspaces, Doc) as Doc;
+        const workspaceCount = DocListCast(workspaces.data).length + 1;
+        const freeformOptions: DocumentOptions = {
             x: 0,
             y: 400,
             width: this._panelWidth * .7,
@@ -196,10 +196,10 @@ export class MainView extends React.Component {
             title: "Collection " + workspaceCount,
             backgroundColor: "white"
         };
-        let freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
+        const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         Doc.AddDocToList(Doc.GetProto(CurrentUserUtils.UserDocument.documents as Doc), "data", freeformDoc);
-        var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] };
-        let mainDoc = Docs.Create.DockDocument([freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${workspaceCount}` }, id);
+        const dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] };
+        const mainDoc = Docs.Create.DockDocument([freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${workspaceCount}` }, id);
         Doc.AddDocToList(workspaces, "data", mainDoc);
         // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
         setTimeout(() => this.openWorkspace(mainDoc), 0);
@@ -213,7 +213,7 @@ export class MainView extends React.Component {
             !("presentationView" in doc) && (doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })]));
             this.userDoc ? (this.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc);
         }
-        let state = this._urlState;
+        const state = this._urlState;
         if (state.sharing === true && !this.userDoc) {
             DocServer.Control.makeReadOnly();
         } else {
@@ -263,8 +263,8 @@ export class MainView extends React.Component {
 
     @computed get dockingContent() {
         const mainContainer = this.mainContainer;
-        let flyoutWidth = this.flyoutWidth; // bcz: need to be here because Measure messes with observables.
-        let flyoutTranslate = this._flyoutTranslate;
+        const flyoutWidth = this.flyoutWidth; // bcz: need to be here because Measure messes with observables.
+        const flyoutTranslate = this._flyoutTranslate;
         return <Measure offset onResize={this.onResize}>
             {({ measureRef }) =>
                 <div ref={measureRef} id="mainContent-div" style={{ width: `calc(100% - ${flyoutTranslate ? flyoutWidth : 0}px`, transform: `translate(${flyoutTranslate ? flyoutWidth : 0}px, 0px)` }} onDrop={this.onDrop}>
@@ -352,11 +352,11 @@ export class MainView extends React.Component {
     mainContainerXf = () => new Transform(0, -this._buttonBarHeight, 1);
 
     @computed get flyout() {
-        let sidebarContent = this.userDoc && this.userDoc.sidebarContainer;
+        const sidebarContent = this.userDoc && this.userDoc.sidebarContainer;
         if (!(sidebarContent instanceof Doc)) {
             return (null);
         }
-        let sidebarButtonsDoc = Cast(CurrentUserUtils.UserDocument.sidebarButtons, Doc) as Doc;
+        const sidebarButtonsDoc = Cast(CurrentUserUtils.UserDocument.sidebarButtons, Doc) as Doc;
         sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30;
         return <div className="mainView-flyoutContainer" >
             <div className="mainView-tabButtons" style={{ height: `${this._buttonBarHeight}px` }}>
@@ -463,7 +463,7 @@ export class MainView extends React.Component {
 
     buttonBarXf = () => {
         if (!this._docBtnRef.current) return Transform.Identity();
-        let { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current);
+        const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current);
         return new Transform(-translateX, -translateY, 1 / scale);
     }
     @computed get docButtons() {
diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx
index 221a0260a..9198fe3e3 100644
--- a/src/client/views/MainViewModal.tsx
+++ b/src/client/views/MainViewModal.tsx
@@ -14,9 +14,9 @@ export interface MainViewOverlayProps {
 export default class MainViewModal extends React.Component<MainViewOverlayProps> {
 
     render() {
-        let p = this.props;
-        let dialogueOpacity = p.dialogueBoxDisplayedOpacity || 1;
-        let overlayOpacity = p.overlayDisplayedOpacity || 0.4;
+        const p = this.props;
+        const dialogueOpacity = p.dialogueBoxDisplayedOpacity || 1;
+        const overlayOpacity = p.overlayDisplayedOpacity || 0.4;
         return !p.isDisplayed ? (null) : (
             <div style={{ pointerEvents: p.isDisplayed ? p.interactive ? "all" : "none" : "none" }}>
                 <div
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx
index 41453f8b2..8859c14cb 100644
--- a/src/client/views/MetadataEntryMenu.tsx
+++ b/src/client/views/MetadataEntryMenu.tsx
@@ -99,8 +99,8 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
             } else {
                 let childSuccess = true;
                 if (this._addChildren) {
-                    for (let document of doc) {
-                        let collectionChildren = await DocListCastAsync(document.data);
+                    for (const document of doc) {
+                        const collectionChildren = await DocListCastAsync(document.data);
                         if (collectionChildren) {
                             childSuccess = collectionChildren.every(c => KeyValueBox.ApplyKVPScript(c, this._currentKey, script));
                         }
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 9869e24d1..32ada293c 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -148,7 +148,7 @@ export class OverlayView extends React.Component {
         return CurrentUserUtils.UserDocument.overlays instanceof Doc && DocListCast(CurrentUserUtils.UserDocument.overlays.data).map(d => {
             d.inOverlay = true;
             let offsetx = 0, offsety = 0;
-            let onPointerMove = action((e: PointerEvent) => {
+            const onPointerMove = action((e: PointerEvent) => {
                 if (e.buttons === 1) {
                     d.x = e.clientX + offsetx;
                     d.y = e.clientY + offsety;
@@ -156,14 +156,14 @@ export class OverlayView extends React.Component {
                     e.preventDefault();
                 }
             });
-            let onPointerUp = action((e: PointerEvent) => {
+            const onPointerUp = action((e: PointerEvent) => {
                 document.removeEventListener("pointermove", onPointerMove);
                 document.removeEventListener("pointerup", onPointerUp);
                 e.stopPropagation();
                 e.preventDefault();
             });
 
-            let onPointerDown = (e: React.PointerEvent) => {
+            const onPointerDown = (e: React.PointerEvent) => {
                 offsetx = NumCast(d.x) - e.clientX;
                 offsety = NumCast(d.y) - e.clientY;
                 e.stopPropagation();
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 136a272ab..fd0287b6c 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -26,7 +26,7 @@ export class PreviewCursor extends React.Component<{}> {
     paste = (e: ClipboardEvent) => {
         if (PreviewCursor.Visible) {
             if (e.clipboardData) {
-                let newPoint = PreviewCursor._getTransform().transformPoint(PreviewCursor._clickPoint[0], PreviewCursor._clickPoint[1]);
+                const newPoint = PreviewCursor._getTransform().transformPoint(PreviewCursor._clickPoint[0], PreviewCursor._clickPoint[1]);
                 runInAction(() => { PreviewCursor.Visible = false; });
 
 
@@ -44,7 +44,7 @@ export class PreviewCursor extends React.Component<{}> {
                     }
 
                     // tests for URL and makes web document
-                    let re: any = /^https?:\/\//g;
+                    const re: any = /^https?:\/\//g;
                     if (re.test(e.clipboardData.getData("text/plain"))) {
                         const url = e.clipboardData.getData("text/plain");
                         PreviewCursor._addDocument(Docs.Create.WebDocument(url, {
@@ -56,7 +56,7 @@ export class PreviewCursor extends React.Component<{}> {
                     }
 
                     // creates text document
-                    let newBox = Docs.Create.TextDocument({
+                    const newBox = Docs.Create.TextDocument({
                         width: 200, height: 100,
                         x: newPoint[0],
                         y: newPoint[1],
@@ -69,10 +69,10 @@ export class PreviewCursor extends React.Component<{}> {
                 }
                 //pasting in images
                 if (e.clipboardData.getData("text/html") !== "" && e.clipboardData.getData("text/html").includes("<img src=")) {
-                    let re: any = /<img src="(.*?)"/g;
-                    let arr: any[] = re.exec(e.clipboardData.getData("text/html"));
+                    const re: any = /<img src="(.*?)"/g;
+                    const arr: any[] = re.exec(e.clipboardData.getData("text/html"));
 
-                    let img: Doc = Docs.Create.ImageDocument(
+                    const img: Doc = Docs.Create.ImageDocument(
                         arr[1], {
                         width: 300, title: arr[1],
                         x: newPoint[0],
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index 8ef9f3be6..65d96f364 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -59,7 +59,7 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
             onFocus = this.onFocus;
             onBlur = this.onBlur;
         }
-        let params = <EditableView
+        const params = <EditableView
             contents={""}
             display={"block"}
             maxHeight={72}
@@ -96,9 +96,9 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
             }
         }
         // tslint:disable-next-line: no-unnecessary-callback-wrapper
-        let params: string[] = [];
-        let setParams = (p: string[]) => params.splice(0, params.length, ...p);
-        let scriptingBox = <ScriptBox initialText={originalText} setParams={setParams} onCancel={overlayDisposer} onSave={(text, onError) => {
+        const params: string[] = [];
+        const setParams = (p: string[]) => params.splice(0, params.length, ...p);
+        const scriptingBox = <ScriptBox initialText={originalText} setParams={setParams} onCancel={overlayDisposer} onSave={(text, onError) => {
             if (prewrapper) {
                 text = prewrapper + text + (postwrapper ? postwrapper : "");
             }
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index c65b338b4..e6116ca09 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -61,19 +61,19 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
 
     toggleFloat = (e: React.ChangeEvent<HTMLInputElement>): void => {
         SelectionManager.DeselectAll();
-        let topDocView = this.props.docs[0];
-        let topDoc = topDocView.props.Document;
-        let xf = topDocView.props.ScreenToLocalTransform();
-        let ex = e.target.clientLeft;
-        let ey = e.target.clientTop;
+        const topDocView = this.props.docs[0];
+        const topDoc = topDocView.props.Document;
+        const xf = topDocView.props.ScreenToLocalTransform();
+        const ex = e.target.clientLeft;
+        const ey = e.target.clientTop;
         undoBatch(action(() => topDoc.z = topDoc.z ? 0 : 1))();
         if (e.target.checked) {
             setTimeout(() => {
-                let newDocView = DocumentManager.Instance.getDocumentView(topDoc);
+                const newDocView = DocumentManager.Instance.getDocumentView(topDoc);
                 if (newDocView) {
-                    let de = new DragManager.DocumentDragData([topDoc]);
+                    const de = new DragManager.DocumentDragData([topDoc]);
                     de.moveDocument = topDocView.props.moveDocument;
-                    let xf = newDocView.ContentDiv!.getBoundingClientRect();
+                    const xf = newDocView.ContentDiv!.getBoundingClientRect();
                     DragManager.StartDocumentDrag([newDocView.ContentDiv!], de, ex, ey, {
                         offsetX: (ex - xf.left), offsetY: (ey - xf.top),
                         handlers: { dragComplete: () => { }, },
@@ -82,9 +82,9 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                 }
             }, 10);
         } else if (topDocView.props.ContainingCollectionView) {
-            let collView = topDocView.props.ContainingCollectionView;
-            let [sx, sy] = xf.inverse().transformPoint(0, 0);
-            let [x, y] = collView.props.ScreenToLocalTransform().transformPoint(sx, sy);
+            const collView = topDocView.props.ContainingCollectionView;
+            const [sx, sy] = xf.inverse().transformPoint(0, 0);
+            const [x, y] = collView.props.ScreenToLocalTransform().transformPoint(sx, sy);
             topDoc.x = x;
             topDoc.y = y;
         }
@@ -122,7 +122,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     @action
     toggleChrome = (): void => {
         this.props.docs.map(dv => {
-            let layout = Doc.Layout(dv.Document);
+            const layout = Doc.Layout(dv.Document);
             layout.chromeStatus = (layout.chromeStatus !== "disabled" ? "disabled" : "enabled");
         });
     }
@@ -147,8 +147,8 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
             document.removeEventListener("pointermove", this.onAliasButtonMoved);
             document.removeEventListener("pointerup", this.onAliasButtonUp);
 
-            let dragDocView = this.props.docs[0];
-            let dragData = new DragManager.DocumentDragData([dragDocView.props.Document]);
+            const dragDocView = this.props.docs[0];
+            const dragData = new DragManager.DocumentDragData([dragDocView.props.Document]);
             const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
             dragData.embedDoc = true;
             dragData.dropAction = "alias";
@@ -165,8 +165,8 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     }
 
     render() {
-        let layout = Doc.Layout(this.props.docs[0].Document);
-        let templateMenu: Array<JSX.Element> = [];
+        const layout = Doc.Layout(this.props.docs[0].Document);
+        const templateMenu: Array<JSX.Element> = [];
         this.props.templates.forEach((checked, template) =>
             templateMenu.push(<TemplateToggle key={template.Name} template={template} checked={checked} toggle={this.toggleTemplate} />));
         templateMenu.push(<OtherToggle key={"float"} name={"Float"} checked={this.props.docs[0].Document.z ? true : false} toggle={this.toggleFloat} />);
diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx
index 0056a1d96..183d3e4e8 100644
--- a/src/client/views/Touchable.tsx
+++ b/src/client/views/Touchable.tsx
@@ -17,7 +17,7 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
     @action
     protected onTouchStart = (e: React.TouchEvent): void => {
         for (let i = 0; i < e.targetTouches.length; i++) {
-            let pt: any = e.targetTouches.item(i);
+            const pt: any = e.targetTouches.item(i);
             // pen is also a touch, but with a radius of 0.5 (at least with the surface pens). i doubt anyone's fingers are 2 pixels wide,
             // and this seems to be the only way of differentiating pen and touch on touch events
             if (pt.radiusX > 2 && pt.radiusY > 2) {
@@ -59,7 +59,7 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
         }
 
         for (let i = 0; i < e.targetTouches.length; i++) {
-            let pt = e.targetTouches.item(i);
+            const pt = e.targetTouches.item(i);
             if (pt) {
                 if (this.prevPoints.has(pt.identifier)) {
                     this.prevPoints.set(pt.identifier, pt);
@@ -78,7 +78,7 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
 
         // remove all the touches associated with the event
         for (let i = 0; i < e.targetTouches.length; i++) {
-            let pt = e.targetTouches.item(i);
+            const pt = e.targetTouches.item(i);
             if (pt) {
                 if (this.prevPoints.has(pt.identifier)) {
                     this.prevPoints.delete(pt.identifier);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 3040e74b0..77e6e1c93 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -96,13 +96,13 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     @undoBatch
     @action
     public OpenFullScreen(docView: DocumentView) {
-        let document = Doc.MakeAlias(docView.props.Document);
-        let dataDoc = docView.props.DataDoc;
-        let newItemStackConfig = {
+        const document = Doc.MakeAlias(docView.props.Document);
+        const dataDoc = docView.props.DataDoc;
+        const newItemStackConfig = {
             type: 'stack',
             content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)]
         };
-        var docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
+        const docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
         this._goldenLayout.root.contentItems[0].addChild(docconfig);
         docconfig.callDownwards('_$init');
         this._goldenLayout._$maximiseItem(docconfig);
@@ -113,7 +113,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     }
 
     public CloseFullScreen = () => {
-        let target = this._goldenLayout._maximisedItem;
+        const target = this._goldenLayout._maximisedItem;
         if (target !== null && this._maximizedSrc) {
             this._goldenLayout._maximisedItem.remove();
             SelectionManager.SelectDoc(this._maximizedSrc, false);
@@ -130,7 +130,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     @action
     public static CloseRightSplit(document: Doc): boolean {
         if (!CollectionDockingView.Instance) return false;
-        let instance = CollectionDockingView.Instance;
+        const instance = CollectionDockingView.Instance;
         let retVal = false;
         if (instance._goldenLayout.root.contentItems[0].isRow) {
             retVal = Array.from(instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => {
@@ -176,21 +176,21 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     @action
     public static AddRightSplit(document: Doc, dataDoc: Doc | undefined, minimize: boolean = false) {
         if (!CollectionDockingView.Instance) return false;
-        let instance = CollectionDockingView.Instance;
-        let newItemStackConfig = {
+        const instance = CollectionDockingView.Instance;
+        const newItemStackConfig = {
             type: 'stack',
             content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)]
         };
 
-        var newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
+        const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
 
         if (instance._goldenLayout.root.contentItems.length === 0) {
             instance._goldenLayout.root.addChild(newContentItem);
         } else if (instance._goldenLayout.root.contentItems[0].isRow) {
             instance._goldenLayout.root.contentItems[0].addChild(newContentItem);
         } else {
-            var collayout = instance._goldenLayout.root.contentItems[0];
-            var newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
+            const collayout = instance._goldenLayout.root.contentItems[0];
+            const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
             collayout.parent.replaceChild(collayout, newRow);
 
             newRow.addChild(newContentItem, undefined, true);
@@ -213,7 +213,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     @action
     public AddTab = (stack: any, document: Doc, dataDocument: Doc | undefined) => {
         Doc.GetProto(document).lastOpened = new DateField;
-        let docContentConfig = CollectionDockingView.makeDocumentConfig(document, dataDocument);
+        const docContentConfig = CollectionDockingView.makeDocumentConfig(document, dataDocument);
         if (stack === undefined) {
             let stack: any = this._goldenLayout.root;
             while (!stack.isStack) {
@@ -236,7 +236,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     }
 
     setupGoldenLayout() {
-        var config = StrCast(this.props.Document.dockingConfig);
+        const config = StrCast(this.props.Document.dockingConfig);
         if (config) {
             if (!this._goldenLayout) {
                 runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
@@ -280,7 +280,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                         // Because this is in a set timeout, if this component unmounts right after mounting,
                         // we will leak a GoldenLayout, because we try to destroy it before we ever create it
                         setTimeout(() => this.setupGoldenLayout(), 1);
-                        let userDoc = CurrentUserUtils.UserDocument;
+                        const userDoc = CurrentUserUtils.UserDocument;
                         userDoc && DocListCast((userDoc.workspaces as Doc).data).map(d => d.workspaceBrush = false);
                         this.props.Document.workspaceBrush = true;
                     }
@@ -311,7 +311,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     }
     @action
     onResize = (event: any) => {
-        var cur = this._containerRef.current;
+        const cur = this._containerRef.current;
 
         // bcz: since GoldenLayout isn't a React component itself, we need to notify it to resize when its document container's size has changed
         this._goldenLayout && this._goldenLayout.updateSize(cur!.getBoundingClientRect().width, cur!.getBoundingClientRect().height);
@@ -330,19 +330,19 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     @action
     onPointerDown = (e: React.PointerEvent): void => {
         this._isPointerDown = true;
-        let onPointerUp = action(() => {
+        const onPointerUp = action(() => {
             window.removeEventListener("pointerup", onPointerUp);
             this._isPointerDown = false;
         });
         window.addEventListener("pointerup", onPointerUp);
-        var className = (e.target as any).className;
+        const className = (e.target as any).className;
         if (className === "messageCounter") {
             e.stopPropagation();
             e.preventDefault();
-            let x = e.clientX;
-            let y = e.clientY;
-            let docid = (e.target as any).DashDocId;
-            let tab = (e.target as any).parentElement as HTMLElement;
+            const x = e.clientX;
+            const y = e.clientY;
+            const docid = (e.target as any).DashDocId;
+            const tab = (e.target as any).parentElement as HTMLElement;
             DocServer.GetRefField(docid).then(action(async (sourceDoc: Opt<Field>) =>
                 (sourceDoc instanceof Doc) && DragLinksAsDocuments(tab, x, y, sourceDoc)));
         }
@@ -352,18 +352,18 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     }
 
     updateDataField = async (json: string) => {
-        let matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
-        let docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", ""));
+        const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
+        const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", ""));
 
         if (docids) {
-            let docs = (await Promise.all(docids.map(id => DocServer.GetRefField(id)))).filter(f => f).map(f => f as Doc);
+            const docs = (await Promise.all(docids.map(id => DocServer.GetRefField(id)))).filter(f => f).map(f => f as Doc);
             Doc.GetProto(this.props.Document)[this.props.fieldKey] = new List<Doc>(docs);
         }
     }
 
     @undoBatch
     stateChanged = () => {
-        var json = JSON.stringify(this._goldenLayout.toConfig());
+        const json = JSON.stringify(this._goldenLayout.toConfig());
         this.props.Document.dockingConfig = json;
         this.updateDataField(json);
 
@@ -380,7 +380,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     }
 
     htmlToElement(html: string) {
-        var template = document.createElement('template');
+        const template = document.createElement('template');
         html = html.trim(); // Never return a text node of whitespace as the result
         template.innerHTML = html;
         return template.content.firstChild;
@@ -392,24 +392,24 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                 tab.contentItem.parent.config.fixed = true;
             }
 
-            let doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId) as Doc;
-            let dataDoc = await DocServer.GetRefField(tab.contentItem.config.props.dataDocumentId) as Doc;
+            const doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId) as Doc;
+            const dataDoc = await DocServer.GetRefField(tab.contentItem.config.props.dataDocumentId) as Doc;
             if (doc instanceof Doc) {
-                let dragSpan = document.createElement("span");
+                const dragSpan = document.createElement("span");
                 dragSpan.style.position = "relative";
                 dragSpan.style.bottom = "6px";
                 dragSpan.style.paddingLeft = "4px";
                 dragSpan.style.paddingRight = "2px";
-                let gearSpan = document.createElement("span");
+                const gearSpan = document.createElement("span");
                 gearSpan.style.position = "relative";
                 gearSpan.style.paddingLeft = "0px";
                 gearSpan.style.paddingRight = "12px";
-                let upDiv = document.createElement("span");
+                const upDiv = document.createElement("span");
                 const stack = tab.contentItem.parent;
                 // shifts the focus to this tab when another tab is dragged over it
                 tab.element[0].onmouseenter = (e: any) => {
                     if (!this._isPointerDown || !SelectionManager.GetIsDragging()) return;
-                    var activeContentItem = tab.header.parent.getActiveContentItem();
+                    const activeContentItem = tab.header.parent.getActiveContentItem();
                     if (tab.contentItem !== activeContentItem) {
                         tab.header.parent.setActiveContentItem(tab.contentItem);
                     }
@@ -447,12 +447,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         tab.closeElement.off('click') //unbind the current click handler
             .click(async function () {
                 tab.reactionDisposer && tab.reactionDisposer();
-                let doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId);
+                const doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId);
                 if (doc instanceof Doc) {
-                    let theDoc = doc;
+                    const theDoc = doc;
                     CollectionDockingView.Instance._removedDocs.push(theDoc);
 
-                    let userDoc = CurrentUserUtils.UserDocument;
+                    const userDoc = CurrentUserUtils.UserDocument;
                     let recent: Doc | undefined;
                     if (userDoc && (recent = await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc))) {
                         Doc.AddDocToList(recent, "data", doc, undefined, true, true);
@@ -489,13 +489,13 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
 
                 stack.remove();
                 stack.contentItems.forEach(async (contentItem: any) => {
-                    let doc = await DocServer.GetRefField(contentItem.config.props.documentId);
+                    const doc = await DocServer.GetRefField(contentItem.config.props.documentId);
                     if (doc instanceof Doc) {
                         let recent: Doc | undefined;
                         if (CurrentUserUtils.UserDocument && (recent = await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc))) {
                             Doc.AddDocToList(recent, "data", doc, undefined, true, true);
                         }
-                        let theDoc = doc;
+                        const theDoc = doc;
                         CollectionDockingView.Instance._removedDocs.push(theDoc);
                     }
                 });
@@ -505,7 +505,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             .off('click') //unbind the current click handler
             .click(action(function () {
                 stack.config.fixed = !stack.config.fixed;
-                // var url = Utils.prepend("/doc/" + stack.contentItems[0].tab.contentItem.config.props.documentId);
+                // const url = Utils.prepend("/doc/" + stack.contentItems[0].tab.contentItem.config.props.documentId);
                 // let win = window.open(url, stack.contentItems[0].tab.title, "width=300,height=400");
             }));
     }
@@ -563,9 +563,9 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     @action
     public PinDoc(doc: Doc) {
         //add this new doc to props.Document
-        let curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc;
+        const curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc;
         if (curPres) {
-            let pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" });
+            const pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" });
             Doc.GetProto(pinDoc).presentationTargetDoc = doc;
             Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.presentationTargetDoc instanceof Doc) && this.presentationTargetDoc.title.toString()');
             const data = Cast(curPres.data, listSpec(Doc));
@@ -581,8 +581,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     }
 
     componentDidMount() {
-        let observer = new _global.ResizeObserver(action((entries: any) => {
-            for (let entry of entries) {
+        const observer = new _global.ResizeObserver(action((entries: any) => {
+            for (const entry of entries) {
                 this._panelWidth = entry.contentRect.width;
                 this._panelHeight = entry.contentRect.height;
             }
@@ -625,14 +625,14 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         const nativeH = this.nativeHeight();
         const nativeW = this.nativeWidth();
         if (!nativeW || !nativeH) return 1;
-        let wscale = this.panelWidth() / nativeW;
+        const wscale = this.panelWidth() / nativeW;
         return wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale;
     }
 
     ScreenToLocalTransform = () => {
         if (this._mainCont && this._mainCont.children) {
-            let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.children[0].firstChild as HTMLElement);
-            scale = Utils.GetScreenTransform(this._mainCont).scale;
+            const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont.children[0].firstChild as HTMLElement);
+            const scale = Utils.GetScreenTransform(this._mainCont).scale;
             return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale);
         }
         return Transform.Identity();
@@ -657,7 +657,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     @computed get docView() {
         if (!this._document) return (null);
         const document = this._document;
-        let resolvedDataDoc = document.layout instanceof Doc ? document : this._dataDoc;
+        const resolvedDataDoc = document.layout instanceof Doc ? document : this._dataDoc;
         return <DocumentView key={document[Id]}
             Document={document}
             DataDoc={resolvedDataDoc}
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index df4b00b3a..d697e721b 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -64,9 +64,9 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         if (this._collapsed) {
             this.props.setDocHeight(this._heading, 20);
         } else {
-            let rawHeight = this._contRef.current!.getBoundingClientRect().height + 15; //+ 15 accounts for the group header
-            let transformScale = this.props.screenToLocalTransform().Scale;
-            let trueHeight = rawHeight * transformScale;
+            const rawHeight = this._contRef.current!.getBoundingClientRect().height + 15; //+ 15 accounts for the group header
+            const transformScale = this.props.screenToLocalTransform().Scale;
+            const trueHeight = rawHeight * transformScale;
             this.props.setDocHeight(this._heading, trueHeight);
         }
     }
@@ -77,8 +77,8 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         if (de.data instanceof DragManager.DocumentDragData) {
             (this.props.parent.Document.dropConverter instanceof ScriptField) &&
                 this.props.parent.Document.dropConverter.script.run({ dragData: de.data });
-            let key = StrCast(this.props.parent.props.Document.sectionFilter);
-            let castedValue = this.getValue(this._heading);
+            const key = StrCast(this.props.parent.props.Document.sectionFilter);
+            const castedValue = this.getValue(this._heading);
             de.data.droppedDocuments.forEach(d => d[key] = castedValue);
             this.props.parent.drop(e, de);
             e.stopPropagation();
@@ -86,7 +86,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     });
 
     getValue = (value: string): any => {
-        let parsed = parseInt(value);
+        const parsed = parseInt(value);
         if (!isNaN(parsed)) return parsed;
         if (value.toLowerCase().indexOf("true") > -1) return true;
         if (value.toLowerCase().indexOf("false") > -1) return false;
@@ -96,8 +96,8 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     @action
     headingChanged = (value: string, shiftDown?: boolean) => {
         this._createAliasSelected = false;
-        let key = StrCast(this.props.parent.props.Document.sectionFilter);
-        let castedValue = this.getValue(value);
+        const key = StrCast(this.props.parent.props.Document.sectionFilter);
+        const castedValue = this.getValue(value);
         if (castedValue) {
             if (this.props.parent.sectionHeaders) {
                 if (this.props.parent.sectionHeaders.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
@@ -135,18 +135,18 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     @action
     addDocument = (value: string, shiftDown?: boolean) => {
         this._createAliasSelected = false;
-        let key = StrCast(this.props.parent.props.Document.sectionFilter);
-        let newDoc = Docs.Create.TextDocument({ height: 18, width: 200, title: value });
+        const key = StrCast(this.props.parent.props.Document.sectionFilter);
+        const newDoc = Docs.Create.TextDocument({ height: 18, width: 200, title: value });
         newDoc[key] = this.getValue(this.props.heading);
         return this.props.parent.props.addDocument(newDoc);
     }
 
     deleteRow = undoBatch(action(() => {
         this._createAliasSelected = false;
-        let key = StrCast(this.props.parent.props.Document.sectionFilter);
+        const key = StrCast(this.props.parent.props.Document.sectionFilter);
         this.props.docList.forEach(d => d[key] = undefined);
         if (this.props.parent.sectionHeaders && this.props.headingObject) {
-            let index = this.props.parent.sectionHeaders.indexOf(this.props.headingObject);
+            const index = this.props.parent.sectionHeaders.indexOf(this.props.headingObject);
             this.props.parent.sectionHeaders.splice(index, 1);
         }
     }));
@@ -162,18 +162,18 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     }
 
     startDrag = (e: PointerEvent) => {
-        let [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
+        const [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
         if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
-            let alias = Doc.MakeAlias(this.props.parent.props.Document);
-            let key = StrCast(this.props.parent.props.Document.sectionFilter);
+            const alias = Doc.MakeAlias(this.props.parent.props.Document);
+            const key = StrCast(this.props.parent.props.Document.sectionFilter);
             let value = this.getValue(this._heading);
             value = typeof value === "string" ? `"${value}"` : value;
-            let script = `return doc.${key} === ${value}`;
-            let compiled = CompileScript(script, { params: { doc: Doc.name } });
+            const script = `return doc.${key} === ${value}`;
+            const compiled = CompileScript(script, { params: { doc: Doc.name } });
             if (compiled.compiled) {
-                let scriptField = new ScriptField(compiled);
+                const scriptField = new ScriptField(compiled);
                 alias.viewSpecScript = scriptField;
-                let dragData = new DragManager.DocumentDragData([alias]);
+                const dragData = new DragManager.DocumentDragData([alias]);
                 DragManager.StartDocumentDrag([this._headerRef.current!], dragData, e.clientX, e.clientY);
             }
 
@@ -196,7 +196,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         e.stopPropagation();
         e.preventDefault();
 
-        let [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX, e.clientY);
+        const [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX, e.clientY);
         this._startDragPosition = { x: dx, y: dy };
 
         if (this._createAliasSelected) {
@@ -209,17 +209,17 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     }
 
     renderColorPicker = () => {
-        let selected = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
+        const selected = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
 
-        let pink = PastelSchemaPalette.get("pink2");
-        let purple = PastelSchemaPalette.get("purple4");
-        let blue = PastelSchemaPalette.get("bluegreen1");
-        let yellow = PastelSchemaPalette.get("yellow4");
-        let red = PastelSchemaPalette.get("red2");
-        let green = PastelSchemaPalette.get("bluegreen7");
-        let cyan = PastelSchemaPalette.get("bluegreen5");
-        let orange = PastelSchemaPalette.get("orange1");
-        let gray = "#f1efeb";
+        const pink = PastelSchemaPalette.get("pink2");
+        const purple = PastelSchemaPalette.get("purple4");
+        const blue = PastelSchemaPalette.get("bluegreen1");
+        const yellow = PastelSchemaPalette.get("yellow4");
+        const red = PastelSchemaPalette.get("red2");
+        const green = PastelSchemaPalette.get("bluegreen7");
+        const cyan = PastelSchemaPalette.get("bluegreen5");
+        const orange = PastelSchemaPalette.get("orange1");
+        const gray = "#f1efeb";
 
         return (
             <div className="collectionStackingView-colorPicker">
@@ -242,7 +242,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     toggleVisibility = action(() => this._collapsed = !this._collapsed);
 
     renderMenu = () => {
-        let selected = this._createAliasSelected;
+        const selected = this._createAliasSelected;
         return (<div className="collectionStackingView-optionPicker">
             <div className="optionOptions">
                 <div className={"optionPicker" + (selected === true ? " active" : "")} onClick={this.toggleAlias}>Create Alias</div>
@@ -258,10 +258,10 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     }
 
     @computed get contentLayout() {
-        let rows = Math.max(1, Math.min(this.props.docList.length, Math.floor((this.props.parent.props.PanelWidth() - 2 * this.props.parent.xMargin) / (this.props.parent.columnWidth + this.props.parent.gridGap))));
-        let style = this.props.parent; const collapsed = this._collapsed;
-        let chromeStatus = this.props.parent.props.Document.chromeStatus;
-        let newEditableViewProps = {
+        const rows = Math.max(1, Math.min(this.props.docList.length, Math.floor((this.props.parent.props.PanelWidth() - 2 * this.props.parent.xMargin) / (this.props.parent.columnWidth + this.props.parent.gridGap))));
+        const style = this.props.parent; const collapsed = this._collapsed;
+        const chromeStatus = this.props.parent.props.Document.chromeStatus;
+        const newEditableViewProps = {
             GetValue: () => "",
             SetValue: this.addDocument,
             contents: "+ NEW",
@@ -293,10 +293,10 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     }
 
     @computed get headingView() {
-        let heading = this._heading;
-        let key = StrCast(this.props.parent.props.Document.sectionFilter);
-        let evContents = heading ? heading : this.props.type && this.props.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
-        let headerEditableViewProps = {
+        const heading = this._heading;
+        const key = StrCast(this.props.parent.props.Document.sectionFilter);
+        const evContents = heading ? heading : this.props.type && this.props.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
+        const headerEditableViewProps = {
             GetValue: () => evContents,
             SetValue: this.headingChanged,
             contents: evContents,
@@ -343,8 +343,8 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     }
     render() {
         const background = this._background; //to account for observables in Measure
-        let contentlayout = this.contentLayout;
-        let headingview = this.headingView;
+        const contentlayout = this.contentLayout;
+        const headingview = this.headingView;
         return <Measure offset onResize={this.handleResize}>
             {({ measureRef }) => {
                 return <div ref={measureRef}>
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 54a36f691..1700c14cf 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -1,7 +1,7 @@
 import React = require("react");
-import { action, computed, observable, trace, untracked, toJS } from "mobx";
+import { action, observable } from "mobx";
 import { observer } from "mobx-react";
-import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults, Column } from "react-table";
+import { CellInfo } from "react-table";
 import "react-table/react-table.css";
 import { emptyFunction, returnFalse, returnZero, returnOne } from "../../../Utils";
 import { Doc, DocListCast, DocListCastAsync, Field, Opt } from "../../../new_fields/Doc";
@@ -9,7 +9,7 @@ import { Id } from "../../../new_fields/FieldSymbols";
 import { SetupDrag, DragManager } from "../../util/DragManager";
 import { CompileScript } from "../../util/Scripting";
 import { Transform } from "../../util/Transform";
-import { COLLECTION_BORDER_WIDTH, MAX_ROW_HEIGHT } from '../globalCssVariables.scss';
+import { MAX_ROW_HEIGHT } from '../globalCssVariables.scss';
 import '../DocumentDecorations.scss';
 import { EditableView } from "../EditableView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
@@ -89,8 +89,8 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
         // this._isEditing = true;
         // this.props.setIsEditing(true);
 
-        let field = this.props.rowProps.original[this.props.rowProps.column.id!];
-        let doc = FieldValue(Cast(field, Doc));
+        const field = this.props.rowProps.original[this.props.rowProps.column.id!];
+        const doc = FieldValue(Cast(field, Doc));
         if (typeof field === "object" && doc) this.props.setPreviewDoc(doc);
     }
 
@@ -106,12 +106,12 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
 
     private drop = (e: Event, de: DragManager.DropEvent) => {
         if (de.data instanceof DragManager.DocumentDragData) {
-            let fieldKey = this.props.rowProps.column.id as string;
+            const fieldKey = this.props.rowProps.column.id as string;
             if (de.data.draggedDocuments.length === 1) {
                 this._document[fieldKey] = de.data.draggedDocuments[0];
             }
             else {
-                let coll = Docs.Create.SchemaDocument([new SchemaHeaderField("title", "#f1efeb")], de.data.draggedDocuments, {});
+                const coll = Docs.Create.SchemaDocument([new SchemaHeaderField("title", "#f1efeb")], de.data.draggedDocuments, {});
                 this._document[fieldKey] = coll;
             }
             e.stopPropagation();
@@ -138,9 +138,9 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
     // }
 
     renderCellWithType(type: string | undefined) {
-        let dragRef: React.RefObject<HTMLDivElement> = React.createRef();
+        const dragRef: React.RefObject<HTMLDivElement> = React.createRef();
 
-        let props: FieldViewProps = {
+        const props: FieldViewProps = {
             Document: this.props.rowProps.original,
             DataDoc: this.props.rowProps.original,
             fieldKey: this.props.rowProps.column.id as string,
@@ -161,23 +161,23 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
             ContentScaling: returnOne
         };
 
-        let field = props.Document[props.fieldKey];
-        let doc = FieldValue(Cast(field, Doc));
-        let fieldIsDoc = (type === "document" && typeof field === "object") || (typeof field === "object" && doc);
+        const field = props.Document[props.fieldKey];
+        const doc = FieldValue(Cast(field, Doc));
+        const fieldIsDoc = (type === "document" && typeof field === "object") || (typeof field === "object" && doc);
 
-        let onItemDown = (e: React.PointerEvent) => {
+        const onItemDown = (e: React.PointerEvent) => {
             if (fieldIsDoc) {
                 SetupDrag(this._focusRef, () => this._document[props.fieldKey] instanceof Doc ? this._document[props.fieldKey] : this._document,
                     this._document[props.fieldKey] instanceof Doc ? (doc: Doc, target: Doc, addDoc: (newDoc: Doc) => any) => addDoc(doc) : this.props.moveDocument,
                     this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e);
             }
         };
-        let onPointerEnter = (e: React.PointerEvent): void => {
+        const onPointerEnter = (e: React.PointerEvent): void => {
             if (e.buttons === 1 && SelectionManager.GetIsDragging() && (type === "document" || type === undefined)) {
                 dragRef.current!.className = "collectionSchemaView-cellContainer doc-drag-over";
             }
         };
-        let onPointerLeave = (e: React.PointerEvent): void => {
+        const onPointerLeave = (e: React.PointerEvent): void => {
             dragRef.current!.className = "collectionSchemaView-cellContainer";
         };
 
@@ -187,7 +187,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
         if (type === "string") contents = typeof field === "string" ? (StrCast(field) === "" ? "--" : StrCast(field)) : "--" + typeof field + "--";
         if (type === "boolean") contents = typeof field === "boolean" ? (BoolCast(field) ? "true" : "false") : "--" + typeof field + "--";
         if (type === "document") {
-            let doc = FieldValue(Cast(field, Doc));
+            const doc = FieldValue(Cast(field, Doc));
             contents = typeof field === "object" ? doc ? StrCast(doc.title) === "" ? "--" : StrCast(doc.title) : `--${typeof field}--` : `--${typeof field}--`;
         }
 
@@ -215,7 +215,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
                             height={"auto"}
                             maxHeight={Number(MAX_ROW_HEIGHT)}
                             GetValue={() => {
-                                let field = props.Document[props.fieldKey];
+                                const field = props.Document[props.fieldKey];
                                 if (Field.IsField(field)) {
                                     return Field.toScriptString(field);
                                 }
@@ -226,7 +226,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
                                 if (value.startsWith(":=")) {
                                     return this.props.setComputed(value.substring(2), props.Document, this.props.rowProps.column.id!, this.props.row, this.props.col);
                                 }
-                                let script = CompileScript(value, { requiredType: type, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
+                                const script = CompileScript(value, { requiredType: type, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
                                 if (!script.compiled) {
                                     return false;
                                 }
@@ -287,15 +287,15 @@ export class CollectionSchemaCheckboxCell extends CollectionSchemaCell {
     @action
     toggleChecked = (e: React.ChangeEvent<HTMLInputElement>) => {
         this._isChecked = e.target.checked;
-        let script = CompileScript(e.target.checked.toString(), { requiredType: "boolean", addReturn: true, params: { this: Doc.name } });
+        const script = CompileScript(e.target.checked.toString(), { requiredType: "boolean", addReturn: true, params: { this: Doc.name } });
         if (script.compiled) {
             this.applyToDoc(this._document, this.props.row, this.props.col, script.run);
         }
     }
 
     render() {
-        let reference = React.createRef<HTMLDivElement>();
-        let onItemDown = (e: React.PointerEvent) => {
+        const reference = React.createRef<HTMLDivElement>();
+        const onItemDown = (e: React.PointerEvent) => {
             (!this.props.CollectionView || !this.props.CollectionView.props.isSelected() ? undefined :
                 SetupDrag(reference, () => this._document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e));
         };
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index d24f63fbb..0114342b9 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -1,5 +1,5 @@
 import React = require("react");
-import { action, computed, observable, trace, untracked } from "mobx";
+import { action, observable } from "mobx";
 import { observer } from "mobx-react";
 import "./CollectionSchemaView.scss";
 import { faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faSortAmountDown, faSortAmountUp, faTimes } from '@fortawesome/free-solid-svg-icons';
@@ -7,10 +7,8 @@ import { library, IconProp } from "@fortawesome/fontawesome-svg-core";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { Flyout, anchorPoints } from "../DocumentDecorations";
 import { ColumnType } from "./CollectionSchemaView";
-import { emptyFunction } from "../../../Utils";
-import { contains } from "typescript-collections/dist/lib/arrays";
 import { faFile } from "@fortawesome/free-regular-svg-icons";
-import { SchemaHeaderField, RandomPastel, PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField";
+import { SchemaHeaderField, PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField";
 import { undoBatch } from "../../util/UndoManager";
 
 library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile as any, faSortAmountDown, faSortAmountUp, faTimes);
@@ -32,7 +30,7 @@ export interface HeaderProps {
 
 export class CollectionSchemaHeader extends React.Component<HeaderProps> {
     render() {
-        let icon: IconProp = this.props.keyType === ColumnType.Number ? "hashtag" : this.props.keyType === ColumnType.String ? "font" :
+        const icon: IconProp = this.props.keyType === ColumnType.Number ? "hashtag" : this.props.keyType === ColumnType.String ? "font" :
             this.props.keyType === ColumnType.Boolean ? "check-square" : this.props.keyType === ColumnType.Doc ? "file" : "align-justify";
         return (
             <div className="collectionSchemaView-header" style={{ background: this.props.keyValue.color }}>
@@ -139,7 +137,7 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps>
     renderTypes = () => {
         if (this.props.typeConst) return <></>;
 
-        let type = this.props.columnField.type;
+        const type = this.props.columnField.type;
         return (
             <div className="collectionSchema-headerMenu-group">
                 <label>Column type:</label>
@@ -170,7 +168,7 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps>
     }
 
     renderSorting = () => {
-        let sort = this.props.columnField.desc;
+        const sort = this.props.columnField.desc;
         return (
             <div className="collectionSchema-headerMenu-group">
                 <label>Sort by:</label>
@@ -193,14 +191,14 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps>
     }
 
     renderColors = () => {
-        let selected = this.props.columnField.color;
+        const selected = this.props.columnField.color;
 
-        let pink = PastelSchemaPalette.get("pink2");
-        let purple = PastelSchemaPalette.get("purple2");
-        let blue = PastelSchemaPalette.get("bluegreen1");
-        let yellow = PastelSchemaPalette.get("yellow4");
-        let red = PastelSchemaPalette.get("red2");
-        let gray = "#f1efeb";
+        const pink = PastelSchemaPalette.get("pink2");
+        const purple = PastelSchemaPalette.get("purple2");
+        const blue = PastelSchemaPalette.get("bluegreen1");
+        const yellow = PastelSchemaPalette.get("yellow4");
+        const red = PastelSchemaPalette.get("red2");
+        const gray = "#f1efeb";
 
         return (
             <div className="collectionSchema-headerMenu-group">
@@ -291,8 +289,8 @@ class KeysDropdown extends React.Component<KeysDropdownProps> {
     @action
     onKeyDown = (e: React.KeyboardEvent): void => {
         if (e.key === "Enter") {
-            let keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
-            let exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 ||
+            const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
+            const exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 ||
                 this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
 
             if (!exactFound && this._searchTerm !== "" && this.props.canAddNew) {
@@ -334,11 +332,11 @@ class KeysDropdown extends React.Component<KeysDropdownProps> {
     renderOptions = (): JSX.Element[] | JSX.Element => {
         if (!this._isOpen) return <></>;
 
-        let keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
-        let exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 ||
+        const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
+        const exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 ||
             this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
 
-        let options = keyOptions.map(key => {
+        const options = keyOptions.map(key => {
             return <div key={key} className="key-option" onClick={() => { this.onSelect(key); this.setSearchTerm(""); }}>{key}</div>;
         });
 
diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index 274c8b6d1..90320df82 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -1,18 +1,18 @@
 import React = require("react");
-import { ReactTableDefaults, TableCellRenderer, ComponentPropsGetterR, ComponentPropsGetter0, RowInfo } from "react-table";
+import { ReactTableDefaults, TableCellRenderer, RowInfo } from "react-table";
 import "./CollectionSchemaView.scss";
 import { Transform } from "../../util/Transform";
 import { Doc } from "../../../new_fields/Doc";
 import { DragManager, SetupDrag } from "../../util/DragManager";
 import { SelectionManager } from "../../util/SelectionManager";
-import { Cast, FieldValue, StrCast } from "../../../new_fields/Types";
+import { Cast, FieldValue } from "../../../new_fields/Types";
 import { ContextMenu } from "../ContextMenu";
 import { action } from "mobx";
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faGripVertical, faTrash } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { DocumentManager } from "../../util/DocumentManager";
-import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
+import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { undoBatch } from "../../util/UndoManager";
 
 library.add(faGripVertical, faTrash);
@@ -43,10 +43,10 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
         document.removeEventListener("pointermove", this.onPointerMove);
     }
     onDragMove = (e: PointerEvent): void => {
-        let x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
-        let rect = this._header!.current!.getBoundingClientRect();
-        let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + ((rect.right - rect.left) / 2), rect.top);
-        let before = x[0] < bounds[0];
+        const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
+        const rect = this._header!.current!.getBoundingClientRect();
+        const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + ((rect.right - rect.left) / 2), rect.top);
+        const before = x[0] < bounds[0];
         this._header!.current!.className = "collectionSchema-col-wrapper";
         if (before) this._header!.current!.className += " col-before";
         if (!before) this._header!.current!.className += " col-after";
@@ -62,10 +62,10 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
 
     colDrop = (e: Event, de: DragManager.DropEvent) => {
         document.removeEventListener("pointermove", this.onDragMove, true);
-        let x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
-        let rect = this._header!.current!.getBoundingClientRect();
-        let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + ((rect.right - rect.left) / 2), rect.top);
-        let before = x[0] < bounds[0];
+        const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
+        const rect = this._header!.current!.getBoundingClientRect();
+        const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + ((rect.right - rect.left) / 2), rect.top);
+        const before = x[0] < bounds[0];
         if (de.data instanceof DragManager.ColumnDragData) {
             this.props.reorderColumns(de.data.colKey, this.props.columnValue, before, this.props.allColumns);
             return true;
@@ -74,21 +74,21 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
     }
 
     onPointerMove = (e: PointerEvent) => {
-        let onRowMove = (e: PointerEvent) => {
+        const onRowMove = (e: PointerEvent) => {
             e.stopPropagation();
             e.preventDefault();
 
             document.removeEventListener("pointermove", onRowMove);
             document.removeEventListener('pointerup', onRowUp);
-            let dragData = new DragManager.ColumnDragData(this.props.columnValue);
+            const dragData = new DragManager.ColumnDragData(this.props.columnValue);
             DragManager.StartColumnDrag(this._dragRef.current!, dragData, e.x, e.y);
         };
-        let onRowUp = (): void => {
+        const onRowUp = (): void => {
             document.removeEventListener("pointermove", onRowMove);
             document.removeEventListener('pointerup', onRowUp);
         };
         if (e.buttons === 1) {
-            let [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
+            const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
             if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
                 document.removeEventListener("pointermove", this.onPointerMove);
                 e.stopPropagation();
@@ -106,14 +106,14 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
     @action
     onPointerDown = (e: React.PointerEvent, ref: React.RefObject<HTMLDivElement>) => {
         this._dragRef = ref;
-        let [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX, e.clientY);
+        const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX, e.clientY);
         this._startDragPosition = { x: dx, y: dy };
         document.addEventListener("pointermove", this.onPointerMove);
     }
 
 
     render() {
-        let reference = React.createRef<HTMLDivElement>();
+        const reference = React.createRef<HTMLDivElement>();
 
         return (
             <div className="collectionSchema-col" ref={this.createColDropTarget}>
@@ -152,10 +152,10 @@ export class MovableRow extends React.Component<MovableRowProps> {
         document.removeEventListener("pointermove", this.onDragMove, true);
     }
     onDragMove = (e: PointerEvent): void => {
-        let x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
-        let rect = this._header!.current!.getBoundingClientRect();
-        let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
-        let before = x[1] < bounds[1];
+        const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
+        const rect = this._header!.current!.getBoundingClientRect();
+        const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
+        const before = x[1] < bounds[1];
         this._header!.current!.className = "collectionSchema-row-wrapper";
         if (before) this._header!.current!.className += " row-above";
         if (!before) this._header!.current!.className += " row-below";
@@ -173,16 +173,16 @@ export class MovableRow extends React.Component<MovableRowProps> {
         const rowDoc = FieldValue(Cast(this.props.rowInfo.original, Doc));
         if (!rowDoc) return false;
 
-        let x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
-        let rect = this._header!.current!.getBoundingClientRect();
-        let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
-        let before = x[1] < bounds[1];
+        const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
+        const rect = this._header!.current!.getBoundingClientRect();
+        const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
+        const before = x[1] < bounds[1];
 
         if (de.data instanceof DragManager.DocumentDragData) {
             e.stopPropagation();
             if (de.data.draggedDocuments[0] === rowDoc) return true;
-            let addDocument = (doc: Doc) => this.props.addDoc(doc, rowDoc, before);
-            let movedDocs = de.data.draggedDocuments;
+            const addDocument = (doc: Doc) => this.props.addDoc(doc, rowDoc, before);
+            const movedDocs = de.data.draggedDocuments;
             return (de.data.dropAction || de.data.userDropAction) ?
                 de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before) || added, false)
                 : (de.data.moveDocument) ?
@@ -193,14 +193,14 @@ export class MovableRow extends React.Component<MovableRowProps> {
     }
 
     onRowContextMenu = (e: React.MouseEvent): void => {
-        let description = this.props.rowWrapped ? "Unwrap text on row" : "Text wrap row";
+        const description = this.props.rowWrapped ? "Unwrap text on row" : "Text wrap row";
         ContextMenu.Instance.addItem({ description: description, event: () => this.props.textWrapRow(this.props.rowInfo.original), icon: "file-pdf" });
     }
 
     @undoBatch
     @action
     move: DragManager.MoveFunction = (doc: Doc, target: Doc, addDoc) => {
-        let targetView = DocumentManager.Instance.getDocumentView(target);
+        const targetView = DocumentManager.Instance.getDocumentView(target);
         if (targetView && targetView.props.ContainingCollectionDoc) {
             return doc !== target && doc !== targetView.props.ContainingCollectionDoc && this.props.removeDoc(doc) && addDoc(doc);
         }
@@ -217,8 +217,8 @@ export class MovableRow extends React.Component<MovableRowProps> {
         const doc = FieldValue(Cast(original, Doc));
         if (!doc) return <></>;
 
-        let reference = React.createRef<HTMLDivElement>();
-        let onItemDown = SetupDrag(reference, () => doc, this.move);
+        const reference = React.createRef<HTMLDivElement>();
+        const onItemDown = SetupDrag(reference, () => doc, this.move);
 
         let className = "collectionSchema-row";
         if (this.props.rowFocused) className += " row-focused";
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 65856cad3..f336eaf75 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -94,11 +94,11 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     }
     @action
     onDividerMove = (e: PointerEvent): void => {
-        let nativeWidth = this._mainCont!.getBoundingClientRect();
-        let minWidth = 40;
-        let maxWidth = 1000;
-        let movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0];
-        let width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth;
+        const nativeWidth = this._mainCont!.getBoundingClientRect();
+        const minWidth = 40;
+        const maxWidth = 1000;
+        const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0];
+        const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth;
         this.props.Document.schemaPreviewWidth = width;
     }
     @action
@@ -136,7 +136,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
 
     @computed
     get previewPanel() {
-        let layoutDoc = this.previewDocument ? Doc.expandTemplateLayout(this.previewDocument, this.props.DataDoc) : undefined;
+        const layoutDoc = this.previewDocument ? Doc.expandTemplateLayout(this.previewDocument, this.props.DataDoc) : undefined;
         return <div ref={this.createTarget}>
             <ContentFittingDocumentView
                 Document={layoutDoc}
@@ -258,11 +258,11 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
     @computed get childDocs() {
         if (this.props.childDocs) return this.props.childDocs;
 
-        let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
+        const doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
         return DocListCast(doc[this.props.fieldKey]);
     }
     set childDocs(docs: Doc[]) {
-        let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
+        const doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
         doc[this.props.fieldKey] = new List<Doc>(docs);
     }
 
@@ -288,12 +288,12 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
 
     @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
     @computed get tableColumns(): Column<Doc>[] {
-        let possibleKeys = this.documentKeys.filter(key => this.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1);
-        let columns: Column<Doc>[] = [];
-        let tableIsFocused = this.props.isFocused(this.props.Document);
-        let focusedRow = this._focusedCell.row;
-        let focusedCol = this._focusedCell.col;
-        let isEditable = !this._headerIsEditing;
+        const possibleKeys = this.documentKeys.filter(key => this.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1);
+        const columns: Column<Doc>[] = [];
+        const tableIsFocused = this.props.isFocused(this.props.Document);
+        const focusedRow = this._focusedCell.row;
+        const focusedCol = this._focusedCell.col;
+        const isEditable = !this._headerIsEditing;
 
         if (this.childDocs.reduce((found, doc) => found || doc.type === "collection", false)) {
             columns.push(
@@ -313,8 +313,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
             );
         }
 
-        let cols = this.columns.map(col => {
-            let header = <CollectionSchemaHeader
+        const cols = this.columns.map(col => {
+            const header = <CollectionSchemaHeader
                 keyValue={col}
                 possibleKeys={possibleKeys}
                 existingKeys={this.columns.map(c => c.heading)}
@@ -333,11 +333,11 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
                 accessor: (doc: Doc) => doc ? doc[col.heading] : 0,
                 id: col.heading,
                 Cell: (rowProps: CellInfo) => {
-                    let rowIndex = rowProps.index;
-                    let columnIndex = this.columns.map(c => c.heading).indexOf(rowProps.column.id!);
-                    let isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
+                    const rowIndex = rowProps.index;
+                    const columnIndex = this.columns.map(c => c.heading).indexOf(rowProps.column.id!);
+                    const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
 
-                    let props: CellProps = {
+                    const props: CellProps = {
                         row: rowIndex,
                         col: columnIndex,
                         rowProps: rowProps,
@@ -358,7 +358,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
                         getField: this.getField,
                     };
 
-                    let colType = this.getColumnType(col);
+                    const colType = this.getColumnType(col);
                     if (colType === ColumnType.Number) return <CollectionSchemaNumberCell {...props} />;
                     if (colType === ColumnType.String) return <CollectionSchemaStringCell {...props} />;
                     if (colType === ColumnType.Boolean) return <CollectionSchemaCheckboxCell {...props} />;
@@ -384,9 +384,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
     constructor(props: SchemaTableProps) {
         super(props);
         // convert old schema columns (list of strings) into new schema columns (list of schema header fields)
-        let oldSchemaColumns = Cast(this.props.Document.schemaColumns, listSpec("string"), []);
+        const oldSchemaColumns = Cast(this.props.Document.schemaColumns, listSpec("string"), []);
         if (oldSchemaColumns && oldSchemaColumns.length && typeof oldSchemaColumns[0] !== "object") {
-            let newSchemaColumns = oldSchemaColumns.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i);
+            const newSchemaColumns = oldSchemaColumns.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i);
             this.props.Document.schemaColumns = new List<SchemaHeaderField>(newSchemaColumns);
         }
     }
@@ -418,10 +418,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
     private getTdProps: ComponentPropsGetterR = (state, rowInfo, column, instance) => {
         if (!rowInfo || column) return {};
 
-        let row = rowInfo.index;
+        const row = rowInfo.index;
         //@ts-ignore
-        let col = this.columns.map(c => c.heading).indexOf(column!.id);
-        let isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document);
+        const col = this.columns.map(c => c.heading).indexOf(column!.id);
+        const isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document);
         // TODO: editing border doesn't work :(
         return {
             style: {
@@ -432,7 +432,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
 
     @action
     onCloseCollection = (collection: Doc): void => {
-        let index = this._openCollections.findIndex(col => col === collection[Id]);
+        const index = this._openCollections.findIndex(col => col === collection[Id]);
         if (index > -1) this._openCollections.splice(index, 1);
     }
 
@@ -450,7 +450,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
     @action
     onKeyDown = (e: KeyboardEvent): void => {
         if (!this._cellIsEditing && !this._headerIsEditing && this.props.isFocused(this.props.Document)) {// && this.props.isSelected(true)) {
-            let direction = e.key === "Tab" ? "tab" : e.which === 39 ? "right" : e.which === 37 ? "left" : e.which === 38 ? "up" : e.which === 40 ? "down" : "";
+            const direction = e.key === "Tab" ? "tab" : e.which === 39 ? "right" : e.which === 37 ? "left" : e.which === 38 ? "up" : e.which === 40 ? "down" : "";
             this._focusedCell = this.changeFocusedCellByDirection(direction, this._focusedCell.row, this._focusedCell.col);
 
             const pdoc = FieldValue(this.childDocs[this._focusedCell.row]);
@@ -479,7 +479,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
 
     @undoBatch
     createRow = () => {
-        let newDoc = Docs.Create.TextDocument({ title: "", width: 100, height: 30 });
+        const newDoc = Docs.Create.TextDocument({ title: "", width: 100, height: 30 });
         this.props.addDocument(newDoc);
     }
 
@@ -498,7 +498,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
     @undoBatch
     @action
     deleteColumn = (key: string) => {
-        let columns = this.columns;
+        const columns = this.columns;
         if (columns === undefined) {
             this.columns = new List<SchemaHeaderField>([]);
         } else {
@@ -513,7 +513,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
     @undoBatch
     @action
     changeColumns = (oldKey: string, newKey: string, addNew: boolean) => {
-        let columns = this.columns;
+        const columns = this.columns;
         if (columns === undefined) {
             this.columns = new List<SchemaHeaderField>([new SchemaHeaderField(newKey, "f1efeb")]);
         } else {
@@ -523,7 +523,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
             } else {
                 const index = columns.map(c => c.heading).indexOf(oldKey);
                 if (index > -1) {
-                    let column = columns[index];
+                    const column = columns[index];
                     column.setHeading(newKey);
                     columns[index] = column;
                     this.columns = columns;
@@ -554,8 +554,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
     setColumnType = (columnField: SchemaHeaderField, type: ColumnType): void => {
         if (columnTypes.get(columnField.heading)) return;
 
-        let columns = this.columns;
-        let index = columns.indexOf(columnField);
+        const columns = this.columns;
+        const index = columns.indexOf(columnField);
         if (index > -1) {
             columnField.setType(NumCast(type));
             columns[index] = columnField;
@@ -575,8 +575,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
 
     @undoBatch
     setColumnColor = (columnField: SchemaHeaderField, color: string): void => {
-        let columns = this.columns;
-        let index = columns.indexOf(columnField);
+        const columns = this.columns;
+        const index = columns.indexOf(columnField);
         if (index > -1) {
             columnField.setColor(color);
             columns[index] = columnField;
@@ -589,10 +589,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
 
     @undoBatch
     reorderColumns = (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => {
-        let columns = [...columnsValues];
-        let oldIndex = columns.indexOf(toMove);
-        let relIndex = columns.indexOf(relativeTo);
-        let newIndex = (oldIndex > relIndex && !before) ? relIndex + 1 : (oldIndex < relIndex && before) ? relIndex - 1 : relIndex;
+        const columns = [...columnsValues];
+        const oldIndex = columns.indexOf(toMove);
+        const relIndex = columns.indexOf(relativeTo);
+        const newIndex = (oldIndex > relIndex && !before) ? relIndex + 1 : (oldIndex < relIndex && before) ? relIndex - 1 : relIndex;
 
         if (oldIndex === newIndex) return;
 
@@ -603,17 +603,17 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
     @undoBatch
     @action
     setColumnSort = (columnField: SchemaHeaderField, descending: boolean | undefined) => {
-        let columns = this.columns;
-        let index = columns.findIndex(c => c.heading === columnField.heading);
-        let column = columns[index];
+        const columns = this.columns;
+        const index = columns.findIndex(c => c.heading === columnField.heading);
+        const column = columns[index];
         column.setDesc(descending);
         columns[index] = column;
         this.columns = columns;
     }
 
     get documentKeys() {
-        let docs = this.childDocs;
-        let keys: { [key: string]: boolean } = {};
+        const docs = this.childDocs;
+        const 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.
@@ -628,8 +628,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
 
     @action
     toggleTextWrapRow = (doc: Doc): void => {
-        let textWrapped = this.textWrappedRows;
-        let index = textWrapped.findIndex(id => doc[Id] === id);
+        const textWrapped = this.textWrappedRows;
+        const index = textWrapped.findIndex(id => doc[Id] === id);
 
         index > -1 ? textWrapped.splice(index, 1) : textWrapped.push(doc[Id]);
 
@@ -638,10 +638,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
 
     @computed
     get reactTable() {
-        let children = this.childDocs;
-        let hasCollectionChild = children.reduce((found, doc) => found || doc.type === "collection", false);
-        let expandedRowsList = this._openCollections.map(col => children.findIndex(doc => doc[Id] === col).toString());
-        let expanded = {};
+        const children = this.childDocs;
+        const hasCollectionChild = children.reduce((found, doc) => found || doc.type === "collection", false);
+        const expandedRowsList = this._openCollections.map(col => children.findIndex(doc => doc[Id] === col).toString());
+        const expanded = {};
         //@ts-ignore
         expandedRowsList.forEach(row => expanded[row] = true);
         console.log("text wrapped rows", ...[...this.textWrappedRows]); // TODO: get component to rerender on text wrap change without needign to console.log :((((
@@ -668,10 +668,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
     }
 
     onResizedChange = (newResized: Resize[], event: any) => {
-        let columns = this.columns;
+        const columns = this.columns;
         newResized.forEach(resized => {
-            let index = columns.findIndex(c => c.heading === resized.id);
-            let column = columns[index];
+            const index = columns.findIndex(c => c.heading === resized.id);
+            const column = columns[index];
             column.setWidth(resized.value);
             columns[index] = column;
         });
@@ -688,16 +688,16 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
     makeDB = async () => {
         let csv: string = this.columns.reduce((val, col) => val + col + ",", "");
         csv = csv.substr(0, csv.length - 1) + "\n";
-        let self = this;
+        const self = this;
         this.childDocs.map(doc => {
             csv += self.columns.reduce((val, col) => val + (doc[col.heading] ? doc[col.heading]!.toString() : "0") + ",", "");
             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);
+        const dbName = StrCast(this.props.Document.title);
+        const res = await Gateway.Instance.PostSchema(csv, dbName);
         if (self.props.CollectionView && self.props.CollectionView.props.addDocument) {
-            let schemaDoc = await Docs.Create.DBDocument("https://www.cs.brown.edu/" + dbName, { title: dbName }, { dbDoc: self.props.Document });
+            const schemaDoc = await Docs.Create.DBDocument("https://www.cs.brown.edu/" + dbName, { title: dbName }, { dbDoc: self.props.Document });
             if (schemaDoc) {
                 //self.props.CollectionView.props.addDocument(schemaDoc, false);
                 self.props.Document.schemaDoc = schemaDoc;
@@ -706,7 +706,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
     }
 
     getField = (row: number, col?: number) => {
-        let docs = this.childDocs;
+        const docs = this.childDocs;
 
         row = row % docs.length;
         while (row < 0) row += docs.length;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index e564f1193..955fcda80 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -1,7 +1,7 @@
 import React = require("react");
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { CursorProperty } from "csstype";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import Switch from 'rc-switch';
 import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
@@ -10,7 +10,7 @@ import { List } from "../../../new_fields/List";
 import { listSpec } from "../../../new_fields/Schema";
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from "../../../new_fields/Types";
-import { emptyFunction, Utils, numberRange } from "../../../Utils";
+import { emptyFunction, Utils } from "../../../Utils";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DragManager } from "../../util/DragManager";
 import { Transform } from "../../util/Transform";
@@ -56,15 +56,15 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     children(docs: Doc[]) {
         this._docXfs.length = 0;
         return docs.map((d, i) => {
-            let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d);
-            let layoutDoc = pair.layout ? Doc.Layout(pair.layout) : d;
-            let width = () => Math.min(layoutDoc.nativeWidth && !layoutDoc.ignoreAspect && !this.props.Document.fillColumn ? layoutDoc[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
-            let height = () => this.getDocHeight(layoutDoc);
-            let dref = React.createRef<HTMLDivElement>();
-            let dxf = () => this.getDocTransform(layoutDoc, dref.current!);
+            const pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d);
+            const layoutDoc = pair.layout ? Doc.Layout(pair.layout) : d;
+            const width = () => Math.min(layoutDoc.nativeWidth && !layoutDoc.ignoreAspect && !this.props.Document.fillColumn ? layoutDoc[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
+            const height = () => this.getDocHeight(layoutDoc);
+            const dref = React.createRef<HTMLDivElement>();
+            const dxf = () => this.getDocTransform(layoutDoc, dref.current!);
             this._docXfs.push({ dxf: dxf, width: width, height: height });
-            let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
-            let style = this.isStackingView ? { width: width(), marginTop: i === 0 ? 0 : this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
+            const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
+            const style = this.isStackingView ? { width: width(), marginTop: i === 0 ? 0 : this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
             return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
                 {this.getDisplayDoc(pair.layout || d, pair.data, dxf, width)}
             </div>;
@@ -83,20 +83,20 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             return new Map<SchemaHeaderField, Doc[]>();
         }
         const sectionHeaders = this.sectionHeaders;
-        let fields = new Map<SchemaHeaderField, Doc[]>(sectionHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
+        const fields = new Map<SchemaHeaderField, Doc[]>(sectionHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
         this.filteredChildren.map(d => {
-            let sectionValue = (d[this.sectionFilter] ? d[this.sectionFilter] : `NO ${this.sectionFilter.toUpperCase()} VALUE`) as object;
+            const sectionValue = (d[this.sectionFilter] ? d[this.sectionFilter] : `NO ${this.sectionFilter.toUpperCase()} VALUE`) as object;
             // the next five lines ensures that floating point rounding errors don't create more than one section -syip
-            let parsed = parseInt(sectionValue.toString());
-            let castedSectionValue = !isNaN(parsed) ? parsed : sectionValue;
+            const parsed = parseInt(sectionValue.toString());
+            const castedSectionValue = !isNaN(parsed) ? parsed : sectionValue;
 
             // look for if header exists already
-            let existingHeader = sectionHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`));
+            const existingHeader = sectionHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`));
             if (existingHeader) {
                 fields.get(existingHeader)!.push(d);
             }
             else {
-                let newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`);
+                const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`);
                 fields.set(newSchemaHeader, [d]);
                 sectionHeaders.push(newSchemaHeader);
             }
@@ -108,26 +108,26 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         super.componentDidMount();
         this._heightDisposer = reaction(() => {
             if (this.props.Document.autoHeight) {
-                let sectionsList = Array.from(this.Sections.size ? this.Sections.values() : [this.filteredChildren]);
+                const sectionsList = Array.from(this.Sections.size ? this.Sections.values() : [this.filteredChildren]);
                 if (this.isStackingView) {
-                    let res = this.props.ContentScaling() * sectionsList.reduce((maxHght, s) => {
-                        let r1 = Math.max(maxHght,
+                    const res = this.props.ContentScaling() * sectionsList.reduce((maxHght, s) => {
+                        const r1 = Math.max(maxHght,
                             (this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => {
-                                let val = height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap);
+                                const val = height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap);
                                 return val;
                             }, this.yMargin));
                         return r1;
                     }, 0);
                     return res;
                 } else {
-                    let sum = Array.from(this._heightMap.values()).reduce((acc: number, curr: number) => acc += curr, 0);
+                    const sum = Array.from(this._heightMap.values()).reduce((acc: number, curr: number) => acc += curr, 0);
                     return this.props.ContentScaling() * (sum + (this.Sections.size ? (this.props.Document.miniHeaders ? 20 : 85) : -15));
                 }
             }
             return -1;
         },
             (hgt: number) => {
-                let doc = hgt === -1 ? undefined : this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
+                const doc = hgt === -1 ? undefined : this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
                 doc && hgt > 0 && (Doc.Layout(doc).height = hgt);
             },
             { fireImmediately: true }
@@ -162,9 +162,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     @computed get onClickHandler() { return ScriptCast(this.Document.onChildClick); }
 
     getDisplayDoc(doc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) {
-        let layoutDoc = Doc.Layout(doc);
-        let height = () => this.getDocHeight(doc);
-        let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]());
+        const layoutDoc = Doc.Layout(doc);
+        const height = () => this.getDocHeight(doc);
+        const finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]());
         return <ContentFittingDocumentView
             Document={doc}
             DataDocument={dataDoc}
@@ -192,12 +192,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
     getDocHeight(d?: Doc) {
         if (!d) return 0;
-        let layoutDoc = Doc.Layout(d);
-        let nw = NumCast(layoutDoc.nativeWidth);
-        let nh = NumCast(layoutDoc.nativeHeight);
+        const layoutDoc = Doc.Layout(d);
+        const nw = NumCast(layoutDoc.nativeWidth);
+        const nh = NumCast(layoutDoc.nativeHeight);
         let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
         if (!layoutDoc.ignoreAspect && !layoutDoc.fitWidth && nw && nh) {
-            let aspect = nw && nh ? nh / nw : 1;
+            const aspect = nw && nh ? nh / nw : 1;
             if (!(d.nativeWidth && !layoutDoc.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid);
             return wid * aspect;
         }
@@ -215,8 +215,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
     @action
     onDividerMove = (e: PointerEvent): void => {
-        let dragPos = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
-        let delta = dragPos - this._columnStart;
+        const dragPos = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
+        const delta = dragPos - this._columnStart;
         this._columnStart = dragPos;
         this.layoutDoc.columnWidth = Math.max(10, this.columnWidth + delta);
     }
@@ -238,13 +238,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
-        let where = [de.x, de.y];
+        const where = [de.x, de.y];
         let targInd = -1;
         let plusOne = false;
         if (de.data instanceof DragManager.DocumentDragData) {
             this._docXfs.map((cd, i) => {
-                let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
-                let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height());
+                const pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
+                const pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height());
                 if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) {
                     targInd = i;
                     plusOne = (where[1] > (pos[1] + pos1[1]) / 2 ? 1 : 0) ? true : false;
@@ -252,12 +252,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             });
         }
         if (super.drop(e, de)) {
-            let newDoc = de.data.droppedDocuments[0];
-            let docs = this.childDocList;
+            const newDoc = de.data.droppedDocuments[0];
+            const docs = this.childDocList;
             if (docs) {
                 if (targInd === -1) targInd = docs.length;
                 else targInd = docs.indexOf(this.filteredChildren[targInd]);
-                let srcInd = docs.indexOf(newDoc);
+                const srcInd = docs.indexOf(newDoc);
                 docs.splice(srcInd, 1);
                 docs.splice((targInd > srcInd ? targInd - 1 : targInd) + (plusOne ? 1 : 0), 0, newDoc);
             }
@@ -267,19 +267,19 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     @undoBatch
     @action
     onDrop = async (e: React.DragEvent): Promise<void> => {
-        let where = [e.clientX, e.clientY];
+        const where = [e.clientX, e.clientY];
         let targInd = -1;
         this._docXfs.map((cd, i) => {
-            let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
-            let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height());
+            const pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
+            const pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height());
             if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) {
                 targInd = i;
             }
         });
         super.onDrop(e, {}, () => {
             if (targInd !== -1) {
-                let newDoc = this.childDocs[this.childDocs.length - 1];
-                let docs = this.childDocList;
+                const newDoc = this.childDocs[this.childDocs.length - 1];
+                const docs = this.childDocList;
                 if (docs) {
                     docs.splice(docs.length - 1, 1);
                     docs.splice(targInd, 0, newDoc);
@@ -289,13 +289,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
     headings = () => Array.from(this.Sections.keys());
     sectionStacking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
-        let key = this.sectionFilter;
+        const key = this.sectionFilter;
         let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
-        let types = docList.length ? docList.map(d => typeof d[key]) : this.childDocs.map(d => typeof d[key]);
+        const types = docList.length ? docList.map(d => typeof d[key]) : this.childDocs.map(d => typeof d[key]);
         if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
             type = types[0];
         }
-        let cols = () => this.isStackingView ? 1 : Math.max(1, Math.min(this.filteredChildren.length,
+        const cols = () => this.isStackingView ? 1 : Math.max(1, Math.min(this.filteredChildren.length,
             Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap))));
         return <CollectionStackingViewFieldColumn
             key={heading ? heading.heading : ""}
@@ -313,23 +313,23 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
 
     getDocTransform(doc: Doc, dref: HTMLDivElement) {
         if (!dref) return Transform.Identity();
-        let y = this._scroll; // required for document decorations to update when the text box container is scrolled
-        let { scale, translateX, translateY } = Utils.GetScreenTransform(dref);
-        let outerXf = Utils.GetScreenTransform(this._masonryGridRef!);
-        let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
+        const y = this._scroll; // required for document decorations to update when the text box container is scrolled
+        const { scale, translateX, translateY } = Utils.GetScreenTransform(dref);
+        const outerXf = Utils.GetScreenTransform(this._masonryGridRef!);
+        const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
         return this.props.ScreenToLocalTransform().
             translate(offset[0], offset[1] + (this.props.ChromeHeight && this.props.ChromeHeight() < 0 ? this.props.ChromeHeight() : 0)).
             scale(NumCast(doc.width, 1) / this.columnWidth);
     }
 
     sectionMasonry = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
-        let key = this.sectionFilter;
+        const key = this.sectionFilter;
         let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
-        let types = docList.length ? docList.map(d => typeof d[key]) : this.childDocs.map(d => typeof d[key]);
+        const types = docList.length ? docList.map(d => typeof d[key]) : this.childDocs.map(d => typeof d[key]);
         if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
             type = types[0];
         }
-        let rows = () => !this.isStackingView ? 1 : Math.max(1, Math.min(docList.length,
+        const rows = () => !this.isStackingView ? 1 : Math.max(1, Math.min(docList.length,
             Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap))));
         return <CollectionMasonryViewFieldRow
             key={heading ? heading.heading : ""}
@@ -356,9 +356,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
 
     sortFunc = (a: [SchemaHeaderField, Doc[]], b: [SchemaHeaderField, Doc[]]): 1 | -1 => {
-        let descending = BoolCast(this.props.Document.stackingHeadersSortDescending);
-        let firstEntry = descending ? b : a;
-        let secondEntry = descending ? a : b;
+        const descending = BoolCast(this.props.Document.stackingHeadersSortDescending);
+        const firstEntry = descending ? b : a;
+        const secondEntry = descending ? a : b;
         return firstEntry[0].heading > secondEntry[0].heading ? 1 : -1;
     }
 
@@ -369,28 +369,28 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     onContextMenu = (e: React.MouseEvent): void => {
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
         if (!e.isPropagationStopped()) {
-            let subItems: ContextMenuProps[] = [];
+            const subItems: ContextMenuProps[] = [];
             subItems.push({ description: `${this.props.Document.fillColumn ? "Variable Size" : "Autosize"} Column`, event: () => this.props.Document.fillColumn = !this.props.Document.fillColumn, icon: "plus" });
             subItems.push({ description: `${this.props.Document.showTitles ? "Hide Titles" : "Show Titles"}`, event: () => this.props.Document.showTitles = !this.props.Document.showTitles ? "title" : "", icon: "plus" });
             subItems.push({ description: `${this.props.Document.showCaptions ? "Hide Captions" : "Show Captions"}`, event: () => this.props.Document.showCaptions = !this.props.Document.showCaptions ? "caption" : "", icon: "plus" });
             ContextMenu.Instance.addItem({ description: "Stacking Options ...", subitems: subItems, icon: "eye" });
 
-            let existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
-            let onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
+            const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
+            const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
             onClicks.push({ description: "Edit onChildClick script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Child Clicked...", this.props.Document, "onChildClick", obj.x, obj.y) });
             !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
         }
     }
 
     render() {
-        let editableViewProps = {
+        const editableViewProps = {
             GetValue: () => "",
             SetValue: this.addGroup,
             contents: "+ ADD A GROUP"
         };
         let sections = [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]];
         if (this.sectionFilter) {
-            let entries = Array.from(this.Sections.entries());
+            const entries = Array.from(this.Sections.entries());
             sections = entries.sort(this.sortFunc);
         }
         return (
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index b9d334b10..80dc482af 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -2,17 +2,14 @@ import React = require("react");
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faPalette } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable, trace, runInAction } from "mobx";
+import { action, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, WidthSym } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
+import { Doc } from "../../../new_fields/Doc";
 import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { ScriptField } from "../../../new_fields/ScriptField";
 import { NumCast, StrCast } from "../../../new_fields/Types";
-import { Utils } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
-import { CompileScript } from "../../util/Scripting";
 import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
@@ -61,8 +58,8 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     columnDrop = action((e: Event, de: DragManager.DropEvent) => {
         this._createAliasSelected = false;
         if (de.data instanceof DragManager.DocumentDragData) {
-            let key = StrCast(this.props.parent.props.Document.sectionFilter);
-            let castedValue = this.getValue(this._heading);
+            const key = StrCast(this.props.parent.props.Document.sectionFilter);
+            const castedValue = this.getValue(this._heading);
             if (castedValue) {
                 de.data.droppedDocuments.forEach(d => d[key] = castedValue);
             }
@@ -74,7 +71,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         }
     });
     getValue = (value: string): any => {
-        let parsed = parseInt(value);
+        const parsed = parseInt(value);
         if (!isNaN(parsed)) {
             return parsed;
         }
@@ -90,8 +87,8 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     @action
     headingChanged = (value: string, shiftDown?: boolean) => {
         this._createAliasSelected = false;
-        let key = StrCast(this.props.parent.props.Document.sectionFilter);
-        let castedValue = this.getValue(value);
+        const key = StrCast(this.props.parent.props.Document.sectionFilter);
+        const castedValue = this.getValue(value);
         if (castedValue) {
             if (this.props.parent.sectionHeaders) {
                 if (this.props.parent.sectionHeaders.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
@@ -135,11 +132,11 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     @action
     addDocument = (value: string, shiftDown?: boolean) => {
         this._createAliasSelected = false;
-        let key = StrCast(this.props.parent.props.Document.sectionFilter);
-        let newDoc = Docs.Create.TextDocument({ height: 18, width: 200, documentText: "@@@" + value, title: value, autoHeight: true });
+        const key = StrCast(this.props.parent.props.Document.sectionFilter);
+        const newDoc = Docs.Create.TextDocument({ height: 18, width: 200, documentText: "@@@" + value, title: value, autoHeight: true });
         newDoc[key] = this.getValue(this.props.heading);
-        let maxHeading = this.props.docList.reduce((maxHeading, doc) => NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0);
-        let heading = maxHeading === 0 || this.props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3;
+        const maxHeading = this.props.docList.reduce((maxHeading, doc) => NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0);
+        const heading = maxHeading === 0 || this.props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3;
         newDoc.heading = heading;
         return this.props.parent.props.addDocument(newDoc);
     }
@@ -147,10 +144,10 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     @action
     deleteColumn = () => {
         this._createAliasSelected = false;
-        let key = StrCast(this.props.parent.props.Document.sectionFilter);
+        const key = StrCast(this.props.parent.props.Document.sectionFilter);
         this.props.docList.forEach(d => d[key] = undefined);
         if (this.props.parent.sectionHeaders && this.props.headingObject) {
-            let index = this.props.parent.sectionHeaders.indexOf(this.props.headingObject);
+            const index = this.props.parent.sectionHeaders.indexOf(this.props.headingObject);
             this.props.parent.sectionHeaders.splice(index, 1);
         }
     }
@@ -166,10 +163,10 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     }
 
     startDrag = (e: PointerEvent) => {
-        let [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
+        const [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
         if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
-            let alias = Doc.MakeAlias(this.props.parent.props.Document);
-            let key = StrCast(this.props.parent.props.Document.sectionFilter);
+            const alias = Doc.MakeAlias(this.props.parent.props.Document);
+            const key = StrCast(this.props.parent.props.Document.sectionFilter);
             let value = this.getValue(this._heading);
             value = typeof value === "string" ? `"${value}"` : value;
             alias.viewSpecScript = ScriptField.MakeFunction(`doc.${key} === ${value}`, { doc: Doc.name });
@@ -195,7 +192,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         e.stopPropagation();
         e.preventDefault();
 
-        let [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX, e.clientY);
+        const [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX, e.clientY);
         this._startDragPosition = { x: dx, y: dy };
 
         if (this._createAliasSelected) {
@@ -208,17 +205,17 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     }
 
     renderColorPicker = () => {
-        let selected = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
+        const selected = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
 
-        let pink = PastelSchemaPalette.get("pink2");
-        let purple = PastelSchemaPalette.get("purple4");
-        let blue = PastelSchemaPalette.get("bluegreen1");
-        let yellow = PastelSchemaPalette.get("yellow4");
-        let red = PastelSchemaPalette.get("red2");
-        let green = PastelSchemaPalette.get("bluegreen7");
-        let cyan = PastelSchemaPalette.get("bluegreen5");
-        let orange = PastelSchemaPalette.get("orange1");
-        let gray = "#f1efeb";
+        const pink = PastelSchemaPalette.get("pink2");
+        const purple = PastelSchemaPalette.get("purple4");
+        const blue = PastelSchemaPalette.get("bluegreen1");
+        const yellow = PastelSchemaPalette.get("yellow4");
+        const red = PastelSchemaPalette.get("red2");
+        const green = PastelSchemaPalette.get("bluegreen7");
+        const cyan = PastelSchemaPalette.get("bluegreen5");
+        const orange = PastelSchemaPalette.get("orange1");
+        const gray = "#f1efeb";
 
         return (
             <div className="collectionStackingView-colorPicker">
@@ -243,7 +240,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     }
 
     renderMenu = () => {
-        let selected = this._createAliasSelected;
+        const selected = this._createAliasSelected;
         return (
             <div className="collectionStackingView-optionPicker">
                 <div className="optionOptions">
@@ -262,16 +259,16 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     @observable _headingsHack: number = 1;
 
     render() {
-        let cols = this.props.cols();
-        let key = StrCast(this.props.parent.props.Document.sectionFilter);
+        const cols = this.props.cols();
+        const key = StrCast(this.props.parent.props.Document.sectionFilter);
         let templatecols = "";
-        let headings = this.props.headings();
-        let heading = this._heading;
-        let style = this.props.parent;
-        let singleColumn = style.isStackingView;
-        let uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
-        let evContents = heading ? heading : this.props.type && this.props.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
-        let headerEditableViewProps = {
+        const headings = this.props.headings();
+        const heading = this._heading;
+        const style = this.props.parent;
+        const singleColumn = style.isStackingView;
+        const uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
+        const evContents = heading ? heading : this.props.type && this.props.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
+        const headerEditableViewProps = {
             GetValue: () => evContents,
             SetValue: this.headingChanged,
             contents: evContents,
@@ -281,7 +278,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
             toggle: this.toggleVisibility,
             color: this._color
         };
-        let newEditableViewProps = {
+        const newEditableViewProps = {
             GetValue: () => "",
             SetValue: this.addDocument,
             contents: "+ NEW",
@@ -290,7 +287,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
             toggle: this.toggleVisibility,
             color: this._color
         };
-        let headingView = this.props.headingObject ?
+        const headingView = this.props.headingObject ?
             <div key={heading} className="collectionStackingView-sectionHeader" ref={this._headerRef}
                 style={{
                     width: (style.columnWidth) /
@@ -335,7 +332,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                 </div>
             </div> : (null);
         for (let i = 0; i < cols; i++) templatecols += `${style.columnWidth / style.numGroupColumns}px `;
-        let chromeStatus = this.props.parent.props.Document.chromeStatus;
+        const chromeStatus = this.props.parent.props.Document.chromeStatus;
         return (
             <div className="collectionStackingViewFieldColumn" key={heading} style={{ width: `${100 / ((uniqueHeadings.length + ((chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ? 1 : 0)) || 1)}%`, background: this._background }}
                 ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}>
diff --git a/src/client/views/collections/CollectionStaffView.tsx b/src/client/views/collections/CollectionStaffView.tsx
index 40e860b12..105061f46 100644
--- a/src/client/views/collections/CollectionStaffView.tsx
+++ b/src/client/views/collections/CollectionStaffView.tsx
@@ -2,7 +2,7 @@ import { CollectionSubView } from "./CollectionSubView";
 import { Transform } from "../../util/Transform";
 import React = require("react");
 import { computed, action, IReactionDisposer, reaction, runInAction, observable } from "mobx";
-import { Doc, HeightSym } from "../../../new_fields/Doc";
+import { Doc } from "../../../new_fields/Doc";
 import { NumCast } from "../../../new_fields/Types";
 import "./CollectionStaffView.scss";
 import { observer } from "mobx-react";
@@ -32,9 +32,9 @@ export class CollectionStaffView extends CollectionSubView(doc => doc) {
     }
 
     @computed get staves() {
-        let staves = [];
+        const staves = [];
         for (let i = 0; i < this._staves; i++) {
-            let rows = [];
+            const rows = [];
             for (let j = 0; j < 5; j++) {
                 rows.push(<div key={`staff-${i}-${j}`} className="collectionStaffView-line"></div>);
             }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index e80825825..0a2e27165 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -19,7 +19,7 @@ import { FieldViewProps } from "../nodes/FieldView";
 import { FormattedTextBox, GoogleRef } from "../nodes/FormattedTextBox";
 import { CollectionView } from "./CollectionView";
 import React = require("react");
-var path = require('path');
+import { basename } from 'path';
 import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils";
 import { ImageUtils } from "../../util/Import & Export/ImageUtils";
 import { Networking } from "../../Network";
@@ -92,7 +92,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
             return Cast(this.dataField, listSpec(Doc));
         }
         get childDocs() {
-            let docs = DocListCast(this.dataField);
+            const docs = DocListCast(this.dataField);
             const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField);
             return viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
         }
@@ -100,10 +100,10 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
         @action
         protected async setCursorPosition(position: [number, number]) {
             let ind;
-            let doc = this.props.Document;
-            let id = CurrentUserUtils.id;
-            let email = Doc.CurrentUserEmail;
-            let pos = { x: position[0], y: position[1] };
+            const doc = this.props.Document;
+            const id = CurrentUserUtils.id;
+            const email = Doc.CurrentUserEmail;
+            const pos = { x: position[0], y: position[1] };
             if (id && email) {
                 const proto = Doc.GetProto(doc);
                 if (!proto) {
@@ -123,7 +123,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                 if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.data.metadata.id === id)) > -1) {
                     cursors[ind].setPosition(pos);
                 } else {
-                    let entry = new CursorField({ metadata: { id: id, identifier: email, timestamp: Date.now() }, position: pos });
+                    const entry = new CursorField({ metadata: { id: id, identifier: email, timestamp: Date.now() }, position: pos });
                     cursors.push(entry);
                 }
             }
@@ -145,7 +145,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                 if (de.data.dropAction || de.data.userDropAction) {
                     added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
                 } else if (de.data.moveDocument) {
-                    let movedDocs = de.data.draggedDocuments;
+                    const movedDocs = de.data.draggedDocuments;
                     added = movedDocs.reduce((added: boolean, d, i) =>
                         de.data.droppedDocuments[i] !== d ? this.props.addDocument(de.data.droppedDocuments[i]) :
                             de.data.moveDocument(d, this.props.Document, this.props.addDocument) || added, false);
@@ -169,8 +169,8 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                 e.stopPropagation(); // bcz: this is a hack to stop propagation when dropping an image on a text document with shift+ctrl
                 return;
             }
-            let html = e.dataTransfer.getData("text/html");
-            let text = e.dataTransfer.getData("text/plain");
+            const html = e.dataTransfer.getData("text/html");
+            const text = e.dataTransfer.getData("text/plain");
 
             if (text && text.startsWith("<div")) {
                 return;
@@ -179,9 +179,9 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
             e.preventDefault();
 
             if (html && FormattedTextBox.IsFragment(html)) {
-                let href = FormattedTextBox.GetHref(html);
+                const href = FormattedTextBox.GetHref(html);
                 if (href) {
-                    let docid = FormattedTextBox.GetDocFromUrl(href);
+                    const docid = FormattedTextBox.GetDocFromUrl(href);
                     if (docid) { // prosemirror text containing link to dash document
                         DocServer.GetRefField(docid).then(f => {
                             if (f instanceof Doc) {
@@ -198,19 +198,19 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                 return;
             }
             if (html && !html.startsWith("<a")) {
-                let tags = html.split("<");
+                const tags = html.split("<");
                 if (tags[0] === "") tags.splice(0, 1);
-                let img = tags[0].startsWith("img") ? tags[0] : tags.length > 1 && tags[1].startsWith("img") ? tags[1] : "";
+                const img = tags[0].startsWith("img") ? tags[0] : tags.length > 1 && tags[1].startsWith("img") ? tags[1] : "";
                 if (img) {
-                    let split = img.split("src=\"")[1].split("\"")[0];
-                    let doc = Docs.Create.ImageDocument(split, { ...options, width: 300 });
+                    const split = img.split("src=\"")[1].split("\"")[0];
+                    const doc = Docs.Create.ImageDocument(split, { ...options, width: 300 });
                     ImageUtils.ExtractExif(doc);
                     this.props.addDocument(doc);
                     return;
                 } else {
-                    let path = window.location.origin + "/doc/";
+                    const path = window.location.origin + "/doc/";
                     if (text.startsWith(path)) {
-                        let docid = text.replace(Utils.prepend("/doc/"), "").split("?")[0];
+                        const docid = text.replace(Utils.prepend("/doc/"), "").split("?")[0];
                         DocServer.GetRefField(docid).then(f => {
                             if (f instanceof Doc) {
                                 if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
@@ -218,7 +218,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                             }
                         });
                     } else {
-                        let htmlDoc = Docs.Create.HtmlDocument(html, { ...options, title: "-web page-", width: 300, height: 300, documentText: text });
+                        const htmlDoc = Docs.Create.HtmlDocument(html, { ...options, title: "-web page-", width: 300, height: 300, documentText: text });
                         this.props.addDocument(htmlDoc);
                     }
                     return;
@@ -231,8 +231,8 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
             }
             let matches: RegExpExecArray | null;
             if ((matches = /(https:\/\/)?docs\.google\.com\/document\/d\/([^\\]+)\/edit/g.exec(text)) !== null) {
-                let newBox = Docs.Create.TextDocument({ ...options, width: 400, height: 200, title: "Awaiting title from Google Docs..." });
-                let proto = newBox.proto!;
+                const newBox = Docs.Create.TextDocument({ ...options, width: 400, height: 200, title: "Awaiting title from Google Docs..." });
+                const proto = newBox.proto!;
                 const documentId = matches[2];
                 proto[GoogleRef] = documentId;
                 proto.data = "Please select this document and then click on its pull button to load its contents from from Google Docs...";
@@ -249,17 +249,17 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                 const mediaItems = await GooglePhotos.Query.AlbumSearch(albumId);
                 console.log(mediaItems);
             }
-            let batch = UndoManager.StartBatch("collection view drop");
-            let promises: Promise<void>[] = [];
+            const batch = UndoManager.StartBatch("collection view drop");
+            const promises: Promise<void>[] = [];
             // tslint:disable-next-line:prefer-for-of
             for (let i = 0; i < e.dataTransfer.items.length; i++) {
-                let item = e.dataTransfer.items[i];
+                const item = e.dataTransfer.items[i];
                 if (item.kind === "string" && item.type.indexOf("uri") !== -1) {
                     let str: string;
-                    let prom = new Promise<string>(resolve => e.dataTransfer.items[i].getAsString(resolve))
+                    const prom = new Promise<string>(resolve => e.dataTransfer.items[i].getAsString(resolve))
                         .then(action((s: string) => rp.head(Utils.CorsProxy(str = s))))
                         .then(result => {
-                            let type = result["content-type"];
+                            const type = result["content-type"];
                             if (type) {
                                 Docs.Get.DocumentFromType(type, str, { ...options, width: 300, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300 })
                                     .then(doc => doc && this.props.addDocument(doc));
@@ -267,23 +267,23 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                         });
                     promises.push(prom);
                 }
-                let type = item.type;
+                const type = item.type;
                 if (item.kind === "file") {
-                    let file = item.getAsFile();
-                    let formData = new FormData();
+                    const file = item.getAsFile();
+                    const formData = new FormData();
 
                     if (!file || !file.type) {
                         continue;
                     }
 
                     formData.append('file', file);
-                    let dropFileName = file ? file.name : "-empty-";
+                    const dropFileName = file ? file.name : "-empty-";
                     promises.push(Networking.PostFormDataToServer("/upload", formData).then(results => {
                         results.map(action(({ clientAccessPath }: any) => {
-                            let full = { ...options, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300, width: 300, title: dropFileName };
-                            let pathname = Utils.prepend(clientAccessPath);
+                            const full = { ...options, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300, width: 300, title: dropFileName };
+                            const pathname = Utils.prepend(clientAccessPath);
                             Docs.Get.DocumentFromType(type, pathname, full).then(doc => {
-                                doc && (Doc.GetProto(doc).fileUpload = path.basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, ""));
+                                doc && (Doc.GetProto(doc).fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, ""));
                                 doc && this.props.addDocument(doc);
                             });
                         }));
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index c4b7e2d31..48ea35c6b 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -3,7 +3,7 @@ import { faAngleRight, faArrowsAltH, faBell, faCamera, faCaretDown, faCaretRight
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../new_fields/Doc';
+import { Doc, DocListCast, Field, HeightSym, WidthSym } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { List } from '../../../new_fields/List';
 import { Document, listSpec } from '../../../new_fields/Schema';
@@ -95,11 +95,11 @@ class TreeView extends React.Component<TreeViewProps> {
     @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.document.maxEmbedHeight, 300); }
     @computed get dataDoc() { return this.templateDataDoc ? this.templateDataDoc : this.props.document; }
     @computed get fieldKey() {
-        let splits = StrCast(Doc.LayoutField(this.props.document)).split("fieldKey={\"");
+        const splits = StrCast(Doc.LayoutField(this.props.document)).split("fieldKey={\"");
         return splits.length > 1 ? splits[1].split("\"")[0] : "data";
     }
     childDocList(field: string) {
-        let layout = Doc.LayoutField(this.props.document) instanceof Doc ? Doc.LayoutField(this.props.document) as Doc : undefined;
+        const layout = Doc.LayoutField(this.props.document) instanceof Doc ? Doc.LayoutField(this.props.document) as Doc : undefined;
         return ((this.props.dataDoc ? Cast(this.props.dataDoc[field], listSpec(Doc)) : undefined) ||
             (layout ? Cast(layout[field], listSpec(Doc)) : undefined) ||
             Cast(this.props.document[field], listSpec(Doc))) as Doc[];
@@ -149,10 +149,10 @@ class TreeView extends React.Component<TreeViewProps> {
     }
     onDragMove = (e: PointerEvent): void => {
         Doc.UnBrushDoc(this.dataDoc);
-        let pt = [e.clientX, e.clientY]
-        let rect = this._header!.current!.getBoundingClientRect();
-        let before = pt[1] < rect.top + rect.height / 2;
-        let inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && DocListCast(this.dataDoc[this.fieldKey]).length);
+        const pt = [e.clientX, e.clientY];
+        const rect = this._header!.current!.getBoundingClientRect();
+        const before = pt[1] < rect.top + rect.height / 2;
+        const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && DocListCast(this.dataDoc[this.fieldKey]).length);
         this._header!.current!.className = "treeViewItem-header";
         if (inside) this._header!.current!.className += " treeViewItem-header-inside";
         else if (before) this._header!.current!.className += " treeViewItem-header-above";
@@ -172,8 +172,8 @@ class TreeView extends React.Component<TreeViewProps> {
         SetValue={undoBatch((value: string) => Doc.SetInPlace(this.props.document, key, value, false) || true)}
         OnFillDown={undoBatch((value: string) => {
             Doc.SetInPlace(this.props.document, key, value, false);
-            let layoutDoc = this.props.document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.document.layoutCustom)) : undefined;
-            let doc = layoutDoc || Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
+            const layoutDoc = this.props.document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.document.layoutCustom)) : undefined;
+            const doc = layoutDoc || Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
             TreeView.loadId = doc[Id];
             return this.props.addDocument(doc);
         })}
@@ -205,7 +205,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" });
                 ContextMenu.Instance.addItem({ description: "Create New Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
             }
-            ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.Create.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" });
+            ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { const kvp = Docs.Create.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" });
             ContextMenu.Instance.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.document, StrCast(this.props.document.title), () => { }, () => { }), icon: "file" });
             ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15);
             e.stopPropagation();
@@ -215,13 +215,13 @@ class TreeView extends React.Component<TreeViewProps> {
 
     @undoBatch
     treeDrop = (e: Event, de: DragManager.DropEvent) => {
-        let pt = [de.x, de.y];
-        let rect = this._header!.current!.getBoundingClientRect();
-        let before = pt[1] < rect.top + rect.height / 2;
-        let inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && DocListCast(this.dataDoc[this.fieldKey]).length);
+        const pt = [de.x, de.y];
+        const rect = this._header!.current!.getBoundingClientRect();
+        const before = pt[1] < rect.top + rect.height / 2;
+        const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && DocListCast(this.dataDoc[this.fieldKey]).length);
         if (de.data instanceof DragManager.LinkDragData) {
-            let sourceDoc = de.data.linkSourceDocument;
-            let destDoc = this.props.document;
+            const sourceDoc = de.data.linkSourceDocument;
+            const destDoc = this.props.document;
             DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc });
             e.stopPropagation();
         }
@@ -232,7 +232,7 @@ class TreeView extends React.Component<TreeViewProps> {
             if (inside) {
                 addDoc = (doc: Doc) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) || addDoc(doc);
             }
-            let movedDocs = (de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments);
+            const movedDocs = (de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments);
             return ((de.data.dropAction && (de.data.options !== this.props.treeViewId)) || de.data.userDropAction) ?
                 de.data.droppedDocuments.reduce((added, d) => addDoc(d) || added, false)
                 : de.data.moveDocument ?
@@ -243,23 +243,23 @@ class TreeView extends React.Component<TreeViewProps> {
     }
 
     docTransform = () => {
-        let { scale, translateX, translateY } = Utils.GetScreenTransform(this._dref.current!);
-        let outerXf = this.props.outerXf();
-        let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
-        let finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1] + (this.props.ChromeHeight && this.props.ChromeHeight() < 0 ? this.props.ChromeHeight() : 0));
+        const { scale, translateX, translateY } = Utils.GetScreenTransform(this._dref.current!);
+        const outerXf = this.props.outerXf();
+        const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
+        const finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1] + (this.props.ChromeHeight && this.props.ChromeHeight() < 0 ? this.props.ChromeHeight() : 0));
         return finalXf;
     }
     docWidth = () => {
-        let layoutDoc = Doc.Layout(this.props.document);
-        let aspect = NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth);
+        const layoutDoc = Doc.Layout(this.props.document);
+        const aspect = NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth);
         if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20));
         return NumCast(layoutDoc.nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20;
     }
     docHeight = () => {
-        let layoutDoc = Doc.Layout(this.props.document);
-        let bounds = this.boundsOfCollectionDocument;
+        const layoutDoc = Doc.Layout(this.props.document);
+        const bounds = this.boundsOfCollectionDocument;
         return Math.min(this.MAX_EMBED_HEIGHT, (() => {
-            let aspect = NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth, 1);
+            const aspect = NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth, 1);
             if (aspect) return this.docWidth() * aspect;
             if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x);
             return layoutDoc.fitWidth ? (!this.props.document.nativeHeight ? NumCast(this.props.containingCollection.height) :
@@ -270,18 +270,18 @@ class TreeView extends React.Component<TreeViewProps> {
     }
 
     @computed get expandedField() {
-        let ids: { [key: string]: string } = {};
-        let doc = this.props.document;
+        const ids: { [key: string]: string } = {};
+        const doc = this.props.document;
         doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
 
-        let rows: JSX.Element[] = [];
-        for (let key of Object.keys(ids).slice().sort()) {
-            let contents = doc[key];
+        const rows: JSX.Element[] = [];
+        for (const key of Object.keys(ids).slice().sort()) {
+            const contents = doc[key];
             let contentElement: (JSX.Element | null)[] | JSX.Element = [];
 
             if (contents instanceof Doc || Cast(contents, listSpec(Doc))) {
-                let remDoc = (doc: Doc) => this.remove(doc, key);
-                let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
+                const remDoc = (doc: Doc) => this.remove(doc, key);
+                const addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
                 contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] :
                     DocListCast(contents), this.props.treeViewId, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                     this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
@@ -310,9 +310,9 @@ class TreeView extends React.Component<TreeViewProps> {
     @computed get renderContent() {
         const expandKey = this.treeViewExpandedView === this.fieldKey ? this.fieldKey : this.treeViewExpandedView === "links" ? "links" : undefined;
         if (expandKey !== undefined) {
-            let remDoc = (doc: Doc) => this.remove(doc, expandKey);
-            let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true);
-            let docs = expandKey === "links" ? this.childLinks : this.childDocs;
+            const remDoc = (doc: Doc) => this.remove(doc, expandKey);
+            const addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true);
+            const docs = expandKey === "links" ? this.childLinks : this.childDocs;
             return <ul key={expandKey + "more"}>
                 {!docs ? (null) :
                     TreeView.GetChildElements(docs, this.props.treeViewId, Doc.Layout(this.props.document),
@@ -326,7 +326,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 {this.expandedField}
             </div></ul>;
         } else {
-            let layoutDoc = Doc.Layout(this.props.document);
+            const layoutDoc = Doc.Layout(this.props.document);
             return <div ref={this._dref} style={{ display: "inline-block", height: this.docHeight() }} key={this.props.document[Id] + this.props.document.title}>
                 <ContentFittingDocumentView
                     Document={layoutDoc}
@@ -363,10 +363,10 @@ class TreeView extends React.Component<TreeViewProps> {
      */
     @computed
     get renderTitle() {
-        let reference = React.createRef<HTMLDivElement>();
-        let onItemDown = SetupDrag(reference, () => this.dataDoc, this.move, this.props.dropAction, this.props.treeViewId, true);
+        const reference = React.createRef<HTMLDivElement>();
+        const onItemDown = SetupDrag(reference, () => this.dataDoc, this.move, this.props.dropAction, this.props.treeViewId, true);
 
-        let headerElements = (
+        const headerElements = (
             <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
                 onPointerDown={action(() => {
                     if (this.treeViewOpen) {
@@ -379,7 +379,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 })}>
                 {this.treeViewExpandedView}
             </span>);
-        let openRight = (<div className="treeViewItem-openRight" onPointerDown={this.onPointerDown} onClick={this.openRight}>
+        const openRight = (<div className="treeViewItem-openRight" onPointerDown={this.onPointerDown} onClick={this.openRight}>
             <FontAwesomeIcon title="open in pane on right" icon="angle-right" size="lg" />
         </div>);
         return <>
@@ -440,28 +440,28 @@ class TreeView extends React.Component<TreeViewProps> {
             childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result);
         }
 
-        let docs = childDocs.slice();
-        let dataExtension = containingCollection[key + "_ext"] as Doc;
-        let ascending = dataExtension && BoolCast(dataExtension.sortAscending, null);
+        const docs = childDocs.slice();
+        const dataExtension = containingCollection[key + "_ext"] as Doc;
+        const ascending = dataExtension && BoolCast(dataExtension.sortAscending, null);
         if (ascending !== undefined) {
 
-            let sortAlphaNum = (a: string, b: string): 0 | 1 | -1 => {
-                var reN = /[0-9]*$/;
-                var aA = a.replace(reN, ""); // get rid of trailing numbers
-                var bA = b.replace(reN, "");
+            const sortAlphaNum = (a: string, b: string): 0 | 1 | -1 => {
+                const reN = /[0-9]*$/;
+                const aA = a.replace(reN, ""); // get rid of trailing numbers
+                const bA = b.replace(reN, "");
                 if (aA === bA) {  // if header string matches, then compare numbers numerically
-                    var aN = parseInt(a.match(reN)![0], 10);
-                    var bN = parseInt(b.match(reN)![0], 10);
+                    const aN = parseInt(a.match(reN)![0], 10);
+                    const bN = parseInt(b.match(reN)![0], 10);
                     return aN === bN ? 0 : aN > bN ? 1 : -1;
                 } else {
                     return aA > bA ? 1 : -1;
                 }
-            }
+            };
             docs.sort(function (a, b): 0 | 1 | -1 {
-                let descA = ascending ? b : a;
-                let descB = ascending ? a : b;
-                let first = descA.title;
-                let second = descB.title;
+                const descA = ascending ? b : a;
+                const descB = ascending ? a : b;
+                const first = descA.title;
+                const second = descB.title;
                 // TODO find better way to sort how to sort..................
                 if (typeof first === 'number' && typeof second === 'number') {
                     return (first - second) > 0 ? 1 : -1;
@@ -479,17 +479,17 @@ class TreeView extends React.Component<TreeViewProps> {
             });
         }
 
-        let rowWidth = () => panelWidth() - 20;
+        const rowWidth = () => panelWidth() - 20;
         return docs.map((child, i) => {
             const pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, key, child);
             if (!pair.layout || pair.data instanceof Promise) {
                 return (null);
             }
 
-            let indent = i === 0 ? undefined : () => {
+            const indent = i === 0 ? undefined : () => {
                 if (StrCast(docs[i - 1].layout).indexOf("fieldKey") !== -1) {
-                    let fieldKeysub = StrCast(docs[i - 1].layout).split("fieldKey")[1];
-                    let fieldKey = fieldKeysub.split("\"")[1];
+                    const fieldKeysub = StrCast(docs[i - 1].layout).split("fieldKey")[1];
+                    const fieldKey = fieldKeysub.split("\"")[1];
                     if (fieldKey && Cast(docs[i - 1][fieldKey], listSpec(Doc)) !== undefined) {
                         Doc.AddDocToList(docs[i - 1], fieldKey, child);
                         docs[i - 1].treeViewOpen = true;
@@ -497,21 +497,21 @@ class TreeView extends React.Component<TreeViewProps> {
                     }
                 }
             };
-            let outdent = !parentCollectionDoc ? undefined : () => {
+            const outdent = !parentCollectionDoc ? undefined : () => {
                 if (StrCast(parentCollectionDoc.layout).indexOf("fieldKey") !== -1) {
-                    let fieldKeysub = StrCast(parentCollectionDoc.layout).split("fieldKey")[1];
-                    let fieldKey = fieldKeysub.split("\"")[1];
+                    const fieldKeysub = StrCast(parentCollectionDoc.layout).split("fieldKey")[1];
+                    const fieldKey = fieldKeysub.split("\"")[1];
                     Doc.AddDocToList(parentCollectionDoc, fieldKey, child, parentPrevSibling, false);
                     parentCollectionDoc.treeViewOpen = true;
                     remove(child);
                 }
             };
-            let addDocument = (doc: Doc, relativeTo?: Doc, before?: boolean) => {
+            const addDocument = (doc: Doc, relativeTo?: Doc, before?: boolean) => {
                 return add(doc, relativeTo ? relativeTo : docs[i], before !== undefined ? before : false);
             };
             const childLayout = Doc.Layout(pair.layout);
-            let rowHeight = () => {
-                let aspect = NumCast(childLayout.nativeWidth, 0) / NumCast(childLayout.nativeHeight, 0);
+            const rowHeight = () => {
+                const aspect = NumCast(childLayout.nativeWidth, 0) / NumCast(childLayout.nativeHeight, 0);
                 return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]();
             };
             return !(child instanceof Doc) ? (null) : <TreeView
@@ -566,7 +566,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
 
     @action
     remove = (document: Document): boolean => {
-        let children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
+        const children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
         if (children.indexOf(document) !== -1) {
             children.splice(children.indexOf(document), 1);
             return true;
@@ -587,7 +587,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
             e.preventDefault();
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
         } else {
-            let layoutItems: ContextMenuProps[] = [];
+            const layoutItems: ContextMenuProps[] = [];
             layoutItems.push({ description: (this.props.Document.preventTreeViewOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.props.Document.preventTreeViewOpen = !this.props.Document.preventTreeViewOpen, icon: "paint-brush" });
             layoutItems.push({ description: (this.props.Document.hideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.props.Document.hideHeaderFields = !this.props.Document.hideHeaderFields, icon: "paint-brush" });
             ContextMenu.Instance.addItem({ description: "Treeview Options ...", subitems: layoutItems, icon: "eye" });
@@ -606,9 +606,9 @@ export class CollectionTreeView extends CollectionSubView(Document) {
     }
 
     render() {
-        let dropAction = StrCast(this.props.Document.dropAction) as dropActionType;
-        let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before, false, false, false);
-        let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
+        const dropAction = StrCast(this.props.Document.dropAction) as dropActionType;
+        const addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before, false, false, false);
+        const moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
         return !this.childDocs ? (null) : (
             <div id="body" className="collectionTreeView-dropTarget"
                 style={{ overflow: "auto", background: StrCast(this.props.Document.backgroundColor, "lightgray"), paddingTop: `${NumCast(this.props.Document.yMargin, 20)}px` }}
@@ -625,8 +625,8 @@ export class CollectionTreeView extends CollectionSubView(Document) {
                     SetValue={undoBatch((value: string) => Doc.SetInPlace(this.dataDoc, "title", value, false) || true)}
                     OnFillDown={undoBatch((value: string) => {
                         Doc.SetInPlace(this.dataDoc, "title", value, false);
-                        let layoutDoc = this.props.Document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.Document.layoutCustom)) : undefined;
-                        let doc = layoutDoc || Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
+                        const layoutDoc = this.props.Document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.Document.layoutCustom)) : undefined;
+                        const doc = layoutDoc || Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
                         TreeView.loadId = doc[Id];
                         Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true, false, false, false);
                     })} />
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 4c49054d2..54f5a2c42 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -84,7 +84,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
     public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; }
 
     get collectionViewType(): CollectionViewType | undefined {
-        let viewField = Cast(this.props.Document.viewType, "number");
+        const viewField = Cast(this.props.Document.viewType, "number");
         if (CollectionView._safeMode) {
             if (viewField === CollectionViewType.Freeform) {
                 return CollectionViewType.Tree;
@@ -101,7 +101,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
             () => {
                 // chrome status is one of disabled, collapsed, or visible. this determines initial state from document
                 // chrome status may also be view-mode, in reference to stacking view's toggle mode. it is essentially disabled mode, but prevents the toggle button from showing up on the left sidebar.
-                let chromeStatus = this.props.Document.chromeStatus;
+                const chromeStatus = this.props.Document.chromeStatus;
                 if (chromeStatus && (chromeStatus === "disabled" || chromeStatus === "collapsed")) {
                     runInAction(() => this._collapsed = true);
                 }
@@ -119,9 +119,9 @@ export class CollectionView extends Touchable<FieldViewProps> {
 
     @action.bound
     addDocument(doc: Doc): boolean {
-        let targetDataDoc = Doc.GetProto(this.props.Document);
+        const targetDataDoc = Doc.GetProto(this.props.Document);
         Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc);
-        let extension = Doc.fieldExtensionDoc(targetDataDoc, this.props.fieldKey);  // set metadata about the field being rendered (ie, the set of documents) on an extension field for that field
+        const extension = Doc.fieldExtensionDoc(targetDataDoc, this.props.fieldKey);  // set metadata about the field being rendered (ie, the set of documents) on an extension field for that field
         extension && (extension.lastModified = new DateField(new Date(Date.now())));
         Doc.GetProto(doc).lastOpened = new DateField;
         return true;
@@ -129,9 +129,9 @@ export class CollectionView extends Touchable<FieldViewProps> {
 
     @action.bound
     removeDocument(doc: Doc): boolean {
-        let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
+        const docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
         docView && SelectionManager.DeselectDoc(docView);
-        let value = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
+        const value = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
         let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1);
         index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
 
@@ -163,7 +163,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
     }
 
     private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
-        let props = { ...this.props, ...renderProps, chromeCollapsed: this._collapsed, ChromeHeight: this.chromeHeight, CollectionView: this, annotationsKey: "" };
+        const props = { ...this.props, ...renderProps, chromeCollapsed: this._collapsed, ChromeHeight: this.chromeHeight, CollectionView: this, annotationsKey: "" };
         switch (type) {
             case CollectionViewType.Schema: return (<CollectionSchemaView key="collview" {...props} />);
             case CollectionViewType.Docking: return (<CollectionDockingView key="collview" {...props} />);
@@ -186,7 +186,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
 
     private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
         // currently cant think of a reason for collection docking view to have a chrome. mind may change if we ever have nested docking views -syip
-        let chrome = this.props.Document.chromeStatus === "disabled" || type === CollectionViewType.Docking ? (null) :
+        const chrome = this.props.Document.chromeStatus === "disabled" || type === CollectionViewType.Docking ? (null) :
             <CollectionViewBaseChrome CollectionView={this} key="chrome" type={type} collapse={this.collapse} />;
         return [chrome, this.SubViewHelper(type, renderProps)];
     }
@@ -194,8 +194,8 @@ export class CollectionView extends Touchable<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
-            let existingVm = ContextMenu.Instance.findByDescription("View Modes...");
-            let subItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
+            const existingVm = ContextMenu.Instance.findByDescription("View Modes...");
+            const subItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
             subItems.push({ description: "Freeform", event: () => { this.props.Document.viewType = CollectionViewType.Freeform; }, icon: "signature" });
             if (CollectionView._safeMode) {
                 ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document.viewType = CollectionViewType.Invalid, icon: "project-diagram" });
@@ -221,13 +221,13 @@ export class CollectionView extends Touchable<FieldViewProps> {
             subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" });
             !existingVm && ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" });
 
-            let existing = ContextMenu.Instance.findByDescription("Layout...");
-            let layoutItems = existing && "subitems" in existing ? existing.subitems : [];
+            const existing = ContextMenu.Instance.findByDescription("Layout...");
+            const layoutItems = existing && "subitems" in existing ? existing.subitems : [];
             layoutItems.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" });
             !existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" });
 
-            let more = ContextMenu.Instance.findByDescription("More...");
-            let moreItems = more && "subitems" in more ? more.subitems : [];
+            const more = ContextMenu.Instance.findByDescription("More...");
+            const moreItems = more && "subitems" in more ? more.subitems : [];
             moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.props.Document) });
             !more && ContextMenu.Instance.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
         }
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 06fca7c38..4161e5d6e 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -13,7 +13,6 @@ import { DragManager } from "../../util/DragManager";
 import { undoBatch } from "../../util/UndoManager";
 import { EditableView } from "../EditableView";
 import { COLLECTION_BORDER_WIDTH } from "../globalCssVariables.scss";
-import { DocLike } from "../MetadataEntryMenu";
 import { CollectionViewType } from "./CollectionView";
 import { CollectionView } from "./CollectionView";
 import "./CollectionViewChromes.scss";
@@ -33,7 +32,7 @@ interface Filter {
     contains: boolean;
 }
 
-let stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation();
+const stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation();
 
 @observer
 export class CollectionViewBaseChrome extends React.Component<CollectionViewChromeProps> {
@@ -80,11 +79,11 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
     @computed private get filterValue() { return Cast(this.props.CollectionView.props.Document.viewSpecScript, ScriptField); }
 
     getFilters = (script: string) => {
-        let re: any = /(!)?\(\(\(doc\.(\w+)\s+&&\s+\(doc\.\w+\s+as\s+\w+\)\.includes\(\"(\w+)\"\)/g;
-        let arr: any[] = re.exec(script);
-        let toReturn: Filter[] = [];
+        const re: any = /(!)?\(\(\(doc\.(\w+)\s+&&\s+\(doc\.\w+\s+as\s+\w+\)\.includes\(\"(\w+)\"\)/g;
+        const arr: any[] = re.exec(script);
+        const toReturn: Filter[] = [];
         if (arr !== null) {
-            let filter: Filter = {
+            const filter: Filter = {
                 key: arr[2],
                 value: arr[3],
                 contains: (arr[1] === "!") ? false : true,
@@ -120,14 +119,14 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
 
         let fields: Filter[] = [];
         if (this.filterValue) {
-            let string = this.filterValue.script.originalScript;
+            const string = this.filterValue.script.originalScript;
             fields = this.getFilters(string);
         }
 
         runInAction(() => {
             this.addKeyRestrictions(fields);
             // chrome status is one of disabled, collapsed, or visible. this determines initial state from document
-            let chromeStatus = this.props.CollectionView.props.Document.chromeStatus;
+            const chromeStatus = this.props.CollectionView.props.Document.chromeStatus;
             if (chromeStatus) {
                 if (chromeStatus === "disabled") {
                     throw new Error("how did you get here, if chrome status is 'disabled' on a collection, a chrome shouldn't even be instantiated!");
@@ -183,7 +182,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
 
     @action
     addKeyRestriction = (e: React.MouseEvent) => {
-        let index = this._keyRestrictions.length;
+        const index = this._keyRestrictions.length;
         this._keyRestrictions.push([<KeyRestrictionRow field="" value="" key={Utils.GenerateGuid()} contains={true} script={(value: string) => runInAction(() => this._keyRestrictions[index][1] = value)} />, ""]);
 
         this.openViewSpecs(e);
@@ -194,26 +193,26 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
 
         this.openViewSpecs(e);
 
-        let keyRestrictionScript = "(" + this._keyRestrictions.map(i => i[1]).filter(i => i.length > 0).join(" && ") + ")";
-        let yearOffset = this._dateWithinValue[1] === 'y' ? 1 : 0;
-        let monthOffset = this._dateWithinValue[1] === 'm' ? parseInt(this._dateWithinValue[0]) : 0;
-        let weekOffset = this._dateWithinValue[1] === 'w' ? parseInt(this._dateWithinValue[0]) : 0;
-        let dayOffset = (this._dateWithinValue[1] === 'd' ? parseInt(this._dateWithinValue[0]) : 0) + weekOffset * 7;
+        const keyRestrictionScript = "(" + this._keyRestrictions.map(i => i[1]).filter(i => i.length > 0).join(" && ") + ")";
+        const yearOffset = this._dateWithinValue[1] === 'y' ? 1 : 0;
+        const monthOffset = this._dateWithinValue[1] === 'm' ? parseInt(this._dateWithinValue[0]) : 0;
+        const weekOffset = this._dateWithinValue[1] === 'w' ? parseInt(this._dateWithinValue[0]) : 0;
+        const dayOffset = (this._dateWithinValue[1] === 'd' ? parseInt(this._dateWithinValue[0]) : 0) + weekOffset * 7;
         let dateRestrictionScript = "";
         if (this._dateValue instanceof Date) {
-            let lowerBound = new Date(this._dateValue.getFullYear() - yearOffset, this._dateValue.getMonth() - monthOffset, this._dateValue.getDate() - dayOffset);
-            let upperBound = new Date(this._dateValue.getFullYear() + yearOffset, this._dateValue.getMonth() + monthOffset, this._dateValue.getDate() + dayOffset + 1);
+            const lowerBound = new Date(this._dateValue.getFullYear() - yearOffset, this._dateValue.getMonth() - monthOffset, this._dateValue.getDate() - dayOffset);
+            const upperBound = new Date(this._dateValue.getFullYear() + yearOffset, this._dateValue.getMonth() + monthOffset, this._dateValue.getDate() + dayOffset + 1);
             dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`;
         }
         else {
-            let createdDate = new Date(this._dateValue);
+            const createdDate = new Date(this._dateValue);
             if (!isNaN(createdDate.getTime())) {
-                let lowerBound = new Date(createdDate.getFullYear() - yearOffset, createdDate.getMonth() - monthOffset, createdDate.getDate() - dayOffset);
-                let upperBound = new Date(createdDate.getFullYear() + yearOffset, createdDate.getMonth() + monthOffset, createdDate.getDate() + dayOffset + 1);
+                const lowerBound = new Date(createdDate.getFullYear() - yearOffset, createdDate.getMonth() - monthOffset, createdDate.getDate() - dayOffset);
+                const upperBound = new Date(createdDate.getFullYear() + yearOffset, createdDate.getMonth() + monthOffset, createdDate.getDate() + dayOffset + 1);
                 dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`;
             }
         }
-        let fullScript = dateRestrictionScript.length || keyRestrictionScript.length ? dateRestrictionScript.length ?
+        const fullScript = dateRestrictionScript.length || keyRestrictionScript.length ? dateRestrictionScript.length ?
             `${dateRestrictionScript} ${keyRestrictionScript.length ? "&&" : ""} (${keyRestrictionScript})` :
             `(${keyRestrictionScript}) ${dateRestrictionScript.length ? "&&" : ""} ${dateRestrictionScript}` :
             "true";
@@ -270,7 +269,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
             value={this.pivotKeyDisplay}
             onChange={action((e: React.ChangeEvent<HTMLInputElement>) => this.pivotKeyDisplay = e.currentTarget.value)}
             onKeyPress={action((e: React.KeyboardEvent<HTMLInputElement>) => {
-                let value = e.currentTarget.value;
+                const value = e.currentTarget.value;
                 if (e.which === 13) {
                     this.pivotKey = value;
                     this.pivotKeyDisplay = "";
@@ -357,7 +356,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
     dragPointerMove = (e: PointerEvent) => {
         e.stopPropagation();
         e.preventDefault();
-        let [dx, dy] = [e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y];
+        const [dx, dy] = [e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y];
         if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
             this._buttonizableCommands.filter(c => c.title === this._currentKey).map(c =>
                 DragManager.StartButtonDrag([this._commandRef.current!], c.script, c.title,
@@ -373,7 +372,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
     }
 
     render() {
-        let collapsed = this.props.CollectionView.props.Document.chromeStatus !== "enabled";
+        const collapsed = this.props.CollectionView.props.Document.chromeStatus !== "enabled";
         return (
             <div className="collectionViewChrome-cont" style={{ top: collapsed ? -70 : 0, height: collapsed ? 0 : undefined }}>
                 <div className="collectionViewChrome">
@@ -480,7 +479,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
 
     getKeySuggestions = async (value: string): Promise<string[]> => {
         value = value.toLowerCase();
-        let docs = DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey]);
+        const docs = DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey]);
         if (docs instanceof Doc) {
             return Object.keys(docs).filter(key => key.toLowerCase().startsWith(value));
         } else {
@@ -571,31 +570,31 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewCh
 
     @undoBatch
     togglePreview = () => {
-        let dividerWidth = 4;
-        let borderWidth = Number(COLLECTION_BORDER_WIDTH);
-        let panelWidth = this.props.CollectionView.props.PanelWidth();
-        let previewWidth = NumCast(this.props.CollectionView.props.Document.schemaPreviewWidth);
-        let tableWidth = panelWidth - 2 * borderWidth - dividerWidth - previewWidth;
+        const dividerWidth = 4;
+        const borderWidth = Number(COLLECTION_BORDER_WIDTH);
+        const panelWidth = this.props.CollectionView.props.PanelWidth();
+        const previewWidth = NumCast(this.props.CollectionView.props.Document.schemaPreviewWidth);
+        const tableWidth = panelWidth - 2 * borderWidth - dividerWidth - previewWidth;
         this.props.CollectionView.props.Document.schemaPreviewWidth = previewWidth === 0 ? Math.min(tableWidth / 3, 200) : 0;
     }
 
     @undoBatch
     @action
     toggleTextwrap = async () => {
-        let textwrappedRows = Cast(this.props.CollectionView.props.Document.textwrappedSchemaRows, listSpec("string"), []);
+        const textwrappedRows = Cast(this.props.CollectionView.props.Document.textwrappedSchemaRows, listSpec("string"), []);
         if (textwrappedRows.length) {
             this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>([]);
         } else {
-            let docs = DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey]);
-            let allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]);
+            const docs = DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey]);
+            const allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]);
             this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>(allRows);
         }
     }
 
 
     render() {
-        let previewWidth = NumCast(this.props.CollectionView.props.Document.schemaPreviewWidth);
-        let textWrapped = Cast(this.props.CollectionView.props.Document.textwrappedSchemaRows, listSpec("string"), []).length > 0;
+        const previewWidth = NumCast(this.props.CollectionView.props.Document.schemaPreviewWidth);
+        const textWrapped = Cast(this.props.CollectionView.props.Document.textwrappedSchemaRows, listSpec("string"), []).length > 0;
 
         return (
             <div className="collectionSchemaViewChrome-cont">
diff --git a/src/client/views/collections/KeyRestrictionRow.tsx b/src/client/views/collections/KeyRestrictionRow.tsx
index e35b7d7d3..f3071b316 100644
--- a/src/client/views/collections/KeyRestrictionRow.tsx
+++ b/src/client/views/collections/KeyRestrictionRow.tsx
@@ -1,8 +1,6 @@
 import * as React from "react";
 import { observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField";
-import { Doc } from "../../../new_fields/Doc";
 
 interface IKeyRestrictionProps {
     contains: boolean;
@@ -20,13 +18,13 @@ export default class KeyRestrictionRow extends React.Component<IKeyRestrictionPr
     render() {
         if (this._key && this._value) {
             let parsedValue: string | number = `"${this._value}"`;
-            let parsed = parseInt(this._value);
+            const parsed = parseInt(this._value);
             let type = "string";
             if (!isNaN(parsed)) {
                 parsedValue = parsed;
                 type = "number";
             }
-            let scriptText = `${this._contains ? "" : "!"}(((doc.${this._key} && (doc.${this._key} as ${type})${type === "string" ? ".includes" : "<="}(${parsedValue}))) ||
+            const scriptText = `${this._contains ? "" : "!"}(((doc.${this._key} && (doc.${this._key} as ${type})${type === "string" ? ".includes" : "<="}(${parsedValue}))) ||
                 ((doc.data_ext && doc.data_ext.${this._key}) && (doc.data_ext.${this._key} as ${type})${type === "string" ? ".includes" : "<="}(${parsedValue}))))`;
             // let doc = new Doc();
             // ((doc.data_ext && doc.data_ext!.text) && (doc.data_ext!.text as string).includes("hello"));
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index 4eb9e9d1e..75ee8de32 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -34,7 +34,7 @@ export class SelectorContextMenu extends React.Component<SelectorProps> {
     }
 
     async fetchDocuments() {
-        let aliases = (await SearchUtil.GetAliasesOfDocument(this.props.Document)).filter(doc => doc !== this.props.Document);
+        const aliases = (await SearchUtil.GetAliasesOfDocument(this.props.Document)).filter(doc => doc !== this.props.Document);
         const { docs } = await SearchUtil.Search("", true, { fq: `data_l:"${this.props.Document[Id]}"` });
         const map: Map<Doc, Doc> = new Map;
         const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search("", true, { fq: `data_l:"${doc[Id]}"` }).then(result => result.docs)));
@@ -129,7 +129,7 @@ export class ButtonSelector extends React.Component<{ Document: Doc, Stack: any
     render() {
         let flyout;
         if (this.hover) {
-            let view = DocumentManager.Instance.getDocumentView(this.props.Document);
+            const view = DocumentManager.Instance.getDocumentView(this.props.Document);
             flyout = !view ? (null) : (
                 <div className="PDS-flyout" title=" " onMouseLeave={this.onMouseLeave}>
                     <DocumentButtonBar views={[view]} stack={this.props.Stack} />
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 4a32c1647..012115b1f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -77,7 +77,7 @@ export function computePivotLayout(poolData: ObservableMap<string, any>, pivotDo
             fontSize: NumCast(pivotDoc.pivotFontSize, 10)
         });
         for (const doc of val) {
-            let layoutDoc = Doc.Layout(doc);
+            const layoutDoc = Doc.Layout(doc);
             let wid = pivotAxisWidth;
             let hgt = layoutDoc.nativeWidth ? (NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth)) * pivotAxisWidth : pivotAxisWidth;
             if (hgt > pivotAxisWidth) {
@@ -100,7 +100,7 @@ export function computePivotLayout(poolData: ObservableMap<string, any>, pivotDo
     });
 
     childPairs.map(pair => {
-        let defaultPosition = {
+        const defaultPosition = {
             x: NumCast(pair.layout.x),
             y: NumCast(pair.layout.y),
             z: NumCast(pair.layout.z),
@@ -108,7 +108,7 @@ export function computePivotLayout(poolData: ObservableMap<string, any>, pivotDo
             height: NumCast(pair.layout.height)
         };
         const pos = docMap.get(pair.layout) || defaultPosition;
-        let data = poolData.get(pair.layout[Id]);
+        const data = poolData.get(pair.layout[Id]);
         if (!data || pos.x !== data.x || pos.y !== data.y || pos.z !== data.z || pos.width !== data.width || pos.height !== data.height) {
             runInAction(() => poolData.set(pair.layout[Id], { transition: "transform 1s", ...pos }));
         }
@@ -118,10 +118,10 @@ export function computePivotLayout(poolData: ObservableMap<string, any>, pivotDo
 
 export function AddCustomFreeFormLayout(doc: Doc, dataKey: string): () => void {
     return () => {
-        let addOverlay = (key: "arrangeScript" | "arrangeInit", options: OverlayElementOptions, params?: Record<string, string>, requiredType?: string) => {
+        const addOverlay = (key: "arrangeScript" | "arrangeInit", options: OverlayElementOptions, params?: Record<string, string>, requiredType?: string) => {
             let overlayDisposer: () => void = emptyFunction; // filled in below after we have a reference to the scriptingBox
             const scriptField = Cast(doc[key], ScriptField);
-            let scriptingBox = <ScriptBox initialText={scriptField && scriptField.script.originalScript}
+            const scriptingBox = <ScriptBox initialText={scriptField && scriptField.script.originalScript}
                 // tslint:disable-next-line: no-unnecessary-callback-wrapper
                 onCancel={() => overlayDisposer()}  // don't get rid of the function wrapper-- we don't want to use the current value of overlayDiposer, but the one set below
                 onSave={(text, onError) => {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index b00728079..5e4b4fd27 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -6,9 +6,8 @@ import "./CollectionFreeFormLinkView.scss";
 import React = require("react");
 import v5 = require("uuid/v5");
 import { DocumentType } from "../../../documents/DocumentTypes";
-import { observable, action, reaction, IReactionDisposer, trace } from "mobx";
-import { StrCast, Cast } from "../../../../new_fields/Types";
-import { TraceMobx } from "../../../../new_fields/util";
+import { observable, action, reaction, IReactionDisposer } from "mobx";
+import { StrCast } from "../../../../new_fields/Types";
 
 export interface CollectionFreeFormLinkViewProps {
     A: DocumentView;
@@ -26,22 +25,22 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
             action(() => {
                 setTimeout(action(() => this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
                 setTimeout(action(() => this._opacity = 0.05), 750); // this will unhighlight the link line.
-                let acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
-                let bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
-                let adiv = (acont.length ? acont[0] : this.props.A.ContentDiv!);
-                let bdiv = (bcont.length ? bcont[0] : this.props.B.ContentDiv!);
-                let a = adiv.getBoundingClientRect();
-                let b = bdiv.getBoundingClientRect();
-                let abounds = adiv.parentElement!.getBoundingClientRect();
-                let bbounds = bdiv.parentElement!.getBoundingClientRect();
-                let apt = Utils.closestPtBetweenRectangles(abounds.left, abounds.top, abounds.width, abounds.height,
+                const acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
+                const bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
+                const adiv = (acont.length ? acont[0] : this.props.A.ContentDiv!);
+                const bdiv = (bcont.length ? bcont[0] : this.props.B.ContentDiv!);
+                const a = adiv.getBoundingClientRect();
+                const b = bdiv.getBoundingClientRect();
+                const abounds = adiv.parentElement!.getBoundingClientRect();
+                const bbounds = bdiv.parentElement!.getBoundingClientRect();
+                const apt = Utils.closestPtBetweenRectangles(abounds.left, abounds.top, abounds.width, abounds.height,
                     bbounds.left, bbounds.top, bbounds.width, bbounds.height,
                     a.left + a.width / 2, a.top + a.height / 2);
-                let bpt = Utils.closestPtBetweenRectangles(bbounds.left, bbounds.top, bbounds.width, bbounds.height,
+                const bpt = Utils.closestPtBetweenRectangles(bbounds.left, bbounds.top, bbounds.width, bbounds.height,
                     abounds.left, abounds.top, abounds.width, abounds.height,
                     apt.point.x, apt.point.y);
-                let afield = StrCast(this.props.A.props.Document[StrCast(this.props.A.props.layoutKey, "layout")]).indexOf("anchor1") === -1 ? "anchor2" : "anchor1";
-                let bfield = afield === "anchor1" ? "anchor2" : "anchor1";
+                const afield = StrCast(this.props.A.props.Document[StrCast(this.props.A.props.layoutKey, "layout")]).indexOf("anchor1") === -1 ? "anchor2" : "anchor1";
+                const bfield = afield === "anchor1" ? "anchor2" : "anchor1";
                 this.props.A.props.Document[afield + "_x"] = (apt.point.x - abounds.left) / abounds.width * 100;
                 this.props.A.props.Document[afield + "_y"] = (apt.point.y - abounds.top) / abounds.height * 100;
                 this.props.A.props.Document[bfield + "_x"] = (bpt.point.x - bbounds.left) / bbounds.width * 100;
@@ -55,18 +54,18 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
     }
 
     render() {
-        let acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
-        let bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
-        let a = (acont.length ? acont[0] : this.props.A.ContentDiv!).getBoundingClientRect();
-        let b = (bcont.length ? bcont[0] : this.props.B.ContentDiv!).getBoundingClientRect();
-        let apt = Utils.closestPtBetweenRectangles(a.left, a.top, a.width, a.height,
+        const acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
+        const bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
+        const a = (acont.length ? acont[0] : this.props.A.ContentDiv!).getBoundingClientRect();
+        const b = (bcont.length ? bcont[0] : this.props.B.ContentDiv!).getBoundingClientRect();
+        const apt = Utils.closestPtBetweenRectangles(a.left, a.top, a.width, a.height,
             b.left, b.top, b.width, b.height,
             a.left + a.width / 2, a.top + a.height / 2);
-        let bpt = Utils.closestPtBetweenRectangles(b.left, b.top, b.width, b.height,
+        const bpt = Utils.closestPtBetweenRectangles(b.left, b.top, b.width, b.height,
             a.left, a.top, a.width, a.height,
             apt.point.x, apt.point.y);
-        let pt1 = [apt.point.x, apt.point.y];
-        let pt2 = [bpt.point.x, bpt.point.y];
+        const pt1 = [apt.point.x, apt.point.y];
+        const pt2 = [bpt.point.x, bpt.point.y];
         return (<line key="linkLine" className="collectionfreeformlinkview-linkLine"
             style={{ opacity: this._opacity }}
             x1={`${pt1[0]}`} y1={`${pt1[1]}`}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index e9191c176..218012fe1 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -72,11 +72,11 @@ export class CollectionFreeFormLinksView extends React.Component {
     }
     @computed
     get uniqueConnections() {
-        let connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => {
+        const connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => {
             if (!drawnPairs.reduce((found, drawnPair) => {
-                let match1 = (connection.a === drawnPair.a && connection.b === drawnPair.b);
-                let match2 = (connection.a === drawnPair.b && connection.b === drawnPair.a);
-                let match = match1 || match2;
+                const match1 = (connection.a === drawnPair.a && connection.b === drawnPair.b);
+                const match2 = (connection.a === drawnPair.b && connection.b === drawnPair.a);
+                const match = match1 || match2;
                 if (match && !drawnPair.l.reduce((found, link) => found || link[Id] === connection.l[Id], false)) {
                     drawnPair.l.push(connection.l);
                 }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index b8148852d..bb9ae4326 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -13,14 +13,14 @@ import v5 = require("uuid/v5");
 export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
 
     protected getCursors(): CursorField[] {
-        let doc = this.props.Document;
+        const doc = this.props.Document;
 
-        let id = CurrentUserUtils.id;
+        const id = CurrentUserUtils.id;
         if (!id) {
             return [];
         }
 
-        let cursors = Cast(doc.cursors, listSpec(CursorField));
+        const cursors = Cast(doc.cursors, listSpec(CursorField));
 
         const now = mobxUtils.now();
         // const now = Date.now();
@@ -30,7 +30,7 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
     private crosshairs?: HTMLCanvasElement;
     drawCrosshairs = (backgroundColor: string) => {
         if (this.crosshairs) {
-            let ctx = this.crosshairs.getContext('2d');
+            const ctx = this.crosshairs.getContext('2d');
             if (ctx) {
                 ctx.fillStyle = backgroundColor;
                 ctx.fillRect(0, 0, 20, 20);
@@ -62,8 +62,8 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
 
     get sharedCursors() {
         return this.getCursors().map(c => {
-            let m = c.data.metadata;
-            let l = c.data.position;
+            const m = c.data.metadata;
+            const l = c.data.position;
             this.drawCrosshairs("#" + v5(m.id, v5.URL).substring(0, 6).toUpperCase() + "22");
             return (
                 <div key={m.id} className="collectionFreeFormRemoteCursors-cont"
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 9506ce084..c69b264c4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,12 +1,12 @@
 import { library } from "@fortawesome/fontawesome-svg-core";
 import { faEye } from "@fortawesome/free-regular-svg-icons";
 import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons";
-import { action, computed, observable, trace, ObservableMap, untracked, reaction, runInAction, IReactionDisposer } from "mobx";
+import { action, computed, observable, ObservableMap, reaction, runInAction, IReactionDisposer } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc";
+import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync } from "../../../../new_fields/Doc";
 import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas";
 import { Id } from "../../../../new_fields/FieldSymbols";
-import { InkTool } from "../../../../new_fields/InkField";
+import { InkTool, InkField, InkData } from "../../../../new_fields/InkField";
 import { createSchema, makeInterface } from "../../../../new_fields/Schema";
 import { ScriptField } from "../../../../new_fields/ScriptField";
 import { BoolCast, Cast, DateCast, NumCast, StrCast } from "../../../../new_fields/Types";
@@ -26,7 +26,7 @@ import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss"
 import { ContextMenu } from "../../ContextMenu";
 import { ContextMenuProps } from "../../ContextMenuItem";
 import { InkingControl } from "../../InkingControl";
-import { CreatePolyline, InkingStroke } from "../../InkingStroke";
+import { CreatePolyline } from "../../InkingStroke";
 import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
 import { DocumentViewProps } from "../../nodes/DocumentView";
 import { FormattedTextBox } from "../../nodes/FormattedTextBox";
@@ -39,8 +39,9 @@ import "./CollectionFreeFormView.scss";
 import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
-import { computedFn, keepAlive } from "mobx-utils";
+import { computedFn } from "mobx-utils";
 import { TraceMobx } from "../../../../new_fields/util";
+import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
 
 library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
 
@@ -98,10 +99,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     private getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY());
     private addLiveTextBox = (newBox: Doc) => {
         FormattedTextBox.SelectOnLoad = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
-        let maxHeading = this.childDocs.reduce((maxHeading, doc) => NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0);
+        const maxHeading = this.childDocs.reduce((maxHeading, doc) => NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0);
         let heading = maxHeading === 0 || this.childDocs.length === 0 ? 1 : maxHeading === 1 ? 2 : 0;
         if (heading === 0) {
-            let sorted = this.childDocs.filter(d => d.type === DocumentType.TEXT && d.data_ext instanceof Doc && d.data_ext.lastModified).sort((a, b) => DateCast((Cast(a.data_ext, Doc) as Doc).lastModified).date > DateCast((Cast(b.data_ext, Doc) as Doc).lastModified).date ? 1 :
+            const sorted = this.childDocs.filter(d => d.type === DocumentType.TEXT && d.data_ext instanceof Doc && d.data_ext.lastModified).sort((a, b) => DateCast((Cast(a.data_ext, Doc) as Doc).lastModified).date > DateCast((Cast(b.data_ext, Doc) as Doc).lastModified).date ? 1 :
                 DateCast((Cast(a.data_ext, Doc) as Doc).lastModified).date < DateCast((Cast(b.data_ext, Doc) as Doc).lastModified).date ? -1 : 0);
             heading = !sorted.length ? Math.max(1, maxHeading) : NumCast(sorted[sorted.length - 1].heading) === 1 ? 2 : NumCast(sorted[sorted.length - 1].heading);
         }
@@ -109,7 +110,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         this.addDocument(newBox);
     }
     private addDocument = (newBox: Doc) => {
-        let added = this.props.addDocument(newBox);
+        const added = this.props.addDocument(newBox);
         added && this.bringToFront(newBox);
         added && this.updateCluster(newBox);
         return added;
@@ -126,36 +127,36 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     @action
     onDrop = (e: React.DragEvent): Promise<void> => {
-        var pt = this.getTransform().transformPoint(e.pageX, e.pageY);
+        const pt = this.getTransform().transformPoint(e.pageX, e.pageY);
         return super.onDrop(e, { x: pt[0], y: pt[1] });
     }
 
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
-        let xf = this.getTransform();
-        let xfo = this.getTransformOverlay();
-        let [xp, yp] = xf.transformPoint(de.x, de.y);
-        let [xpo, ypo] = xfo.transformPoint(de.x, de.y);
+        const xf = this.getTransform();
+        const xfo = this.getTransformOverlay();
+        const [xp, yp] = xf.transformPoint(de.x, de.y);
+        const [xpo, ypo] = xfo.transformPoint(de.x, de.y);
         if (super.drop(e, de)) {
             if (de.data instanceof DragManager.DocumentDragData) {
                 if (de.data.droppedDocuments.length) {
-                    let firstDoc = de.data.droppedDocuments[0];
-                    let z = NumCast(firstDoc.z);
-                    let x = (z ? xpo : xp) - de.data.offset[0];
-                    let y = (z ? ypo : yp) - de.data.offset[1];
-                    let dropX = NumCast(firstDoc.x);
-                    let dropY = NumCast(firstDoc.y);
+                    const firstDoc = de.data.droppedDocuments[0];
+                    const z = NumCast(firstDoc.z);
+                    const x = (z ? xpo : xp) - de.data.offset[0];
+                    const y = (z ? ypo : yp) - de.data.offset[1];
+                    const dropX = NumCast(firstDoc.x);
+                    const dropY = NumCast(firstDoc.y);
                     de.data.droppedDocuments.forEach(action((d: Doc) => {
-                        let layoutDoc = Doc.Layout(d);
+                        const layoutDoc = Doc.Layout(d);
                         d.x = x + NumCast(d.x) - dropX;
                         d.y = y + NumCast(d.y) - dropY;
                         if (!NumCast(layoutDoc.width)) {
                             layoutDoc.width = 300;
                         }
                         if (!NumCast(layoutDoc.height)) {
-                            let nw = NumCast(layoutDoc.nativeWidth);
-                            let nh = NumCast(layoutDoc.nativeHeight);
+                            const nw = NumCast(layoutDoc.nativeWidth);
+                            const nh = NumCast(layoutDoc.nativeHeight);
                             layoutDoc.height = nw && nh ? nh / nw * NumCast(layoutDoc.width) : 300;
                         }
                         this.bringToFront(d);
@@ -166,11 +167,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             }
             else if (de.data instanceof DragManager.AnnotationDragData) {
                 if (de.data.dropDocument) {
-                    let dragDoc = de.data.dropDocument;
-                    let x = xp - de.data.offset[0];
-                    let y = yp - de.data.offset[1];
-                    let dropX = NumCast(dragDoc.x);
-                    let dropY = NumCast(dragDoc.y);
+                    const dragDoc = de.data.dropDocument;
+                    const x = xp - de.data.offset[0];
+                    const y = yp - de.data.offset[1];
+                    const dropX = NumCast(dragDoc.x);
+                    const dropY = NumCast(dragDoc.y);
                     dragDoc.x = x + NumCast(dragDoc.x) - dropX;
                     dragDoc.y = y + NumCast(dragDoc.y) - dropY;
                     de.data.targetContext = this.props.Document; // dropped a PDF annotation, so we need to set the targetContext on the dragData which the PDF view uses at the end of the drop operation
@@ -183,23 +184,23 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     pickCluster(probe: number[]) {
         return this.childLayoutPairs.map(pair => pair.layout).reduce((cluster, cd) => {
-            let layoutDoc = Doc.Layout(cd);
-            let cx = NumCast(cd.x) - this._clusterDistance;
-            let cy = NumCast(cd.y) - this._clusterDistance;
-            let cw = NumCast(layoutDoc.width) + 2 * this._clusterDistance;
-            let ch = NumCast(layoutDoc.height) + 2 * this._clusterDistance;
+            const layoutDoc = Doc.Layout(cd);
+            const cx = NumCast(cd.x) - this._clusterDistance;
+            const cy = NumCast(cd.y) - this._clusterDistance;
+            const cw = NumCast(layoutDoc.width) + 2 * this._clusterDistance;
+            const ch = NumCast(layoutDoc.height) + 2 * this._clusterDistance;
             return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ?
                 NumCast(cd.cluster) : cluster;
         }, -1);
     }
     tryDragCluster(e: PointerEvent | TouchEvent) {
-        let ptsParent = e instanceof PointerEvent ? e : e.targetTouches.item(0);
+        const ptsParent = e instanceof PointerEvent ? e : e.targetTouches.item(0);
         if (ptsParent) {
-            let cluster = this.pickCluster(this.getTransform().transformPoint(ptsParent.clientX, ptsParent.clientY));
+            const cluster = this.pickCluster(this.getTransform().transformPoint(ptsParent.clientX, ptsParent.clientY));
             if (cluster !== -1) {
-                let eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => NumCast(cd.cluster) === cluster);
-                let clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.CollectionView)!);
-                let de = new DragManager.DocumentDragData(eles);
+                const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => NumCast(cd.cluster) === cluster);
+                const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.CollectionView)!);
+                const de = new DragManager.DocumentDragData(eles);
                 de.moveDocument = this.props.moveDocument;
                 const [left, top] = clusterDocs[0].props.ScreenToLocalTransform().scale(clusterDocs[0].props.ContentScaling()).inverse().transformPoint(0, 0);
                 de.offset = this.getTransform().transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
@@ -225,10 +226,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     @undoBatch
     @action
     updateCluster(doc: Doc) {
-        let childLayouts = this.childLayoutPairs.map(pair => pair.layout);
+        const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
         if (this.props.Document.useClusters) {
             this._clusterSets.map(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1));
-            let preferredInd = NumCast(doc.cluster);
+            const preferredInd = NumCast(doc.cluster);
             doc.cluster = -1;
             this._clusterSets.map((set, i) => set.map(member => {
                 if (doc.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
@@ -255,15 +256,15 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     getClusterColor = (doc: Doc) => {
         let clusterColor = "";
-        let cluster = NumCast(doc.cluster);
+        const cluster = NumCast(doc.cluster);
         if (this.Document.useClusters) {
             if (this._clusterSets.length <= cluster) {
                 setTimeout(() => this.updateCluster(doc), 0);
             } else {
                 // choose a cluster color from a palette
-                let colors = ["#da42429e", "#31ea318c", "#8c4000", "#4a7ae2c4", "#d809ff", "#ff7601", "#1dffff", "yellow", "#1b8231f2", "#000000ad"];
+                const colors = ["#da42429e", "#31ea318c", "#8c4000", "#4a7ae2c4", "#d809ff", "#ff7601", "#1dffff", "yellow", "#1b8231f2", "#000000ad"];
                 clusterColor = colors[cluster % colors.length];
-                let set = this._clusterSets[cluster] && this._clusterSets[cluster].filter(s => s.backgroundColor && (s.backgroundColor !== s.defaultBackgroundColor));
+                const set = this._clusterSets[cluster] && this._clusterSets[cluster].filter(s => s.backgroundColor && (s.backgroundColor !== s.defaultBackgroundColor));
                 // override the cluster color with an explicitly set color on a non-background document.  then override that with an explicitly set color on a background document
                 set && set.filter(s => !s.isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
                 set && set.filter(s => s.isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
@@ -287,7 +288,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) {
                 e.stopPropagation();
                 e.preventDefault();
-                let point = this.getTransform().transformPoint(e.pageX, e.pageY);
+                const point = this.getTransform().transformPoint(e.pageX, e.pageY);
                 this._points.push({ x: point[0], y: point[1] });
             }
             // if not using a pen and in no ink mode
@@ -324,7 +325,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     @action
     handle1PointerDown = (e: React.TouchEvent) => {
-        let pt = e.targetTouches.item(0);
+        const pt = e.targetTouches.item(0);
         if (pt) {
             this._hitCluster = this.props.Document.useCluster ? this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY)) !== -1 : false;
         }
@@ -335,9 +336,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && this._points.length <= 1) return;
 
         if (this._points.length > 1) {
-            let B = this.svgBounds;
-            let points = this._points.map(p => ({ x: p.x - B.left, y: p.y - B.top }));
-            let inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, parseInt(InkingControl.Instance.selectedWidth), points, { width: B.width, height: B.height, x: B.left, y: B.top });
+            const B = this.svgBounds;
+            const points = this._points.map(p => ({ x: p.x - B.left, y: p.y - B.top }));
+            const inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, parseInt(InkingControl.Instance.selectedWidth), points, { width: B.width, height: B.height, x: B.left, y: B.top });
             this.addDocument(inkDoc);
             this._points = [];
         }
@@ -355,26 +356,26 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
         let x = this.Document.panX || 0;
         let y = this.Document.panY || 0;
-        let docs = this.childLayoutPairs.map(pair => pair.layout);
-        let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
+        const docs = this.childLayoutPairs.map(pair => pair.layout);
+        const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
         if (!this.isAnnotationOverlay) {
             PDFMenu.Instance.fadeOut(true);
-            let minx = docs.length ? NumCast(docs[0].x) : 0;
-            let maxx = docs.length ? NumCast(docs[0].width) + minx : minx;
-            let miny = docs.length ? NumCast(docs[0].y) : 0;
-            let maxy = docs.length ? NumCast(docs[0].height) + miny : miny;
-            let ranges = docs.filter(doc => doc).reduce((range, doc) => {
-                let layoutDoc = Doc.Layout(doc);
-                let x = NumCast(doc.x);
-                let xe = x + NumCast(layoutDoc.width);
-                let y = NumCast(doc.y);
-                let ye = y + NumCast(layoutDoc.height);
+            const minx = docs.length ? NumCast(docs[0].x) : 0;
+            const maxx = docs.length ? NumCast(docs[0].width) + minx : minx;
+            const miny = docs.length ? NumCast(docs[0].y) : 0;
+            const maxy = docs.length ? NumCast(docs[0].height) + miny : miny;
+            const ranges = docs.filter(doc => doc).reduce((range, doc) => {
+                const layoutDoc = Doc.Layout(doc);
+                const x = NumCast(doc.x);
+                const xe = x + NumCast(layoutDoc.width);
+                const y = NumCast(doc.y);
+                const ye = y + NumCast(layoutDoc.height);
                 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 cscale = this.props.ContainingCollectionDoc ? NumCast(this.props.ContainingCollectionDoc.scale) : 1;
-            let panelDim = this.props.ScreenToLocalTransform().transformDirection(this.props.PanelWidth() / this.zoomScaling() * cscale,
+            const cscale = this.props.ContainingCollectionDoc ? NumCast(this.props.ContainingCollectionDoc.scale) : 1;
+            const panelDim = this.props.ScreenToLocalTransform().transformDirection(this.props.PanelWidth() / this.zoomScaling() * cscale,
                 this.props.PanelHeight() / this.zoomScaling() * cscale);
             if (ranges[0][0] - dx > (this.panX() + panelDim[0] / 2)) x = ranges[0][1] + panelDim[0] / 2;
             if (ranges[0][1] - dx < (this.panX() - panelDim[0] / 2)) x = ranges[0][0] - panelDim[0] / 2;
@@ -397,7 +398,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         if (!e.cancelBubble) {
             const selectedTool = InkingControl.Instance.selectedTool;
             if (selectedTool === InkTool.Highlighter || selectedTool === InkTool.Pen || InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
-                let point = this.getTransform().transformPoint(e.clientX, e.clientY);
+                const point = this.getTransform().transformPoint(e.clientX, e.clientY);
                 this._points.push({ x: point[0], y: point[1] });
             }
             else if (selectedTool === InkTool.None) {
@@ -418,7 +419,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     handle1PointerMove = (e: TouchEvent) => {
         // panning a workspace
         if (!e.cancelBubble) {
-            let pt = e.targetTouches.item(0);
+            const pt = e.targetTouches.item(0);
             if (pt) {
                 if (InkingControl.Instance.selectedTool === InkTool.None) {
                     if (this._hitCluster && this.tryDragCluster(e)) {
@@ -431,7 +432,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                     this.pan(pt);
                 }
                 else if (InkingControl.Instance.selectedTool !== InkTool.Eraser && InkingControl.Instance.selectedTool !== InkTool.Scrubber) {
-                    let point = this.getTransform().transformPoint(pt.clientX, pt.clientY);
+                    const point = this.getTransform().transformPoint(pt.clientX, pt.clientY);
                     this._points.push({ x: point[0], y: point[1] });
                 }
             }
@@ -443,28 +444,28 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     handle2PointersMove = (e: TouchEvent) => {
         // pinch zooming
         if (!e.cancelBubble) {
-            let pt1: Touch | null = e.targetTouches.item(0);
-            let pt2: Touch | null = e.targetTouches.item(1);
+            const pt1: Touch | null = e.targetTouches.item(0);
+            const pt2: Touch | null = e.targetTouches.item(1);
             if (!pt1 || !pt2) return;
 
             if (this.prevPoints.size === 2) {
-                let oldPoint1 = this.prevPoints.get(pt1.identifier);
-                let oldPoint2 = this.prevPoints.get(pt2.identifier);
+                const oldPoint1 = this.prevPoints.get(pt1.identifier);
+                const oldPoint2 = this.prevPoints.get(pt2.identifier);
                 if (oldPoint1 && oldPoint2) {
-                    let dir = InteractionUtils.Pinching(pt1, pt2, oldPoint1, oldPoint2);
+                    const dir = InteractionUtils.Pinching(pt1, pt2, oldPoint1, oldPoint2);
 
                     // if zooming, zoom
                     if (dir !== 0) {
-                        let d1 = Math.sqrt(Math.pow(pt1.clientX - oldPoint1.clientX, 2) + Math.pow(pt1.clientY - oldPoint1.clientY, 2));
-                        let d2 = Math.sqrt(Math.pow(pt2.clientX - oldPoint2.clientX, 2) + Math.pow(pt2.clientY - oldPoint2.clientY, 2));
-                        let centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
-                        let centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
+                        const d1 = Math.sqrt(Math.pow(pt1.clientX - oldPoint1.clientX, 2) + Math.pow(pt1.clientY - oldPoint1.clientY, 2));
+                        const d2 = Math.sqrt(Math.pow(pt2.clientX - oldPoint2.clientX, 2) + Math.pow(pt2.clientY - oldPoint2.clientY, 2));
+                        const centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
+                        const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
 
                         // calculate the raw delta value
-                        let rawDelta = (dir * (d1 + d2));
+                        const rawDelta = (dir * (d1 + d2));
 
                         // this floors and ceils the delta value to prevent jitteriness
-                        let delta = Math.sign(rawDelta) * Math.min(Math.abs(rawDelta), 16);
+                        const delta = Math.sign(rawDelta) * Math.min(Math.abs(rawDelta), 16);
                         this.zoom(centerX, centerY, delta);
                         this.prevPoints.set(pt1.identifier, pt1);
                         this.prevPoints.set(pt2.identifier, pt2);
@@ -472,8 +473,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                     // this is not zooming. derive some form of panning from it.
                     else {
                         // use the centerx and centery as the "new mouse position"
-                        let centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
-                        let centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
+                        const centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
+                        const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
                         this.pan({ clientX: centerX, clientY: centerY });
                         this._lastX = centerX;
                         this._lastY = centerY;
@@ -486,12 +487,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     handle2PointersDown = (e: React.TouchEvent) => {
-        let pt1: React.Touch | null = e.targetTouches.item(0);
-        let pt2: React.Touch | null = e.targetTouches.item(1);
+        const pt1: React.Touch | null = e.targetTouches.item(0);
+        const pt2: React.Touch | null = e.targetTouches.item(1);
         if (!pt1 || !pt2) return;
 
-        let centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
-        let centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
+        const centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
+        const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
         this._lastX = centerX;
         this._lastY = centerY;
     }
@@ -510,11 +511,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             deltaScale = 1 / this.zoomScaling();
         }
         if (deltaScale < 0) deltaScale = -deltaScale;
-        let [x, y] = this.getTransform().transformPoint(pointX, pointY);
-        let localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
+        const [x, y] = this.getTransform().transformPoint(pointX, pointY);
+        const localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
 
         if (localTransform.Scale >= 0.15) {
-            let safeScale = Math.min(Math.max(0.15, localTransform.Scale), 40);
+            const safeScale = Math.min(Math.max(0.15, localTransform.Scale), 40);
             this.props.Document.scale = Math.abs(safeScale);
             this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
         }
@@ -536,7 +537,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     setPan(panX: number, panY: number, panType: string = "None") {
         if (!this.Document.lockedTransform || this.Document.inOverlay) {
             this.Document.panTransformType = panType;
-            var scale = this.getLocalTransform().inverse().Scale;
+            const scale = this.getLocalTransform().inverse().Scale;
             const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
             const newPanY = Math.min((this.props.Document.scrollHeight !== undefined ? NumCast(this.Document.scrollHeight) : (1 - 1 / scale) * this.nativeHeight), Math.max(0, panY));
             this.Document.panX = this.isAnnotationOverlay ? newPanX : panX;
@@ -576,23 +577,23 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         }
         SelectionManager.DeselectAll();
         if (this.props.Document.scrollHeight) {
-            let annotOn = Cast(doc.annotationOn, Doc) as Doc;
+            const annotOn = Cast(doc.annotationOn, Doc) as Doc;
             if (!annotOn) {
                 this.props.focus(doc);
             } else {
-                let contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn.height);
-                let offset = annotOn && (contextHgt / 2 * 96 / 72);
+                const contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn.height);
+                const offset = annotOn && (contextHgt / 2 * 96 / 72);
                 this.props.Document.scrollY = NumCast(doc.y) - offset;
             }
         } else {
-            let layoutdoc = Doc.Layout(doc);
+            const layoutdoc = Doc.Layout(doc);
             const newPanX = NumCast(doc.x) + NumCast(layoutdoc.width) / 2;
             const newPanY = NumCast(doc.y) + NumCast(layoutdoc.height) / 2;
             const newState = HistoryUtil.getState();
             newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY };
             HistoryUtil.pushState(newState);
 
-            let savedState = { px: this.Document.panX, py: this.Document.panY, s: this.Document.scale, pt: this.Document.panTransformType };
+            const savedState = { px: this.Document.panX, py: this.Document.panY, s: this.Document.scale, pt: this.Document.panTransformType };
 
             this.setPan(newPanX, newPanY, "Ease");
             Doc.BrushDoc(this.props.Document);
@@ -677,7 +678,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         }
     }
 
-    childDataProvider = computedFn(function childDataProvider(doc: Doc) { return (this as any)._layoutPoolData.get(doc[Id]); }.bind(this));
+    childDataProvider = computedFn(function childDataProvider(this: any, doc: Doc) { return this._layoutPoolData.get(doc[Id]); }.bind(this));
 
     doPivotLayout(poolData: ObservableMap<string, any>) {
         return computePivotLayout(poolData, this.props.Document, this.childDocs,
@@ -685,10 +686,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     doFreeformLayout(poolData: ObservableMap<string, any>) {
-        let layoutDocs = this.childLayoutPairs.map(pair => pair.layout);
+        const layoutDocs = this.childLayoutPairs.map(pair => pair.layout);
         const initResult = this.Document.arrangeInit && this.Document.arrangeInit.script.run({ docs: layoutDocs, collection: this.Document }, console.log);
         let state = initResult && initResult.success ? initResult.result.scriptState : undefined;
-        let elements = initResult && initResult.success ? this.viewDefsToJSX(initResult.result.views) : [];
+        const elements = initResult && initResult.success ? this.viewDefsToJSX(initResult.result.views) : [];
 
         this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => {
             const data = poolData.get(pair.layout[Id]);
@@ -737,7 +738,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     layoutDocsInGrid = () => {
         UndoManager.RunInBatch(() => {
             const docs = DocListCast(this.Document[this.props.fieldKey]);
-            let startX = this.Document.panX || 0;
+            const startX = this.Document.panX || 0;
             let x = startX;
             let y = this.Document.panY || 0;
             let i = 0;
@@ -762,8 +763,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         this.Document.isRuleProvider && this.childLayoutPairs.map(pair =>
             // iterate over the children of a displayed document (or if the displayed document is a template, iterate over the children of that template)
             DocListCast(Doc.Layout(pair.layout).data).map(heading => {
-                let headingPair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, heading);
-                let headingLayout = headingPair.layout && (pair.layout.data_ext instanceof Doc) && (pair.layout.data_ext[`Layout[${headingPair.layout[Id]}]`] as Doc) || headingPair.layout;
+                const headingPair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, heading);
+                const headingLayout = headingPair.layout && (pair.layout.data_ext instanceof Doc) && (pair.layout.data_ext[`Layout[${headingPair.layout[Id]}]`] as Doc) || headingPair.layout;
                 if (headingLayout && NumCast(headingLayout.heading) > 0 && headingLayout.backgroundColor !== headingLayout.defaultBackgroundColor) {
                     Doc.GetProto(this.props.Document)["ruleColor_" + NumCast(headingLayout.heading)] = headingLayout.backgroundColor;
                 }
@@ -772,11 +773,23 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     analyzeStrokes = async () => {
-        //  CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ["inkAnalysis", "handwriting"], data.inkData);
+        const children = await DocListCastAsync(this.dataDoc.data);
+        if (!children) {
+            return;
+        }
+        const inkData: InkData[] = [];
+        for (const doc of children) {
+            const data = Cast(doc.data, InkField)?.inkData;
+            data && inkData.push(data);
+        }
+        if (!inkData.length) {
+            return;
+        }
+        CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ["inkAnalysis", "handwriting"], inkData);
     }
 
     onContextMenu = (e: React.MouseEvent) => {
-        let layoutItems: ContextMenuProps[] = [];
+        const layoutItems: ContextMenuProps[] = [];
 
         if (this.childDocs.some(d => BoolCast(d.isTemplateDoc))) {
             layoutItems.push({ description: "Template Layout Instance", event: () => this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" });
@@ -795,7 +808,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                 input.accept = ".zip";
                 input.onchange = async _e => {
                     const upload = Utils.prepend("/uploadDoc");
-                    let formData = new FormData();
+                    const formData = new FormData();
                     const file = input.files && input.files[0];
                     if (file) {
                         formData.append('file', file);
@@ -830,7 +843,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
 
     private childViews = () => {
-        let children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : [];
+        const children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : [];
         return [
             ...children,
             ...this.views,
@@ -838,12 +851,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     @computed get svgBounds() {
-        let xs = this._points.map(p => p.x);
-        let ys = this._points.map(p => p.y);
-        let right = Math.max(...xs);
-        let left = Math.min(...xs);
-        let bottom = Math.max(...ys);
-        let top = Math.min(...ys);
+        const xs = this._points.map(p => p.x);
+        const ys = this._points.map(p => p.y);
+        const right = Math.max(...xs);
+        const left = Math.min(...xs);
+        const bottom = Math.max(...ys);
+        const top = Math.min(...ys);
         return { right: right, left: left, bottom: bottom, top: top, width: right - left, height: bottom - top };
     }
 
@@ -852,7 +865,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             return (null);
         }
 
-        let B = this.svgBounds;
+        const B = this.svgBounds;
 
         return (
             <svg width={B.width} height={B.height} style={{ transform: `translate(${B.left}px, ${B.top}px)` }}>
@@ -862,7 +875,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     children = () => {
-        let eles: JSX.Element[] = [];
+        const eles: JSX.Element[] = [];
         this.extensionDoc && (eles.push(...this.childViews()));
         this.currentStroke && (eles.push(this.currentStroke));
         eles.push(<CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />);
@@ -917,7 +930,7 @@ interface CollectionFreeFormViewPannableContentsProps {
 @observer
 class CollectionFreeFormViewPannableContents extends React.Component<CollectionFreeFormViewPannableContentsProps>{
     render() {
-        let freeformclass = "collectionfreeformview" + (this.props.easing() ? "-ease" : "-none");
+        const freeformclass = "collectionfreeformview" + (this.props.easing() ? "-ease" : "-none");
         const cenx = this.props.centeringShiftX();
         const ceny = this.props.centeringShiftY();
         const panx = -this.props.panX();
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 28ddc19d7..32e39d25e 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -21,7 +21,7 @@ export default class MarqueeOptionsMenu extends AntimodeMenu {
     }
 
     render() {
-        let buttons = [
+        const buttons = [
             <button
                 className="antimodeMenu-button"
                 title="Create a Collection"
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 5ed3fecb5..dc68ae81c 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,7 +1,7 @@
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, DocListCast } from "../../../../new_fields/Doc";
-import { InkField, PointData } from "../../../../new_fields/InkField";
+import { InkField } from "../../../../new_fields/InkField";
 import { List } from "../../../../new_fields/List";
 import { listSpec } from "../../../../new_fields/Schema";
 import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField";
@@ -15,11 +15,9 @@ import { Transform } from "../../../util/Transform";
 import { undoBatch } from "../../../util/UndoManager";
 import { PreviewCursor } from "../../PreviewCursor";
 import { CollectionViewType } from "../CollectionView";
-import { CollectionFreeFormView } from "./CollectionFreeFormView";
 import "./MarqueeView.scss";
 import React = require("react");
 import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
-import InkSelectDecorations from "../../InkSelectDecorations";
 import { SubCollectionViewProps } from "../CollectionSubView";
 
 interface MarqueeViewProps {
@@ -67,26 +65,27 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     @action
     onKeyPress = (e: KeyboardEvent) => {
         //make textbox and add it to this collection
+        // tslint:disable-next-line:prefer-const
         let [x, y] = this.props.getTransform().transformPoint(this._downX, this._downY);
         if (e.key === "q" && e.ctrlKey) {
             e.preventDefault();
             (async () => {
-                let text: string = await navigator.clipboard.readText();
-                let ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
+                const text: string = await navigator.clipboard.readText();
+                const 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(";") ||
                         ns[i].endsWith(".\r") || ns[i].endsWith(".") ||
                         ns[i].endsWith(":\r") || ns[i].endsWith(":")) && i < ns.length - 1) {
-                        let sub = ns[i].endsWith("\r") ? 1 : 0;
-                        let br = ns[i + 1].trim() === "";
+                        const sub = ns[i].endsWith("\r") ? 1 : 0;
+                        const br = ns[i + 1].trim() === "";
                         ns.splice(i, 2, ns[i].substr(0, ns[i].length - sub) + ns[i + 1].trimLeft());
                         if (br) break;
                     }
                 }
                 ns.map(line => {
-                    let indent = line.search(/\S|$/);
-                    let newBox = Docs.Create.TextDocument({ width: 200, height: 35, x: x + indent / 3 * 10, y: y, documentText: "@@@" + line, title: line });
+                    const indent = line.search(/\S|$/);
+                    const newBox = Docs.Create.TextDocument({ width: 200, height: 35, x: x + indent / 3 * 10, y: y, documentText: "@@@" + line, title: line });
                     this.props.addDocument(newBox);
                     y += 40 * this.props.getTransform().Scale;
                 });
@@ -94,7 +93,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         } else if (e.key === "b" && e.ctrlKey) {
             e.preventDefault();
             navigator.clipboard.readText().then(text => {
-                let ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
+                const ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
                 if (ns.length === 1 && text.startsWith("http")) {
                     this.props.addDocument(Docs.Create.ImageDocument(text, { nativeWidth: 300, width: 300, x: x, y: y }));// paste an image from its URL in the paste buffer
                 } else {
@@ -105,8 +104,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             this.props.addLiveTextDocument(
                 Docs.Create.TextDocument({ width: 200, height: 100, x: x, y: y, autoHeight: true, title: "-typed text-" }));
         } else if (e.keyCode > 48 && e.keyCode <= 57) {
-            let notes = DocListCast((CurrentUserUtils.UserDocument.noteTypes as Doc).data);
-            let text = Docs.Create.TextDocument({ width: 200, height: 100, x: x, y: y, autoHeight: true, title: "-typed text-" });
+            const notes = DocListCast((CurrentUserUtils.UserDocument.noteTypes as Doc).data);
+            const text = Docs.Create.TextDocument({ width: 200, height: 100, x: x, y: y, autoHeight: true, title: "-typed text-" });
             text.layout = notes[(e.keyCode - 49) % notes.length];
             this.props.addLiveTextDocument(text);
         }
@@ -124,31 +123,31 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             ns.splice(0, 1);
         }
         if (ns.length > 0) {
-            let columns = ns[0].split("\t");
-            let docList: Doc[] = [];
+            const columns = ns[0].split("\t");
+            const docList: Doc[] = [];
             let groupAttr: string | number = "";
-            let rowProto = new Doc();
+            const rowProto = new Doc();
             rowProto.title = rowProto.Id;
             rowProto.width = 200;
             rowProto.isPrototype = true;
             for (let i = 1; i < ns.length - 1; i++) {
-                let values = ns[i].split("\t");
+                const values = ns[i].split("\t");
                 if (values.length === 1 && columns.length > 1) {
                     groupAttr = values[0];
                     continue;
                 }
-                let docDataProto = Doc.MakeDelegate(rowProto);
+                const docDataProto = Doc.MakeDelegate(rowProto);
                 docDataProto.isPrototype = true;
                 columns.forEach((col, i) => docDataProto[columns[i]] = (values.length > i ? ((values[i].indexOf(Number(values[i]).toString()) !== -1) ? Number(values[i]) : values[i]) : undefined));
                 if (groupAttr) {
                     docDataProto._group = groupAttr;
                 }
                 docDataProto.title = i.toString();
-                let doc = Doc.MakeDelegate(docDataProto);
+                const doc = Doc.MakeDelegate(docDataProto);
                 doc.width = 200;
                 docList.push(doc);
             }
-            let newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group", "#f1efeb")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c, "#f1efeb"))], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 });
+            const newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group", "#f1efeb")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c, "#f1efeb"))], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 });
 
             this.props.addDocument(newCol);
         }
@@ -193,13 +192,13 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     onPointerUp = (e: PointerEvent): void => {
         if (!this.props.active(true)) this.props.selectDocuments([this.props.Document], []);
         if (this._visible) {
-            let mselect = this.marqueeSelect();
+            const mselect = this.marqueeSelect();
             if (!e.shiftKey) {
                 SelectionManager.DeselectAll(mselect.length ? undefined : this.props.Document);
             }
             // let inkselect = this.ink ? this.marqueeInkSelect(this.ink.inkData) : new Map();
             // let inks = inkselect.size ? [{ Document: this.inkDoc, Ink: inkselect }] : [];
-            let docs = mselect.length ? mselect : [this.props.Document];
+            const docs = mselect.length ? mselect : [this.props.Document];
             this.props.selectDocuments(docs, []);
         }
         if (!this._commandExecuted && (Math.abs(this.Bounds.height * this.Bounds.width) > 100)) {
@@ -212,7 +211,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         }
         this.cleanupInteractions(true, this._commandExecuted);
 
-        let hideMarquee = () => {
+        const hideMarquee = () => {
             this.hideMarquee();
             MarqueeOptionsMenu.Instance.fadeOut(true);
             document.removeEventListener("pointerdown", hideMarquee);
@@ -260,10 +259,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     @computed
     get Bounds() {
-        let left = this._downX < this._lastX ? this._downX : this._lastX;
-        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);
+        const left = this._downX < this._lastX ? this._downX : this._lastX;
+        const top = this._downY < this._lastY ? this._downY : this._lastY;
+        const topLeft = this.props.getTransform().transformPoint(left, top);
+        const 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]) };
     }
 
@@ -302,15 +301,15 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     }
 
     getCollection = (selected: Doc[]) => {
-        let bounds = this.Bounds;
-        let defaultPalette = ["rgb(114,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)",
+        const bounds = this.Bounds;
+        const defaultPalette = ["rgb(114,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)",
             "rgb(209,150,226)", "rgb(127,235,144)", "rgb(252,188,189)", "rgb(247,175,81)",];
-        let colorPalette = Cast(this.props.Document.colorPalette, listSpec("string"));
+        const colorPalette = Cast(this.props.Document.colorPalette, listSpec("string"));
         if (!colorPalette) this.props.Document.colorPalette = new List<string>(defaultPalette);
-        let palette = Array.from(Cast(this.props.Document.colorPalette, listSpec("string")) as string[]);
-        let usedPaletted = new Map<string, number>();
+        const palette = Array.from(Cast(this.props.Document.colorPalette, listSpec("string")) as string[]);
+        const usedPaletted = new Map<string, number>();
         [...this.props.activeDocuments(), this.props.Document].map(child => {
-            let bg = StrCast(Doc.Layout(child).backgroundColor);
+            const bg = StrCast(Doc.Layout(child).backgroundColor);
             if (palette.indexOf(bg) !== -1) {
                 palette.splice(palette.indexOf(bg), 1);
                 if (usedPaletted.get(bg)) usedPaletted.set(bg, usedPaletted.get(bg)! + 1);
@@ -320,10 +319,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         usedPaletted.delete("#f1efeb");
         usedPaletted.delete("white");
         usedPaletted.delete("rgba(255,255,255,1)");
-        let usedSequnce = Array.from(usedPaletted.keys()).sort((a, b) => usedPaletted.get(a)! < usedPaletted.get(b)! ? -1 : usedPaletted.get(a)! > usedPaletted.get(b)! ? 1 : 0);
-        let chosenColor = (usedPaletted.size === 0) ? "white" : palette.length ? palette[0] : usedSequnce[0];
-        let inkData = this.ink ? this.ink.inkData : undefined;
-        let newCollection = Docs.Create.FreeformDocument(selected, {
+        const usedSequnce = Array.from(usedPaletted.keys()).sort((a, b) => usedPaletted.get(a)! < usedPaletted.get(b)! ? -1 : usedPaletted.get(a)! > usedPaletted.get(b)! ? 1 : 0);
+        const chosenColor = (usedPaletted.size === 0) ? "white" : palette.length ? palette[0] : usedSequnce[0];
+        // const inkData = this.ink ? this.ink.inkData : undefined;
+        const newCollection = Docs.Create.FreeformDocument(selected, {
             x: bounds.left,
             y: bounds.top,
             panX: 0,
@@ -334,7 +333,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             height: bounds.height,
             title: "a nested collection",
         });
-        let dataExtensionField = Doc.CreateDocumentExtensionForField(newCollection, "data");
+        // const dataExtensionField = Doc.CreateDocumentExtensionForField(newCollection, "data");
         // dataExtensionField.ink = inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined;
         // this.marqueeInkDelete(inkData);
         this.hideMarquee();
@@ -343,8 +342,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     @action
     collection = (e: KeyboardEvent | React.PointerEvent | undefined) => {
-        let bounds = this.Bounds;
-        let selected = this.marqueeSelect(false);
+        const bounds = this.Bounds;
+        const selected = this.marqueeSelect(false);
         if (e instanceof KeyboardEvent ? e.key === "c" : true) {
             selected.map(d => {
                 this.props.removeDocument(d);
@@ -354,7 +353,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 return d;
             });
         }
-        let newCollection = this.getCollection(selected);
+        const newCollection = this.getCollection(selected);
         this.props.addDocument(newCollection);
         this.props.selectDocuments([newCollection], []);
         MarqueeOptionsMenu.Instance.fadeOut(true);
@@ -363,9 +362,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     @action
     summary = (e: KeyboardEvent | React.PointerEvent | undefined) => {
-        let bounds = this.Bounds;
-        let selected = this.marqueeSelect(false);
-        let newCollection = this.getCollection(selected);
+        const bounds = this.Bounds;
+        const selected = this.marqueeSelect(false);
+        const newCollection = this.getCollection(selected);
 
         selected.map(d => {
             this.props.removeDocument(d);
@@ -375,13 +374,13 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             return d;
         });
         newCollection.chromeStatus = "disabled";
-        let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, autoHeight: true, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
+        const summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, autoHeight: true, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
         Doc.GetProto(summary).summarizedDocs = new List<Doc>([newCollection]);
         newCollection.x = bounds.left + bounds.width;
         Doc.GetProto(newCollection).summaryDoc = summary;
         Doc.GetProto(newCollection).title = ComputedField.MakeFunction(`summaryTitle(this);`);
         if (e instanceof KeyboardEvent ? e.key === "s" : true) { // summary is wrapped in an expand/collapse container that also contains the summarized documents in a free form view.
-            let container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, chromeStatus: "disabled", title: "-summary-" });
+            const container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, chromeStatus: "disabled", title: "-summary-" });
             container.viewType = CollectionViewType.Stacking;
             container.autoHeight = true;
             Doc.GetProto(summary).maximizeLocation = "inPlace";  // or "onRight"
@@ -462,42 +461,42 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     // }
 
     marqueeSelect(selectBackgrounds: boolean = true) {
-        let selRect = this.Bounds;
-        let selection: Doc[] = [];
+        const selRect = this.Bounds;
+        const selection: Doc[] = [];
         this.props.activeDocuments().filter(doc => !doc.isBackground && doc.z === undefined).map(doc => {
-            let layoutDoc = Doc.Layout(doc);
-            var x = NumCast(doc.x);
-            var y = NumCast(doc.y);
-            var w = NumCast(layoutDoc.width);
-            var h = NumCast(layoutDoc.height);
+            const layoutDoc = Doc.Layout(doc);
+            const x = NumCast(doc.x);
+            const y = NumCast(doc.y);
+            const w = NumCast(layoutDoc.width);
+            const h = NumCast(layoutDoc.height);
             if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
                 selection.push(doc);
             }
         });
         if (!selection.length && selectBackgrounds) {
             this.props.activeDocuments().filter(doc => doc.z === undefined).map(doc => {
-                let layoutDoc = Doc.Layout(doc);
-                var x = NumCast(doc.x);
-                var y = NumCast(doc.y);
-                var w = NumCast(layoutDoc.width);
-                var h = NumCast(layoutDoc.height);
+                const layoutDoc = Doc.Layout(doc);
+                const x = NumCast(doc.x);
+                const y = NumCast(doc.y);
+                const w = NumCast(layoutDoc.width);
+                const h = NumCast(layoutDoc.height);
                 if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
                     selection.push(doc);
                 }
             });
         }
         if (!selection.length) {
-            let left = this._downX < this._lastX ? this._downX : this._lastX;
-            let top = this._downY < this._lastY ? this._downY : this._lastY;
-            let topLeft = this.props.getContainerTransform().transformPoint(left, top);
-            let size = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
-            let otherBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
+            const left = this._downX < this._lastX ? this._downX : this._lastX;
+            const top = this._downY < this._lastY ? this._downY : this._lastY;
+            const topLeft = this.props.getContainerTransform().transformPoint(left, top);
+            const size = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
+            const otherBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
             this.props.activeDocuments().filter(doc => doc.z !== undefined).map(doc => {
-                let layoutDoc = Doc.Layout(doc);
-                var x = NumCast(doc.x);
-                var y = NumCast(doc.y);
-                var w = NumCast(layoutDoc.width);
-                var h = NumCast(layoutDoc.height);
+                const layoutDoc = Doc.Layout(doc);
+                const x = NumCast(doc.x);
+                const y = NumCast(doc.y);
+                const w = NumCast(layoutDoc.width);
+                const h = NumCast(layoutDoc.height);
                 if (this.intersectRect({ left: x, top: y, width: w, height: h }, otherBounds)) {
                     selection.push(doc);
                 }
@@ -508,8 +507,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     @computed
     get marqueeDiv() {
-        let p: [number, number] = this._visible ? this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY) : [0, 0];
-        let v = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
+        const p: [number, number] = this._visible ? this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY) : [0, 0];
+        const v = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
         /**
          * @RE - The commented out span below
          * This contains the "C for collection, ..." text on marquees.
diff --git a/src/client/views/document_templates/caption_toggle/DetailedCaptionToggle.tsx b/src/client/views/document_templates/caption_toggle/DetailedCaptionToggle.tsx
index f8104cef3..3aaf4120c 100644
--- a/src/client/views/document_templates/caption_toggle/DetailedCaptionToggle.tsx
+++ b/src/client/views/document_templates/caption_toggle/DetailedCaptionToggle.tsx
@@ -1,8 +1,8 @@
 import * as React from 'react';
-import { FontWeightProperty, FontStyleProperty, FontSizeProperty, ColorProperty } from 'csstype';
+import { FontStyleProperty, ColorProperty } from 'csstype';
 import { observer } from 'mobx-react';
 import { observable, action, runInAction } from 'mobx';
-import { FormattedTextBox, FormattedTextBoxProps } from '../../nodes/FormattedTextBox';
+import { FormattedTextBox } from '../../nodes/FormattedTextBox';
 import { FieldViewProps } from '../../nodes/FieldView';
 
 interface DetailedCaptionDataProps {
@@ -33,7 +33,7 @@ export default class DetailedCaptionToggle extends React.Component<DetailedCapti
     }
 
     render() {
-        let size = this.props.toggleSize || 20;
+        const size = this.props.toggleSize || 20;
         return (
             <div style={{
                 transition: "0.5s opacity ease",
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index ecb3e9db4..bb8a8b47b 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -43,12 +43,12 @@ class GroupTypesDropdown extends React.Component<GroupTypesDropdownProps> {
     @action
     onKeyDown = (e: React.KeyboardEvent): void => {
         if (e.key === "Enter") {
-            let allGroupTypes = Array.from(LinkManager.Instance.getAllGroupTypes());
-            let groupOptions = allGroupTypes.filter(groupType => groupType.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
-            let exactFound = groupOptions.findIndex(groupType => groupType.toUpperCase() === this._searchTerm.toUpperCase());
+            const allGroupTypes = Array.from(LinkManager.Instance.getAllGroupTypes());
+            const groupOptions = allGroupTypes.filter(groupType => groupType.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
+            const exactFound = groupOptions.findIndex(groupType => groupType.toUpperCase() === this._searchTerm.toUpperCase());
 
             if (exactFound > -1) {
-                let groupType = groupOptions[exactFound];
+                const groupType = groupOptions[exactFound];
                 this.props.setGroupType(groupType);
                 this._groupType = groupType;
             } else {
@@ -84,19 +84,19 @@ class GroupTypesDropdown extends React.Component<GroupTypesDropdownProps> {
     renderOptions = (): JSX.Element[] | JSX.Element => {
         if (this._searchTerm === "") return <></>;
 
-        let allGroupTypes = Array.from(LinkManager.Instance.getAllGroupTypes());
-        let groupOptions = allGroupTypes.filter(groupType => groupType.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
-        let exactFound = groupOptions.findIndex(groupType => groupType.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
+        const allGroupTypes = Array.from(LinkManager.Instance.getAllGroupTypes());
+        const groupOptions = allGroupTypes.filter(groupType => groupType.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
+        const exactFound = groupOptions.findIndex(groupType => groupType.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
 
-        let options = groupOptions.map(groupType => {
-            let ref = React.createRef<HTMLDivElement>();
+        const options = groupOptions.map(groupType => {
+            const ref = React.createRef<HTMLDivElement>();
             return <div key={groupType} ref={ref} className="linkEditor-option"
                 onClick={() => this.onOptionClick(groupType, false)}>{groupType}</div>;
         });
 
         // if search term does not already exist as a group type, give option to create new group type
         if (!exactFound && this._searchTerm !== "") {
-            let ref = React.createRef<HTMLDivElement>();
+            const ref = React.createRef<HTMLDivElement>();
             options.push(<div key={""} ref={ref} className="linkEditor-option"
                 onClick={() => this.onOptionClick(this._searchTerm, true)}>Define new "{this._searchTerm}" relationship</div>);
         }
@@ -138,10 +138,10 @@ class LinkMetadataEditor extends React.Component<LinkMetadataEditorProps> {
 
     @action
     setMetadataKey = (value: string): void => {
-        let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);
+        const groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);
 
         // don't allow user to create existing key
-        let newIndex = groupMdKeys.findIndex(key => key.toUpperCase() === value.toUpperCase());
+        const newIndex = groupMdKeys.findIndex(key => key.toUpperCase() === value.toUpperCase());
         if (newIndex > -1) {
             this._keyError = true;
             this._key = value;
@@ -151,7 +151,7 @@ class LinkMetadataEditor extends React.Component<LinkMetadataEditorProps> {
         }
 
         // set new value for key
-        let currIndex = groupMdKeys.findIndex(key => {
+        const currIndex = groupMdKeys.findIndex(key => {
             return StrCast(key).toUpperCase() === this._key.toUpperCase();
         });
         if (currIndex === -1) console.error("LinkMetadataEditor: key was not found");
@@ -172,9 +172,9 @@ class LinkMetadataEditor extends React.Component<LinkMetadataEditorProps> {
 
     @action
     removeMetadata = (): void => {
-        let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);
+        const groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);
 
-        let index = groupMdKeys.findIndex(key => key.toUpperCase() === this._key.toUpperCase());
+        const index = groupMdKeys.findIndex(key => key.toUpperCase() === this._key.toUpperCase());
         if (index === -1) console.error("LinkMetadataEditor: key was not found");
         groupMdKeys.splice(index, 1);
 
@@ -206,7 +206,7 @@ export class LinkGroupEditor extends React.Component<LinkGroupEditorProps> {
     constructor(props: LinkGroupEditorProps) {
         super(props);
 
-        let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(StrCast(props.groupDoc.type));
+        const groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(StrCast(props.groupDoc.type));
         groupMdKeys.forEach(key => {
             this._metadataIds.set(key, Utils.GenerateGuid());
         });
@@ -226,25 +226,25 @@ export class LinkGroupEditor extends React.Component<LinkGroupEditorProps> {
     }
 
     copyGroup = async (groupType: string): Promise<void> => {
-        let sourceGroupDoc = this.props.groupDoc;
+        const sourceGroupDoc = this.props.groupDoc;
         const sourceMdDoc = await Cast(sourceGroupDoc.metadata, Doc);
         if (!sourceMdDoc) return;
 
-        let destDoc = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
+        const destDoc = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
         // let destGroupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, destDoc);
-        let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
+        const keys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
 
         // create new metadata doc with copied kvp
-        let destMdDoc = new Doc();
+        const destMdDoc = new Doc();
         destMdDoc.anchor1 = StrCast(sourceMdDoc.anchor2);
         destMdDoc.anchor2 = StrCast(sourceMdDoc.anchor1);
         keys.forEach(key => {
-            let val = sourceMdDoc[key] === undefined ? "" : StrCast(sourceMdDoc[key]);
+            const val = sourceMdDoc[key] === undefined ? "" : StrCast(sourceMdDoc[key]);
             destMdDoc[key] = val;
         });
 
         // create new group doc with new metadata doc
-        let destGroupDoc = new Doc();
+        const destGroupDoc = new Doc();
         destGroupDoc.type = groupType;
         destGroupDoc.metadata = destMdDoc;
 
@@ -256,7 +256,7 @@ export class LinkGroupEditor extends React.Component<LinkGroupEditorProps> {
     @action
     addMetadata = (groupType: string): void => {
         this._metadataIds.set("new key", Utils.GenerateGuid());
-        let mdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
+        const mdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
         // only add "new key" if there is no other key with value "new key"; prevents spamming
         if (mdKeys.indexOf("new key") === -1) mdKeys.push("new key");
         LinkManager.Instance.setMetadataKeysForGroup(groupType, mdKeys);
@@ -268,17 +268,17 @@ export class LinkGroupEditor extends React.Component<LinkGroupEditorProps> {
     }
 
     renderMetadata = (): JSX.Element[] => {
-        let metadata: Array<JSX.Element> = [];
-        let groupDoc = this.props.groupDoc;
+        const metadata: Array<JSX.Element> = [];
+        const groupDoc = this.props.groupDoc;
         const mdDoc = FieldValue(Cast(groupDoc.metadata, Doc));
         if (!mdDoc) {
             return [];
         }
-        let groupType = StrCast(groupDoc.type);
-        let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
+        const groupType = StrCast(groupDoc.type);
+        const groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
 
         groupMdKeys.forEach((key) => {
-            let val = StrCast(mdDoc[key]);
+            const val = StrCast(mdDoc[key]);
             metadata.push(
                 <LinkMetadataEditor key={"mded-" + this._metadataIds.get(key)} id={this._metadataIds.get(key)!} groupType={groupType} mdDoc={mdDoc} mdKey={key} mdValue={val} changeMdIdKey={this.changeMdIdKey} />
             );
@@ -287,18 +287,18 @@ export class LinkGroupEditor extends React.Component<LinkGroupEditorProps> {
     }
 
     viewGroupAsTable = (groupType: string): JSX.Element => {
-        let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
-        let index = keys.indexOf("");
+        const keys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
+        const index = keys.indexOf("");
         if (index > -1) keys.splice(index, 1);
-        let cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c, "#f1efeb"));
-        let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType);
-        let createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" }));
-        let ref = React.createRef<HTMLDivElement>();
+        const cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c, "#f1efeb"));
+        const docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType);
+        const createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" }));
+        const ref = React.createRef<HTMLDivElement>();
         return <div ref={ref}><button className="linkEditor-button" onPointerDown={SetupDrag(ref, createTable)} title="Drag to view relationship table"><FontAwesomeIcon icon="table" size="sm" /></button></div>;
     }
 
     render() {
-        let groupType = StrCast(this.props.groupDoc.type);
+        const groupType = StrCast(this.props.groupDoc.type);
         // if ((groupType && LinkManager.Instance.getMetadataKeysInGroup(groupType).length > 0) || groupType === "") {
         let buttons;
         if (groupType === "") {
@@ -356,15 +356,15 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
     @action
     addGroup = (): void => {
         // create new metadata document for group
-        let mdDoc = new Doc();
+        const mdDoc = new Doc();
         mdDoc.anchor1 = this.props.sourceDoc.title;
-        let opp = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
+        const opp = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
         if (opp) {
             mdDoc.anchor2 = opp.title;
         }
 
         // create new group document
-        let groupDoc = new Doc();
+        const groupDoc = new Doc();
         groupDoc.type = "";
         groupDoc.metadata = mdDoc;
 
@@ -372,10 +372,10 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
     }
 
     render() {
-        let destination = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
+        const destination = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
 
-        let groupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc);
-        let groups = groupList.map(groupDoc => {
+        const groupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc);
+        const groups = groupList.map(groupDoc => {
             return <LinkGroupEditor key={"gred-" + StrCast(groupDoc.type)} linkDoc={this.props.linkDoc} sourceDoc={this.props.sourceDoc} groupDoc={groupDoc} />;
         });
 
diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx
index efe2c7f2a..29e167ff7 100644
--- a/src/client/views/linking/LinkFollowBox.tsx
+++ b/src/client/views/linking/LinkFollowBox.tsx
@@ -68,14 +68,14 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
         this._contextDisposer = reaction(
             () => this.selectedContextString,
             async () => {
-                let ref = await DocServer.GetRefField(this.selectedContextString);
+                const ref = await DocServer.GetRefField(this.selectedContextString);
                 runInAction(() => {
                     if (ref instanceof Doc) {
                         this.selectedContext = ref;
                     }
                 });
                 if (this.selectedContext instanceof Doc) {
-                    let aliases = await SearchUtil.GetViewsOfDocument(this.selectedContext);
+                    const aliases = await SearchUtil.GetViewsOfDocument(this.selectedContext);
                     runInAction(() => { this.selectedContextAliases = aliases; });
                 }
             }
@@ -90,8 +90,8 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
         if (LinkFollowBox.destinationDoc && this.sourceView && this.sourceView.props.ContainingCollectionDoc) {
             runInAction(() => this.canPan = false);
             if (this.sourceView.props.ContainingCollectionDoc.viewType === CollectionViewType.Freeform) {
-                let docs = Cast(this.sourceView.props.ContainingCollectionDoc.data, listSpec(Doc), []);
-                let aliases = await SearchUtil.GetViewsOfDocument(Doc.GetProto(LinkFollowBox.destinationDoc));
+                const docs = Cast(this.sourceView.props.ContainingCollectionDoc.data, listSpec(Doc), []);
+                const aliases = await SearchUtil.GetViewsOfDocument(Doc.GetProto(LinkFollowBox.destinationDoc));
 
                 aliases.forEach(alias => {
                     if (docs.filter(doc => doc === alias).length > 0) {
@@ -118,8 +118,8 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
 
     async fetchDocuments() {
         if (LinkFollowBox.destinationDoc) {
-            let dest: Doc = LinkFollowBox.destinationDoc;
-            let aliases = await SearchUtil.GetViewsOfDocument(Doc.GetProto(dest));
+            const dest: Doc = LinkFollowBox.destinationDoc;
+            const aliases = await SearchUtil.GetViewsOfDocument(Doc.GetProto(dest));
             const { docs } = await SearchUtil.Search("", true, { fq: `data_l:"${dest[Id]}"` });
             const map: Map<Doc, Doc> = new Map;
             const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search("", true, { fq: `data_l:"${doc[Id]}"` }).then(result => result.docs)));
@@ -128,7 +128,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
             runInAction(async () => {
                 this._docs = docs.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)).map(doc => ({ col: doc, target: dest }));
                 this._otherDocs = Array.from(map.entries()).filter(entry => !Doc.AreProtosEqual(entry[0], CollectionDockingView.Instance.props.Document)).map(([col, target]) => ({ col, target }));
-                let tcontext = LinkFollowBox.linkDoc && (await Cast(LinkFollowBox.linkDoc.anchor2Context, Doc)) as Doc;
+                const tcontext = LinkFollowBox.linkDoc && (await Cast(LinkFollowBox.linkDoc.anchor2Context, Doc)) as Doc;
                 runInAction(() => tcontext && this._docs.splice(0, 0, { col: tcontext, target: dest }));
             });
         }
@@ -157,7 +157,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
     @undoBatch
     openFullScreen = () => {
         if (LinkFollowBox.destinationDoc) {
-            let view = DocumentManager.Instance.getDocumentView(LinkFollowBox.destinationDoc);
+            const view = DocumentManager.Instance.getDocumentView(LinkFollowBox.destinationDoc);
             view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view);
         }
     }
@@ -171,7 +171,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
                 options.context.panX = newPanX;
                 options.context.panY = newPanY;
             }
-            let view = DocumentManager.Instance.getDocumentView(options.context);
+            const view = DocumentManager.Instance.getDocumentView(options.context);
             view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view);
             this.highlightDoc();
         }
@@ -211,7 +211,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
     @undoBatch
     openLinkRight = () => {
         if (LinkFollowBox.destinationDoc) {
-            let alias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
+            const alias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
             (LinkFollowBox._addDocTab || this.props.addDocTab)(alias, undefined, "onRight");
             this.highlightDoc();
             SelectionManager.DeselectAll();
@@ -222,7 +222,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
     @undoBatch
     jumpToLink = async (options: { shouldZoom: boolean }) => {
         if (LinkFollowBox.sourceDoc && LinkFollowBox.linkDoc) {
-            let focus = (document: Doc) => { (LinkFollowBox._addDocTab || this.props.addDocTab)(document, undefined, "inTab"); SelectionManager.DeselectAll(); };
+            const focus = (document: Doc) => { (LinkFollowBox._addDocTab || this.props.addDocTab)(document, undefined, "inTab"); SelectionManager.DeselectAll(); };
             //let focus = (doc: Doc, maxLocation: string) => this.props.focus(docthis.props.focus(LinkFollowBox.destinationDoc, true, 1, () => this.props.addDocTab(doc, undefined, maxLocation));
 
             DocumentManager.Instance.FollowLink(LinkFollowBox.linkDoc, LinkFollowBox.sourceDoc, focus, options && options.shouldZoom, false, undefined);
@@ -232,7 +232,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
     @undoBatch
     openLinkTab = () => {
         if (LinkFollowBox.destinationDoc) {
-            let fullScreenAlias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
+            const fullScreenAlias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
             // this.prosp.addDocTab is empty -- use the link source's addDocTab 
             (LinkFollowBox._addDocTab || this.props.addDocTab)(fullScreenAlias, undefined, "inTab");
 
@@ -264,14 +264,14 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
 
         if (LinkFollowBox.destinationDoc && LinkFollowBox.sourceDoc) {
             if (this.sourceView && this.sourceView.props.addDocument) {
-                let destViews = DocumentManager.Instance.getDocumentViews(LinkFollowBox.destinationDoc);
+                const destViews = DocumentManager.Instance.getDocumentViews(LinkFollowBox.destinationDoc);
                 if (!destViews.find(dv => dv.props.ContainingCollectionView === this.sourceView!.props.ContainingCollectionView)) {
-                    let alias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
-                    let y = NumCast(LinkFollowBox.sourceDoc.y);
-                    let x = NumCast(LinkFollowBox.sourceDoc.x);
+                    const alias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
+                    const y = NumCast(LinkFollowBox.sourceDoc.y);
+                    const x = NumCast(LinkFollowBox.sourceDoc.x);
 
-                    let width = NumCast(LinkFollowBox.sourceDoc.width);
-                    let height = NumCast(LinkFollowBox.sourceDoc.height);
+                    const width = NumCast(LinkFollowBox.sourceDoc.width);
+                    const height = NumCast(LinkFollowBox.sourceDoc.height);
 
                     alias.x = x + width + 30;
                     alias.y = y;
@@ -301,8 +301,8 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
                 this.selectedContext = LinkFollowBox.destinationDoc;
             }
             if (this.selectedOption === "") this.selectedOption = FollowOptions.NOZOOM;
-            let shouldZoom: boolean = this.selectedOption === FollowOptions.NOZOOM ? false : true;
-            let notOpenInContext: boolean = this.selectedContextString === "self" || this.selectedContextString === LinkFollowBox.destinationDoc[Id];
+            const shouldZoom: boolean = this.selectedOption === FollowOptions.NOZOOM ? false : true;
+            const notOpenInContext: boolean = this.selectedContextString === "self" || this.selectedContextString === LinkFollowBox.destinationDoc[Id];
 
             if (this.selectedMode === FollowModes.INPLACE) {
                 if (shouldZoom !== undefined) this.openLinkInPlace({ shouldZoom: shouldZoom });
@@ -328,7 +328,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
 
     @action
     handleModeChange = (e: React.ChangeEvent) => {
-        let target = e.target as HTMLInputElement;
+        const target = e.target as HTMLInputElement;
         this.selectedMode = target.value;
         this.selectedContext = undefined;
         this.selectedContextString = "";
@@ -345,13 +345,13 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
 
     @action
     handleOptionChange = (e: React.ChangeEvent) => {
-        let target = e.target as HTMLInputElement;
+        const target = e.target as HTMLInputElement;
         this.selectedOption = target.value;
     }
 
     @action
     handleContextChange = (e: React.ChangeEvent) => {
-        let target = e.target as HTMLInputElement;
+        const target = e.target as HTMLInputElement;
         this.selectedContextString = target.value;
         // selectedContext is updated in reaction
         this.selectedOption = "";
@@ -360,7 +360,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
     @computed
     get canOpenInPlace() {
         if (this.sourceView && this.sourceView.props.ContainingCollectionDoc) {
-            let colDoc = this.sourceView.props.ContainingCollectionDoc;
+            const colDoc = this.sourceView.props.ContainingCollectionDoc;
             if (colDoc.viewType && colDoc.viewType === CollectionViewType.Freeform) return true;
         }
         return false;
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 27af873b5..52628ba4c 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -34,7 +34,7 @@ export class LinkMenu extends React.Component<Props> {
     }
 
     renderAllGroups = (groups: Map<string, Array<Doc>>): Array<JSX.Element> => {
-        let linkItems: Array<JSX.Element> = [];
+        const linkItems: Array<JSX.Element> = [];
         groups.forEach((group, groupType) => {
             linkItems.push(
                 <LinkMenuGroup
@@ -55,8 +55,8 @@ export class LinkMenu extends React.Component<Props> {
     }
 
     render() {
-        let sourceDoc = this.props.docView.props.Document;
-        let groups: Map<string, Doc[]> = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc);
+        const sourceDoc = this.props.docView.props.Document;
+        const groups: Map<string, Doc[]> = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc);
         if (this._editingLink === undefined) {
             return (
                 <div className="linkMenu">
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index 1891919ce..15aacbbc9 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -51,11 +51,11 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
                 document.removeEventListener("pointermove", this.onLinkButtonMoved);
                 document.removeEventListener("pointerup", this.onLinkButtonUp);
 
-                let draggedDocs = this.props.group.map(linkDoc => {
-                    let opp = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc);
+                const draggedDocs = this.props.group.map(linkDoc => {
+                    const opp = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc);
                     if (opp) return opp;
                 }) as Doc[];
-                let dragData = new DragManager.DocumentDragData(draggedDocs);
+                const dragData = new DragManager.DocumentDragData(draggedDocs);
 
                 DragManager.StartLinkedDocumentDrag([this._drag.current], dragData, e.x, e.y, {
                     handlers: {
@@ -69,19 +69,19 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
     }
 
     viewGroupAsTable = (groupType: string): JSX.Element => {
-        let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
-        let index = keys.indexOf("");
+        const keys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
+        const index = keys.indexOf("");
         if (index > -1) keys.splice(index, 1);
-        let cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c, "#f1efeb"));
-        let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType);
-        let createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" }));
-        let ref = React.createRef<HTMLDivElement>();
+        const cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c, "#f1efeb"));
+        const docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType);
+        const createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" }));
+        const ref = React.createRef<HTMLDivElement>();
         return <div ref={ref}><button className="linkEditor-button linkEditor-tableButton" onPointerDown={SetupDrag(ref, createTable)} title="Drag to view relationship table"><FontAwesomeIcon icon="table" size="sm" /></button></div>;
     }
 
     render() {
-        let groupItems = this.props.group.map(linkDoc => {
-            let destination = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc);
+        const groupItems = this.props.group.map(linkDoc => {
+            const destination = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc);
             if (destination && this.props.sourceDoc) {
                 return <LinkMenuItem key={destination[Id] + this.props.sourceDoc[Id]}
                     groupType={this.props.groupType}
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 238660de3..edf5e9c26 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -36,15 +36,15 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
     }
 
     renderMetadata = (): JSX.Element => {
-        let groups = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc);
-        let index = groups.findIndex(groupDoc => StrCast(groupDoc.type).toUpperCase() === this.props.groupType.toUpperCase());
-        let groupDoc = index > -1 ? groups[index] : undefined;
+        const groups = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc);
+        const index = groups.findIndex(groupDoc => StrCast(groupDoc.type).toUpperCase() === this.props.groupType.toUpperCase());
+        const groupDoc = index > -1 ? groups[index] : undefined;
 
         let mdRows: Array<JSX.Element> = [];
         if (groupDoc) {
-            let mdDoc = Cast(groupDoc.metadata, Doc, null);
+            const mdDoc = Cast(groupDoc.metadata, Doc, null);
             if (mdDoc) {
-                let keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType);
+                const keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType);
                 mdRows = keys.map(key => {
                     return (<div key={key} className="link-metadata-row"><b>{key}</b>: {StrCast(mdDoc[key])}</div>);
                 });
@@ -110,8 +110,8 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
 
     render() {
 
-        let keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType);
-        let canExpand = keys ? keys.length > 0 : false;
+        const keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType);
+        const canExpand = keys ? keys.length > 0 : false;
 
         return (
             <div className="linkMenu-item">
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 77b10e395..95c765e8a 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -56,19 +56,19 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
         this._linkPlayDisposer = reaction(() => this.layoutDoc.scrollToLinkID,
             scrollLinkId => {
                 scrollLinkId && DocListCast(this.dataDoc.links).filter(l => l[Id] === scrollLinkId).map(l => {
-                    let la1 = l.anchor1 as Doc;
-                    let linkTime = Doc.AreProtosEqual(la1, this.dataDoc) ? NumCast(l.anchor1Timecode) : NumCast(l.anchor2Timecode);
+                    const la1 = l.anchor1 as Doc;
+                    const linkTime = Doc.AreProtosEqual(la1, this.dataDoc) ? NumCast(l.anchor1Timecode) : NumCast(l.anchor2Timecode);
                     setTimeout(() => { this.playFrom(linkTime); Doc.linkFollowHighlight(l); }, 250);
                 });
                 scrollLinkId && Doc.SetInPlace(this.layoutDoc, "scrollToLinkID", undefined, false);
             }, { fireImmediately: true });
         this._reactionDisposer = reaction(() => SelectionManager.SelectedDocuments(),
             selected => {
-                let sel = selected.length ? selected[0].props.Document : undefined;
+                const sel = selected.length ? selected[0].props.Document : undefined;
                 this.Document.playOnSelect && sel && !Doc.AreProtosEqual(sel, this.props.Document) && this.playFrom(DateCast(sel.creationTime).date.getTime());
             });
         this._scrubbingDisposer = reaction(() => AudioBox._scrubTime, timeInMillisecondsFrom1970 => {
-            let start = this.extensionDoc && DateCast(this.extensionDoc.recordingStart);
+            const start = this.extensionDoc && DateCast(this.extensionDoc.recordingStart);
             start && this.playFrom((timeInMillisecondsFrom1970 - start.date.getTime()) / 1000);
         });
     }
@@ -127,7 +127,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
 
     recordAudioAnnotation = () => {
         let gumStream: any;
-        let self = this;
+        const self = this;
         const extensionDoc = this.extensionDoc;
         extensionDoc && navigator.mediaDevices.getUserMedia({
             audio: true
@@ -160,7 +160,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
     }
 
     specificContextMenu = (e: React.MouseEvent): void => {
-        let funcs: ContextMenuProps[] = [];
+        const funcs: ContextMenuProps[] = [];
         funcs.push({ description: (this.Document.playOnSelect ? "Don't play" : "Play") + " when document selected", event: () => this.Document.playOnSelect = !this.Document.playOnSelect, icon: "expand-arrows-alt" });
 
         ContextMenu.Instance.addItem({ description: "Audio Funcs...", subitems: funcs, icon: "asterisk" });
@@ -170,7 +170,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
         this._recorder.stop();
         this.dataDoc.duration = (new Date().getTime() - this._recordStart) / 1000;
         this._audioState = "recorded";
-        let ind = AudioBox.ActiveRecordings.indexOf(this.props.Document);
+        const ind = AudioBox.ActiveRecordings.indexOf(this.props.Document);
         ind !== -1 && (AudioBox.ActiveRecordings.splice(ind, 1));
     });
 
@@ -198,13 +198,13 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
     }
 
     @computed get path() {
-        let field = Cast(this.props.Document[this.props.fieldKey], AudioField);
-        let path = (field instanceof AudioField) ? field.url.href : "";
+        const field = Cast(this.props.Document[this.props.fieldKey], AudioField);
+        const path = (field instanceof AudioField) ? field.url.href : "";
         return path === nullAudio ? "" : path;
     }
 
     @computed get audio() {
-        let interactive = this.active() ? "-interactive" : "";
+        const interactive = this.active() ? "-interactive" : "";
         return <audio ref={this.setRef} className={`audiobox-control${interactive}`}>
             <source src={this.path} type="audio/mpeg" />
             Not supported.
@@ -212,7 +212,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
     }
 
     render() {
-        let interactive = this.active() ? "-interactive" : "";
+        const interactive = this.active() ? "-interactive" : "";
         return (!this.extensionDoc ? (null) :
             <div className={`audiobox-container`} onContextMenu={this.specificContextMenu}
                 onClick={!this.path ? this.recordClick : undefined}>
@@ -228,7 +228,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
                             <div className="audiobox-timeline" onClick={e => e.stopPropagation()}
                                 onPointerDown={e => {
                                     if (e.button === 0 && !e.ctrlKey) {
-                                        let rect = (e.target as any).getBoundingClientRect();
+                                        const rect = (e.target as any).getBoundingClientRect();
                                         this._ele!.currentTime = this.Document.currentTimecode = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration);
                                         this.pause();
                                         e.stopPropagation();
diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx
index 659ba154a..34151a311 100644
--- a/src/client/views/nodes/ButtonBox.tsx
+++ b/src/client/views/nodes/ButtonBox.tsx
@@ -51,10 +51,10 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt
     }
 
     specificContextMenu = (e: React.MouseEvent): void => {
-        let funcs: ContextMenuProps[] = [];
+        const funcs: ContextMenuProps[] = [];
         funcs.push({
             description: "Clear Script Params", event: () => {
-                let params = FieldValue(this.Document.buttonParams);
+                const params = FieldValue(this.Document.buttonParams);
                 params && params.map(p => this.props.Document[p] = undefined);
             }, icon: "trash"
         });
@@ -73,8 +73,8 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt
     }
     // (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")")
     render() {
-        let params = this.Document.buttonParams;
-        let missingParams = params && params.filter(p => this.props.Document[p] === undefined);
+        const params = this.Document.buttonParams;
+        const missingParams = params && params.filter(p => this.props.Document[p] === undefined);
         params && params.map(p => DocListCast(this.props.Document[p])); // bcz: really hacky form of prefetching ... 
         return (
             <div className="buttonBox-outerDiv" ref={this.createDropTarget} onContextMenu={this.specificContextMenu}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 2b079fe50..d599de56e 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -31,7 +31,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     get Y() { return this._animPos !== undefined ? this._animPos[1] : this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.dataProvider ? this.dataProvider.y : (this.Document.y || 0); }
     get width() { return this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.props.dataProvider && this.dataProvider ? this.dataProvider.width : this.layoutDoc[WidthSym](); }
     get height() {
-        let hgt = this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.dataProvider && this.dataProvider ? this.dataProvider.height : this.layoutDoc[HeightSym]();
+        const hgt = this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.dataProvider && this.dataProvider ? this.dataProvider.height : this.layoutDoc[HeightSym]();
         return (hgt === undefined && this.nativeWidth && this.nativeHeight) ? this.width * this.nativeHeight / this.nativeWidth : hgt;
     }
     @computed get dataProvider() { return this.props.dataProvider && this.props.dataProvider(this.props.Document) ? this.props.dataProvider(this.props.Document) : undefined; }
@@ -40,13 +40,13 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
 
     @computed get renderScriptDim() {
         if (this.Document.renderScript) {
-            let someView = Cast(this.props.Document.someView, Doc);
-            let minimap = Cast(this.props.Document.minimap, Doc);
+            const someView = Cast(this.props.Document.someView, Doc);
+            const minimap = Cast(this.props.Document.minimap, Doc);
             if (someView instanceof Doc && minimap instanceof Doc) {
-                let x = (NumCast(someView.panX) - NumCast(someView.width) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitX) - NumCast(minimap.fitW) / 2)) / NumCast(minimap.fitW) * NumCast(minimap.width) - NumCast(minimap.width) / 2;
-                let y = (NumCast(someView.panY) - NumCast(someView.height) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitY) - NumCast(minimap.fitH) / 2)) / NumCast(minimap.fitH) * NumCast(minimap.height) - NumCast(minimap.height) / 2;
-                let w = NumCast(someView.width) / NumCast(someView.scale) / NumCast(minimap.fitW) * NumCast(minimap.width);
-                let h = NumCast(someView.height) / NumCast(someView.scale) / NumCast(minimap.fitH) * NumCast(minimap.height);
+                const x = (NumCast(someView.panX) - NumCast(someView.width) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitX) - NumCast(minimap.fitW) / 2)) / NumCast(minimap.fitW) * NumCast(minimap.width) - NumCast(minimap.width) / 2;
+                const y = (NumCast(someView.panY) - NumCast(someView.height) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitY) - NumCast(minimap.fitH) / 2)) / NumCast(minimap.fitH) * NumCast(minimap.height) - NumCast(minimap.height) / 2;
+                const w = NumCast(someView.width) / NumCast(someView.scale) / NumCast(minimap.fitW) * NumCast(minimap.width);
+                const h = NumCast(someView.height) / NumCast(someView.scale) / NumCast(minimap.fitH) * NumCast(minimap.height);
                 return { x: x, y: y, width: w, height: h };
             }
         }
@@ -70,9 +70,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         .scale(1 / this.contentScaling())
 
     borderRounding = () => {
-        let ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined;
-        let ld = this.layoutDoc[StrCast(this.layoutDoc.layoutKey, "layout")] instanceof Doc ? this.layoutDoc[StrCast(this.layoutDoc.layoutKey, "layout")] as Doc : undefined;
-        let br = StrCast((ld || this.props.Document).borderRounding);
+        const ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined;
+        const ld = this.layoutDoc[StrCast(this.layoutDoc.layoutKey, "layout")] instanceof Doc ? this.layoutDoc[StrCast(this.layoutDoc.layoutKey, "layout")] as Doc : undefined;
+        const br = StrCast((ld || this.props.Document).borderRounding);
         return !br && ruleRounding ? ruleRounding : br;
     }
 
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index a5f96d2de..efc907f9b 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -47,7 +47,7 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
     private get nativeWidth() { return NumCast(this.layoutDoc!.nativeWidth, this.props.PanelWidth()); }
     private get nativeHeight() { return NumCast(this.layoutDoc!.nativeHeight, this.props.PanelHeight()); }
     private contentScaling = () => {
-        let wscale = this.props.PanelWidth() / (this.nativeWidth ? this.nativeWidth : this.props.PanelWidth());
+        const wscale = this.props.PanelWidth() / (this.nativeWidth ? this.nativeWidth : this.props.PanelWidth());
         if (wscale * this.nativeHeight > this.props.PanelHeight()) {
             return this.props.PanelHeight() / (this.nativeHeight ? this.nativeHeight : this.props.PanelHeight());
         }
@@ -59,7 +59,7 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (de.data instanceof DragManager.DocumentDragData) {
             this.props.childDocs && this.props.childDocs.map(otherdoc => {
-                let target = Doc.GetProto(otherdoc);
+                const target = Doc.GetProto(otherdoc);
                 target.layout = ComputedField.MakeFunction("this.image_data[0]");
                 target.layoutCustom = Doc.MakeDelegate(de.data.draggedDocuments[0]);
             });
diff --git a/src/client/views/nodes/DocuLinkBox.tsx b/src/client/views/nodes/DocuLinkBox.tsx
index d73407903..a22472e9e 100644
--- a/src/client/views/nodes/DocuLinkBox.tsx
+++ b/src/client/views/nodes/DocuLinkBox.tsx
@@ -36,12 +36,12 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
         (e.button === 0 && !e.ctrlKey) && e.stopPropagation();
     }
     onPointerMove = action((e: PointerEvent) => {
-        let cdiv = this._ref && this._ref.current && this._ref.current.parentElement;
+        const cdiv = this._ref && this._ref.current && this._ref.current.parentElement;
         if (cdiv && (Math.abs(e.clientX - this._downx) > 5 || Math.abs(e.clientY - this._downy) > 5)) {
-            let bounds = cdiv.getBoundingClientRect();
-            let pt = Utils.getNearestPointInPerimeter(bounds.left, bounds.top, bounds.width, bounds.height, e.clientX, e.clientY);
-            let separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY));
-            let dragdist = Math.sqrt((pt[0] - this._downx) * (pt[0] - this._downx) + (pt[1] - this._downy) * (pt[1] - this._downy));
+            const bounds = cdiv.getBoundingClientRect();
+            const pt = Utils.getNearestPointInPerimeter(bounds.left, bounds.top, bounds.width, bounds.height, e.clientX, e.clientY);
+            const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY));
+            const dragdist = Math.sqrt((pt[0] - this._downx) * (pt[0] - this._downx) + (pt[1] - this._downy) * (pt[1] - this._downy));
             if (separation > 100) {
                 DragLinksAsDocuments(this._ref.current!, pt[0], pt[1], Cast(this.props.Document[this.props.fieldKey], Doc) as Doc, this.props.Document); // Containging collection is the document, not a collection... hack.
                 document.removeEventListener("pointermove", this.onPointerMove);
@@ -67,14 +67,14 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
     }
 
     render() {
-        let anchorDoc = Cast(this.props.Document[this.props.fieldKey], Doc);
-        let hasAnchor = anchorDoc instanceof Doc && anchorDoc.type === DocumentType.PDFANNO;
-        let y = NumCast(this.props.Document[this.props.fieldKey + "_y"], 100);
-        let x = NumCast(this.props.Document[this.props.fieldKey + "_x"], 100);
-        let c = StrCast(this.props.Document.backgroundColor, "lightblue");
-        let anchor = this.props.fieldKey === "anchor1" ? "anchor2" : "anchor1";
-        let timecode = this.props.Document[anchor + "Timecode"];
-        let targetTitle = StrCast((this.props.Document[anchor]! as Doc).title) + (timecode !== undefined ? ":" + timecode : "");
+        const anchorDoc = Cast(this.props.Document[this.props.fieldKey], Doc);
+        const hasAnchor = anchorDoc instanceof Doc && anchorDoc.type === DocumentType.PDFANNO;
+        const y = NumCast(this.props.Document[this.props.fieldKey + "_y"], 100);
+        const x = NumCast(this.props.Document[this.props.fieldKey + "_x"], 100);
+        const c = StrCast(this.props.Document.backgroundColor, "lightblue");
+        const anchor = this.props.fieldKey === "anchor1" ? "anchor2" : "anchor1";
+        const timecode = this.props.Document[anchor + "Timecode"];
+        const targetTitle = StrCast((this.props.Document[anchor]! as Doc).title) + (timecode !== undefined ? ":" + timecode : "");
         return <div className="docuLinkBox-cont" onPointerDown={this.onPointerDown} onClick={this.onClick} title={targetTitle}
             ref={this._ref} style={{
                 background: c, left: `calc(${x}% - 12.5px)`, top: `calc(${y}% - 12.5px)`,
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index b9b84d5ce..13fd3cde1 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -83,7 +83,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     }
 
     CreateBindings(): JsxBindings {
-        let list = {
+        const list = {
             ...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit,
             Document: this.layoutDoc,
             DataDoc: this.dataDoc,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 057b4eecd..63ce6233c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -122,7 +122,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     startDragging(x: number, y: number, dropAction: dropActionType, applyAsTemplate?: boolean) {
         if (this._mainCont.current) {
-            let dragData = new DragManager.DocumentDragData([this.props.Document]);
+            const dragData = new DragManager.DocumentDragData([this.props.Document]);
             const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0);
             dragData.offset = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top);
             dragData.dropAction = dropAction;
@@ -143,7 +143,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             e.stopPropagation();
             let preventDefault = true;
             if (this._doubleTap && this.props.renderDepth && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
-                let fullScreenAlias = Doc.MakeAlias(this.props.Document);
+                const fullScreenAlias = Doc.MakeAlias(this.props.Document);
                 if (StrCast(fullScreenAlias.layoutKey) !== "layoutCustom" && fullScreenAlias.layoutCustom !== undefined) {
                     fullScreenAlias.layoutKey = "layoutCustom";
                 }
@@ -166,9 +166,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
 
     buttonClick = async (altKey: boolean, ctrlKey: boolean) => {
-        let maximizedDocs = await DocListCastAsync(this.Document.maximizedDocs);
-        let summarizedDocs = await DocListCastAsync(this.Document.summarizedDocs);
-        let linkDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document);
+        const maximizedDocs = await DocListCastAsync(this.Document.maximizedDocs);
+        const summarizedDocs = await DocListCastAsync(this.Document.summarizedDocs);
+        const linkDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document);
         let expandedDocs: Doc[] = [];
         expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs;
         expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs;
@@ -179,7 +179,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             maxLocation = this.Document.maximizeLocation = (!ctrlKey ? !altKey ? maxLocation : (maxLocation !== "inPlace" ? "inPlace" : "onRight") : (maxLocation !== "inPlace" ? "inPlace" : "inTab"));
             if (maxLocation === "inPlace") {
                 expandedDocs.forEach(maxDoc => this.props.addDocument && this.props.addDocument(maxDoc));
-                let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.layoutDoc.width) / 2, NumCast(this.layoutDoc.height) / 2);
+                const scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.layoutDoc.width) / 2, NumCast(this.layoutDoc.height) / 2);
                 DocumentManager.Instance.animateBetweenPoint(scrpt, expandedDocs);
             } else {
                 expandedDocs.forEach(maxDoc => (!this.props.addDocTab(maxDoc, undefined, "close") && this.props.addDocTab(maxDoc, undefined, maxLocation)));
@@ -278,7 +278,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             fieldTemplate.heading = 1;
             fieldTemplate.autoHeight = true;
 
-            let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: doc.title + "_layout", width: width + 20, height: Math.max(100, height + 45) });
+            const docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: doc.title + "_layout", width: width + 20, height: Math.max(100, height + 45) });
 
             Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate), true);
             Doc.ApplyTemplateTo(docTemplate, dataDoc || doc, "layoutCustom", undefined);
@@ -324,10 +324,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @action
     onDrop = (e: React.DragEvent) => {
-        let text = e.dataTransfer.getData("text/plain");
+        const text = e.dataTransfer.getData("text/plain");
         if (!e.isDefaultPrevented() && text && text.startsWith("<div")) {
-            let oldLayout = this.Document.layout || "";
-            let layout = text.replace("{layout}", oldLayout);
+            const oldLayout = this.Document.layout || "";
+            const layout = text.replace("{layout}", oldLayout);
             this.Document.layout = layout;
             e.stopPropagation();
             e.preventDefault();
@@ -348,11 +348,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @undoBatch
     @action
     makeIntoPortal = async () => {
-        let anchors = await Promise.all(DocListCast(this.Document.links).map(async (d: Doc) => Cast(d.anchor2, Doc)));
+        const anchors = await Promise.all(DocListCast(this.Document.links).map(async (d: Doc) => Cast(d.anchor2, Doc)));
         if (!anchors.find(anchor2 => anchor2 && anchor2.title === this.Document.title + ".portal" ? true : false)) {
-            let portalID = (this.Document.title + ".portal").replace(/^-/, "").replace(/\([0-9]*\)$/, "");
+            const portalID = (this.Document.title + ".portal").replace(/^-/, "").replace(/\([0-9]*\)$/, "");
             DocServer.GetRefField(portalID).then(existingPortal => {
-                let portal = existingPortal instanceof Doc ? existingPortal : Docs.Create.FreeformDocument([], { width: (this.layoutDoc.width || 0) + 10, height: this.layoutDoc.height || 0, title: portalID });
+                const portal = existingPortal instanceof Doc ? existingPortal : Docs.Create.FreeformDocument([], { width: (this.layoutDoc.width || 0) + 10, height: this.layoutDoc.height || 0, title: portalID });
                 DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: portal }, portalID, "portal link");
                 this.Document.isButton = true;
             });
@@ -400,7 +400,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         e.preventDefault();
 
         const cm = ContextMenu.Instance;
-        let subitems: ContextMenuProps[] = [];
+        const subitems: ContextMenuProps[] = [];
         subitems.push({ description: "Open Full Screen", event: () => CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this), icon: "desktop" });
         subitems.push({ description: "Open Tab        ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab"), icon: "folder" });
         subitems.push({ description: "Open Right      ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "onRight"), icon: "caret-square-right" });
@@ -410,8 +410,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" });
 
 
-        let existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
-        let onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
+        const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
+        const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
         onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
         onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript("toggleDetail(this)"), icon: "window-restore" });
         onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
@@ -425,7 +425,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         });
         !existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
 
-        let funcs: ContextMenuProps[] = [];
+        const funcs: ContextMenuProps[] = [];
         if (this.Document.onDragStart) {
             funcs.push({ description: "Drag an Alias", icon: "edit", event: () => this.Document.dragFactory && (this.Document.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
             funcs.push({ description: "Drag a Copy", icon: "edit", event: () => this.Document.dragFactory && (this.Document.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) });
@@ -433,8 +433,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             ContextMenu.Instance.addItem({ description: "OnDrag...", subitems: funcs, icon: "asterisk" });
         }
 
-        let existing = ContextMenu.Instance.findByDescription("Layout...");
-        let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
+        const existing = ContextMenu.Instance.findByDescription("Layout...");
+        const layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
         layoutItems.push({ description: this.Document.isBackground ? "As Foreground" : "As Background", event: this.makeBackground, icon: this.Document.lockedPosition ? "unlock" : "lock" });
         if (this.props.DataDoc) {
             layoutItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc!), icon: "concierge-bell" });
@@ -453,8 +453,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
         !existing && cm.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" });
 
-        let more = ContextMenu.Instance.findByDescription("More...");
-        let moreItems: ContextMenuProps[] = more && "subitems" in more ? more.subitems : [];
+        const more = ContextMenu.Instance.findByDescription("More...");
+        const moreItems: ContextMenuProps[] = more && "subitems" in more ? more.subitems : [];
 
         if (!ClientUtils.RELEASE) {
             // let copies: ContextMenuProps[] = [];
@@ -489,7 +489,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
         runInAction(() => {
             if (!ClientUtils.RELEASE) {
-                let setWriteMode = (mode: DocServer.WriteMode) => {
+                const setWriteMode = (mode: DocServer.WriteMode) => {
                     DocServer.AclsMode = mode;
                     const mode1 = mode;
                     const mode2 = mode === DocServer.WriteMode.Default ? mode : DocServer.WriteMode.Playground;
@@ -503,7 +503,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     DocServer.setFieldWriteMode("scale", mode2);
                     DocServer.setFieldWriteMode("viewType", mode2);
                 };
-                let aclsMenu: ContextMenuProps[] = [];
+                const aclsMenu: ContextMenuProps[] = [];
                 aclsMenu.push({ description: "Default (write/read all)", event: () => setWriteMode(DocServer.WriteMode.Default), icon: DocServer.AclsMode === DocServer.WriteMode.Default ? "check" : "exclamation" });
                 aclsMenu.push({ description: "Playground (write own/no read)", event: () => setWriteMode(DocServer.WriteMode.Playground), icon: DocServer.AclsMode === DocServer.WriteMode.Playground ? "check" : "exclamation" });
                 aclsMenu.push({ description: "Live Playground (write own/read others)", event: () => setWriteMode(DocServer.WriteMode.LivePlayground), icon: DocServer.AclsMode === DocServer.WriteMode.LivePlayground ? "check" : "exclamation" });
@@ -539,8 +539,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); };
 
     chromeHeight = () => {
-        let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined;
-        let showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.Document.showTitle);
+        const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined;
+        const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.Document.showTitle);
         return (showTitle ? 25 : 0) + 1;
     }
 
@@ -585,8 +585,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     // if it's a tempoarl link (currently just for Audio), then the audioBox will display the anchor and we don't want to display it here.
     // would be good to generalize this some way.
     isNonTemporalLink = (linkDoc: Doc) => {
-        let anchor = Cast(Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? linkDoc.anchor1 : linkDoc.anchor2, Doc) as Doc;
-        let ept = Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? linkDoc.anchor1Timecode : linkDoc.anchor2Timecode;
+        const anchor = Cast(Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? linkDoc.anchor1 : linkDoc.anchor2, Doc) as Doc;
+        const ept = Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? linkDoc.anchor1Timecode : linkDoc.anchor2Timecode;
         return anchor.type === DocumentType.AUDIO && NumCast(ept) ? false : true;
     }
 
@@ -651,14 +651,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @action
     handle2PointersMove = (e: TouchEvent) => {
-        let pt1 = e.targetTouches.item(0);
-        let pt2 = e.targetTouches.item(1);
+        const pt1 = e.targetTouches.item(0);
+        const pt2 = e.targetTouches.item(1);
         if (pt1 && pt2 && this.prevPoints.has(pt1.identifier) && this.prevPoints.has(pt2.identifier)) {
-            let oldPoint1 = this.prevPoints.get(pt1.identifier);
-            let oldPoint2 = this.prevPoints.get(pt2.identifier);
-            let pinching = InteractionUtils.Pinning(pt1, pt2, oldPoint1!, oldPoint2!);
+            const oldPoint1 = this.prevPoints.get(pt1.identifier);
+            const oldPoint2 = this.prevPoints.get(pt2.identifier);
+            const pinching = InteractionUtils.Pinning(pt1, pt2, oldPoint1!, oldPoint2!);
             if (pinching !== 0) {
-                let newWidth = Math.max(Math.abs(oldPoint1!.clientX - oldPoint2!.clientX), Math.abs(pt1.clientX - pt2.clientX));
+                const newWidth = Math.max(Math.abs(oldPoint1!.clientX - oldPoint2!.clientX), Math.abs(pt1.clientX - pt2.clientX));
                 this.props.Document.width = newWidth;
             }
         }
@@ -679,12 +679,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const localScale = fullDegree;
 
         const animDims = this.Document.animateToDimensions ? Array.from(this.Document.animateToDimensions) : undefined;
-        let animheight = animDims ? animDims[1] : "100%";
-        let animwidth = animDims ? animDims[0] : "100%";
+        const animheight = animDims ? animDims[1] : "100%";
+        const animwidth = animDims ? animDims[0] : "100%";
 
         const highlightColors = ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"];
         const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"];
-        let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc.viewType !== CollectionViewType.Linear;
+        const highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc.viewType !== CollectionViewType.Linear;
         return <div className={`documentView-node${this.topMost ? "-topmost" : ""}`} ref={this._mainCont}
             onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
             onPointerEnter={e => Doc.BrushDoc(this.props.Document)} onPointerLeave={e => Doc.UnBrushDoc(this.props.Document)}
diff --git a/src/client/views/nodes/FaceRectangle.tsx b/src/client/views/nodes/FaceRectangle.tsx
index 887efc0d5..20afa4565 100644
--- a/src/client/views/nodes/FaceRectangle.tsx
+++ b/src/client/views/nodes/FaceRectangle.tsx
@@ -12,7 +12,7 @@ export default class FaceRectangle extends React.Component<{ rectangle: Rectangl
     }
 
     render() {
-        let rectangle = this.props.rectangle;
+        const rectangle = this.props.rectangle;
         return (
             <div
                 style={{
diff --git a/src/client/views/nodes/FaceRectangles.tsx b/src/client/views/nodes/FaceRectangles.tsx
index acf1aced3..3c7f1f206 100644
--- a/src/client/views/nodes/FaceRectangles.tsx
+++ b/src/client/views/nodes/FaceRectangles.tsx
@@ -20,10 +20,10 @@ export interface RectangleTemplate {
 export default class FaceRectangles extends React.Component<FaceRectanglesProps> {
 
     render() {
-        let faces = DocListCast(this.props.document.faces);
-        let templates: RectangleTemplate[] = faces.map(faceDoc => {
-            let rectangle = Cast(faceDoc.faceRectangle, Doc) as Doc;
-            let style = {
+        const faces = DocListCast(this.props.document.faces);
+        const templates: RectangleTemplate[] = faces.map(faceDoc => {
+            const rectangle = Cast(faceDoc.faceRectangle, Doc) as Doc;
+            const style = {
                 top: NumCast(rectangle.top),
                 left: NumCast(rectangle.left),
                 width: NumCast(rectangle.width),
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index 960b55e3e..2433251b3 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -25,8 +25,8 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
         this._backgroundReaction = reaction(() => this.props.Document.backgroundColor,
             () => {
                 if (this._ref && this._ref.current) {
-                    let col = Utils.fromRGBAstr(getComputedStyle(this._ref.current).backgroundColor);
-                    let colsum = (col.r + col.g + col.b);
+                    const col = Utils.fromRGBAstr(getComputedStyle(this._ref.current).backgroundColor);
+                    const colsum = (col.r + col.g + col.b);
                     if (colsum / col.a > 600 || col.a < 0.25) runInAction(() => this._foregroundColor = "black");
                     else if (colsum / col.a <= 600 || col.a >= .25) runInAction(() => this._foregroundColor = "white");
                 }
@@ -36,8 +36,8 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
         this._backgroundReaction && this._backgroundReaction();
     }
     render() {
-        let referenceDoc = (this.props.Document.dragFactory instanceof Doc ? this.props.Document.dragFactory : this.props.Document);
-        let referenceLayout = Doc.Layout(referenceDoc);
+        const referenceDoc = (this.props.Document.dragFactory instanceof Doc ? this.props.Document.dragFactory : this.props.Document);
+        const referenceLayout = Doc.Layout(referenceDoc);
         return <button className="fontIconBox-outerDiv" title={StrCast(this.props.Document.title)} ref={this._ref}
             style={{
                 background: StrCast(referenceLayout.backgroundColor),
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index a5530f32d..57321de49 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1,6 +1,6 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faEdit, faSmile, faTextHeight, faUpload } from '@fortawesome/free-solid-svg-icons';
-import _ from "lodash";
+import { isEqual } from "lodash";
 import { action, computed, IReactionDisposer, Lambda, observable, reaction, runInAction, trace } from "mobx";
 import { observer } from "mobx-react";
 import { baseKeymap } from "prosemirror-commands";
@@ -103,8 +103,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         return html.indexOf("data-pm-slice") !== -1;
     }
     public static GetHref(html: string): string {
-        let parser = new DOMParser();
-        let parsedHtml = parser.parseFromString(html, 'text/html');
+        const parser = new DOMParser();
+        const parsedHtml = parser.parseFromString(html, 'text/html');
         if (parsedHtml.body.childNodes.length === 1 && parsedHtml.body.childNodes[0].childNodes.length === 1 &&
             (parsedHtml.body.childNodes[0].childNodes[0] as any).href) {
             return (parsedHtml.body.childNodes[0].childNodes[0] as any).href;
@@ -126,12 +126,12 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
 
     @undoBatch
     public setFontColor(color: string) {
-        let view = this._editorView!;
+        const view = this._editorView!;
         if (view.state.selection.from === view.state.selection.to) return false;
         if (view.state.selection.to - view.state.selection.from > view.state.doc.nodeSize - 3) {
             this.layoutDoc.color = color;
         }
-        let colorMark = view.state.schema.mark(view.state.schema.marks.pFontColor, { color: color });
+        const colorMark = view.state.schema.mark(view.state.schema.marks.pFontColor, { color: color });
         view.dispatch(view.state.tr.addMark(view.state.selection.from, view.state.selection.to, colorMark));
         return true;
     }
@@ -147,9 +147,9 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
 
     doLinkOnDeselect() {
         Array.from(this.linkOnDeselect.entries()).map(entry => {
-            let key = entry[0];
-            let value = entry[1];
-            let id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
+            const key = entry[0];
+            const value = entry[1];
+            const id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
             DocServer.GetRefField(value).then(doc => {
                 DocServer.GetRefField(id).then(linkDoc => {
                     this.dataDoc[key] = doc || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value);
@@ -164,24 +164,24 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
 
     dispatchTransaction = (tx: Transaction) => {
         if (this._editorView) {
-            let metadata = tx.selection.$from.marks().find((m: Mark) => m.type === schema.marks.metadata);
+            const metadata = tx.selection.$from.marks().find((m: Mark) => m.type === schema.marks.metadata);
             if (metadata) {
-                let range = tx.selection.$from.blockRange(tx.selection.$to);
+                const range = tx.selection.$from.blockRange(tx.selection.$to);
                 let text = range ? tx.doc.textBetween(range.start, range.end) : "";
                 let textEndSelection = tx.selection.to;
                 for (; textEndSelection < range!.end && text[textEndSelection - range!.start] !== " "; textEndSelection++) { }
                 text = text.substr(0, textEndSelection - range!.start);
                 text = text.split(" ")[text.split(" ").length - 1];
-                let split = text.split("::");
+                const split = text.split("::");
                 if (split.length > 1 && split[1]) {
-                    let key = split[0];
-                    let value = split[split.length - 1];
+                    const key = split[0];
+                    const value = split[split.length - 1];
                     this.linkOnDeselect.set(key, value);
 
-                    let id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
+                    const id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
                     const link = this._editorView.state.schema.marks.link.create({ href: `http://localhost:1050/doc/${id}`, location: "onRight", title: value });
                     const mval = this._editorView.state.schema.marks.metadataVal.create();
-                    let offset = (tx.selection.to === range!.end - 1 ? -1 : 0);
+                    const offset = (tx.selection.to === range!.end - 1 ? -1 : 0);
                     tx = tx.addMark(textEndSelection - value.length + offset, textEndSelection, link).addMark(textEndSelection - value.length + offset, textEndSelection, mval);
                     this.dataDoc[key] = value;
                 }
@@ -189,7 +189,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             const state = this._editorView.state.apply(tx);
             this._editorView.updateState(state);
 
-            let tsel = this._editorView.state.selection.$from;
+            const tsel = this._editorView.state.selection.$from;
             tsel.marks().filter(m => m.type === this._editorView!.state.schema.marks.user_mark).map(m => AudioBox.SetScrubTime(Math.max(0, m.attrs.modified * 5000 - 1000)));
             this._applyingChange = true;
             this.extensionDoc && (this.extensionDoc.lastModified = new DateField(new Date(Date.now())));
@@ -202,8 +202,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
 
     updateTitle = () => {
         if (StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.Document.customTitle) {
-            let str = this._editorView.state.doc.textContent;
-            let titlestr = str.substr(0, Math.min(40, str.length));
+            const str = this._editorView.state.doc.textContent;
+            const titlestr = str.substr(0, Math.min(40, str.length));
             this.dataDoc.title = "-" + titlestr + (str.length > 40 ? "..." : "");
         }
     }
@@ -212,11 +212,11 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         if (this._editorView && (this._editorView as any).docView) {
             const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
             const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true });
-            let res = terms.map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term));
+            const res = terms.map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term));
             let tr = this._editorView.state.tr;
-            let flattened: TextSelection[] = [];
+            const flattened: TextSelection[] = [];
             res.map(r => r.map(h => flattened.push(h)));
-            let lastSel = Math.min(flattened.length - 1, this._searchIndex);
+            const lastSel = Math.min(flattened.length - 1, this._searchIndex);
             flattened.forEach((h: TextSelection, ind: number) => tr = tr.addMark(h.from, h.to, ind === lastSel ? activeMark : mark));
             this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
             this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(flattened[lastSel].from), tr.doc.resolve(flattened[lastSel].to))).scrollIntoView());
@@ -227,13 +227,13 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         if (this._editorView && (this._editorView as any).docView) {
             const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
             const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true });
-            let end = this._editorView.state.doc.nodeSize - 2;
+            const end = this._editorView.state.doc.nodeSize - 2;
             this._editorView.dispatch(this._editorView.state.tr.removeMark(0, end, mark).removeMark(0, end, activeMark));
         }
     }
     setAnnotation = (start: number, end: number, mark: Mark, opened: boolean, keep: boolean = false) => {
-        let view = this._editorView!;
-        let nmark = view.state.schema.marks.user_mark.create({ ...mark.attrs, userid: keep ? Doc.CurrentUserEmail : mark.attrs.userid, opened: opened });
+        const view = this._editorView!;
+        const nmark = view.state.schema.marks.user_mark.create({ ...mark.attrs, userid: keep ? Doc.CurrentUserEmail : mark.attrs.userid, opened: opened });
         view.dispatch(view.state.tr.removeMark(start, end, nmark).addMark(start, end, nmark));
     }
     protected createDropTarget = (ele: HTMLDivElement) => {
@@ -266,16 +266,16 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                 e.stopPropagation();
                 // embed document when dragging with a userDropAction or an embedDoc flag set
             } else if (de.data.userDropAction || de.data.embedDoc) {
-                let target = de.data.droppedDocuments[0];
+                const target = de.data.droppedDocuments[0];
                 // const link = DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: target }, "Embedded Doc:" + target.title);
                 // if (link) {
                 target.fitToBox = true;
-                let node = schema.nodes.dashDoc.create({
+                const node = schema.nodes.dashDoc.create({
                     width: target[WidthSym](), height: target[HeightSym](),
                     title: "dashDoc", docid: target[Id],
                     float: "right"
                 });
-                let view = this._editorView!;
+                const view = this._editorView!;
                 view.dispatch(view.state.tr.insert(view.posAtCoords({ left: de.x, top: de.y })!.pos, node));
                 this.tryUpdateHeight();
                 e.stopPropagation();
@@ -292,7 +292,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         if (node.isBlock) {
             // tslint:disable-next-line: prefer-for-of
             for (let i = 0; i < (context.content as any).content.length; i++) {
-                let result = this.getNodeEndpoints((context.content as any).content[i], node);
+                const result = this.getNodeEndpoints((context.content as any).content[i], node);
                 if (result) {
                     return {
                         from: result.from + offset + (context.type.name === "doc" ? 0 : 1),
@@ -313,9 +313,10 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         let ret: TextSelection[] = [];
 
         if (node.isTextblock) {
-            let index = 0, foundAt, ep = this.getNodeEndpoints(pm.state.doc, node);
+            let index = 0, foundAt;
+            const ep = this.getNodeEndpoints(pm.state.doc, node);
             while (ep && (foundAt = node.textContent.slice(index).search(RegExp(find, "i"))) > -1) {
-                let sel = new TextSelection(pm.state.doc.resolve(ep.from + index + foundAt + 1), pm.state.doc.resolve(ep.from + index + foundAt + find.length + 1));
+                const sel = new TextSelection(pm.state.doc.resolve(ep.from + index + foundAt + 1), pm.state.doc.resolve(ep.from + index + foundAt + find.length + 1));
                 ret.push(sel);
                 index = index + foundAt + find.length;
             }
@@ -348,13 +349,13 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         }
         if (FormattedTextBox._highlights.indexOf("By Recent Minute") !== -1) {
             addStyleSheetRule(FormattedTextBox._userStyleSheet, "userMark-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" });
-            let min = Math.round(Date.now() / 1000 / 60);
+            const min = Math.round(Date.now() / 1000 / 60);
             numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, "userMark-min-" + (min - i), { opacity: ((10 - i - 1) / 10).toString() }));
             setTimeout(() => this.updateHighlights());
         }
         if (FormattedTextBox._highlights.indexOf("By Recent Hour") !== -1) {
             addStyleSheetRule(FormattedTextBox._userStyleSheet, "userMark-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" });
-            let hr = Math.round(Date.now() / 1000 / 60 / 60);
+            const hr = Math.round(Date.now() / 1000 / 60 / 60);
             numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, "userMark-hr-" + (hr - i), { opacity: ((10 - i - 1) / 10).toString() }));
         }
     }
@@ -362,7 +363,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     toggleSidebar = () => this.props.Document.sidebarWidthPercent = StrCast(this.props.Document.sidebarWidthPercent, "0%") === "0%" ? "25%" : "0%";
 
     specificContextMenu = (e: React.MouseEvent): void => {
-        let funcs: ContextMenuProps[] = [];
+        const funcs: ContextMenuProps[] = [];
         funcs.push({ description: "Toggle Sidebar", event: () => { e.stopPropagation(); this.toggleSidebar(); }, icon: "expand-arrows-alt" });
         funcs.push({ description: "Record Bullet", event: () => { e.stopPropagation(); this.recordBullet(); }, icon: "expand-arrows-alt" });
         ["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option =>
@@ -403,8 +404,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     }
 
     recordBullet = async () => {
-        let completedCue = "end session";
-        let results = await DictationManager.Controls.listen({
+        const completedCue = "end session";
+        const results = await DictationManager.Controls.listen({
             interimHandler: this.setCurrentBulletContent,
             continuous: { indefinite: false },
             terminators: [completedCue, "bullet", "next"]
@@ -420,20 +421,20 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     setCurrentBulletContent = (value: string) => {
         if (this._editorView) {
             let state = this._editorView.state;
-            let from = state.selection.from;
-            let to = state.selection.to;
+            const from = state.selection.from;
+            const to = state.selection.to;
             this._editorView.dispatch(state.tr.insertText(value, from, to));
             state = this._editorView.state;
-            let updated = TextSelection.create(state.doc, from, from + value.length);
+            const updated = TextSelection.create(state.doc, from, from + value.length);
             this._editorView.dispatch(state.tr.setSelection(updated));
         }
     }
 
     nextBullet = (pos: number) => {
         if (this._editorView) {
-            let frag = Fragment.fromArray(this.newListItems(2));
+            const frag = Fragment.fromArray(this.newListItems(2));
             if (this._editorView.state.doc.resolve(pos).depth >= 2) {
-                let slice = new Slice(frag, 2, 2);
+                const slice = new Slice(frag, 2, 2);
                 let state = this._editorView.state;
                 this._editorView.dispatch(state.tr.step(new ReplaceStep(pos, pos, slice)));
                 pos += 4;
@@ -471,8 +472,10 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     }
 
     componentDidMount() {
-        this.pullFromGoogleDoc(this.checkState);
-        this.dataDoc[GoogleRef] && this.dataDoc.unchanged && runInAction(() => DocumentDecorations.Instance.isAnimatingFetch = true);
+        if (DocumentButtonBar.Instance) {
+            this.pullFromGoogleDoc(this.checkState);
+            this.dataDoc[GoogleRef] && this.dataDoc.unchanged && runInAction(() => DocumentButtonBar.Instance.isAnimatingFetch = true);
+        }
 
         this._reactionDisposer = reaction(
             () => {
@@ -481,7 +484,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             },
             incomingValue => {
                 if (this._editorView && !this._applyingChange) {
-                    let updatedState = JSON.parse(incomingValue);
+                    const updatedState = JSON.parse(incomingValue);
                     this._editorView.updateState(EditorState.fromJSON(this.config, updatedState));
                     this.tryUpdateHeight();
                 }
@@ -493,7 +496,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             () => {
                 if (!DocumentButtonBar.hasPulledHack) {
                     DocumentButtonBar.hasPulledHack = true;
-                    let unchanged = this.dataDoc.unchanged;
+                    const unchanged = this.dataDoc.unchanged;
                     this.pullFromGoogleDoc(unchanged ? this.checkState : this.updateState);
                 }
             }
@@ -514,7 +517,6 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             () => this.tryUpdateHeight()
         );
 
-
         this.setupEditor(this.config, this.dataDoc, this.props.fieldKey);
 
         this._searchReactionDisposer = reaction(() => {
@@ -528,10 +530,9 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             }
         }, { fireImmediately: true });
 
-
         this._rulesReactionDisposer = reaction(() => {
-            let ruleProvider = this.props.ruleProvider;
-            let heading = NumCast(this.layoutDoc.heading);
+            const ruleProvider = this.props.ruleProvider;
+            const heading = NumCast(this.layoutDoc.heading);
             if (ruleProvider instanceof Doc) {
                 return {
                     align: StrCast(ruleProvider["ruleAlign_" + heading], ""),
@@ -547,7 +548,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                 rules && setTimeout(() => {
                     const view = this._editorView!;
                     if (this._proseRef) {
-                        let n = new NodeSelection(view.state.doc.resolve(0));
+                        const n = new NodeSelection(view.state.doc.resolve(0));
                         if (this._editorView!.state.doc.textContent === "") {
                             view.dispatch(view.state.tr.setSelection(new TextSelection(view.state.doc.resolve(0), view.state.doc.resolve(2))).
                                 replaceSelectionWith(this._editorView!.state.schema.nodes.paragraph.create({ align: rules.align }), true));
@@ -562,10 +563,10 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         this._scrollToRegionReactionDisposer = reaction(
             () => StrCast(this.layoutDoc.scrollToLinkID),
             async (scrollToLinkID) => {
-                let findLinkFrag = (frag: Fragment, editor: EditorView) => {
+                const findLinkFrag = (frag: Fragment, editor: EditorView) => {
                     const nodes: Node[] = [];
                     frag.forEach((node, index) => {
-                        let examinedNode = findLinkNode(node, editor);
+                        const examinedNode = findLinkNode(node, editor);
                         if (examinedNode && examinedNode.textContent) {
                             nodes.push(examinedNode);
                             start += index;
@@ -573,7 +574,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                     });
                     return { frag: Fragment.fromArray(nodes), start: start };
                 };
-                let findLinkNode = (node: Node, editor: EditorView) => {
+                const findLinkNode = (node: Node, editor: EditorView) => {
                     if (!node.isText) {
                         const content = findLinkFrag(node.content, editor);
                         return node.copy(content.frag);
@@ -585,8 +586,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
 
                 let start = -1;
                 if (this._editorView && scrollToLinkID) {
-                    let editor = this._editorView;
-                    let ret = findLinkFrag(editor.state.doc.content, editor);
+                    const editor = this._editorView;
+                    const ret = findLinkFrag(editor.state.doc.content, editor);
 
                     if (ret.frag.size > 2 && ret.start >= 0) {
                         let selection = TextSelection.near(editor.state.doc.resolve(ret.start)); // default to near the start
@@ -610,28 +611,28 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
 
     pushToGoogleDoc = async () => {
         this.pullFromGoogleDoc(async (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => {
-            let modes = GoogleApiClientUtils.Docs.WriteMode;
+            const modes = GoogleApiClientUtils.Docs.WriteMode;
             let mode = modes.Replace;
             let reference: Opt<GoogleApiClientUtils.Docs.Reference> = Cast(this.dataDoc[GoogleRef], "string");
             if (!reference) {
                 mode = modes.Insert;
                 reference = { title: StrCast(this.dataDoc.title) };
             }
-            let redo = async () => {
+            const redo = async () => {
                 if (this._editorView && reference) {
-                    let content = await RichTextUtils.GoogleDocs.Export(this._editorView.state);
-                    let response = await GoogleApiClientUtils.Docs.write({ reference, content, mode });
+                    const content = await RichTextUtils.GoogleDocs.Export(this._editorView.state);
+                    const response = await GoogleApiClientUtils.Docs.write({ reference, content, mode });
                     response && (this.dataDoc[GoogleRef] = response.documentId);
-                    let pushSuccess = response !== undefined && !("errors" in response);
+                    const pushSuccess = response !== undefined && !("errors" in response);
                     dataDoc.unchanged = pushSuccess;
                     DocumentButtonBar.Instance.startPushOutcome(pushSuccess);
                 }
             };
-            let undo = () => {
+            const undo = () => {
                 if (!exportState) {
                     return;
                 }
-                let content: GoogleApiClientUtils.Docs.Content = {
+                const content: GoogleApiClientUtils.Docs.Content = {
                     text: exportState.text,
                     requests: []
                 };
@@ -645,8 +646,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     }
 
     pullFromGoogleDoc = async (handler: PullHandler) => {
-        let dataDoc = this.dataDoc;
-        let documentId = StrCast(dataDoc[GoogleRef]);
+        const dataDoc = this.dataDoc;
+        const documentId = StrCast(dataDoc[GoogleRef]);
         let exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>;
         if (documentId) {
             exportState = await RichTextUtils.GoogleDocs.Import(documentId, dataDoc);
@@ -661,8 +662,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             dataDoc.data = new RichTextField(JSON.stringify(exportState.state.toJSON()));
             setTimeout(() => {
                 if (this._editorView) {
-                    let state = this._editorView.state;
-                    let end = state.doc.content.size - 1;
+                    const state = this._editorView.state;
+                    const end = state.doc.content.size - 1;
                     this._editorView.dispatch(state.tr.setSelection(TextSelection.create(state.doc, end, end)));
                 }
             }, 0);
@@ -677,9 +678,9 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
 
     checkState = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => {
         if (exportState && this._editorView) {
-            let equalContent = _.isEqual(this._editorView.state.doc, exportState.state.doc);
-            let equalTitles = dataDoc.title === exportState.title;
-            let unchanged = equalContent && equalTitles;
+            const equalContent = isEqual(this._editorView.state.doc, exportState.state.doc);
+            const equalTitles = dataDoc.title === exportState.title;
+            const unchanged = equalContent && equalTitles;
             dataDoc.unchanged = unchanged;
             DocumentButtonBar.Instance.setPullState(unchanged);
         }
@@ -707,7 +708,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     }
 
     handlePaste = (view: EditorView, event: Event, slice: Slice): boolean => {
-        let cbe = event as ClipboardEvent;
+        const cbe = event as ClipboardEvent;
         const pdfDocId = cbe.clipboardData && cbe.clipboardData.getData("dash/pdfOrigin");
         const pdfRegionId = cbe.clipboardData && cbe.clipboardData.getData("dash/pdfRegion");
         if (pdfDocId && pdfRegionId) {
@@ -717,18 +718,18 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                         setTimeout(async () => {
                             const extension = Doc.fieldExtensionDoc(pdfDoc, "data");
                             if (extension) {
-                                let targetAnnotations = await DocListCastAsync(extension.annotations);// bcz: NO... this assumes the pdf is using its 'data' field.  need to have the PDF's view handle updating its own annotations
+                                const targetAnnotations = await DocListCastAsync(extension.annotations);// bcz: NO... this assumes the pdf is using its 'data' field.  need to have the PDF's view handle updating its own annotations
                                 targetAnnotations && targetAnnotations.push(pdfRegion);
                             }
                         });
 
-                        let link = DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: pdfRegion, ctx: pdfDoc }, "note on " + pdfDoc.title, "pasted PDF link");
+                        const link = DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: pdfRegion, ctx: pdfDoc }, "note on " + pdfDoc.title, "pasted PDF link");
                         if (link) {
                             cbe.clipboardData!.setData("dash/linkDoc", link[Id]);
-                            let linkId = link[Id];
-                            let frag = addMarkToFrag(slice.content, (node: Node) => addLinkMark(node, StrCast(pdfDoc.title), linkId));
+                            const linkId = link[Id];
+                            const frag = addMarkToFrag(slice.content, (node: Node) => addLinkMark(node, StrCast(pdfDoc.title), linkId));
                             slice = new Slice(frag, slice.openStart, slice.openEnd);
-                            var tr = view.state.tr.replaceSelection(slice);
+                            const tr = view.state.tr.replaceSelection(slice);
                             view.dispatch(tr.scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste"));
                         }
                     }
@@ -758,11 +759,11 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     }
 
     private setupEditor(config: any, doc: Doc, fieldKey: string) {
-        let field = doc ? Cast(doc[fieldKey], RichTextField) : undefined;
+        const field = doc ? Cast(doc[fieldKey], RichTextField) : undefined;
         let startup = StrCast(doc.documentText);
         startup = startup.startsWith("@@@") ? startup.replace("@@@", "") : "";
         if (!field && doc) {
-            let text = StrCast(doc[fieldKey]);
+            const text = StrCast(doc[fieldKey]);
             if (text) {
                 startup = text;
             } else if (Cast(doc[fieldKey], "number")) {
@@ -770,16 +771,16 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             }
         }
         if (this._proseRef) {
-            let self = this;
+            const self = this;
             this._editorView && this._editorView.destroy();
             this._editorView = new EditorView(this._proseRef, {
                 state: field && field.Data ? EditorState.fromJSON(config, JSON.parse(field.Data)) : EditorState.create(config),
                 handleScrollToSelection: (editorView) => {
-                    let ref = editorView.domAtPos(editorView.state.selection.from);
+                    const ref = editorView.domAtPos(editorView.state.selection.from);
                     let refNode = ref.node as any;
                     while (refNode && !("getBoundingClientRect" in refNode)) refNode = refNode.parentElement;
-                    let r1 = refNode && refNode.getBoundingClientRect();
-                    let r3 = self._ref.current!.getBoundingClientRect();
+                    const r1 = refNode && refNode.getBoundingClientRect();
+                    const r3 = self._ref.current!.getBoundingClientRect();
                     if (r1.top < r3.top || r1.top > r3.bottom) {
                         r1 && (self._ref.current!.scrollTop += (r1.top - r3.top) * self.props.ScreenToLocalTransform().Scale);
                     }
@@ -803,7 +804,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             }
         }
 
-        let selectOnLoad = this.props.Document[Id] === FormattedTextBox.SelectOnLoad;
+        const selectOnLoad = this.props.Document[Id] === FormattedTextBox.SelectOnLoad;
         if (selectOnLoad) {
             FormattedTextBox.SelectOnLoad = "";
             this.props.select(false);
@@ -838,7 +839,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     }
     onPointerDown = (e: React.PointerEvent): void => {
         FormattedTextBoxComment.textBox = this;
-        let pos = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY });
+        const pos = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY });
         pos && (this._nodeClicked = this._editorView!.state.doc.nodeAt(pos.pos));
         if (this.props.onClick && e.button === 0) {
             e.preventDefault();
@@ -923,16 +924,16 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     hitBulletTargets(x: number, y: number, offsetX: number, select: boolean, highlightOnly = false) {
         clearStyleSheetRules(FormattedTextBox._bulletStyleSheet);
         if (this.props.isSelected(true) && offsetX < 40) {
-            let pos = this._editorView!.posAtCoords({ left: x, top: y });
+            const pos = this._editorView!.posAtCoords({ left: x, top: y });
             if (pos && pos.pos > 0) {
-                let node = this._editorView!.state.doc.nodeAt(pos.pos);
-                let node2 = node?.type === schema.nodes.paragraph ? this._editorView!.state.doc.nodeAt(pos.pos - 1) : undefined;
+                const node = this._editorView!.state.doc.nodeAt(pos.pos);
+                const node2 = node?.type === schema.nodes.paragraph ? this._editorView!.state.doc.nodeAt(pos.pos - 1) : undefined;
                 if ((node === this._nodeClicked || highlightOnly) && (node2?.type === schema.nodes.ordered_list || node2?.type === schema.nodes.list_item)) {
-                    let hit = this._editorView!.domAtPos(pos.pos).node as any;   // let beforeEle = document.querySelector("." + hit.className) as Element;
-                    let before = hit ? window.getComputedStyle(hit, ':before') : undefined;
-                    let beforeWidth = before ? Number(before.getPropertyValue('width').replace("px", "")) : undefined;
+                    const hit = this._editorView!.domAtPos(pos.pos).node as any;   // let beforeEle = document.querySelector("." + hit.className) as Element;
+                    const before = hit ? window.getComputedStyle(hit, ':before') : undefined;
+                    const beforeWidth = before ? Number(before.getPropertyValue('width').replace("px", "")) : undefined;
                     if (beforeWidth && offsetX < beforeWidth * .9) {
-                        let ol = this._editorView!.state.doc.nodeAt(pos.pos - 2) ? this._editorView!.state.doc.nodeAt(pos.pos - 2) : undefined;
+                        const ol = this._editorView!.state.doc.nodeAt(pos.pos - 2) ? this._editorView!.state.doc.nodeAt(pos.pos - 2) : undefined;
                         if (ol?.type === schema.nodes.ordered_list && select) {
                             if (!highlightOnly) {
                                 this._editorView!.dispatch(this._editorView!.state.tr.setSelection(new NodeSelection(this._editorView!.state.doc.resolve(pos.pos - 2))));
@@ -953,11 +954,11 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     onMouseUp = (e: React.MouseEvent): void => {
         e.stopPropagation();
 
-        let view = this._editorView as any;
+        const view = this._editorView as any;
         // this interposes on prosemirror's upHandler to prevent prosemirror's up from invoked multiple times when there 
         // are nested prosemirrors.  We only want the lowest level prosemirror to be invoked.
         if (view.mouseDown) {
-            let originalUpHandler = view.mouseDown.up;
+            const originalUpHandler = view.mouseDown.up;
             view.root.removeEventListener("mouseup", originalUpHandler);
             view.mouseDown.up = (e: MouseEvent) => {
                 !(e as any).formattedHandled && originalUpHandler(e);
@@ -969,7 +970,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     }
 
     tooltipTextMenuPlugin() {
-        let self = FormattedTextBox;
+        const self = FormattedTextBox;
         return new Plugin({
             view(newView) {
                 return self.ToolTipTextMenu = FormattedTextBox.getToolTip(newView);
@@ -978,7 +979,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     }
 
     tooltipLinkingMenuPlugin() {
-        let myprops = this.props;
+        const myprops = this.props;
         return new Plugin({
             view(_editorView) {
                 return new TooltipLinkingMenu(_editorView, myprops);
@@ -1001,7 +1002,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         if (e.key === "Tab" || e.key === "Enter") {
             e.preventDefault();
         }
-        let mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.round(Date.now() / 1000 / 5) });
+        const mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.round(Date.now() / 1000 / 5) });
         this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(mark));
 
         if (!this._undoTyping) {
@@ -1018,8 +1019,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         const scrollHeight = this._ref.current?.scrollHeight;
         if (!this.layoutDoc.animateToPos && this.layoutDoc.autoHeight && scrollHeight &&
             getComputedStyle(this._ref.current!.parentElement!).top === "0px") {  // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
-            let nh = this.Document.isTemplateField ? 0 : NumCast(this.dataDoc.nativeHeight, 0);
-            let dh = NumCast(this.layoutDoc.height, 0);
+            const nh = this.Document.isTemplateField ? 0 : NumCast(this.dataDoc.nativeHeight, 0);
+            const dh = NumCast(this.layoutDoc.height, 0);
             this.layoutDoc.height = Math.max(10, (nh ? dh / nh * scrollHeight : scrollHeight) + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0));
             this.dataDoc.nativeHeight = nh ? scrollHeight : undefined;
         }
@@ -1030,8 +1031,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     @computed get annotationsKey() { return "annotations"; }
     render() {
         TraceMobx();
-        let rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
-        let interactive = InkingControl.Instance.selectedTool || this.layoutDoc.isBackground;
+        const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
+        const interactive = InkingControl.Instance.selectedTool || this.layoutDoc.isBackground;
         if (this.props.isSelected()) {
             FormattedTextBox.ToolTipTextMenu!.updateFromDash(this._editorView!, undefined, this.props);
         } else if (FormattedTextBoxComment.textBox === this) {
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index c076fd34a..2ec30e3b3 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -81,7 +81,7 @@ export class FormattedTextBoxComment {
             FormattedTextBoxComment.tooltip.style.display = "none";
             FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipInput);
             FormattedTextBoxComment.tooltip.onpointerdown = (e: PointerEvent) => {
-                let keep = e.target && (e.target as any).type === "checkbox" ? true : false;
+                const keep = e.target && (e.target as any).type === "checkbox" ? true : false;
                 const textBox = FormattedTextBoxComment.textBox;
                 if (FormattedTextBoxComment.linkDoc && !keep && textBox) {
                     DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
@@ -113,7 +113,7 @@ export class FormattedTextBoxComment {
     }
 
     static update(view: EditorView, lastState?: EditorState) {
-        let state = view.state;
+        const 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)) {
@@ -136,11 +136,11 @@ export class FormattedTextBoxComment {
         // this section checks to see if the insertion point is over text entered by a different user.  If so, it sets ths comment text to indicate the user and the modification date
         if (state.selection.$from) {
             nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark);
-            let naft = findEndOfMark(state.selection.$from, view, findOtherUserMark);
-            let noselection = view.state.selection.$from === view.state.selection.$to;
+            const naft = findEndOfMark(state.selection.$from, view, findOtherUserMark);
+            const noselection = view.state.selection.$from === view.state.selection.$to;
             let child: any = null;
             state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node));
-            let mark = child && findOtherUserMark(child.marks);
+            const mark = child && findOtherUserMark(child.marks);
             if (mark && child && (nbef || naft) && (!mark.attrs.opened || noselection)) {
                 FormattedTextBoxComment.SetState(FormattedTextBoxComment.textBox, mark.attrs.opened, state.selection.$from.pos - nbef, state.selection.$from.pos + naft, mark);
             }
@@ -153,10 +153,10 @@ export class FormattedTextBoxComment {
         // this checks if the selection is a hyperlink.  If so, it displays the target doc's text for internal links, and the url of the target for external links. 
         if (set === "none" && state.selection.$from) {
             nbef = findStartOfMark(state.selection.$from, view, findLinkMark);
-            let naft = findEndOfMark(state.selection.$from, view, findLinkMark);
+            const naft = findEndOfMark(state.selection.$from, view, findLinkMark);
             let child: any = null;
             state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node));
-            let mark = child && findLinkMark(child.marks);
+            const mark = child && findLinkMark(child.marks);
             if (mark && child && nbef && naft) {
                 FormattedTextBoxComment.tooltipText.textContent = "external => " + mark.attrs.href;
                 if (mark.attrs.href.startsWith("https://en.wikipedia.org/wiki/")) {
@@ -167,7 +167,7 @@ export class FormattedTextBoxComment {
                 }
                 (FormattedTextBoxComment.tooltipText as any).href = mark.attrs.href;
                 if (mark.attrs.href.indexOf(Utils.prepend("/doc/")) === 0) {
-                    let docTarget = mark.attrs.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
+                    const docTarget = mark.attrs.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
                     docTarget && DocServer.GetRefField(docTarget).then(linkDoc => {
                         if (linkDoc instanceof Doc) {
                             FormattedTextBoxComment.linkDoc = linkDoc;
@@ -210,12 +210,12 @@ export class FormattedTextBoxComment {
         if (set !== "none") {
             // These are in screen coordinates
             // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
-            let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
+            const start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
             // The box in which the tooltip is positioned, to use as base
-            let box = (document.getElementById("mainView-container") as any).getBoundingClientRect();
+            const box = (document.getElementById("mainView-container") as any).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);
+            const left = Math.max((start.left + end.left) / 2, start.left + 3);
             FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px";
             FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px";
         }
diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
index 60f547b1e..9462ff024 100644
--- a/src/client/views/nodes/IconBox.tsx
+++ b/src/client/views/nodes/IconBox.tsx
@@ -51,7 +51,7 @@ export class IconBox extends React.Component<FieldViewProps> {
     }
 
     public static DocumentIcon(layout: string) {
-        let button = layout.indexOf("PDFBox") !== -1 ? faFilePdf :
+        const button = layout.indexOf("PDFBox") !== -1 ? faFilePdf :
             layout.indexOf("ImageBox") !== -1 ? faImage :
                 layout.indexOf("Formatted") !== -1 ? faStickyNote :
                     layout.indexOf("Video") !== -1 ? faFilm :
@@ -65,14 +65,14 @@ export class IconBox extends React.Component<FieldViewProps> {
     }
 
     specificContextMenu = (): void => {
-        let cm = ContextMenu.Instance;
+        const cm = ContextMenu.Instance;
         cm.addItem({ description: this.props.Document.hideLabel ? "Show label with icon" : "Remove label from icon", event: this.setLabelField, icon: "tag" });
         if (!this.props.Document.hideLabel) {
             cm.addItem({ description: "Use Target Title", event: () => IconBox.AutomaticTitle(this.props.Document), icon: "text-height" });
         }
     }
     render() {
-        let label = this.props.Document.hideLabel ? "" : this.props.Document.title;
+        const label = this.props.Document.hideLabel ? "" : this.props.Document.title;
         return (
             <div className="iconBox-container" onContextMenu={this.specificContextMenu}>
                 {this.minimizedIcon}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index c283e4f21..b4a51657f 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -28,8 +28,8 @@ import { documentSchema } from '../../../new_fields/documentSchemas';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { TraceMobx } from '../../../new_fields/util';
 import { SelectionManager } from '../../util/SelectionManager';
-var requestImageSize = require('../../util/request-image-size');
-var path = require('path');
+const requestImageSize = require('../../util/request-image-size');
+const path = require('path');
 const { Howl } = require('howler');
 
 
@@ -88,7 +88,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
     recordAudioAnnotation = () => {
         let gumStream: any;
         let recorder: any;
-        let self = this;
+        const self = this;
         const extensionDoc = this.extensionDoc;
         extensionDoc && navigator.mediaDevices.getUserMedia({
             audio: true
@@ -105,9 +105,9 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
                 const files = await res.json();
                 const url = Utils.prepend(files[0].path);
                 // upload to server with known URL 
-                let audioDoc = Docs.Create.AudioDocument(url, { title: "audio test", width: 200, height: 32 });
+                const audioDoc = Docs.Create.AudioDocument(url, { title: "audio test", width: 200, height: 32 });
                 audioDoc.treeViewExpandedView = "layout";
-                let audioAnnos = Cast(extensionDoc.audioAnnotations, listSpec(Doc));
+                const audioAnnos = Cast(extensionDoc.audioAnnotations, listSpec(Doc));
                 if (audioAnnos === undefined) {
                     extensionDoc.audioAnnotations = new List([audioDoc]);
                 } else {
@@ -126,10 +126,10 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
 
     @undoBatch
     rotate = action(() => {
-        let nw = this.Document.nativeWidth;
-        let nh = this.Document.nativeHeight;
-        let w = this.Document.width;
-        let h = this.Document.height;
+        const nw = this.Document.nativeWidth;
+        const nh = this.Document.nativeHeight;
+        const w = this.Document.width;
+        const h = this.Document.height;
         this.Document.rotation = ((this.Document.rotation || 0) + 90) % 360;
         this.Document.nativeWidth = nh;
         this.Document.nativeHeight = nw;
@@ -140,12 +140,12 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
     specificContextMenu = (e: React.MouseEvent): void => {
         const field = Cast(this.Document[this.props.fieldKey], ImageField);
         if (field) {
-            let funcs: ContextMenuProps[] = [];
+            const funcs: ContextMenuProps[] = [];
             funcs.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" });
             funcs.push({ description: "Rotate", event: this.rotate, icon: "expand-arrows-alt" });
 
-            let existingAnalyze = ContextMenu.Instance.findByDescription("Analyzers...");
-            let modes: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
+            const existingAnalyze = ContextMenu.Instance.findByDescription("Analyzers...");
+            const modes: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
             modes.push({ description: "Generate Tags", event: this.generateMetadata, icon: "tag" });
             modes.push({ description: "Find Faces", event: this.extractFaces, icon: "camera" });
             !existingAnalyze && ContextMenu.Instance.addItem({ description: "Analyzers...", subitems: modes, icon: "hand-point-right" });
@@ -155,8 +155,8 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
     }
 
     extractFaces = () => {
-        let converter = (results: any) => {
-            let faceDocs = new List<Doc>();
+        const converter = (results: any) => {
+            const faceDocs = new List<Doc>();
             results.reduce((face: CognitiveServices.Image.Face, faceDocs: List<Doc>) => faceDocs.push(Docs.Get.DocumentHierarchyFromJson(face, `Face: ${face.faceId}`)!), new List<Doc>());
             return faceDocs;
         };
@@ -164,12 +164,12 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
     }
 
     generateMetadata = (threshold: Confidence = Confidence.Excellent) => {
-        let converter = (results: any) => {
-            let tagDoc = new Doc;
-            let tagsList = new List();
+        const converter = (results: any) => {
+            const tagDoc = new Doc;
+            const tagsList = new List();
             results.tags.map((tag: Tag) => {
                 tagsList.push(tag.name);
-                let sanitized = tag.name.replace(" ", "_");
+                const sanitized = tag.name.replace(" ", "_");
                 tagDoc[sanitized] = ComputedField.MakeFunction(`(${tag.confidence} >= this.confidence) ? ${tag.confidence} : "${ComputedField.undefined}"`);
             });
             this.extensionDoc && (this.extensionDoc.generatedTags = tagsList);
@@ -181,7 +181,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
     }
 
     @computed private get url() {
-        let data = Cast(this.dataDoc[this.props.fieldKey], ImageField);
+        const data = Cast(this.dataDoc[this.props.fieldKey], ImageField);
         return data ? data.url.href : undefined;
     }
 
@@ -194,7 +194,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
         } else if (!(lower.endsWith(".png") || lower.endsWith(".jpg") || lower.endsWith(".jpeg"))) {
             return url.href;//Why is this here
         }
-        let ext = path.extname(url.href);
+        const ext = path.extname(url.href);
         const suffix = this.props.renderDepth < 1 ? "_o" : this._curSuffix;
         return url.href.replace(ext, suffix + ext);
     }
@@ -208,7 +208,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
         if (this._curSuffix === "_l") this._largeRetryCount++;
     }
     @action onError = () => {
-        let timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount;
+        const timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount;
         if (timeout < 10) {
             setTimeout(this.retryPath, Math.min(10000, timeout * 5));
         }
@@ -218,9 +218,9 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
     resize = (srcpath: string) => {
         requestImageSize(srcpath)
             .then((size: any) => {
-                let rotation = NumCast(this.dataDoc.rotation) % 180;
-                let realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size;
-                let aspect = realsize.height / realsize.width;
+                const rotation = NumCast(this.dataDoc.rotation) % 180;
+                const realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size;
+                const aspect = realsize.height / realsize.width;
                 if (this.Document.width && (Math.abs(1 - NumCast(this.Document.height) / NumCast(this.Document.width) / (realsize.height / realsize.width)) > 0.1)) {
                     setTimeout(action(() => {
                         this.Document.height = this.Document[WidthSym]() * aspect;
@@ -234,9 +234,9 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
     fadesize = (srcpath: string) => {
         requestImageSize(srcpath)
             .then((size: any) => {
-                let rotation = NumCast(this.dataDoc.rotation) % 180;
-                let realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size;
-                let aspect = realsize.height / realsize.width;
+                const rotation = NumCast(this.dataDoc.rotation) % 180;
+                const realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size;
+                const aspect = realsize.height / realsize.width;
                 if (this.Document.width && (Math.abs(1 - NumCast(this.Document.height) / NumCast(this.Document.width) / (realsize.height / realsize.width)) > 0.1)) {
                     setTimeout(action(() => {
                         this.Document.height = this.Document[WidthSym]() * aspect;
@@ -250,10 +250,10 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
 
     @action
     onPointerEnter = () => {
-        let self = this;
-        let audioAnnos = this.extensionDoc && DocListCast(this.extensionDoc.audioAnnotations);
+        const self = this;
+        const audioAnnos = this.extensionDoc && DocListCast(this.extensionDoc.audioAnnotations);
         if (audioAnnos && audioAnnos.length && this._audioState === 0) {
-            let anno = audioAnnos[Math.floor(Math.random() * audioAnnos.length)];
+            const anno = audioAnnos[Math.floor(Math.random() * audioAnnos.length)];
             anno.data instanceof AudioField && new Howl({
                 src: [anno.data.url.href],
                 format: ["mp3"],
@@ -289,32 +289,32 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
         const extensionDoc = this.extensionDoc;
         if (!extensionDoc) return (null);
         // let transform = this.props.ScreenToLocalTransform().inverse();
-        let pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50;
+        const pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50;
         // var [sptX, sptY] = transform.transformPoint(0, 0);
         // let [bptX, bptY] = transform.transformPoint(pw, this.props.PanelHeight());
         // let w = bptX - sptX;
 
-        let nativeWidth = (this.Document.nativeWidth || pw);
-        let nativeHeight = (this.Document.nativeHeight || 1);
+        const nativeWidth = (this.Document.nativeWidth || pw);
+        const nativeHeight = (this.Document.nativeHeight || 1);
         let paths = [[Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png"), nativeWidth / nativeHeight]];
         // this._curSuffix = "";
         // if (w > 20) {
-        let alts = DocListCast(extensionDoc.Alternates);
-        let altpaths = alts.filter(doc => doc.data instanceof ImageField).map(doc => [this.choosePath((doc.data as ImageField).url), doc[WidthSym]() / doc[HeightSym]()]);
-        let field = this.dataDoc[this.props.fieldKey];
+        const alts = DocListCast(extensionDoc.Alternates);
+        const altpaths = alts.filter(doc => doc.data instanceof ImageField).map(doc => [this.choosePath((doc.data as ImageField).url), doc[WidthSym]() / doc[HeightSym]()]);
+        const field = this.dataDoc[this.props.fieldKey];
         // if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s";
         // else if (w < 600 && this._mediumRetryCount < 10) this._curSuffix = "_m";
         // else if (this._largeRetryCount < 10) this._curSuffix = "_l";
         if (field instanceof ImageField) paths = [[this.choosePath(field.url), nativeWidth / nativeHeight]];
         paths.push(...altpaths);
         // }
-        let dragging = !SelectionManager.GetIsDragging() ? "" : "-dragging";
-        let rotation = NumCast(this.Document.rotation, 0);
-        let aspect = (rotation % 180) ? this.Document[HeightSym]() / this.Document[WidthSym]() : 1;
-        let shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0;
-        let srcpath = paths[Math.min(paths.length - 1, (this.Document.curPage || 0))][0] as string;
-        let srcaspect = paths[Math.min(paths.length - 1, (this.Document.curPage || 0))][1] as number;
-        let fadepath = paths[Math.min(paths.length - 1, 1)][0] as string;
+        const dragging = !SelectionManager.GetIsDragging() ? "" : "-dragging";
+        const rotation = NumCast(this.Document.rotation, 0);
+        const aspect = (rotation % 180) ? this.Document[HeightSym]() / this.Document[WidthSym]() : 1;
+        const shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0;
+        const srcpath = paths[Math.min(paths.length - 1, (this.Document.curPage || 0))][0] as string;
+        const srcaspect = paths[Math.min(paths.length - 1, (this.Document.curPage || 0))][1] as number;
+        const fadepath = paths[Math.min(paths.length - 1, 1)][0] as string;
 
         !this.Document.ignoreAspect && this.resize(srcpath);
 
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index aa6e135fe..fba29e4cd 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -53,13 +53,13 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         }
     }
     public static CompileKVPScript(value: string): KVPScript | undefined {
-        let eq = value.startsWith("=");
+        const eq = value.startsWith("=");
         value = eq ? value.substr(1) : value;
         const dubEq = value.startsWith(":=") ? "computed" : value.startsWith(";=") ? "script" : false;
         value = dubEq ? value.substr(2) : value;
-        let options: ScriptOptions = { addReturn: true, params: { this: "Doc" } };
+        const options: ScriptOptions = { addReturn: true, params: { this: "Doc" } };
         if (dubEq) options.typecheck = false;
-        let script = CompileScript(value, options);
+        const script = CompileScript(value, options);
         if (!script.compiled) {
             return undefined;
         }
@@ -76,7 +76,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         } else if (type === "script") {
             field = new ScriptField(script);
         } else {
-            let res = script.run({ this: target }, console.log);
+            const res = script.run({ this: target }, console.log);
             if (!res.success) return false;
             field = res.result;
         }
@@ -106,14 +106,14 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     rowHeight = () => 30;
 
     createTable = () => {
-        let doc = this.fieldDocToLayout;
+        const doc = this.fieldDocToLayout;
         if (!doc) {
             return <tr><td>Loading...</td></tr>;
         }
-        let realDoc = doc;
+        const realDoc = doc;
 
-        let ids: { [key: string]: string } = {};
-        let protos = Doc.GetAllPrototypes(doc);
+        const ids: { [key: string]: string } = {};
+        const protos = Doc.GetAllPrototypes(doc);
         for (const proto of protos) {
             Object.keys(proto).forEach(key => {
                 if (!(key in ids) && realDoc[key] !== ComputedField.undefined) {
@@ -122,10 +122,10 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
             });
         }
 
-        let rows: JSX.Element[] = [];
+        const rows: JSX.Element[] = [];
         let i = 0;
         const self = this;
-        for (let key of Object.keys(ids).slice().sort()) {
+        for (const key of Object.keys(ids).slice().sort()) {
             rows.push(<KeyValuePair doc={realDoc} addDocTab={this.props.addDocTab} PanelWidth={this.props.PanelWidth} PanelHeight={this.rowHeight}
                 ref={(function () {
                     let oldEl: KeyValuePair | undefined;
@@ -163,7 +163,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
 
     @action
     onDividerMove = (e: PointerEvent): void => {
-        let nativeWidth = this._mainCont.current!.getBoundingClientRect();
+        const nativeWidth = this._mainCont.current!.getBoundingClientRect();
         this.props.Document.schemaSplitPercentage = Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100));
     }
     @action
@@ -179,10 +179,10 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     }
 
     getTemplate = async () => {
-        let parent = Docs.Create.StackingDocument([], { width: 800, height: 800, title: "Template" });
+        const parent = Docs.Create.StackingDocument([], { width: 800, height: 800, title: "Template" });
         parent.singleColumn = false;
         parent.columnWidth = 100;
-        for (let row of this.rows.filter(row => row.isChecked)) {
+        for (const row of this.rows.filter(row => row.isChecked)) {
             await this.createTemplateField(parent, row);
             row.uncheck();
         }
@@ -190,17 +190,17 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     }
 
     createTemplateField = async (parentStackingDoc: Doc, row: KeyValuePair) => {
-        let metaKey = row.props.keyName;
-        let sourceDoc = await Cast(this.props.Document.data, Doc);
+        const metaKey = row.props.keyName;
+        const sourceDoc = await Cast(this.props.Document.data, Doc);
         if (!sourceDoc) {
             return;
         }
 
-        let fieldTemplate = await this.inferType(sourceDoc[metaKey], metaKey);
+        const fieldTemplate = await this.inferType(sourceDoc[metaKey], metaKey);
         if (!fieldTemplate) {
             return;
         }
-        let previousViewType = fieldTemplate.viewType;
+        const previousViewType = fieldTemplate.viewType;
         Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(parentStackingDoc));
         previousViewType && (fieldTemplate.viewType = previousViewType);
 
@@ -208,14 +208,14 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     }
 
     inferType = async (data: FieldResult, metaKey: string) => {
-        let options = { width: 300, height: 300, title: metaKey };
+        const options = { width: 300, height: 300, title: metaKey };
         if (data instanceof RichTextField || typeof data === "string" || typeof data === "number") {
             return Docs.Create.TextDocument(options);
         } else if (data instanceof List) {
             if (data.length === 0) {
                 return Docs.Create.StackingDocument([], options);
             }
-            let first = await Cast(data[0], Doc);
+            const first = await Cast(data[0], Doc);
             if (!first || !first.data) {
                 return Docs.Create.StackingDocument([], options);
             }
@@ -235,7 +235,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     }
 
     render() {
-        let dividerDragger = this.splitPercentage === 0 ? (null) :
+        const 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>;
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 225565964..bca68cdd9 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -53,7 +53,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
     }
 
     render() {
-        let props: FieldViewProps = {
+        const props: FieldViewProps = {
             Document: this.props.doc,
             DataDoc: this.props.doc,
             ContainingCollectionView: undefined,
@@ -73,7 +73,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             pinToPres: returnZero,
             ContentScaling: returnOne
         };
-        let contents = <FieldView {...props} />;
+        const contents = <FieldView {...props} />;
         // let fieldKey = Object.keys(props.Document).indexOf(props.fieldKey) !== -1 ? props.fieldKey : "(" + props.fieldKey + ")";
         let protoCount = 0;
         let doc: Doc | undefined = props.Document;
@@ -85,9 +85,9 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             doc = doc.proto;
         }
         const parenCount = Math.max(0, protoCount - 1);
-        let keyStyle = protoCount === 0 ? "black" : "blue";
+        const keyStyle = protoCount === 0 ? "black" : "blue";
 
-        let hover = { transition: "0.3s ease opacity", opacity: this.isPointerOver || this.isChecked ? 1 : 0 };
+        const hover = { transition: "0.3s ease opacity", opacity: this.isPointerOver || this.isChecked ? 1 : 0 };
 
         return (
             <tr className={this.props.rowStyle} onPointerEnter={action(() => this.isPointerOver = true)} onPointerLeave={action(() => this.isPointerOver = false)}>
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 87be1f4e9..0f5cc0fa9 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -95,7 +95,7 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
     @undoBatch
     @action
     private applyFilter = () => {
-        let scriptText = this._scriptValue ? this._scriptValue :
+        const scriptText = this._scriptValue ? this._scriptValue :
             this._keyValue && this._valueValue ? `this.${this._keyValue} === ${this._valueValue}` : "true";
         this.props.Document.filterScript = ScriptField.MakeFunction(scriptText);
     }
@@ -116,7 +116,7 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
     searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => this._searchString = e.currentTarget.value;
 
     settingsPanel() {
-        let pageBtns = <>
+        const pageBtns = <>
             <button className="pdfBox-overlayButton-iconCont" key="back" title="Page Back"
                 onPointerDown={e => e.stopPropagation()} onClick={e => this.backPage()} style={{ left: 45, top: 5 }}>
                 <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-left"} size="sm" />
@@ -186,7 +186,7 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
 
     specificContextMenu = (e: React.MouseEvent): void => {
         const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
-        let funcs: ContextMenuProps[] = [];
+        const funcs: ContextMenuProps[] = [];
         pdfUrl && funcs.push({ description: "Copy path", event: () => Utils.CopyText(pdfUrl.url.pathname), icon: "expand-arrows-alt" });
         funcs.push({ description: "Toggle Fit Width " + (this.Document.fitWidth ? "Off" : "On"), event: () => this.Document.fitWidth = !this.Document.fitWidth, icon: "expand-arrows-alt" });
 
@@ -195,7 +195,7 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
 
     @computed get contentScaling() { return this.props.ContentScaling(); }
     @computed get renderTitleBox() {
-        let classname = "pdfBox" + (this.active() ? "-interactive" : "");
+        const classname = "pdfBox" + (this.active() ? "-interactive" : "");
         return <div className={classname} style={{
             width: !this.props.Document.fitWidth ? NumCast(this.props.Document.nativeWidth) : `${100 / this.contentScaling}%`,
             height: !this.props.Document.fitWidth ? NumCast(this.props.Document.nativeHeight) : `${100 / this.contentScaling}%`,
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index cbb83b511..1e6894f37 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -42,7 +42,7 @@ export class PresBox extends React.Component<FieldViewProps> {
             if (value) {
                 value.forEach((item, i) => {
                     if (item instanceof Doc && item.type !== DocumentType.PRESELEMENT) {
-                        let pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" });
+                        const pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" });
                         Doc.GetProto(pinDoc).presentationTargetDoc = item;
                         Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.presentationTargetDoc instanceof Doc) && this.presentationTargetDoc.title.toString()');
                         value.splice(i, 1, pinDoc);
@@ -61,9 +61,9 @@ export class PresBox extends React.Component<FieldViewProps> {
     next = async () => {
         const current = NumCast(this.props.Document.selectedDoc);
         //asking to get document at current index
-        let docAtCurrentNext = await this.getDocAtIndex(current + 1);
+        const docAtCurrentNext = await this.getDocAtIndex(current + 1);
         if (docAtCurrentNext !== undefined) {
-            let presDocs = DocListCast(this.props.Document[this.props.fieldKey]);
+            const presDocs = DocListCast(this.props.Document[this.props.fieldKey]);
             let nextSelected = current + 1;
 
             for (; nextSelected < presDocs.length - 1; nextSelected++) {
@@ -78,15 +78,15 @@ export class PresBox extends React.Component<FieldViewProps> {
     back = async () => {
         const current = NumCast(this.props.Document.selectedDoc);
         //requesting for the doc at current index
-        let docAtCurrent = await this.getDocAtIndex(current);
+        const docAtCurrent = await this.getDocAtIndex(current);
         if (docAtCurrent !== undefined) {
 
             //asking for its presentation id.
             let prevSelected = current;
             let zoomOut: boolean = false;
 
-            let presDocs = await DocListCastAsync(this.props.Document[this.props.fieldKey]);
-            let currentsArray: Doc[] = [];
+            const presDocs = await DocListCastAsync(this.props.Document[this.props.fieldKey]);
+            const currentsArray: Doc[] = [];
             for (; presDocs && prevSelected > 0 && presDocs[prevSelected].groupButton; prevSelected--) {
                 currentsArray.push(presDocs[prevSelected]);
             }
@@ -104,8 +104,8 @@ export class PresBox extends React.Component<FieldViewProps> {
             //If so making sure to zoom out, which goes back to state before zooming action
             if (current > 0) {
                 if (zoomOut || docAtCurrent.showButton) {
-                    let prevScale = NumCast(this.childDocs[prevSelected].viewScale, null);
-                    let curScale = DocumentManager.Instance.getScaleOfDocView(this.childDocs[current]);
+                    const prevScale = NumCast(this.childDocs[prevSelected].viewScale, null);
+                    const curScale = DocumentManager.Instance.getScaleOfDocView(this.childDocs[current]);
                     if (prevScale !== undefined && prevScale !== curScale) {
                         DocumentManager.Instance.zoomIntoScale(docAtCurrent, prevScale);
                     }
@@ -162,13 +162,13 @@ export class PresBox extends React.Component<FieldViewProps> {
      * te option open, navigates to that element.
      */
     navigateToElement = async (curDoc: Doc, fromDocIndex: number) => {
-        let fromDoc = this.childDocs[fromDocIndex].presentationTargetDoc as Doc;
+        const fromDoc = this.childDocs[fromDocIndex].presentationTargetDoc as Doc;
         let docToJump = curDoc;
         let willZoom = false;
 
-        let presDocs = DocListCast(this.props.Document[this.props.fieldKey]);
+        const presDocs = DocListCast(this.props.Document[this.props.fieldKey]);
         let nextSelected = presDocs.indexOf(curDoc);
-        let currentDocGroups: Doc[] = [];
+        const currentDocGroups: Doc[] = [];
         for (; nextSelected < presDocs.length - 1; nextSelected++) {
             if (!presDocs[nextSelected + 1].groupButton) {
                 break;
@@ -190,11 +190,11 @@ export class PresBox extends React.Component<FieldViewProps> {
         //docToJump stayed same meaning, it was not in the group or was the last element in the group
         if (docToJump === curDoc) {
             //checking if curDoc has navigation open
-            let target = await curDoc.presentationTargetDoc as Doc;
+            const target = await curDoc.presentationTargetDoc as Doc;
             if (curDoc.navButton) {
                 DocumentManager.Instance.jumpToDocument(target, false);
             } else if (curDoc.showButton) {
-                let curScale = DocumentManager.Instance.getScaleOfDocView(fromDoc);
+                const curScale = DocumentManager.Instance.getScaleOfDocView(fromDoc);
                 //awaiting jump so that new scale can be found, since jumping is async
                 await DocumentManager.Instance.jumpToDocument(target, true);
                 curDoc.viewScale = DocumentManager.Instance.getScaleOfDocView(target);
@@ -207,11 +207,11 @@ export class PresBox extends React.Component<FieldViewProps> {
             }
             return;
         }
-        let curScale = DocumentManager.Instance.getScaleOfDocView(fromDoc);
+        const curScale = DocumentManager.Instance.getScaleOfDocView(fromDoc);
 
         //awaiting jump so that new scale can be found, since jumping is async
         await DocumentManager.Instance.jumpToDocument(await docToJump.presentationTargetDoc as Doc, willZoom);
-        let newScale = DocumentManager.Instance.getScaleOfDocView(await curDoc.presentationTargetDoc as Doc);
+        const newScale = DocumentManager.Instance.getScaleOfDocView(await curDoc.presentationTargetDoc as Doc);
         curDoc.viewScale = newScale;
         //saving the scale that user was on
         if (curScale !== 1) {
@@ -238,7 +238,7 @@ export class PresBox extends React.Component<FieldViewProps> {
     public removeDocument = (doc: Doc) => {
         const value = FieldValue(Cast(this.props.Document[this.props.fieldKey], listSpec(Doc)));
         if (value) {
-            let indexOfDoc = value.indexOf(doc);
+            const indexOfDoc = value.indexOf(doc);
             if (indexOfDoc !== - 1) {
                 value.splice(indexOfDoc, 1)[0];
                 return true;
@@ -337,13 +337,13 @@ export class PresBox extends React.Component<FieldViewProps> {
     @action
     initializeScaleViews = (docList: Doc[], viewtype: number) => {
         this.props.Document.chromeStatus = "disabled";
-        let hgt = (viewtype === CollectionViewType.Tree) ? 50 : 72;
+        const hgt = (viewtype === CollectionViewType.Tree) ? 50 : 72;
         docList.forEach((doc: Doc) => {
             doc.presBox = this.props.Document;
             doc.presBoxKey = this.props.fieldKey;
             doc.collapsedHeight = hgt;
             doc.height = ComputedField.MakeFunction("this.collapsedHeight + Number(this.embedOpen ? 100:0)");
-            let curScale = NumCast(doc.viewScale, null);
+            const curScale = NumCast(doc.viewScale, null);
             if (curScale === undefined) {
                 doc.viewScale = 1;
             }
@@ -352,7 +352,7 @@ export class PresBox extends React.Component<FieldViewProps> {
 
 
     selectElement = (doc: Doc) => {
-        let index = DocListCast(this.props.Document[this.props.fieldKey]).indexOf(doc);
+        const index = DocListCast(this.props.Document[this.props.fieldKey]).indexOf(doc);
         index !== -1 && this.gotoDocument(index, NumCast(this.props.Document.selectedDoc));
     }
 
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 741dcada0..376d27380 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -22,7 +22,7 @@ import { InkingControl } from "../InkingControl";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./VideoBox.scss";
 import { documentSchema, positionSchema } from "../../../new_fields/documentSchemas";
-var path = require('path');
+const path = require('path');
 
 export const timeSchema = createSchema({
     currentTimecode: "number",  // the current time of a video or other linear, time-based document.  Note, should really get set on an extension field, but that's more complicated when it needs to be set since the extension doc needs to be found first
@@ -54,9 +54,9 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
     }
 
     videoLoad = () => {
-        let aspect = this.player!.videoWidth / this.player!.videoHeight;
-        var nativeWidth = (this.Document.nativeWidth || 0);
-        var nativeHeight = (this.Document.nativeHeight || 0);
+        const aspect = this.player!.videoWidth / this.player!.videoHeight;
+        const nativeWidth = (this.Document.nativeWidth || 0);
+        const nativeHeight = (this.Document.nativeHeight || 0);
         if (!nativeWidth || !nativeHeight) {
             if (!this.Document.nativeWidth) this.Document.nativeWidth = this.player!.videoWidth;
             this.Document.nativeHeight = (this.Document.nativeWidth || 0) / aspect;
@@ -101,12 +101,12 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
     }
 
     @action public Snapshot() {
-        let width = this.Document.width || 0;
-        let height = this.Document.height || 0;
-        var canvas = document.createElement('canvas');
+        const width = this.Document.width || 0;
+        const height = this.Document.height || 0;
+        const canvas = document.createElement('canvas');
         canvas.width = 640;
         canvas.height = 640 * (this.Document.nativeHeight || 0) / (this.Document.nativeWidth || 1);
-        var ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
+        const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
         if (ctx) {
             ctx.rect(0, 0, canvas.width, canvas.height);
             ctx.fillStyle = "blue";
@@ -115,20 +115,20 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
         }
 
         if (!this._videoRef) { // can't find a way to take snapshots of videos
-            let b = Docs.Create.ButtonDocument({
+            const b = Docs.Create.ButtonDocument({
                 x: (this.Document.x || 0) + width, y: (this.Document.y || 0),
                 width: 150, height: 50, title: (this.Document.currentTimecode || 0).toString()
             });
             b.onClick = ScriptField.MakeScript(`this.currentTimecode = ${(this.Document.currentTimecode || 0)}`);
         } else {
             //convert to desired file format
-            var dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
+            const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
             // if you want to preview the captured image,
-            let filename = path.basename(encodeURIComponent("snapshot" + StrCast(this.Document.title).replace(/\..*$/, "") + "_" + (this.Document.currentTimecode || 0).toString().replace(/\./, "_")));
+            const filename = path.basename(encodeURIComponent("snapshot" + StrCast(this.Document.title).replace(/\..*$/, "") + "_" + (this.Document.currentTimecode || 0).toString().replace(/\./, "_")));
             VideoBox.convertDataUri(dataUrl, filename).then(returnedFilename => {
                 if (returnedFilename) {
-                    let url = this.choosePath(Utils.prepend(returnedFilename));
-                    let imageSummary = Docs.Create.ImageDocument(url, {
+                    const url = this.choosePath(Utils.prepend(returnedFilename));
+                    const imageSummary = Docs.Create.ImageDocument(url, {
                         x: (this.Document.x || 0) + width, y: (this.Document.y || 0),
                         width: 150, height: height / width * 150, title: "--snapshot" + (this.Document.currentTimecode || 0) + " image-"
                     });
@@ -150,9 +150,9 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
         if (this.props.setVideoBox) this.props.setVideoBox(this);
 
         if (this.youtubeVideoId) {
-            let youtubeaspect = 400 / 315;
-            var nativeWidth = (this.Document.nativeWidth || 0);
-            var nativeHeight = (this.Document.nativeHeight || 0);
+            const youtubeaspect = 400 / 315;
+            const nativeWidth = (this.Document.nativeWidth || 0);
+            const nativeHeight = (this.Document.nativeHeight || 0);
             if (!nativeWidth || !nativeHeight) {
                 if (!this.Document.nativeWidth) this.Document.nativeWidth = 600;
                 this.Document.nativeHeight = (this.Document.nativeWidth || 0) / youtubeaspect;
@@ -181,7 +181,7 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
 
     public static async convertDataUri(imageUri: string, returnedFilename: string) {
         try {
-            let posting = Utils.prepend("/uploadURI");
+            const posting = Utils.prepend("/uploadURI");
             const returnedUri = await rp.post(posting, {
                 body: {
                     uri: imageUri,
@@ -196,10 +196,10 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
         }
     }
     specificContextMenu = (e: React.MouseEvent): void => {
-        let field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
+        const field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
         if (field) {
-            let url = field.url.href;
-            let subitems: ContextMenuProps[] = [];
+            const url = field.url.href;
+            const subitems: ContextMenuProps[] = [];
             subitems.push({ description: "Copy path", event: () => { Utils.CopyText(url); }, icon: "expand-arrows-alt" });
             subitems.push({ description: "Toggle Show Controls", event: action(() => VideoBox._showControls = !VideoBox._showControls), icon: "expand-arrows-alt" });
             subitems.push({ description: "Take Snapshot", event: () => this.Snapshot(), icon: "expand-arrows-alt" });
@@ -208,9 +208,9 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
     }
 
     @computed get content() {
-        let field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
-        let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
-        let style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
+        const field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
+        const interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
+        const style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
         return !field ? <div>Loading</div> :
             <video className={`${style}`} key="video" ref={this.setVideoRef} onCanPlay={this.videoLoad} controls={VideoBox._showControls}
                 onPlay={() => this.Play()} onSeeked={this.updateTimecode} onPause={() => this.Pause()} onClick={e => e.preventDefault()}>
@@ -220,7 +220,7 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
     }
 
     @computed get youtubeVideoId() {
-        let field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
+        const field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
         return field && field.url.href.indexOf("youtube") !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : "";
     }
 
@@ -231,9 +231,9 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
         }
         else this._youtubeContentCreated = false;
 
-        let iframe = e.target;
+        const iframe = e.target;
         let started = true;
-        let onYoutubePlayerStateChange = (event: any) => runInAction(() => {
+        const onYoutubePlayerStateChange = (event: any) => runInAction(() => {
             if (started && event.data === YT.PlayerState.PLAYING) {
                 started = false;
                 this._youtubePlayer && this._youtubePlayer.unMute();
@@ -243,12 +243,12 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
             if (event.data === YT.PlayerState.PLAYING && !this._playing) this.Play(false);
             if (event.data === YT.PlayerState.PAUSED && this._playing) this.Pause(false);
         });
-        let onYoutubePlayerReady = (event: any) => {
+        const onYoutubePlayerReady = (event: any) => {
             this._reactionDisposer && this._reactionDisposer();
             this._youtubeReactionDisposer && this._youtubeReactionDisposer();
             this._reactionDisposer = reaction(() => this.Document.currentTimecode, () => !this._playing && this.Seek(this.Document.currentTimecode || 0));
             this._youtubeReactionDisposer = reaction(() => [this.props.isSelected(), DocumentDecorations.Instance.Interacting, InkingControl.Instance.selectedTool], () => {
-                let interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected(true) && !DocumentDecorations.Instance.Interacting;
+                const interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected(true) && !DocumentDecorations.Instance.Interacting;
                 iframe.style.pointerEvents = interactive ? "all" : "none";
             }, { fireImmediately: true });
         };
@@ -261,8 +261,8 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
 
     }
     private get uIButtons() {
-        let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale);
-        let curTime = (this.Document.currentTimecode || 0);
+        const scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale);
+        const curTime = (this.Document.currentTimecode || 0);
         return ([<div className="videoBox-time" key="time" onPointerDown={this.onResetDown} >
             <span>{"" + Math.round(curTime)}</span>
             <span style={{ fontSize: 8 }}>{" " + Math.round((curTime - Math.trunc(curTime)) * 100)}</span>
@@ -318,8 +318,8 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
     @computed get youtubeContent() {
         this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
         this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
-        let style = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
-        let start = untracked(() => Math.round(this.Document.currentTimecode || 0));
+        const style = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
+        const start = untracked(() => Math.round(this.Document.currentTimecode || 0));
         return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
             onLoad={this.youtubeIframeLoaded} className={`${style}`} width={(this.Document.nativeWidth || 640)} height={(this.Document.nativeHeight || 390)}
             src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=1&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._showControls ? 1 : 0}`} />;
@@ -327,7 +327,7 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
 
     @action.bound
     addDocumentWithTimestamp(doc: Doc): boolean {
-        var curTime = (this.Document.currentTimecode || -1);
+        const curTime = (this.Document.currentTimecode || -1);
         curTime !== -1 && (doc.displayTimecode = curTime);
         return this.addDocument(doc);
     }
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 5af743859..b0a93224d 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -36,11 +36,11 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
 
     componentWillMount() {
 
-        let field = Cast(this.props.Document[this.props.fieldKey], WebField);
+        const field = Cast(this.props.Document[this.props.fieldKey], WebField);
         if (field && field.url.href.indexOf("youtube") !== -1) {
-            let youtubeaspect = 400 / 315;
-            var nativeWidth = NumCast(this.layoutDoc.nativeWidth);
-            var nativeHeight = NumCast(this.layoutDoc.nativeHeight);
+            const youtubeaspect = 400 / 315;
+            const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
+            const nativeHeight = NumCast(this.layoutDoc.nativeHeight);
             if (!nativeWidth || !nativeHeight || Math.abs(nativeWidth / nativeHeight - youtubeaspect) > 0.05) {
                 if (!nativeWidth) this.layoutDoc.nativeWidth = 600;
                 this.layoutDoc.nativeHeight = NumCast(this.layoutDoc.nativeWidth) / youtubeaspect;
@@ -65,7 +65,7 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
 
     @action
     setURL() {
-        let urlField: FieldResult<WebField> = Cast(this.props.Document.data, WebField);
+        const urlField: FieldResult<WebField> = Cast(this.props.Document.data, WebField);
         if (urlField) this.url = urlField.url.toString();
         else this.url = "";
     }
@@ -80,10 +80,10 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
 
     switchToText = () => {
         let url: string = "";
-        let field = Cast(this.props.Document[this.props.fieldKey], WebField);
+        const field = Cast(this.props.Document[this.props.fieldKey], WebField);
         if (field) url = field.url.href;
 
-        let newBox = Docs.Create.TextDocument({
+        const newBox = Docs.Create.TextDocument({
             x: NumCast(this.props.Document.x),
             y: NumCast(this.props.Document.y),
             title: url,
@@ -167,7 +167,7 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
 
     @computed
     get content() {
-        let field = this.dataDoc[this.props.fieldKey];
+        const field = this.dataDoc[this.props.fieldKey];
         let view;
         if (field instanceof HtmlField) {
             view = <span id="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
@@ -176,15 +176,15 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
         } else {
             view = <iframe src={"https://crossorigin.me/https://cs.brown.edu"} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
         }
-        let content =
+        const content =
             <div style={{ width: "100%", height: "100%", position: "absolute" }} onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
                 {this.urlEditor()}
                 {view}
             </div>;
 
-        let frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
+        const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
 
-        let classname = "webBox-cont" + (this.props.isSelected() && InkingControl.Instance.selectedTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+        const classname = "webBox-cont" + (this.props.isSelected() && InkingControl.Instance.selectedTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
         return (
             <>
                 <div className={classname}  >
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 936af9ab8..6599c0e3c 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -62,11 +62,11 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
     }
 
     deleteAnnotation = () => {
-        let annotation = DocListCast(this.props.extensionDoc.annotations);
-        let group = FieldValue(Cast(this.props.document.group, Doc));
+        const annotation = DocListCast(this.props.extensionDoc.annotations);
+        const group = FieldValue(Cast(this.props.document.group, Doc));
         if (group) {
             if (annotation.indexOf(group) !== -1) {
-                let newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc)));
+                const newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc)));
                 this.props.extensionDoc.annotations = new List<Doc>(newAnnotations);
             }
 
@@ -77,7 +77,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
     }
 
     pinToPres = () => {
-        let group = FieldValue(Cast(this.props.document.group, Doc));
+        const group = FieldValue(Cast(this.props.document.group, Doc));
         group && this.props.pinToPres(group);
     }
 
@@ -93,7 +93,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
             e.stopPropagation();
         }
         else if (e.button === 0) {
-            let annoGroup = await Cast(this.props.document.group, Doc);
+            const annoGroup = await Cast(this.props.document.group, Doc);
             if (annoGroup) {
                 DocumentManager.Instance.FollowLink(undefined, annoGroup,
                     (doc: Doc, maxLocation: string) => this.props.addDocTab(doc, undefined, e.ctrlKey ? "inTab" : "onRight"),
@@ -105,9 +105,9 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
 
 
     addTag = (key: string, value: string): boolean => {
-        let group = FieldValue(Cast(this.props.document.group, Doc));
+        const group = FieldValue(Cast(this.props.document.group, Doc));
         if (group) {
-            let valNum = parseInt(value);
+            const valNum = parseInt(value);
             group[key] = isNaN(valNum) ? value : valNum;
             return true;
         }
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index c64741769..38e10e99a 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -128,7 +128,7 @@ export default class PDFMenu extends AntimodeMenu {
     }
 
     render() {
-        let buttons = this.Status === "pdf" || this.Status === "snippet" ?
+        const buttons = this.Status === "pdf" || this.Status === "snippet" ?
             [
                 <button key="1" className="antimodeMenu-button" title="Click to Highlight" onClick={this.highlightClicked} style={this.Highlighting ? { backgroundColor: "#121212" } : {}}>
                     <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} /></button>,
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index c075a4f99..69aacc902 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -169,7 +169,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
 
     copy = (e: ClipboardEvent) => {
         if (this.props.active(true) && e.clipboardData) {
-            let annoDoc = this.makeAnnotationDocument("rgba(3,144,152,0.3)");  // copied text markup color (blueish)
+            const annoDoc = this.makeAnnotationDocument("rgba(3,144,152,0.3)");  // copied text markup color (blueish)
             if (annoDoc) {
                 e.clipboardData.setData("text/plain", this._selectionText);
                 e.clipboardData.setData("dash/pdfOrigin", this.props.Document[Id]);
@@ -212,7 +212,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
         this._filterReactionDisposer = reaction(
             () => ({ scriptField: Cast(this.Document.filterScript, ScriptField), annos: this._annotations.slice() }),
             action(({ scriptField, annos }: { scriptField: FieldResult<ScriptField>, annos: Doc[] }) => {
-                let oldScript = this._script.originalScript;
+                const oldScript = this._script.originalScript;
                 this._script = scriptField && scriptField.script.compiled ? scriptField.script : CompileScript("return true") as CompiledScript;
                 if (this._script.originalScript !== oldScript) {
                     this.Index = -1;
@@ -240,8 +240,8 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
             this.gotoPage(this.Document.curPage || 1);
         }));
         document.addEventListener("pagerendered", action(() => this._showCover = this._showWaiting = false));
-        var pdfLinkService = new PDFJSViewer.PDFLinkService();
-        let pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService });
+        const pdfLinkService = new PDFJSViewer.PDFLinkService();
+        const pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService });
         this._pdfViewer = new PDFJSViewer.PDFViewer({
             container: this._mainCont.current,
             viewer: this._viewer.current,
@@ -260,12 +260,12 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
         if (this._savedAnnotations.size() === 0) return undefined;
         let mainAnnoDoc = Docs.Create.InstanceFromProto(new Doc(), "", {});
         let mainAnnoDocProto = Doc.GetProto(mainAnnoDoc);
-        let annoDocs: Doc[] = [];
+        const annoDocs: Doc[] = [];
         let maxX = -Number.MAX_VALUE;
         let minY = Number.MAX_VALUE;
         if ((this._savedAnnotations.values()[0][0] as any).marqueeing) {
-            let anno = this._savedAnnotations.values()[0][0];
-            let annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, title: "Annotation on " + this.Document.title });
+            const anno = this._savedAnnotations.values()[0][0];
+            const annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, title: "Annotation on " + this.Document.title });
             if (anno.style.left) annoDoc.x = parseInt(anno.style.left);
             if (anno.style.top) annoDoc.y = parseInt(anno.style.top);
             if (anno.style.height) annoDoc.height = parseInt(anno.style.height);
@@ -280,7 +280,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
             mainAnnoDocProto.y = annoDoc.y;
         } else {
             this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => value.map(anno => {
-                let annoDoc = new Doc();
+                const annoDoc = new Doc();
                 if (anno.style.left) annoDoc.x = parseInt(anno.style.left);
                 if (anno.style.top) annoDoc.y = parseInt(anno.style.top);
                 if (anno.style.height) annoDoc.height = parseInt(anno.style.height);
@@ -324,7 +324,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
     @action
     scrollToAnnotation = (scrollToAnnotation: Doc) => {
         if (scrollToAnnotation) {
-            let offset = this.visibleHeight() / 2 * 96 / 72;
+            const offset = this.visibleHeight() / 2 * 96 / 72;
             this._mainCont.current && smoothScroll(500, this._mainCont.current, NumCast(scrollToAnnotation.y) - offset);
             Doc.linkFollowHighlight(scrollToAnnotation);
         }
@@ -356,7 +356,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
             this._annotationLayer.current.append(div);
             div.style.backgroundColor = "yellow";
             div.style.opacity = "0.5";
-            let savedPage = this._savedAnnotations.getValue(page);
+            const savedPage = this._savedAnnotations.getValue(page);
             if (savedPage) {
                 savedPage.push(div);
                 this._savedAnnotations.setValue(page, savedPage);
@@ -382,7 +382,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
             });
         }
         else if (this._mainCont.current) {
-            let executeFind = () => {
+            const executeFind = () => {
                 this._pdfViewer.findController.executeCommand('find', {
                     caseSensitive: false,
                     findPrevious: !fwd,
@@ -398,7 +398,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
 
     @action
     onPointerDown = (e: React.PointerEvent): void => {
-        let hit = document.elementFromPoint(e.clientX, e.clientY);
+        const hit = document.elementFromPoint(e.clientX, e.clientY);
         if (hit && hit.localName === "span" && this.props.isSelected(true)) {  // drag selecting text stops propagation
             e.button === 0 && e.stopPropagation();
         }
@@ -425,7 +425,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
             }
             else if (this._mainCont.current) {
                 // set marquee x and y positions to the spatially transformed position
-                let boundingRect = this._mainCont.current.getBoundingClientRect();
+                const boundingRect = this._mainCont.current.getBoundingClientRect();
                 this._startX = this._marqueeX = (e.clientX - boundingRect.left) * (this._mainCont.current.offsetWidth / boundingRect.width);
                 this._startY = this._marqueeY = (e.clientY - boundingRect.top) * (this._mainCont.current.offsetHeight / boundingRect.height) + this._mainCont.current.scrollTop;
                 this._marqueeHeight = this._marqueeWidth = 0;
@@ -442,7 +442,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
     onSelectMove = (e: PointerEvent): void => {
         if (this._marqueeing && this._mainCont.current) {
             // transform positions and find the width and height to set the marquee to
-            let boundingRect = this._mainCont.current.getBoundingClientRect();
+            const boundingRect = this._mainCont.current.getBoundingClientRect();
             this._marqueeWidth = ((e.clientX - boundingRect.left) * (this._mainCont.current.offsetWidth / boundingRect.width)) - this._startX;
             this._marqueeHeight = ((e.clientY - boundingRect.top) * (this._mainCont.current.offsetHeight / boundingRect.height)) - this._startY + this._mainCont.current.scrollTop;
             this._marqueeX = Math.min(this._startX, this._startX + this._marqueeWidth);
@@ -460,16 +460,16 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
     @action
     createTextAnnotation = (sel: Selection, selRange: Range) => {
         if (this._mainCont.current) {
-            let boundingRect = this._mainCont.current.getBoundingClientRect();
-            let clientRects = selRange.getClientRects();
+            const boundingRect = this._mainCont.current.getBoundingClientRect();
+            const clientRects = selRange.getClientRects();
             for (let i = 0; i < clientRects.length; i++) {
-                let rect = clientRects.item(i);
+                const rect = clientRects.item(i);
                 if (rect) {
-                    let scaleY = this._mainCont.current.offsetHeight / boundingRect.height;
-                    let scaleX = this._mainCont.current.offsetWidth / boundingRect.width;
+                    const scaleY = this._mainCont.current.offsetHeight / boundingRect.height;
+                    const scaleX = this._mainCont.current.offsetWidth / boundingRect.width;
                     if (rect.width !== this._mainCont.current.clientWidth &&
                         (i === 0 || !intersectRect(clientRects[i], clientRects[i - 1]))) {
-                        let annoBox = document.createElement("div");
+                        const annoBox = document.createElement("div");
                         annoBox.className = "pdfViewer-annotationBox";
                         // transforms the positions from screen onto the pdf div
                         annoBox.style.top = ((rect.top - boundingRect.top) * scaleY / this._zoomed + this._mainCont.current.scrollTop).toString();
@@ -497,10 +497,10 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
         this._savedAnnotations.clear();
         if (this._marqueeing) {
             if (this._marqueeWidth > 10 || this._marqueeHeight > 10) {
-                let marquees = this._mainCont.current!.getElementsByClassName("pdfViewer-dragAnnotationBox");
+                const marquees = this._mainCont.current!.getElementsByClassName("pdfViewer-dragAnnotationBox");
                 if (marquees && marquees.length) { // copy the marquee and convert it to a permanent annotation. 
-                    let style = (marquees[0] as HTMLDivElement).style;
-                    let copy = document.createElement("div");
+                    const style = (marquees[0] as HTMLDivElement).style;
+                    const copy = document.createElement("div");
                     copy.style.left = style.left;
                     copy.style.top = style.top;
                     copy.style.width = style.width;
@@ -521,9 +521,9 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
             this._marqueeing = false;
         }
         else {
-            let sel = window.getSelection();
+            const sel = window.getSelection();
             if (sel && sel.type === "Range") {
-                let selRange = sel.getRangeAt(0);
+                const selRange = sel.getRangeAt(0);
                 this.createTextAnnotation(sel, selRange);
                 PDFMenu.Instance.jumpTo(e.clientX, e.clientY);
             }
@@ -543,7 +543,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
     @action
     highlight = (color: string) => {
         // creates annotation documents for current highlights
-        let annotationDoc = this.makeAnnotationDocument(color);
+        const annotationDoc = this.makeAnnotationDocument(color);
         annotationDoc && this.props.addDocument && this.props.addDocument(annotationDoc);
         return annotationDoc;
     }
@@ -556,10 +556,10 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
     startDrag = (e: PointerEvent, ele: HTMLElement): void => {
         e.preventDefault();
         e.stopPropagation();
-        let targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "Note linked to " + this.props.Document.title });
+        const targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "Note linked to " + this.props.Document.title });
         const annotationDoc = this.highlight("rgba(146, 245, 95, 0.467)"); // yellowish highlight color when dragging out a text selection
         if (annotationDoc) {
-            let dragData = new DragManager.AnnotationDragData(this.props.Document, annotationDoc, targetDoc);
+            const dragData = new DragManager.AnnotationDragData(this.props.Document, annotationDoc, targetDoc);
             DragManager.StartAnnotationDrag([ele], dragData, e.pageX, e.pageY, {
                 handlers: {
                     dragComplete: () => !(dragData as any).linkedToDoc &&
@@ -572,8 +572,8 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
     }
 
     createSnippet = (marquee: { left: number, top: number, width: number, height: number }): void => {
-        let view = Doc.MakeAlias(this.props.Document);
-        let data = Doc.MakeDelegate(Doc.GetProto(this.props.Document));
+        const view = Doc.MakeAlias(this.props.Document);
+        const data = Doc.MakeDelegate(Doc.GetProto(this.props.Document));
         data.title = StrCast(data.title) + "_snippet";
         view.proto = data;
         view.nativeHeight = marquee.height;
@@ -605,8 +605,8 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
                 this.Document.nativeHeight = nativeWidth * this._coverPath.height / this._coverPath.width;
             }).bind(this), 0);
         }
-        let nativeWidth = (this.Document.nativeWidth || 0);
-        let nativeHeight = (this.Document.nativeHeight || 0);
+        const nativeWidth = (this.Document.nativeWidth || 0);
+        const nativeHeight = (this.Document.nativeHeight || 0);
         return <img key={this._coverPath.path} src={this._coverPath.path} onError={action(() => this._coverPath.path = "http://www.cs.brown.edu/~bcz/face.gif")} onLoad={action(() => this._showWaiting = false)}
             style={{ position: "absolute", display: "inline-block", top: 0, left: 0, width: `${nativeWidth}px`, height: `${nativeHeight}px` }} />;
     }
@@ -615,7 +615,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
     onZoomWheel = (e: React.WheelEvent) => {
         e.stopPropagation();
         if (e.ctrlKey) {
-            let curScale = Number(this._pdfViewer.currentScaleValue);
+            const curScale = Number(this._pdfViewer.currentScaleValue);
             this._pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale - curScale * e.deltaY / 1000));
             this._zoomed = Number(this._pdfViewer.currentScaleValue);
         }
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index f50a3a0ef..47f876ac2 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -161,9 +161,9 @@ export class PresElementBox extends DocComponent<FieldViewProps, PresDocument>(P
             return (null);
         }
 
-        let propDocWidth = NumCast(this.layoutDoc.nativeWidth);
-        let propDocHeight = NumCast(this.layoutDoc.nativeHeight);
-        let scale = () => 175 / NumCast(this.layoutDoc.nativeWidth, 175);
+        const propDocWidth = NumCast(this.layoutDoc.nativeWidth);
+        const propDocHeight = NumCast(this.layoutDoc.nativeHeight);
+        const scale = () => 175 / NumCast(this.layoutDoc.nativeWidth, 175);
         return (
             <div className="presElementBox-embedded" style={{
                 height: propDocHeight === 0 ? NumCast(this.layoutDoc.height) - NumCast(this.layoutDoc.collapsedHeight) : propDocHeight * scale(),
@@ -193,9 +193,9 @@ export class PresElementBox extends DocComponent<FieldViewProps, PresDocument>(P
     }
 
     render() {
-        let treecontainer = this.props.ContainingCollectionDoc && this.props.ContainingCollectionDoc.viewType === CollectionViewType.Tree;
-        let className = "presElementBox-item" + (this.currentIndex === this.indexInPres ? " presElementBox-selected" : "");
-        let pbi = "presElementBox-interaction";
+        const treecontainer = this.props.ContainingCollectionDoc && this.props.ContainingCollectionDoc.viewType === CollectionViewType.Tree;
+        const className = "presElementBox-item" + (this.currentIndex === this.indexInPres ? " presElementBox-selected" : "");
+        const pbi = "presElementBox-interaction";
         return (
             <div className={className} key={this.props.Document[Id] + this.indexInPres}
                 style={{ outlineWidth: Doc.IsBrushed(this.targetDoc) ? `1px` : "0px", }}
diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx
index 7812c9d97..12e1bc265 100644
--- a/src/client/views/search/FilterBox.tsx
+++ b/src/client/views/search/FilterBox.tsx
@@ -79,7 +79,7 @@ export class FilterBox extends React.Component {
                 acc[i].addEventListener("click", function (this: HTMLElement) {
                     this.classList.toggle("active");
 
-                    var panel = this.nextElementSibling as HTMLElement;
+                    const panel = this.nextElementSibling as HTMLElement;
                     if (panel.style.maxHeight) {
                         panel.style.overflow = "hidden";
                         panel.style.maxHeight = "";
@@ -96,7 +96,7 @@ export class FilterBox extends React.Component {
                     }
                 });
 
-                let el = acc[i] as HTMLElement;
+                const el = acc[i] as HTMLElement;
                 el.click();
             }
         });
@@ -105,14 +105,14 @@ export class FilterBox extends React.Component {
     @action.bound
     minimizeAll() {
         $('document').ready(function () {
-            var acc = document.getElementsByClassName('filter-header');
+            const acc = document.getElementsByClassName('filter-header');
 
             // tslint:disable-next-line: prefer-for-of
             for (var i = 0; i < acc.length; i++) {
-                let classList = acc[i].classList;
+                const classList = acc[i].classList;
                 if (classList.contains("active")) {
                     acc[i].classList.toggle("active");
-                    var panel = acc[i].nextElementSibling as HTMLElement;
+                    const panel = acc[i].nextElementSibling as HTMLElement;
                     panel.style.overflow = "hidden";
                     panel.style.maxHeight = "";
                 }
@@ -128,10 +128,10 @@ export class FilterBox extends React.Component {
     }
 
     basicRequireWords(query: string): string {
-        let oldWords = query.split(" ");
-        let newWords: string[] = [];
+        const oldWords = query.split(" ");
+        const newWords: string[] = [];
         oldWords.forEach(word => {
-            let newWrd = "+" + word;
+            const newWrd = "+" + word;
             newWords.push(newWrd);
         });
         query = newWords.join(" ");
@@ -140,7 +140,7 @@ export class FilterBox extends React.Component {
     }
 
     basicFieldFilters(query: string, type: string): string {
-        let oldWords = query.split(" ");
+        const oldWords = query.split(" ");
         let mod = "";
 
         if (type === Keys.AUTHOR) {
@@ -151,9 +151,9 @@ export class FilterBox extends React.Component {
             mod = " title_t:";
         }
 
-        let newWords: string[] = [];
+        const newWords: string[] = [];
         oldWords.forEach(word => {
-            let newWrd = mod + word;
+            const newWrd = mod + word;
             newWords.push(newWrd);
         });
 
@@ -183,11 +183,11 @@ export class FilterBox extends React.Component {
     //gets all of the collections of all the docviews that are selected
     //if a collection is the only thing selected, search only in that collection (not its container)
     getCurCollections(): Doc[] {
-        let selectedDocs: DocumentView[] = SelectionManager.SelectedDocuments();
-        let collections: Doc[] = [];
+        const selectedDocs: DocumentView[] = SelectionManager.SelectedDocuments();
+        const collections: Doc[] = [];
 
         selectedDocs.forEach(async element => {
-            let layout: string = StrCast(element.props.Document.layout);
+            const layout: string = StrCast(element.props.Document.layout);
             //checks if selected view (element) is a collection. if it is, adds to list to search through
             if (layout.indexOf("Collection") > -1) {
                 //makes sure collections aren't added more than once
@@ -229,14 +229,14 @@ export class FilterBox extends React.Component {
     }
 
     addCollectionFilter(query: string): string {
-        let collections: Doc[] = this.getCurCollections();
-        let oldWords = query.split(" ");
+        const collections: Doc[] = this.getCurCollections();
+        const oldWords = query.split(" ");
 
-        let collectionString: string[] = [];
+        const collectionString: string[] = [];
         collections.forEach(doc => {
-            let proto = doc.proto;
-            let protoId = (proto || doc)[Id];
-            let colString: string = "{!join from=data_l to=id}id:" + protoId + " ";
+            const proto = doc.proto;
+            const protoId = (proto || doc)[Id];
+            const colString: string = "{!join from=data_l to=id}id:" + protoId + " ";
             collectionString.push(colString);
         });
 
@@ -254,9 +254,9 @@ export class FilterBox extends React.Component {
         if (this._icons.length === 9) {
             return docs;
         }
-        let finalDocs: Doc[] = [];
+        const finalDocs: Doc[] = [];
         docs.forEach(doc => {
-            let layoutresult = Cast(doc.type, "string");
+            const layoutresult = Cast(doc.type, "string");
             if (layoutresult && this._icons.includes(layoutresult)) {
                 finalDocs.push(doc);
             }
diff --git a/src/client/views/search/IconButton.tsx b/src/client/views/search/IconButton.tsx
index d2cfe7fad..f01508141 100644
--- a/src/client/views/search/IconButton.tsx
+++ b/src/client/views/search/IconButton.tsx
@@ -108,7 +108,7 @@ export class IconButton extends React.Component<IconButtonProps>{
 
     @action.bound
     onClick = () => {
-        let newList: string[] = FilterBox.Instance.getIcons();
+        const newList: string[] = FilterBox.Instance.getIcons();
 
         if (!this._isSelected) {
             this._isSelected = true;
diff --git a/src/client/views/search/NaviconButton.tsx b/src/client/views/search/NaviconButton.tsx
index 3fa36b163..0fa4a0fca 100644
--- a/src/client/views/search/NaviconButton.tsx
+++ b/src/client/views/search/NaviconButton.tsx
@@ -4,7 +4,7 @@ import "./NaviconButton.scss";
 import * as $ from 'jquery';
 import { observable } from 'mobx';
 
-export interface NaviconProps{
+export interface NaviconProps {
     onClick(): void;
 }
 
@@ -13,19 +13,21 @@ export class NaviconButton extends React.Component<NaviconProps> {
     @observable private _ref: React.RefObject<HTMLAnchorElement> = React.createRef();
 
     componentDidMount = () => {
-        let that = this;
-        if(this._ref.current){this._ref.current.addEventListener("click", function(e) {
-            e.preventDefault();
-            if(that._ref.current){
-                that._ref.current.classList.toggle('active');
-                return false;
-            }
-        });}
+        const that = this;
+        if (this._ref.current) {
+            this._ref.current.addEventListener("click", function (e) {
+                e.preventDefault();
+                if (that._ref.current) {
+                    that._ref.current.classList.toggle('active');
+                    return false;
+                }
+            });
+        }
     }
 
     render() {
         return (
-            <a id="hamburger-icon" href="#" ref = {this._ref} title="Menu">
+            <a id="hamburger-icon" href="#" ref={this._ref} title="Menu">
                 <span className="line line-1"></span>
                 <span className="line line-2"></span>
                 <span className="line line-3"></span>
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index ff35542ed..c45fbe210 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -89,7 +89,7 @@ export class SearchBox extends React.Component {
 
     public static async convertDataUri(imageUri: string, returnedFilename: string) {
         try {
-            let posting = Utils.prepend("/uploadURI");
+            const posting = Utils.prepend("/uploadURI");
             const returnedUri = await rp.post(posting, {
                 body: {
                     uri: imageUri,
@@ -166,7 +166,7 @@ export class SearchBox extends React.Component {
                     const docs = await Promise.all(res.docs.map(async doc => (await Cast(doc.extendsDoc, Doc)) || doc));
                     const highlights: typeof res.highlighting = {};
                     docs.forEach((doc, index) => highlights[doc[Id]] = highlightList[index]);
-                    let filteredDocs = FilterBox.Instance.filterDocsByType(docs);
+                    const filteredDocs = FilterBox.Instance.filterDocsByType(docs);
                     runInAction(() => {
                         // this._results.push(...filteredDocs);
                         filteredDocs.forEach(doc => {
@@ -198,8 +198,8 @@ export class SearchBox extends React.Component {
 
     collectionRef = React.createRef<HTMLSpanElement>();
     startDragCollection = async () => {
-        let res = await this.getAllResults(FilterBox.Instance.getFinalQuery(this._searchString));
-        let filtered = FilterBox.Instance.filterDocsByType(res.docs);
+        const res = await this.getAllResults(FilterBox.Instance.getFinalQuery(this._searchString));
+        const filtered = FilterBox.Instance.filterDocsByType(res.docs);
         // console.log(this._results)
         const docs = filtered.map(doc => {
             const isProto = Doc.GetT(doc, "isPrototype", "boolean", true);
@@ -266,10 +266,10 @@ export class SearchBox extends React.Component {
 
     @action
     resultsScrolled = (e?: React.UIEvent<HTMLDivElement>) => {
-        let scrollY = e ? e.currentTarget.scrollTop : this.resultsRef.current ? this.resultsRef.current.scrollTop : 0;
-        let itemHght = 53;
-        let startIndex = Math.floor(Math.max(0, scrollY / itemHght));
-        let endIndex = Math.ceil(Math.min(this._numTotalResults - 1, startIndex + (this.resultsRef.current!.getBoundingClientRect().height / itemHght)));
+        const scrollY = e ? e.currentTarget.scrollTop : this.resultsRef.current ? this.resultsRef.current.scrollTop : 0;
+        const itemHght = 53;
+        const startIndex = Math.floor(Math.max(0, scrollY / itemHght));
+        const endIndex = Math.ceil(Math.min(this._numTotalResults - 1, startIndex + (this.resultsRef.current!.getBoundingClientRect().height / itemHght)));
 
         this._endIndex = endIndex === -1 ? 12 : endIndex;
 
@@ -307,7 +307,7 @@ export class SearchBox extends React.Component {
                         this.getResults(this._searchString);
                         if (i < this._results.length) result = this._results[i];
                         if (result) {
-                            let highlights = Array.from([...Array.from(new Set(result[1]).values())]).filter(v => v !== "search_string");
+                            const highlights = Array.from([...Array.from(new Set(result[1]).values())]).filter(v => v !== "search_string");
                             this._visibleElements[i] = <SearchItem doc={result[0]} query={this._searchString} key={result[0][Id]} lines={result[2]} highlighting={highlights} />;
                             this._isSearch[i] = "search";
                         }
@@ -315,7 +315,7 @@ export class SearchBox extends React.Component {
                     else {
                         result = this._results[i];
                         if (result) {
-                            let highlights = Array.from([...Array.from(new Set(result[1]).values())]).filter(v => v !== "search_string");
+                            const highlights = Array.from([...Array.from(new Set(result[1]).values())]).filter(v => v !== "search_string");
                             this._visibleElements[i] = <SearchItem doc={result[0]} query={this._searchString} key={result[0][Id]} lines={result[2]} highlighting={highlights} />;
                             this._isSearch[i] = "search";
                         }
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index f1d825aa0..6ffff0562 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -52,7 +52,7 @@ export class SelectorContextMenu extends React.Component<SearchItemProps> {
     }
 
     async fetchDocuments() {
-        let aliases = (await SearchUtil.GetViewsOfDocument(this.props.doc)).filter(doc => doc !== this.props.doc);
+        const aliases = (await SearchUtil.GetViewsOfDocument(this.props.doc)).filter(doc => doc !== this.props.doc);
         const { docs } = await SearchUtil.Search("", true, { fq: `data_l:"${this.props.doc[Id]}"` });
         const map: Map<Doc, Doc> = new Map;
         const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search("", true, { fq: `data_l:"${doc[Id]}"` }).then(result => result.docs)));
@@ -82,7 +82,7 @@ export class SelectorContextMenu extends React.Component<SearchItemProps> {
             <div className="parents">
                 <p className="contexts">Contexts:</p>
                 {[...this._docs, ...this._otherDocs].map(doc => {
-                    let item = React.createRef<HTMLDivElement>();
+                    const item = React.createRef<HTMLDivElement>();
                     return <div className="collection" key={doc.col[Id] + doc.target[Id]} ref={item}>
                         <div className="collection-item" onPointerDown={
                             SetupDrag(item, () => doc.col, undefined, undefined, undefined, undefined, () => SearchBox.Instance.closeSearch())}>
@@ -146,11 +146,11 @@ export class SearchItem extends React.Component<SearchItemProps> {
     //@computed
     @action
     public DocumentIcon() {
-        let layoutresult = StrCast(this.props.doc.type);
+        const layoutresult = StrCast(this.props.doc.type);
         if (!this._useIcons) {
-            let returnXDimension = () => this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE);
-            let returnYDimension = () => this._displayDim;
-            let scale = () => returnXDimension() / NumCast(Doc.Layout(this.props.doc).nativeWidth, returnXDimension());
+            const returnXDimension = () => this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE);
+            const returnYDimension = () => this._displayDim;
+            const scale = () => returnXDimension() / NumCast(Doc.Layout(this.props.doc).nativeWidth, returnXDimension());
             const docview = <div
                 onPointerDown={action(() => {
                     this._useIcons = !this._useIcons;
@@ -184,7 +184,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
             </div>;
             return docview;
         }
-        let button = layoutresult.indexOf(DocumentType.PDF) !== -1 ? faFilePdf :
+        const button = layoutresult.indexOf(DocumentType.PDF) !== -1 ? faFilePdf :
             layoutresult.indexOf(DocumentType.IMG) !== -1 ? faImage :
                 layoutresult.indexOf(DocumentType.TEXT) !== -1 ? faStickyNote :
                     layoutresult.indexOf(DocumentType.VID) !== -1 ? faFilm :
@@ -201,7 +201,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
 
     collectionRef = React.createRef<HTMLDivElement>();
     startDocDrag = () => {
-        let doc = this.props.doc;
+        const doc = this.props.doc;
         const isProto = Doc.GetT(doc, "isPrototype", "boolean", true);
         if (isProto) {
             return Doc.MakeDelegate(doc);
@@ -218,7 +218,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
 
     nextHighlight = (e: React.PointerEvent) => {
         e.preventDefault(); e.button === 0 && SearchBox.Instance.openSearch(e);
-        let sstring = StrCast(this.props.doc.search_string);
+        const sstring = StrCast(this.props.doc.search_string);
         this.props.doc.search_string = "";
         setTimeout(() => this.props.doc.search_string = sstring, 0);
     }
@@ -226,8 +226,8 @@ export class SearchItem extends React.Component<SearchItemProps> {
         if (this.props.doc.type === DocumentType.LINK) {
             if (this.props.doc.anchor1 && this.props.doc.anchor2) {
 
-                let doc1 = Cast(this.props.doc.anchor1, Doc, null);
-                let doc2 = Cast(this.props.doc.anchor2, Doc, null);
+                const doc1 = Cast(this.props.doc.anchor1, Doc, null);
+                const doc2 = Cast(this.props.doc.anchor2, Doc, null);
                 Doc.BrushDoc(doc1);
                 Doc.BrushDoc(doc2);
             }
@@ -241,8 +241,8 @@ export class SearchItem extends React.Component<SearchItemProps> {
         if (this.props.doc.type === DocumentType.LINK) {
             if (this.props.doc.anchor1 && this.props.doc.anchor2) {
 
-                let doc1 = Cast(this.props.doc.anchor1, Doc, null);
-                let doc2 = Cast(this.props.doc.anchor2, Doc, null);
+                const doc1 = Cast(this.props.doc.anchor1, Doc, null);
+                const doc2 = Cast(this.props.doc.anchor2, Doc, null);
                 Doc.UnBrushDoc(doc1);
                 Doc.UnBrushDoc(doc2);
             }
diff --git a/src/client/views/search/ToggleBar.tsx b/src/client/views/search/ToggleBar.tsx
index ed5ecd3ba..e4d7f2fd5 100644
--- a/src/client/views/search/ToggleBar.tsx
+++ b/src/client/views/search/ToggleBar.tsx
@@ -33,8 +33,7 @@ export class ToggleBar extends React.Component<ToggleBarProps>{
     }
 
     componentDidMount = () => {
-
-        let totalWidth = 265;
+        const totalWidth = 265;
 
         if (this._originalStatus) {
             this._forwardTimeline.add({
diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx
index 24db3f934..a26d2e06a 100644
--- a/src/debug/Viewer.tsx
+++ b/src/debug/Viewer.tsx
@@ -24,7 +24,7 @@ CursorField;
 function applyToDoc(doc: { [index: string]: FieldResult }, key: string, scriptString: string): boolean;
 function applyToDoc(doc: { [index: number]: FieldResult }, key: number, scriptString: string): boolean;
 function applyToDoc(doc: any, key: string | number, scriptString: string): boolean {
-    let script = CompileScript(scriptString, { addReturn: true, params: { this: doc instanceof Doc ? Doc.name : List.name } });
+    const script = CompileScript(scriptString, { addReturn: true, params: { this: doc instanceof Doc ? Doc.name : List.name } });
     if (!script.compiled) {
         return false;
     }
@@ -85,7 +85,7 @@ class DocumentViewer extends React.Component<{ field: Doc }> {
         let content;
         if (this.expanded) {
             const keys = Object.keys(this.props.field);
-            let fields = keys.map(key => {
+            const fields = keys.map(key => {
                 return (
                     <div key={key}>
                         <b>({key}): </b>
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
index 9fdaac66e..ec698b151 100644
--- a/src/mobile/ImageUpload.tsx
+++ b/src/mobile/ImageUpload.tsx
@@ -34,13 +34,13 @@ class Uploader extends React.Component {
         try {
             this.status = "initializing protos";
             await Docs.Prototypes.initialize();
-            let imgPrev = document.getElementById("img_preview");
+            const imgPrev = document.getElementById("img_preview");
             if (imgPrev) {
-                let files: FileList | null = inputRef.current!.files;
+                const files: FileList | null = inputRef.current!.files;
                 if (files && files.length !== 0) {
                     console.log(files[0]);
                     const name = files[0].name;
-                    let formData = new FormData();
+                    const formData = new FormData();
                     formData.append("file", files[0]);
 
                     const upload = window.location.origin + "/upload";
@@ -52,8 +52,8 @@ class Uploader extends React.Component {
                     this.status = "upload image, getting json";
                     const json = await res.json();
                     json.map(async (file: any) => {
-                        let path = window.location.origin + file;
-                        var doc = Docs.Create.ImageDocument(path, { nativeWidth: 200, width: 200, title: name });
+                        const path = window.location.origin + file;
+                        const doc = Docs.Create.ImageDocument(path, { nativeWidth: 200, width: 200, title: name });
 
                         this.status = "getting user document";
 
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index a811322d7..cf32fa74a 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -21,7 +21,7 @@ export namespace Field {
     export function toKeyValueString(doc: Doc, key: string): string {
         const onDelegate = Object.keys(doc).includes(key);
 
-        let field = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+        const field = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
         if (Field.IsField(field)) {
             return (onDelegate ? "=" : "") + (field instanceof ComputedField ? `:=${field.script.originalScript}` : Field.toScriptString(field));
         }
@@ -255,9 +255,9 @@ export namespace Doc {
         return GetT(doc, "isPrototype", "boolean", true);
     }
     export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) {
-        let hasProto = doc.proto instanceof Doc;
-        let onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1;
-        let onProto = hasProto && Object.getOwnPropertyNames(doc.proto).indexOf(key) !== -1;
+        const hasProto = doc.proto instanceof Doc;
+        const onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1;
+        const onProto = hasProto && Object.getOwnPropertyNames(doc.proto).indexOf(key) !== -1;
         if (onDeleg || !hasProto || (!onProto && !defaultProto)) {
             doc[key] = value;
         } else doc.proto![key] = value;
@@ -306,10 +306,10 @@ export namespace Doc {
     // compare whether documents or their protos match
     export function AreProtosEqual(doc?: Doc, other?: Doc) {
         if (!doc || !other) return false;
-        let r = (doc === other);
-        let r2 = (Doc.GetProto(doc) === other);
-        let r3 = (Doc.GetProto(other) === doc);
-        let r4 = (Doc.GetProto(doc) === Doc.GetProto(other) && Doc.GetProto(other) !== undefined);
+        const r = (doc === other);
+        const r2 = (Doc.GetProto(doc) === other);
+        const r3 = (Doc.GetProto(other) === doc);
+        const r4 = (Doc.GetProto(doc) === Doc.GetProto(other) && Doc.GetProto(other) !== undefined);
         return r || r2 || r3 || r4;
     }
 
@@ -318,7 +318,7 @@ export namespace Doc {
         return doc && (Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc));
     }
     export function GetDataDoc(doc: Doc): Doc {
-        let proto = Doc.GetProto(doc);
+        const proto = Doc.GetProto(doc);
         return proto === doc ? proto : Doc.GetDataDoc(proto);
     }
 
@@ -343,9 +343,9 @@ export namespace Doc {
         if (listDoc[key] === undefined) {
             Doc.GetProto(listDoc)[key] = new List<Doc>();
         }
-        let list = Cast(listDoc[key], listSpec(Doc));
+        const list = Cast(listDoc[key], listSpec(Doc));
         if (list) {
-            let ind = list.indexOf(doc);
+            const ind = list.indexOf(doc);
             if (ind !== -1) {
                 list.splice(ind, 1);
                 return true;
@@ -357,10 +357,10 @@ export namespace Doc {
         if (listDoc[key] === undefined) {
             Doc.GetProto(listDoc)[key] = new List<Doc>();
         }
-        let list = Cast(listDoc[key], listSpec(Doc));
+        const list = Cast(listDoc[key], listSpec(Doc));
         if (list) {
             if (allowDuplicates !== true) {
-                let pind = list.reduce((l, d, i) => d instanceof Doc && d[Id] === doc[Id] ? i : l, -1);
+                const pind = list.reduce((l, d, i) => d instanceof Doc && d[Id] === doc[Id] ? i : l, -1);
                 if (pind !== -1) {
                     list.splice(pind, 1);
                 }
@@ -369,7 +369,7 @@ export namespace Doc {
                 list.splice(0, 0, doc);
             }
             else {
-                let ind = relativeTo ? list.indexOf(relativeTo) : -1;
+                const ind = relativeTo ? list.indexOf(relativeTo) : -1;
                 if (ind === -1) {
                     if (reversed) list.splice(0, 0, doc);
                     else list.push(doc);
@@ -388,9 +388,9 @@ export namespace Doc {
     // Computes the bounds of the contents of a set of documents.
     //
     export function ComputeContentBounds(docList: Doc[]) {
-        let bounds = docList.reduce((bounds, doc) => {
-            var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
-            let [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
+        const bounds = docList.reduce((bounds, doc) => {
+            const [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
+            const [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
             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)
@@ -400,16 +400,16 @@ export namespace Doc {
     }
 
     export function MakeTitled(title: string) {
-        let doc = new Doc();
+        const doc = new Doc();
         doc.title = title;
         return doc;
     }
     export function MakeAlias(doc: Doc) {
-        let alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc) : Doc.MakeDelegate(doc);
+        const alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc) : Doc.MakeDelegate(doc);
         if (alias.layout instanceof Doc) {
             alias.layout = Doc.MakeAlias(alias.layout);
         }
-        let aliasNumber = Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1;
+        const aliasNumber = Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1;
         alias.title = ComputedField.MakeFunction(`renameAlias(this, ${aliasNumber})`);
         return alias;
     }
@@ -437,8 +437,8 @@ export namespace Doc {
         // ... which means we change the layout to be an expanded view of the template layout.  
         // This allows the view override the template's properties and be referenceable as its own document.
 
-        let expandedLayoutFieldKey = "Layout[" + templateLayoutDoc[Id] + "]";
-        let expandedTemplateLayout = dataDoc[expandedLayoutFieldKey];
+        const expandedLayoutFieldKey = "Layout[" + templateLayoutDoc[Id] + "]";
+        const expandedTemplateLayout = dataDoc[expandedLayoutFieldKey];
         if (expandedTemplateLayout instanceof Doc) {
             return expandedTemplateLayout;
         }
@@ -451,9 +451,9 @@ export namespace Doc {
 
     export function GetLayoutDataDocPair(doc: Doc, dataDoc: Doc | undefined, fieldKey: string, childDocLayout: Doc) {
         let layoutDoc: Doc | undefined = childDocLayout;
-        let resolvedDataDoc = !doc.isTemplateField && dataDoc !== doc && dataDoc ? Doc.GetDataDoc(dataDoc) : undefined;
+        const resolvedDataDoc = !doc.isTemplateField && dataDoc !== doc && dataDoc ? Doc.GetDataDoc(dataDoc) : undefined;
         if (resolvedDataDoc && Doc.WillExpandTemplateLayout(childDocLayout, resolvedDataDoc)) {
-            let extensionDoc = fieldExtensionDoc(resolvedDataDoc, StrCast(childDocLayout.templateField, StrCast(childDocLayout.title)));
+            const extensionDoc = fieldExtensionDoc(resolvedDataDoc, StrCast(childDocLayout.templateField, StrCast(childDocLayout.title)));
             layoutDoc = Doc.expandTemplateLayout(childDocLayout, extensionDoc !== resolvedDataDoc ? extensionDoc : undefined);
         } else layoutDoc = childDocLayout;
         return { layout: layoutDoc, data: resolvedDataDoc };
@@ -467,13 +467,13 @@ export namespace Doc {
     // to store annotations, ink, and other data.
     //
     export function fieldExtensionDoc(doc: Doc, fieldKey: string) {
-        let extension = doc[fieldKey + "_ext"] as Doc;
+        const extension = doc[fieldKey + "_ext"] as Doc;
         (extension === undefined) && setTimeout(() => CreateDocumentExtensionForField(doc, fieldKey), 0);
         return extension ? extension : undefined;
     }
 
     export function CreateDocumentExtensionForField(doc: Doc, fieldKey: string) {
-        let docExtensionForField = new Doc(doc[Id] + fieldKey, true);
+        const docExtensionForField = new Doc(doc[Id] + fieldKey, true);
         docExtensionForField.title = fieldKey + ".ext"; // courtesy field--- shouldn't be needed except maybe for debugging
         docExtensionForField.extendsDoc = doc; // this is used by search to map field matches on the extension doc back to the document it extends.
         docExtensionForField.extendsField = fieldKey; // this can be used by search to map matches on the extension doc back to the field that was extended.
@@ -512,7 +512,7 @@ export namespace Doc {
     export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string): Doc {
         const copy = new Doc(copyProtoId, true);
         Object.keys(doc).forEach(key => {
-            let cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+            const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
             const field = ProxyField.WithoutProxy(() => doc[key]);
             if (key === "proto" && copyProto) {
                 if (doc[key] instanceof Doc) {
@@ -551,7 +551,7 @@ export namespace Doc {
     let _applyCount: number = 0;
     export function ApplyTemplate(templateDoc: Doc) {
         if (templateDoc) {
-            let applied = ApplyTemplateTo(templateDoc, Doc.MakeDelegate(new Doc()), "layoutCustom", templateDoc.title + "(..." + _applyCount++ + ")");
+            const applied = ApplyTemplateTo(templateDoc, Doc.MakeDelegate(new Doc()), "layoutCustom", templateDoc.title + "(..." + _applyCount++ + ")");
             applied && (Doc.GetProto(applied).layout = applied.layout);
             return applied;
         }
@@ -567,7 +567,7 @@ export namespace Doc {
             return;
         }
 
-        let layoutCustomLayout = Doc.MakeDelegate(templateDoc);
+        const layoutCustomLayout = Doc.MakeDelegate(templateDoc);
 
         titleTarget && (Doc.GetProto(target).title = titleTarget);
         Doc.GetProto(target).type = DocumentType.TEMPLATE;
@@ -580,7 +580,7 @@ export namespace Doc {
 
     export function MakeMetadataFieldTemplate(fieldTemplate: Doc, templateDataDoc: Doc, suppressTitle: boolean = false): boolean {
         // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
-        let metadataFieldName = StrCast(fieldTemplate.title).replace(/^-/, "");
+        const metadataFieldName = StrCast(fieldTemplate.title).replace(/^-/, "");
         let fieldLayoutDoc = fieldTemplate;
         if (fieldTemplate.layout instanceof Doc) {
             fieldLayoutDoc = Doc.MakeDelegate(fieldTemplate.layout);
@@ -601,11 +601,11 @@ export namespace Doc {
         fieldTemplate.panY = 0;
         fieldTemplate.scale = 1;
         fieldTemplate.showTitle = suppressTitle ? undefined : "title";
-        let data = fieldTemplate.data;
+        const data = fieldTemplate.data;
         // setTimeout(action(() => {
         !templateDataDoc[metadataFieldName] && data instanceof ObjectField && (Doc.GetProto(templateDataDoc)[metadataFieldName] = ObjectField.MakeCopy(data));
-        let layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metadataFieldName}"}`);
-        let layoutDelegate = Doc.Layout(fieldTemplate);
+        const layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metadataFieldName}"}`);
+        const layoutDelegate = Doc.Layout(fieldTemplate);
         layoutDelegate.layout = layout;
         fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout;
         if (fieldTemplate.backgroundColor !== templateDataDoc.defaultBackgroundColor) fieldTemplate.defaultBackgroundColor = fieldTemplate.backgroundColor;
@@ -615,16 +615,16 @@ export namespace Doc {
     }
 
     export function overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) {
-        let doc2Layout = Doc.Layout(doc2);
-        let doc1Layout = Doc.Layout(doc1);
-        var x2 = NumCast(doc2.x) - clusterDistance;
-        var y2 = NumCast(doc2.y) - clusterDistance;
-        var w2 = NumCast(doc2Layout.width) + clusterDistance;
-        var h2 = NumCast(doc2Layout.height) + clusterDistance;
-        var x = NumCast(doc1.x) - clusterDistance;
-        var y = NumCast(doc1.y) - clusterDistance;
-        var w = NumCast(doc1Layout.width) + clusterDistance;
-        var h = NumCast(doc1Layout.height) + clusterDistance;
+        const doc2Layout = Doc.Layout(doc2);
+        const doc1Layout = Doc.Layout(doc1);
+        const x2 = NumCast(doc2.x) - clusterDistance;
+        const y2 = NumCast(doc2.y) - clusterDistance;
+        const w2 = NumCast(doc2Layout.width) + clusterDistance;
+        const h2 = NumCast(doc2Layout.height) + clusterDistance;
+        const x = NumCast(doc1.x) - clusterDistance;
+        const y = NumCast(doc1.y) - clusterDistance;
+        const w = NumCast(doc1Layout.width) + clusterDistance;
+        const h = NumCast(doc1Layout.height) + clusterDistance;
         return doc1.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 });
     }
 
@@ -691,7 +691,7 @@ export namespace Doc {
         Doc.HighlightDoc(destDoc);
         document.removeEventListener("pointerdown", linkFollowUnhighlight);
         document.addEventListener("pointerdown", linkFollowUnhighlight);
-        let x = dt = Date.now();
+        const x = dt = Date.now();
         window.setTimeout(() => dt === x && linkFollowUnhighlight(), 5000);
     }
 
@@ -700,7 +700,7 @@ export namespace Doc {
     }
     const highlightManager = new HighlightBrush();
     export function IsHighlighted(doc: Doc) {
-        let IsHighlighted = highlightManager.HighlightedDoc.get(doc) || highlightManager.HighlightedDoc.get(Doc.GetDataDoc(doc));
+        const IsHighlighted = highlightManager.HighlightedDoc.get(doc) || highlightManager.HighlightedDoc.get(Doc.GetDataDoc(doc));
         return IsHighlighted;
     }
     export function HighlightDoc(doc: Doc) {
@@ -716,10 +716,10 @@ export namespace Doc {
         });
     }
     export function UnhighlightAll() {
-        let mapEntries = highlightManager.HighlightedDoc.keys();
+        const mapEntries = highlightManager.HighlightedDoc.keys();
         let docEntry: IteratorResult<Doc>;
         while (!(docEntry = mapEntries.next()).done) {
-            let targetDoc = docEntry.value;
+            const targetDoc = docEntry.value;
             targetDoc && Doc.UnHighlightDoc(targetDoc);
         }
 
diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts
index 0c7b77fa5..e9101158b 100644
--- a/src/new_fields/List.ts
+++ b/src/new_fields/List.ts
@@ -270,8 +270,8 @@ class ListImpl<T extends Field> extends ObjectField {
     }
 
     [Copy]() {
-        let copiedData = this[Self].__fields.map(f => f instanceof ObjectField ? f[Copy]() : f);
-        let deepCopy = new ListImpl<T>(copiedData as any);
+        const copiedData = this[Self].__fields.map(f => f instanceof ObjectField ? f[Copy]() : f);
+        const deepCopy = new ListImpl<T>(copiedData as any);
         return deepCopy;
     }
 
diff --git a/src/new_fields/RichTextUtils.ts b/src/new_fields/RichTextUtils.ts
index dc5574782..682206aa2 100644
--- a/src/new_fields/RichTextUtils.ts
+++ b/src/new_fields/RichTextUtils.ts
@@ -25,8 +25,8 @@ export namespace RichTextUtils {
 
 
     export const Initialize = (initial?: string) => {
-        let content: any[] = [];
-        let state = {
+        const content: any[] = [];
+        const state = {
             doc: {
                 type: "doc",
                 content,
@@ -56,32 +56,32 @@ export namespace RichTextUtils {
 
     export const ToPlainText = (state: EditorState) => {
         // Because we're working with plain text, just concatenate all paragraphs
-        let content = state.doc.content;
-        let paragraphs: Node<any>[] = [];
+        const content = state.doc.content;
+        const paragraphs: Node<any>[] = [];
         content.forEach(node => node.type.name === "paragraph" && paragraphs.push(node));
 
         // Functions to flatten ProseMirror paragraph objects (and their components) to plain text
         // Concatentate paragraphs and string the result together
-        let textParagraphs: string[] = paragraphs.map(paragraph => {
-            let text: string[] = [];
+        const textParagraphs: string[] = paragraphs.map(paragraph => {
+            const text: string[] = [];
             paragraph.content.forEach(node => node.text && text.push(node.text));
             return text.join(joiner) + delimiter;
         });
-        let plainText = textParagraphs.join(joiner);
+        const plainText = textParagraphs.join(joiner);
         return plainText.substring(0, plainText.length - 1);
     };
 
     export const ToProsemirrorState = (plainText: string, oldState?: RichTextField) => {
         // Remap the text, creating blocks split on newlines
-        let elements = plainText.split(delimiter);
+        const elements = plainText.split(delimiter);
 
         // Google Docs adds in an extra carriage return automatically, so this counteracts it
         !elements[elements.length - 1].length && elements.pop();
 
         // Preserve the current state, but re-write the content to be the blocks
-        let parsed = JSON.parse(oldState ? oldState.Data : Initialize());
+        const parsed = JSON.parse(oldState ? oldState.Data : Initialize());
         parsed.doc.content = elements.map(text => {
-            let paragraph: any = { type: "paragraph" };
+            const paragraph: any = { type: "paragraph" };
             text.length && (paragraph.content = [{ type: "text", marks: [], text }]); // An empty paragraph gets treated as a line break
             return paragraph;
         });
@@ -97,7 +97,7 @@ export namespace RichTextUtils {
 
         export const Export = async (state: EditorState): Promise<GoogleApiClientUtils.Docs.Content> => {
             const nodes: (Node<any> | null)[] = [];
-            let text = ToPlainText(state);
+            const text = ToPlainText(state);
             state.doc.content.forEach(node => {
                 if (!node.childCount) {
                     nodes.push(null);
@@ -168,20 +168,20 @@ export namespace RichTextUtils {
             const title = document.title!;
             const { text, paragraphs } = GoogleApiClientUtils.Docs.Utils.extractText(document);
             let state = FormattedTextBox.blankState();
-            let structured = parseLists(paragraphs);
+            const structured = parseLists(paragraphs);
 
             let position = 3;
-            let lists: ListGroup[] = [];
+            const lists: ListGroup[] = [];
             const indentMap = new Map<ListGroup, BulletPosition[]>();
             let globalOffset = 0;
             const nodes: Node<any>[] = [];
-            for (let element of structured) {
+            for (const element of structured) {
                 if (Array.isArray(element)) {
                     lists.push(element);
-                    let positions: BulletPosition[] = [];
-                    let items = element.map(paragraph => {
-                        let item = listItem(state.schema, paragraph.contents);
-                        let sinks = paragraph.bullet!;
+                    const positions: BulletPosition[] = [];
+                    const items = element.map(paragraph => {
+                        const item = listItem(state.schema, paragraph.contents);
+                        const sinks = paragraph.bullet!;
                         positions.push({
                             value: position + globalOffset,
                             sinks
@@ -208,7 +208,7 @@ export namespace RichTextUtils {
                             }
                         });
                     } else {
-                        let paragraph = paragraphNode(state.schema, element.contents);
+                        const paragraph = paragraphNode(state.schema, element.contents);
                         nodes.push(paragraph);
                         position += paragraph.nodeSize;
                     }
@@ -216,11 +216,11 @@ export namespace RichTextUtils {
             }
             state = state.apply(state.tr.replaceWith(0, 2, nodes));
 
-            let sink = sinkListItem(state.schema.nodes.list_item);
-            let dispatcher = (tr: Transaction) => state = state.apply(tr);
-            for (let list of lists) {
-                for (let pos of indentMap.get(list)!) {
-                    let resolved = state.doc.resolve(pos.value);
+            const sink = sinkListItem(state.schema.nodes.list_item);
+            const dispatcher = (tr: Transaction) => state = state.apply(tr);
+            for (const list of lists) {
+                for (const pos of indentMap.get(list)!) {
+                    const resolved = state.doc.resolve(pos.value);
                     state = state.apply(state.tr.setSelection(new TextSelection(resolved)));
                     for (let i = 0; i < pos.sinks; i++) {
                         sink(state, dispatcher);
@@ -236,9 +236,9 @@ export namespace RichTextUtils {
         type PreparedParagraphs = (ListGroup | Paragraph)[];
 
         const parseLists = (paragraphs: ListGroup) => {
-            let groups: PreparedParagraphs = [];
+            const groups: PreparedParagraphs = [];
             let group: ListGroup = [];
-            for (let paragraph of paragraphs) {
+            for (const paragraph of paragraphs) {
                 if (paragraph.bullet !== undefined) {
                     group.push(paragraph);
                 } else {
@@ -262,8 +262,8 @@ export namespace RichTextUtils {
         };
 
         const paragraphNode = (schema: any, runs: docs_v1.Schema$TextRun[]): Node => {
-            let children = runs.map(run => textNode(schema, run)).filter(child => child !== undefined);
-            let fragment = children.length ? Fragment.from(children) : undefined;
+            const children = runs.map(run => textNode(schema, run)).filter(child => child !== undefined);
+            const fragment = children.length ? Fragment.from(children) : undefined;
             return schema.node("paragraph", null, fragment);
         };
 
@@ -284,7 +284,7 @@ export namespace RichTextUtils {
         };
 
         const textNode = (schema: any, run: docs_v1.Schema$TextRun) => {
-            let text = run.content!.removeTrailingNewlines();
+            const text = run.content!.removeTrailingNewlines();
             return text.length ? schema.text(text, styleToMarks(schema, run.textStyle)) : undefined;
         };
 
@@ -299,17 +299,17 @@ export namespace RichTextUtils {
             if (!textStyle) {
                 return undefined;
             }
-            let marks: Mark[] = [];
+            const marks: Mark[] = [];
             Object.keys(textStyle).forEach(key => {
                 let value: any;
-                let targeted = key as keyof docs_v1.Schema$TextStyle;
+                const targeted = key as keyof docs_v1.Schema$TextStyle;
                 if (value = textStyle[targeted]) {
-                    let attributes: any = {};
+                    const attributes: any = {};
                     let converted = StyleToMark.get(targeted) || targeted;
 
                     value.url && (attributes.href = value.url);
                     if (value.color) {
-                        let object = value.color.rgbColor;
+                        const object = value.color.rgbColor;
                         attributes.color = Color.rgb(["red", "green", "blue"].map(color => object[color] * 255 || 0)).hex();
                     }
                     if (value.magnitude) {
@@ -320,13 +320,13 @@ export namespace RichTextUtils {
                         converted = ImportFontFamilyMapping.get(value.fontFamily) || "timesNewRoman";
                     }
 
-                    let mapped = schema.marks[converted];
+                    const mapped = schema.marks[converted];
                     if (!mapped) {
                         alert(`No mapping found for ${converted}!`);
                         return;
                     }
 
-                    let mark = schema.mark(mapped, attributes);
+                    const mark = schema.mark(mapped, attributes);
                     mark && marks.push(mark);
                 }
             });
@@ -366,9 +366,9 @@ export namespace RichTextUtils {
         const ignored = ["user_mark"];
 
         const marksToStyle = async (nodes: (Node<any> | null)[]): Promise<docs_v1.Schema$Request[]> => {
-            let requests: docs_v1.Schema$Request[] = [];
+            const requests: docs_v1.Schema$Request[] = [];
             let position = 1;
-            for (let node of nodes) {
+            for (const node of nodes) {
                 if (node === null) {
                     position += 2;
                     continue;
@@ -382,7 +382,7 @@ export namespace RichTextUtils {
                 };
                 let mark: Mark<any>;
                 const markMap = BuildMarkMap(marks);
-                for (let markName of Object.keys(schema.marks)) {
+                for (const markName of Object.keys(schema.marks)) {
                     if (ignored.includes(markName) || !(mark = markMap[markName])) {
                         continue;
                     }
diff --git a/src/new_fields/Schema.ts b/src/new_fields/Schema.ts
index b1a449e08..3f0ff4284 100644
--- a/src/new_fields/Schema.ts
+++ b/src/new_fields/Schema.ts
@@ -23,7 +23,7 @@ export type makeInterface<T extends Interface[]> = AllToInterface<T> & Doc & { p
 // export function makeInterface<T extends Interface[], U extends Doc>(schemas: T): (doc: U) => All<T, U>;
 // export function makeInterface<T extends Interface, U extends Doc>(schema: T): (doc: U) => makeInterface<T, U>; 
 export function makeInterface<T extends Interface[]>(...schemas: T): InterfaceFunc<T> {
-    let schema: Interface = {};
+    const schema: Interface = {};
     for (const s of schemas) {
         for (const key in s) {
             schema[key] = s[key];
diff --git a/src/new_fields/ScriptField.ts b/src/new_fields/ScriptField.ts
index cdc9871a8..6b2723663 100644
--- a/src/new_fields/ScriptField.ts
+++ b/src/new_fields/ScriptField.ts
@@ -102,7 +102,7 @@ export class ScriptField extends ObjectField {
         return "script field";
     }
     public static CompileScript(script: string, params: object = {}, addReturn = false) {
-        let compiled = CompileScript(script, {
+        const compiled = CompileScript(script, {
             params: { this: Doc.name, ...params },
             typecheck: false,
             editable: true,
@@ -111,12 +111,12 @@ export class ScriptField extends ObjectField {
         return compiled;
     }
     public static MakeFunction(script: string, params: object = {}) {
-        let compiled = ScriptField.CompileScript(script, params, true);
+        const compiled = ScriptField.CompileScript(script, params, true);
         return compiled.compiled ? new ScriptField(compiled) : undefined;
     }
 
     public static MakeScript(script: string, params: object = {}) {
-        let compiled = ScriptField.CompileScript(script, params, false);
+        const compiled = ScriptField.CompileScript(script, params, false);
         return compiled.compiled ? new ScriptField(compiled) : undefined;
     }
 }
@@ -127,11 +127,11 @@ export class ComputedField extends ScriptField {
     //TODO maybe add an observable cache based on what is passed in for doc, considering there shouldn't really be that many possible values for doc
     value = computedFn((doc: Doc) => this.script.run({ this: doc }, console.log).result);
     public static MakeScript(script: string, params: object = {}, ) {
-        let compiled = ScriptField.CompileScript(script, params, false);
+        const compiled = ScriptField.CompileScript(script, params, false);
         return compiled.compiled ? new ComputedField(compiled) : undefined;
     }
     public static MakeFunction(script: string, params: object = {}) {
-        let compiled = ScriptField.CompileScript(script, params, true);
+        const compiled = ScriptField.CompileScript(script, params, true);
         return compiled.compiled ? new ComputedField(compiled) : undefined;
     }
 }
diff --git a/src/server/ActionUtilities.ts b/src/server/ActionUtilities.ts
index c9fc86fea..c155796a8 100644
--- a/src/server/ActionUtilities.ts
+++ b/src/server/ActionUtilities.ts
@@ -3,10 +3,11 @@ import { ExecOptions } from 'shelljs';
 import { exec } from 'child_process';
 import * as path from 'path';
 import * as rimraf from "rimraf";
+import { yellow } from 'colors';
 
 export const command_line = (command: string, fromDirectory?: string) => {
     return new Promise<string>((resolve, reject) => {
-        let options: ExecOptions = {};
+        const options: ExecOptions = {};
         if (fromDirectory) {
             options.cwd = path.join(__dirname, fromDirectory);
         }
@@ -15,14 +16,14 @@ export const command_line = (command: string, fromDirectory?: string) => {
 };
 
 export const read_text_file = (relativePath: string) => {
-    let target = path.join(__dirname, relativePath);
+    const target = path.join(__dirname, relativePath);
     return new Promise<string>((resolve, reject) => {
         fs.readFile(target, (err, data) => err ? reject(err) : resolve(data.toString()));
     });
 };
 
 export const write_text_file = (relativePath: string, contents: any) => {
-    let target = path.join(__dirname, relativePath);
+    const target = path.join(__dirname, relativePath);
     return new Promise<void>((resolve, reject) => {
         fs.writeFile(target, contents, (err) => err ? reject(err) : resolve());
     });
@@ -42,31 +43,19 @@ export async function log_execution({ startMessage, endMessage, action }: LogDat
     console.log(color, endMessage);
 }
 
-export enum ConsoleColors {
-    Black = `\x1b[30m%s\x1b[0m`,
-    Red = `\x1b[31m%s\x1b[0m`,
-    Green = `\x1b[32m%s\x1b[0m`,
-    Yellow = `\x1b[33m%s\x1b[0m`,
-    Blue = `\x1b[34m%s\x1b[0m`,
-    Magenta = `\x1b[35m%s\x1b[0m`,
-    Cyan = `\x1b[36m%s\x1b[0m`,
-    White = `\x1b[37m%s\x1b[0m`
-}
-
 export function logPort(listener: string, port: number) {
-    process.stdout.write(`${listener} listening on port `);
-    console.log(ConsoleColors.Yellow, port);
+    console.log(`${listener} listening on port ${yellow(String(port))}`);
 }
 
 export function msToTime(duration: number) {
-    let milliseconds = Math.floor((duration % 1000) / 100),
+    const milliseconds = Math.floor((duration % 1000) / 100),
         seconds = Math.floor((duration / 1000) % 60),
         minutes = Math.floor((duration / (1000 * 60)) % 60),
         hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
 
-    let hoursS = (hours < 10) ? "0" + hours : hours;
-    let minutesS = (minutes < 10) ? "0" + minutes : minutes;
-    let secondsS = (seconds < 10) ? "0" + seconds : seconds;
+    const hoursS = (hours < 10) ? "0" + hours : hours;
+    const minutesS = (minutes < 10) ? "0" + minutes : minutes;
+    const secondsS = (seconds < 10) ? "0" + seconds : seconds;
 
     return hoursS + ":" + minutesS + ":" + secondsS + "." + milliseconds;
 }
diff --git a/src/server/ApiManagers/DownloadManager.ts b/src/server/ApiManagers/DownloadManager.ts
index 5bad46eda..2280739fc 100644
--- a/src/server/ApiManagers/DownloadManager.ts
+++ b/src/server/ApiManagers/DownloadManager.ts
@@ -57,14 +57,13 @@ export default class DownloadManager extends ApiManager {
 
         register({
             method: Method.GET,
-            subscription: new RouteSubscriber("/serializeDoc").add("docId"),
+            subscription: new RouteSubscriber("serializeDoc").add("docId"),
             onValidation: async ({ req, res }) => {
                 const { docs, files } = await getDocs(req.params.docId);
                 res.send({ docs, files: Array.from(files) });
             }
         });
 
-
     }
 
 }
diff --git a/src/server/ApiManagers/GeneralGoogleManager.ts b/src/server/ApiManagers/GeneralGoogleManager.ts
index 629684e0c..3617779d5 100644
--- a/src/server/ApiManagers/GeneralGoogleManager.ts
+++ b/src/server/ApiManagers/GeneralGoogleManager.ts
@@ -38,16 +38,19 @@ export default class GeneralGoogleManager extends ApiManager {
 
         register({
             method: Method.POST,
-            subscription: new RouteSubscriber("/googleDocs").add("sector", "action"),
+            subscription: new RouteSubscriber("googleDocs").add("sector", "action"),
             onValidation: async ({ req, res, user }) => {
-                let sector: GoogleApiServerUtils.Service = req.params.sector as GoogleApiServerUtils.Service;
-                let action: GoogleApiServerUtils.Action = req.params.action as GoogleApiServerUtils.Action;
+                const sector: GoogleApiServerUtils.Service = req.params.sector as GoogleApiServerUtils.Service;
+                const action: GoogleApiServerUtils.Action = req.params.action as GoogleApiServerUtils.Action;
                 const endpoint = await GoogleApiServerUtils.GetEndpoint(GoogleApiServerUtils.Service[sector], user.id);
-                let handler = EndpointHandlerMap.get(action);
+                const handler = EndpointHandlerMap.get(action);
                 if (endpoint && handler) {
-                    handler(endpoint, req.body)
-                        .then(response => res.send(response.data))
-                        .catch(exception => res.send(exception));
+                    try {
+                        const response = await handler(endpoint, req.body);
+                        res.send(response.data);
+                    } catch (e) {
+                        res.send(e);
+                    }
                     return;
                 }
                 res.send(undefined);
diff --git a/src/server/ApiManagers/GooglePhotosManager.ts b/src/server/ApiManagers/GooglePhotosManager.ts
index 4a0c0b936..e2539f120 100644
--- a/src/server/ApiManagers/GooglePhotosManager.ts
+++ b/src/server/ApiManagers/GooglePhotosManager.ts
@@ -47,7 +47,7 @@ export default class GooglePhotosManager extends ApiManager {
                 if (!token) {
                     return _error(res, authenticationError);
                 }
-                let failed: GooglePhotosUploadFailure[] = [];
+                const failed: GooglePhotosUploadFailure[] = [];
                 const batched = BatchedArray.from<GooglePhotosUploadUtils.UploadSource>(media, { batchSize: 25 });
                 const newMediaItems = await batched.batchedMapPatientInterval<NewMediaItem>(
                     { magnitude: 100, unit: TimeUnit.Milliseconds },
@@ -87,7 +87,7 @@ export default class GooglePhotosManager extends ApiManager {
                 let failed = 0;
                 if (contents) {
                     const completed: Opt<DashUploadUtils.ImageUploadInformation>[] = [];
-                    for (let item of contents.mediaItems) {
+                    for (const item of contents.mediaItems) {
                         const { contentSize, ...attributes } = await DashUploadUtils.InspectImage(item.baseUrl);
                         const found: Opt<DashUploadUtils.ImageUploadInformation> = await Database.Auxiliary.QueryUploadHistory(contentSize!);
                         if (!found) {
diff --git a/src/server/ApiManagers/PDFManager.ts b/src/server/ApiManagers/PDFManager.ts
index 151b48dd9..a190ab0cb 100644
--- a/src/server/ApiManagers/PDFManager.ts
+++ b/src/server/ApiManagers/PDFManager.ts
@@ -8,7 +8,7 @@ const imageSize = require("probe-image-size");
 import * as express from "express";
 import * as path from "path";
 import { Directory, serverPathToFile, clientPathToFile } from "./UploadManager";
-import { ConsoleColors } from "../ActionUtilities";
+import { red } from "colors";
 
 export default class PDFManager extends ApiManager {
 
@@ -32,10 +32,10 @@ function getOrCreateThumbnail(thumbnailName: string, res: express.Response) {
         const path = serverPathToFile(Directory.pdf_thumbnails, thumbnailName);
         exists(path, (exists: boolean) => {
             if (exists) {
-                let existingThumbnail = createReadStream(path);
+                const existingThumbnail = createReadStream(path);
                 imageSize(existingThumbnail, (err: any, { width, height }: any) => {
                     if (err) {
-                        console.log(ConsoleColors.Red, `In PDF thumbnail response, unable to determine dimensions of ${thumbnailName}:`);
+                        console.log(red(`In PDF thumbnail response, unable to determine dimensions of ${thumbnailName}:`));
                         console.log(err);
                         return;
                     }
@@ -81,7 +81,7 @@ async function CreateThumbnail(file: string, pageNumber: number, res: express.Re
         });
     });
     out.on("error", error => {
-        console.log(ConsoleColors.Red, `In PDF thumbnail creation, encountered the following error when piping ${pngFile}:`);
+        console.log(red(`In PDF thumbnail creation, encountered the following error when piping ${pngFile}:`));
         console.log(error);
     });
 }
@@ -89,8 +89,8 @@ async function CreateThumbnail(file: string, pageNumber: number, res: express.Re
 class NodeCanvasFactory {
 
     create = (width: number, height: number) => {
-        var canvas = createCanvas(width, height);
-        var context = canvas.getContext('2d');
+        const canvas = createCanvas(width, height);
+        const context = canvas.getContext('2d');
         return {
             canvas,
             context
diff --git a/src/server/ApiManagers/SearchManager.ts b/src/server/ApiManagers/SearchManager.ts
index d3f8995b0..7afecbb18 100644
--- a/src/server/ApiManagers/SearchManager.ts
+++ b/src/server/ApiManagers/SearchManager.ts
@@ -1,7 +1,7 @@
 import ApiManager, { Registration } from "./ApiManager";
 import { Method } from "../RouteManager";
 import { Search } from "../Search";
-var findInFiles = require('find-in-files');
+const findInFiles = require('find-in-files');
 import * as path from 'path';
 import { pathToDirectory, Directory } from "./UploadManager";
 
@@ -13,14 +13,14 @@ export default class SearchManager extends ApiManager {
             method: Method.GET,
             subscription: "/textsearch",
             onValidation: async ({ req, res }) => {
-                let q = req.query.q;
+                const q = req.query.q;
                 if (q === undefined) {
                     res.send([]);
                     return;
                 }
-                let results = await findInFiles.find({ 'term': q, 'flags': 'ig' }, pathToDirectory(Directory.text), ".txt$");
-                let resObj: { ids: string[], numFound: number, lines: string[] } = { ids: [], numFound: 0, lines: [] };
-                for (var result in results) {
+                const results = await findInFiles.find({ 'term': q, 'flags': 'ig' }, pathToDirectory(Directory.text), ".txt$");
+                const resObj: { ids: string[], numFound: number, lines: string[] } = { ids: [], numFound: 0, lines: [] };
+                for (const result in results) {
                     resObj.ids.push(path.basename(result, ".txt").replace(/upload_/, ""));
                     resObj.lines.push(results[result].line);
                     resObj.numFound++;
@@ -39,7 +39,7 @@ export default class SearchManager extends ApiManager {
                     res.send([]);
                     return;
                 }
-                let results = await Search.Instance.search(solrQuery);
+                const results = await Search.Instance.search(solrQuery);
                 res.send(results);
             }
         });
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 80ae0ad61..da1f83b75 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -2,7 +2,7 @@ import ApiManager, { Registration } from "./ApiManager";
 import { Method, _success } from "../RouteManager";
 import * as formidable from 'formidable';
 import v4 = require('uuid/v4');
-var AdmZip = require('adm-zip');
+const AdmZip = require('adm-zip');
 import { extname, basename, dirname } from 'path';
 import { createReadStream, createWriteStream, unlink, readFileSync } from "fs";
 import { publicDirectory, filesDirectory } from "..";
@@ -42,12 +42,12 @@ export default class UploadManager extends ApiManager {
             method: Method.POST,
             subscription: "/upload",
             onValidation: async ({ req, res }) => {
-                let form = new formidable.IncomingForm();
+                const form = new formidable.IncomingForm();
                 form.uploadDir = pathToDirectory(Directory.parsed_files);
                 form.keepExtensions = true;
                 return new Promise<void>(resolve => {
                     form.parse(req, async (_err, _fields, files) => {
-                        let results: any[] = [];
+                        const results: any[] = [];
                         for (const key in files) {
                             const result = await DashUploadUtils.upload(files[key]);
                             result && results.push(result);
@@ -63,7 +63,7 @@ export default class UploadManager extends ApiManager {
             method: Method.POST,
             subscription: "/uploadDoc",
             onValidation: ({ req, res }) => {
-                let form = new formidable.IncomingForm();
+                const form = new formidable.IncomingForm();
                 form.keepExtensions = true;
                 // let path = req.body.path;
                 const ids: { [id: string]: string } = {};
@@ -122,8 +122,8 @@ export default class UploadManager extends ApiManager {
                                 zip.getEntries().forEach((entry: any) => {
                                     if (!entry.entryName.startsWith("files/")) return;
                                     let directory = dirname(entry.entryName) + "/";
-                                    let extension = extname(entry.entryName);
-                                    let base = basename(entry.entryName).split(".")[0];
+                                    const extension = extname(entry.entryName);
+                                    const base = basename(entry.entryName).split(".")[0];
                                     try {
                                         zip.extractEntryTo(entry.entryName, publicDirectory, true, false);
                                         directory = "/" + directory;
@@ -139,7 +139,7 @@ export default class UploadManager extends ApiManager {
                                 const json = zip.getEntry("doc.json");
                                 let docs: any;
                                 try {
-                                    let data = JSON.parse(json.getData().toString("utf8"));
+                                    const data = JSON.parse(json.getData().toString("utf8"));
                                     docs = data.docs;
                                     id = data.id;
                                     docs = Object.keys(docs).map(key => docs[key]);
@@ -189,7 +189,7 @@ export default class UploadManager extends ApiManager {
                 return imageDataUri.outputFile(uri, serverPathToFile(Directory.images, filename)).then((savedName: string) => {
                     const ext = extname(savedName).toLowerCase();
                     const { pngs, jpgs } = AcceptibleMedia;
-                    let resizers = [
+                    const resizers = [
                         { resizer: sharp().resize(100, undefined, { withoutEnlargement: true }), suffix: "_s" },
                         { resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: "_m" },
                         { resizer: sharp().resize(900, undefined, { withoutEnlargement: true }), suffix: "_l" },
diff --git a/src/server/ApiManagers/UtilManager.ts b/src/server/ApiManagers/UtilManager.ts
index c1234be6c..601a7d0d0 100644
--- a/src/server/ApiManagers/UtilManager.ts
+++ b/src/server/ApiManagers/UtilManager.ts
@@ -35,11 +35,11 @@ export default class UtilManager extends ApiManager {
             method: Method.GET,
             subscription: "/buxton",
             onValidation: async ({ res }) => {
-                let cwd = '../scraping/buxton';
+                const cwd = '../scraping/buxton';
 
-                let onResolved = (stdout: string) => { console.log(stdout); res.redirect("/"); };
-                let onRejected = (err: any) => { console.error(err.message); res.send(err); };
-                let tryPython3 = () => command_line('python3 scraper.py', cwd).then(onResolved, onRejected);
+                const onResolved = (stdout: string) => { console.log(stdout); res.redirect("/"); };
+                const onRejected = (err: any) => { console.error(err.message); res.send(err); };
+                const tryPython3 = () => command_line('python3 scraper.py', cwd).then(onResolved, onRejected);
 
                 return command_line('python scraper.py', cwd).then(onResolved, tryPython3);
             },
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index 9ccc72e35..d9d985ca5 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -9,10 +9,11 @@ import { AcceptibleMedia } from './SharedMediaTypes';
 import { filesDirectory } from '.';
 import { File } from 'formidable';
 import { basename } from "path";
-import { ConsoleColors, createIfNotExists } from './ActionUtilities';
+import { createIfNotExists } from './ActionUtilities';
 import { ParsedPDF } from "../server/PdfTypes";
 const parse = require('pdf-parse');
 import { Directory, serverPathToFile, clientPathToFile } from './ApiManagers/UploadManager';
+import { red } from 'colors';
 
 export enum SizeSuffix {
     Small = "_s",
@@ -86,12 +87,12 @@ export namespace DashUploadUtils {
                 }
         }
 
-        console.log(ConsoleColors.Red, `Ignoring unsupported file (${name}) with upload type (${type}).`);
+        console.log(red(`Ignoring unsupported file (${name}) with upload type (${type}).`));
         return { clientAccessPath: undefined };
     }
 
     async function UploadPdf(absolutePath: string) {
-        let dataBuffer = fs.readFileSync(absolutePath);
+        const dataBuffer = fs.readFileSync(absolutePath);
         const result: ParsedPDF = await parse(dataBuffer);
         const parsedName = basename(absolutePath);
         await new Promise<void>((resolve, reject) => {
@@ -194,7 +195,7 @@ export namespace DashUploadUtils {
         const { isLocal, stream, normalizedUrl, contentSize, contentType, exifData } = metadata;
         const resolved = filename || generate(prefix, normalizedUrl);
         const extension = format || sanitizeExtension(normalizedUrl || resolved);
-        let information: ImageUploadInformation = {
+        const information: ImageUploadInformation = {
             clientAccessPath: clientPathToFile(Directory.images, resolved),
             serverAccessPaths: {},
             exifData,
@@ -215,8 +216,7 @@ export namespace DashUploadUtils {
             } else if (jpgs.includes(extension)) {
                 resizers.forEach(element => element.resizer = element.resizer.jpeg());
             }
-            for (let { resizer, suffix } of resizers) {
-                let mediaPath: string;
+            for (const { resizer, suffix } of resizers) {
                 await new Promise<void>(resolve => {
                     const filename = InjectSize(resolved, suffix);
                     information.serverAccessPaths[suffix] = serverPathToFile(Directory.images, filename);
diff --git a/src/server/Initialization.ts b/src/server/Initialization.ts
index 8b633a7cd..74770153d 100644
--- a/src/server/Initialization.ts
+++ b/src/server/Initialization.ts
@@ -19,8 +19,9 @@ import * as fs from 'fs';
 import * as request from 'request';
 import RouteSubscriber from './RouteSubscriber';
 import { publicDirectory } from '.';
-import { ConsoleColors, logPort } from './ActionUtilities';
+import { logPort } from './ActionUtilities';
 import { timeMap } from './ApiManagers/UserManager';
+import { blue, yellow } from 'colors';
 
 /* RouteSetter is a wrapper around the server that prevents the server
    from being exposed. */
@@ -53,10 +54,13 @@ export default async function InitializeServer(options: InitializationOptions) {
     registerAuthenticationRoutes(server);
     registerCorsProxy(server);
 
-    const isRelease = determineEnvironment(); //vs. dev mode
+    const isRelease = determineEnvironment();
     routeSetter(new RouteManager(server, isRelease));
 
-    server.listen(listenAtPort, () => logPort("server", listenAtPort));
+    server.listen(listenAtPort, () => {
+        logPort("server", listenAtPort);
+        console.log();
+    });
     return isRelease;
 }
 
@@ -92,8 +96,9 @@ function buildWithMiddleware(server: express.Express) {
 function determineEnvironment() {
     const isRelease = process.env.RELEASE === "true";
 
-    console.log(`running server in ${isRelease ? 'release' : 'debug'} mode`);
-    console.log(process.env.PWD);
+    const color = isRelease ? blue : yellow;
+    const label = isRelease ? "release" : "development";
+    console.log(`\nrunning server in ${color(label)} mode`);
 
     let clientUtils = fs.readFileSync("./src/client/util/ClientUtils.ts.temp", "utf8");
     clientUtils = `//AUTO-GENERATED FILE: DO NOT EDIT\n${clientUtils.replace('"mode"', String(isRelease))}`;
diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts
index 7c49485f1..3af36d879 100644
--- a/src/server/RouteManager.ts
+++ b/src/server/RouteManager.ts
@@ -1,7 +1,7 @@
 import RouteSubscriber from "./RouteSubscriber";
 import { DashUserModel } from "./authentication/models/user_model";
 import * as express from 'express';
-import { ConsoleColors } from "./ActionUtilities";
+import { yellow, cyan, red } from 'colors';
 
 export enum Method {
     GET,
@@ -41,6 +41,12 @@ export default class RouteManager {
         this._isRelease = isRelease;
     }
 
+    log = () => {
+        console.log(yellow("\nthe following server routes have been registered:"));
+        Array.from(registered.keys()).sort().forEach(route => console.log(cyan(route)));
+        console.log();
+    }
+
     /**
      * 
      * @param initializer 
@@ -48,14 +54,14 @@ export default class RouteManager {
     addSupervisedRoute = (initializer: RouteInitializer): void => {
         const { method, subscription, onValidation, onUnauthenticated, onError } = initializer;
         const isRelease = this._isRelease;
-        let supervised = async (req: express.Request, res: express.Response) => {
+        const supervised = async (req: express.Request, res: express.Response) => {
             const { user, originalUrl: target } = req;
             const core = { req, res, isRelease };
             const tryExecute = async (toExecute: (args: any) => any | Promise<any>, args: any) => {
                 try {
                     await toExecute(args);
                 } catch (e) {
-                    console.log(ConsoleColors.Red, target, user?.email ?? "<user logged out>");
+                    console.log(red(target), user?.email ?? "<user logged out>");
                     if (onError) {
                         onError({ ...core, error: e });
                     } else {
@@ -78,7 +84,7 @@ export default class RouteManager {
             }
             setTimeout(() => {
                 if (!res.headersSent) {
-                    console.log("Initiating fallback for ", target);
+                    console.log(`Initiating fallback for ${target}`);
                     const warning = `request to ${target} fell through - this is a fallback response`;
                     res.send({ warning });
                 }
@@ -94,7 +100,7 @@ export default class RouteManager {
             const existing = registered.get(route);
             if (existing) {
                 if (existing.has(method)) {
-                    console.log(ConsoleColors.Red, `\nDuplicate registration error: already registered ${route} with Method[${method}]`);
+                    console.log(red(`\nDuplicate registration error: already registered ${route} with Method[${method}]`));
                     console.log('Please remove duplicate registrations before continuing...\n');
                     process.exit(0);
                 }
diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts
index fbf71f707..2eb764945 100644
--- a/src/server/Websocket/Websocket.ts
+++ b/src/server/Websocket/Websocket.ts
@@ -7,12 +7,13 @@ import { Search } from "../Search";
 import * as io from 'socket.io';
 import YoutubeApi from "../apis/youtube/youtubeApiSample";
 import { GoogleCredentialsLoader } from "../credentials/CredentialsLoader";
-import { ConsoleColors, logPort } from "../ActionUtilities";
+import { logPort } from "../ActionUtilities";
 import { timeMap } from "../ApiManagers/UserManager";
+import { green } from "colors";
 
 export namespace WebSocket {
 
-    let clients: { [key: string]: Client } = {};
+    const clients: { [key: string]: Client } = {};
     export const socketMap = new Map<SocketIO.Socket, string>();
 
     export async function start(serverPort: number, isRelease: boolean) {
@@ -27,7 +28,7 @@ export namespace WebSocket {
         const endpoint = io();
         endpoint.on("connection", function (socket: Socket) {
             socket.use((_packet, next) => {
-                let userEmail = socketMap.get(socket);
+                const userEmail = socketMap.get(socket);
                 if (userEmail) {
                     timeMap[userEmail] = Date.now();
                 }
@@ -85,7 +86,7 @@ export namespace WebSocket {
 
     function barReceived(socket: SocketIO.Socket, userEmail: string) {
         clients[userEmail] = new Client(userEmail.toString());
-        console.log(ConsoleColors.Green, `user ${userEmail} has connected to the web socket`);
+        console.log(green(`user ${userEmail} has connected to the web socket`));
         socketMap.set(socket, userEmail);
     }
 
@@ -178,12 +179,12 @@ export namespace WebSocket {
         for (let key in docfield) {
             if (!key.startsWith("fields.")) continue;
             dynfield = true;
-            let val = docfield[key];
+            const val = docfield[key];
             key = key.substring(7);
             Object.values(suffixMap).forEach(suf => update[key + getSuffix(suf)] = { set: null });
-            let term = ToSearchTerm(val);
+            const term = ToSearchTerm(val);
             if (term !== undefined) {
-                let { suffix, value } = term;
+                const { suffix, value } = term;
                 update[key + suffix] = { set: value };
             }
         }
diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts
index b0f3ba993..329107a71 100644
--- a/src/server/apis/google/GoogleApiServerUtils.ts
+++ b/src/server/apis/google/GoogleApiServerUtils.ts
@@ -135,7 +135,7 @@ export namespace GoogleApiServerUtils {
                 return resolve();
             }
             let routed: Opt<Endpoint>;
-            let parameters: any = { auth, version: "v1" };
+            const parameters: any = { auth, version: "v1" };
             switch (sector) {
                 case Service.Documents:
                     routed = google.docs(parameters).documents;
@@ -318,7 +318,7 @@ export namespace GoogleApiServerUtils {
      */
     async function retrieveCredentials(userId: string): Promise<{ credentials: Opt<Credentials>, refreshed: boolean }> {
         let credentials: Opt<Credentials> = await Database.Auxiliary.GoogleAuthenticationToken.Fetch(userId);
-        let refreshed = false;
+        const refreshed = false;
         if (!credentials) {
             return { credentials: undefined, refreshed };
         }
@@ -340,9 +340,9 @@ export namespace GoogleApiServerUtils {
      * @returns the updated credentials
      */
     async function refreshAccessToken(credentials: Credentials, userId: string): Promise<Credentials> {
-        let headerParameters = { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } };
+        const headerParameters = { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } };
         const { client_id, client_secret } = GoogleCredentialsLoader.ProjectCredentials;
-        let url = `https://oauth2.googleapis.com/token?${qs.stringify({
+        const url = `https://oauth2.googleapis.com/token?${qs.stringify({
             refreshToken: credentials.refresh_token,
             client_id,
             client_secret,
diff --git a/src/server/authentication/config/passport.ts b/src/server/authentication/config/passport.ts
index 726df7fd7..2271109a5 100644
--- a/src/server/authentication/config/passport.ts
+++ b/src/server/authentication/config/passport.ts
@@ -40,7 +40,7 @@ export let isAuthenticated = (req: Request, res: Response, next: NextFunction) =
 export let isAuthorized = (req: Request, res: Response, next: NextFunction) => {
     const provider = req.path.split("/").slice(-1)[0];
 
-    if (_.find((req.user as any).tokens!, { kind: provider })) {
+    if (_.find(req.user?.tokens, { kind: provider })) {
         next();
     } else {
         res.redirect(`/auth/${provider}`);
diff --git a/src/server/authentication/controllers/user_controller.ts b/src/server/authentication/controllers/user_controller.ts
index 517353479..f0086d4ea 100644
--- a/src/server/authentication/controllers/user_controller.ts
+++ b/src/server/authentication/controllers/user_controller.ts
@@ -68,9 +68,9 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
 
 };
 
-let tryRedirectToTarget = (req: Request, res: Response) => {
+const tryRedirectToTarget = (req: Request, res: Response) => {
     if (req.session && req.session.target) {
-        let target = req.session.target;
+        const target = req.session.target;
         req.session.target = undefined;
         res.redirect(target);
     } else {
@@ -148,7 +148,6 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio
     const email = req.body.email;
     async.waterfall([
         function (done: any) {
-            let token: string;
             c.randomBytes(20, function (err: any, buffer: Buffer) {
                 if (err) {
                     done(null);
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index ac4462f78..50bfb4832 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -42,10 +42,10 @@ export class CurrentUserUtils {
 
     // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
     static setupCreatorButtons(doc: Doc) {
-        let notes = CurrentUserUtils.setupNoteTypes(doc);
+        const notes = CurrentUserUtils.setupNoteTypes(doc);
         doc.noteTypes = Docs.Create.TreeDocument(notes, { title: "Note Types", height: 75 });
         doc.activePen = doc;
-        let docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [
+        const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [
             { title: "collection", icon: "folder", ignoreClick: true, drag: 'Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" })' },
             { title: "todo item", icon: "check", ignoreClick: true, drag: 'getCopy(this.dragFactory, true)', dragFactory: notes[notes.length - 1] },
             { title: "web page", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" })' },
@@ -278,7 +278,7 @@ export class CurrentUserUtils {
         if (this._northstarCatalog && CurrentUserUtils._northstarSchemas) {
             this._northstarCatalog.schemas!.push(schema);
             CurrentUserUtils._northstarSchemas.push(schemaDoc);
-            let schemas = Cast(CurrentUserUtils.UserDocument.DBSchemas, listSpec("string"), []);
+            const schemas = Cast(CurrentUserUtils.UserDocument.DBSchemas, listSpec("string"), []);
             schemas.push(schema.displayName!);
             CurrentUserUtils.UserDocument.DBSchemas = new List<string>(schemas);
         }
diff --git a/src/server/database.ts b/src/server/database.ts
index db81245c1..4039e6203 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -71,7 +71,7 @@ export namespace Database {
 
         public async update(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateWriteOpResult) => void, upsert = true, collectionName = Database.DocumentsCollection) {
             if (this.db) {
-                let collection = this.db.collection(collectionName);
+                const collection = this.db.collection(collectionName);
                 const prom = this.currentWrites[id];
                 let newProm: Promise<void>;
                 const run = (): Promise<void> => {
@@ -96,7 +96,7 @@ export namespace Database {
 
         public replace(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateWriteOpResult) => void, upsert = true, collectionName = Database.DocumentsCollection) {
             if (this.db) {
-                let collection = this.db.collection(collectionName);
+                const collection = this.db.collection(collectionName);
                 const prom = this.currentWrites[id];
                 let newProm: Promise<void>;
                 const run = (): Promise<void> => {
diff --git a/src/server/downsize.ts b/src/server/downsize.ts
index ed68fbecc..cd0d83812 100644
--- a/src/server/downsize.ts
+++ b/src/server/downsize.ts
@@ -17,7 +17,7 @@ fs.readdir(folder, async (err, files) => {
     // });
     for (const file of files) {
         const filesplit = file.split(".");
-        let resizers = [
+        const resizers = [
             { resizer: sharp().resize(100, undefined, { withoutEnlargement: true }), suffix: "_s" },
             { resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: "_m" },
             { resizer: sharp().resize(900, undefined, { withoutEnlargement: true }), suffix: "_l" },
diff --git a/src/server/index.ts b/src/server/index.ts
index d77923710..0e0bd0bd6 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -51,7 +51,7 @@ async function preliminaryFunctions() {
  * that will manage the registration of new routes
  * with the server
  */
-function routeSetter({ isRelease, addSupervisedRoute }: RouteManager) {
+function routeSetter({ isRelease, addSupervisedRoute, log }: RouteManager) {
     const managers = [
         new UserManager(),
         new UploadManager(),
@@ -67,6 +67,7 @@ function routeSetter({ isRelease, addSupervisedRoute }: RouteManager) {
 
     // initialize API Managers
     managers.forEach(manager => manager.register(addSupervisedRoute));
+    log();
 
     // initialize the web socket (bidirectional communication: if a user changes
     // a field on one client, that change must be broadcast to all other clients)
@@ -82,8 +83,8 @@ function routeSetter({ isRelease, addSupervisedRoute }: RouteManager) {
     });
 
     const serve: OnUnauthenticated = ({ req, res }) => {
-        let detector = new mobileDetect(req.headers['user-agent'] || "");
-        let filename = detector.mobile() !== null ? 'mobile/image.html' : 'index.html';
+        const detector = new mobileDetect(req.headers['user-agent'] || "");
+        const filename = detector.mobile() !== null ? 'mobile/image.html' : 'index.html';
         res.sendFile(path.join(__dirname, '../../deploy/' + filename));
     };
 
@@ -104,8 +105,8 @@ function routeSetter({ isRelease, addSupervisedRoute }: RouteManager) {
 
 (async function start() {
     await log_execution({
-        startMessage: "starting execution of preliminary functions",
-        endMessage: "completed preliminary functions",
+        startMessage: "\nstarting execution of preliminary functions",
+        endMessage: "completed preliminary functions\n",
         action: preliminaryFunctions
     });
     await initializeServer({ listenAtPort: 1050, routeSetter });
diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts
index 36d828fdb..9e8ace962 100644
--- a/src/typings/index.d.ts
+++ b/src/typings/index.d.ts
@@ -1,6 +1,7 @@
 /// <reference types="node" />
 
 declare module 'googlephotos';
+declare module 'react-image-lightbox-with-rotate';
 
 declare module '@react-pdf/renderer' {
     import * as React from 'react';
diff --git a/test/test.ts b/test/test.ts
index f1cf75bd4..245733e9b 100644
--- a/test/test.ts
+++ b/test/test.ts
@@ -14,21 +14,21 @@ import { createSchema, makeInterface, defaultSpec } from '../src/new_fields/Sche
 import { ImageField } from '../src/new_fields/URLField';
 describe("Document", () => {
     it('should hold fields', () => {
-        let key = "Test";
-        let key2 = "Test2";
-        let field = 15;
-        let doc = new Doc();
+        const key = "Test";
+        const key2 = "Test2";
+        const field = 15;
+        const doc = new Doc();
         doc[key] = field;
-        let getField = Cast(doc[key], "number");
-        let getField2 = Cast(doc[key2], "number");
+        const getField = Cast(doc[key], "number");
+        const getField2 = Cast(doc[key2], "number");
         expect(getField).to.equal(field);
         expect(getField2).to.equal(undefined);
     });
 
     it('should update', () => {
-        let doc = new Doc();
-        let key = "Test";
-        let key2 = "Test2";
+        const doc = new Doc();
+        const key = "Test";
+        const key2 = "Test2";
         let ran = false;
         reaction(() => doc[key], (field) => { ran = true; });
         expect(ran).to.equal(false);
diff --git a/tslint.json b/tslint.json
index 76d28b375..0056f4266 100644
--- a/tslint.json
+++ b/tslint.json
@@ -1,32 +1,18 @@
 {
+    "linterOptions": {
+        "exclude": ["./src/client/northstar/**", "**/*.js"]
+    },
     "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,
+        "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"
@@ -35,13 +21,6 @@
             true,
             "ignore-same-line"
         ],
-        // "quotemark": [
-        //     true,
-        //     "double",
-        //     "jsx-double",
-        //     "avoid-template",
-        //     "avoid-escape"
-        // ],
         "no-tautology-expression": true,
         "unnecessary-constructor": true
         // "trailing-comma": [
@@ -52,6 +31,33 @@
         //     }
         // ],
         // "ordered-imports": true
+        // "quotemark": [
+        //     true,
+        //     "double",
+        //     "jsx-double",
+        //     "avoid-template",
+        //     "avoid-escape"
+        // ],
+        // "object-literal-shorthand": true,
+        // "object-literal-sort-keys": true,
+        // "align": [
+        //     true,
+        //     "parameters",
+        //     "arguments",
+        //     "statements",
+        //     "members",
+        //     "elements"
+        // ],
+        // "no-void-expression": [
+        //     true,
+        //     "ignore-arrow-function-shorthand"
+        // ],
+        // "no-non-null-assertion": true,
+        // "no-var-keyword": true,
+        // "no-var-requires": true,
     },
-    "defaultSeverity": "warning"
+    "defaultSeverity": "warning",
+    "jsRules": {
+
+    }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 710dda13398c01b2b0f63b033ccca0c8ea4f7e49 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 4 Dec 2019 14:28:24 -0500
Subject: fixed mainview flyoutbehavior a bit.  fixed keyvaluebox editing of
 lists a little.

---
 src/client/views/EditableView.tsx                   |  6 +++---
 src/client/views/MainView.tsx                       |  2 +-
 src/client/views/collections/CollectionTreeView.tsx | 16 ++++++++++++++--
 src/client/views/nodes/KeyValueBox.tsx              |  8 ++++----
 src/new_fields/DateField.ts                         |  4 ++++
 src/new_fields/List.ts                              |  3 +--
 6 files changed, 27 insertions(+), 12 deletions(-)

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

diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index f78b61892..ea9d548a1 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -10,7 +10,7 @@ export interface EditableProps {
     /**
      * Called to get the initial value for editing
      *  */
-    GetValue(): string;
+    GetValue(): string | undefined;
 
     /**
      * Called to apply changes
@@ -108,8 +108,8 @@ export class EditableView extends React.Component<EditableProps> {
 
     @action
     private finalizeEdit(value: string, shiftDown: boolean) {
+        this._editing = false;
         if (this.props.SetValue(value, shiftDown)) {
-            this._editing = false;
             this.props.isEditingCallback && this.props.isEditingCallback(false);
         }
     }
@@ -124,7 +124,7 @@ export class EditableView extends React.Component<EditableProps> {
     }
 
     render() {
-        if (this._editing) {
+        if (this._editing && this.props.GetValue() !== undefined) {
             return this.props.autosuggestProps
                 ? <Autosuggest
                     {...this.props.autosuggestProps.autosuggestProps}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index bd20cbcf1..254b2e906 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -450,7 +450,7 @@ export class MainView extends React.Component {
 
     public static expandFlyout = action(() => {
         MainView.Instance._flyoutTranslate = true;
-        MainView.Instance.flyoutWidth = 250;
+        MainView.Instance.flyoutWidth = (MainView.Instance.flyoutWidth || 250);
     });
 
     @computed get expandButton() {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 48ea35c6b..95503147a 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -279,7 +279,7 @@ class TreeView extends React.Component<TreeViewProps> {
             const contents = doc[key];
             let contentElement: (JSX.Element | null)[] | JSX.Element = [];
 
-            if (contents instanceof Doc || Cast(contents, listSpec(Doc))) {
+            if (contents instanceof Doc || (Cast(contents, listSpec(Doc)) && (Cast(contents, listSpec(Doc))!.length && Cast(contents, listSpec(Doc))![0] instanceof Doc))) {
                 const remDoc = (doc: Doc) => this.remove(doc, key);
                 const addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
                 contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] :
@@ -294,7 +294,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     height={13}
                     fontSize={12}
                     GetValue={() => Field.toKeyValueString(doc, key)}
-                    SetValue={(value: string) => KeyValueBox.SetField(doc, key, value)} />;
+                    SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} />;
             }
             rows.push(<div style={{ display: "flex" }} key={key}>
                 <span style={{ fontWeight: "bold" }}>{key + ":"}</span>
@@ -302,6 +302,18 @@ class TreeView extends React.Component<TreeViewProps> {
                 {contentElement}
             </div>);
         }
+        rows.push(<div style={{ display: "flex" }} key={"newKeyValue"}>
+            <EditableView
+                key="editableView"
+                contents={"+key:value"}
+                height={13}
+                fontSize={12}
+                GetValue={() => ""}
+                SetValue={(value: string) => {
+                    value.indexOf(":") !== -1 && KeyValueBox.SetField(doc, value.substring(0, value.indexOf(":")), value.substring(value.indexOf(":") + 1, value.length), true);
+                    return true;
+                }} />
+        </div>);
         return rows;
     }
 
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index fba29e4cd..322d639dc 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -66,10 +66,10 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         return { script, type: dubEq, onDelegate: eq };
     }
 
-    public static ApplyKVPScript(doc: Doc, key: string, kvpScript: KVPScript): boolean {
+    public static ApplyKVPScript(doc: Doc, key: string, kvpScript: KVPScript, forceOnDelegate?: boolean): boolean {
         const { script, type, onDelegate } = kvpScript;
         //const target = onDelegate ? Doc.Layout(doc.layout) : Doc.GetProto(doc); // bcz: TODO need to be able to set fields on layout templates
-        const target = onDelegate ? doc : Doc.GetProto(doc);
+        const target = forceOnDelegate || onDelegate ? doc : Doc.GetProto(doc);
         let field: Field;
         if (type === "computed") {
             field = new ComputedField(script);
@@ -88,10 +88,10 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     }
 
     @undoBatch
-    public static SetField(doc: Doc, key: string, value: string) {
+    public static SetField(doc: Doc, key: string, value: string, forceOnDelegate?: boolean) {
         const script = this.CompileKVPScript(value);
         if (!script) return false;
-        return this.ApplyKVPScript(doc, key, script);
+        return this.ApplyKVPScript(doc, key, script, forceOnDelegate);
     }
 
     onPointerDown = (e: React.PointerEvent): void => {
diff --git a/src/new_fields/DateField.ts b/src/new_fields/DateField.ts
index abec91e06..4f999e5e8 100644
--- a/src/new_fields/DateField.ts
+++ b/src/new_fields/DateField.ts
@@ -19,6 +19,10 @@ export class DateField extends ObjectField {
         return new DateField(this.date);
     }
 
+    toString() {
+        return `${this.date.toISOString()}`;
+    }
+
     [ToScriptString]() {
         return `new DateField(new Date(${this.date.toISOString()}))`;
     }
diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts
index e9101158b..bb48b1bb3 100644
--- a/src/new_fields/List.ts
+++ b/src/new_fields/List.ts
@@ -290,8 +290,7 @@ class ListImpl<T extends Field> extends ObjectField {
     private [SelfProxy]: any;
 
     [ToScriptString]() {
-        return "invalid";
-        // return `new List([${(this as any).map((field => Field.toScriptString(field))}])`;
+        return `new List([${(this as any).map((field: any) => Field.toScriptString(field))}])`;
     }
 }
 export type List<T extends Field> = ListImpl<T> & (T | (T extends RefField ? Promise<T> : never))[];
-- 
cgit v1.2.3-70-g09d2


From 968d678e8aeb6f57cc892e3fe789067a8246c5fe Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 4 Dec 2019 17:47:28 -0500
Subject: started to add paths

---
 src/client/documents/Documents.ts                  |  5 +--
 src/client/util/RichTextSchema.tsx                 |  3 +-
 src/client/views/CollectionLinearView.tsx          |  1 +
 src/client/views/MainView.tsx                      | 11 ++++---
 src/client/views/OverlayView.tsx                   |  1 +
 .../views/collections/CollectionDockingView.tsx    | 38 +++++++++++++---------
 .../views/collections/CollectionSchemaView.tsx     |  1 +
 .../views/collections/CollectionStackingView.tsx   |  1 +
 .../views/collections/CollectionTreeView.tsx       | 36 +++++++++++---------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  1 +
 .../views/nodes/ContentFittingDocumentView.tsx     |  4 ++-
 src/client/views/nodes/DocumentView.tsx            | 12 ++++---
 src/client/views/nodes/FieldView.tsx               |  1 +
 src/client/views/nodes/FormattedTextBoxComment.tsx |  3 +-
 .../views/presentationview/PresElementBox.tsx      |  3 +-
 src/client/views/search/SearchItem.tsx             |  3 +-
 16 files changed, 78 insertions(+), 46 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e0f2858ba..5d5bdfcbd 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -534,7 +534,8 @@ export namespace Docs {
 
         export type DocConfig = {
             doc: Doc,
-            initialWidth?: number
+            initialWidth?: number,
+            path?: Doc[]
         };
 
         export function StandardCollectionDockingDocument(configs: Array<DocConfig>, options: DocumentOptions, id?: string, type: string = "row") {
@@ -543,7 +544,7 @@ export namespace Docs {
                     {
                         type: type,
                         content: [
-                            ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth))
+                            ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth, config.path))
                         ]
                     }
                 ]
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 3b786e61d..4612f2885 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -761,8 +761,9 @@ export class DashDocView {
                     this._dashSpan.style.width = this._outer.style.width = dashDoc[WidthSym]() + "px";
                 });
                 ReactDOM.render(<DocumentView
-                    fitToBox={BoolCast(dashDoc.fitToBox)}
                     Document={dashDoc}
+                    LibraryPath={tbox.props.LibraryPath}
+                    fitToBox={BoolCast(dashDoc.fitToBox)}
                     addDocument={returnFalse}
                     removeDocument={removeDoc}
                     ruleProvider={undefined}
diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx
index 09e4ef99c..293a4feea 100644
--- a/src/client/views/CollectionLinearView.tsx
+++ b/src/client/views/CollectionLinearView.tsx
@@ -74,6 +74,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
                             <DocumentView
                                 Document={pair.layout}
                                 DataDoc={pair.data}
+                                LibraryPath={this.props.LibraryPath}
                                 addDocument={this.props.addDocument}
                                 moveDocument={this.props.moveDocument}
                                 addDocTab={this.props.addDocTab}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 254b2e906..29719a6eb 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -198,8 +198,7 @@ export class MainView extends React.Component {
         };
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         Doc.AddDocToList(Doc.GetProto(CurrentUserUtils.UserDocument.documents as Doc), "data", freeformDoc);
-        const dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] };
-        const mainDoc = Docs.Create.DockDocument([freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${workspaceCount}` }, id);
+        const mainDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().documents as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row");
         Doc.AddDocToList(workspaces, "data", mainDoc);
         // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
         setTimeout(() => this.openWorkspace(mainDoc), 0);
@@ -271,6 +270,7 @@ export class MainView extends React.Component {
                     {!mainContainer ? (null) :
                         <DocumentView Document={mainContainer}
                             DataDoc={undefined}
+                            LibraryPath={[]}
                             addDocument={undefined}
                             addDocTab={this.addDocTabFunc}
                             pinToPres={emptyFunction}
@@ -338,7 +338,7 @@ export class MainView extends React.Component {
         document.removeEventListener("pointerup", this.onPointerUp);
     }
     flyoutWidthFunc = () => this.flyoutWidth;
-    addDocTabFunc = (doc: Doc, data: Opt<Doc>, where: string) => {
+    addDocTabFunc = (doc: Doc, data: Opt<Doc>, where: string, libraryPath?: Doc[]) => {
         if (where === "close") {
             return CollectionDockingView.CloseRightSplit(doc);
         }
@@ -346,7 +346,7 @@ export class MainView extends React.Component {
             this.openWorkspace(doc);
             return true;
         } else {
-            return CollectionDockingView.AddRightSplit(doc, undefined);
+            return CollectionDockingView.AddRightSplit(doc, undefined, undefined, libraryPath);
         }
     }
     mainContainerXf = () => new Transform(0, -this._buttonBarHeight, 1);
@@ -363,6 +363,7 @@ export class MainView extends React.Component {
                 <DocumentView
                     Document={sidebarButtonsDoc}
                     DataDoc={undefined}
+                    LibraryPath={[]}
                     addDocument={undefined}
                     addDocTab={this.addDocTabFunc}
                     pinToPres={emptyFunction}
@@ -389,6 +390,7 @@ export class MainView extends React.Component {
                 <DocumentView
                     Document={sidebarContent}
                     DataDoc={undefined}
+                    LibraryPath={[]}
                     addDocument={undefined}
                     addDocTab={this.addDocTabFunc}
                     pinToPres={emptyFunction}
@@ -474,6 +476,7 @@ export class MainView extends React.Component {
                 <CollectionLinearView
                     Document={CurrentUserUtils.UserDocument.expandingButtons}
                     DataDoc={undefined}
+                    LibraryPath={[]}
                     fieldKey={"data"}
                     annotationsKey={""}
                     select={emptyFunction}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 32ada293c..9b42d199c 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -174,6 +174,7 @@ export class OverlayView extends React.Component {
             return <div className="overlayView-doc" key={d[Id]} onPointerDown={onPointerDown} style={{ transform: `translate(${d.x}px, ${d.y}px)`, display: d.isMinimized ? "none" : "" }}>
                 <DocumentView
                     Document={d}
+                    LibraryPath={[]}
                     ChromeHeight={returnZero}
                     // isSelected={returnFalse}
                     // select={emptyFunction}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 77e6e1c93..e35dc4c00 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -39,7 +39,7 @@ const _global = (window /* browser */ || global /* node */) as any;
 export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
     @observable public static Instances: CollectionDockingView[] = [];
     @computed public static get Instance() { return CollectionDockingView.Instances[0]; }
-    public static makeDocumentConfig(document: Doc, dataDoc: Doc | undefined, width?: number) {
+    public static makeDocumentConfig(document: Doc, dataDoc: Doc | undefined, width?: number, libraryPath?: Doc[]) {
         return {
             type: 'react-component',
             component: 'DocumentFrameRenderer',
@@ -47,7 +47,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             width: width,
             props: {
                 documentId: document[Id],
-                dataDocumentId: dataDoc && dataDoc[Id] !== document[Id] ? dataDoc[Id] : ""
+                dataDocumentId: dataDoc && dataDoc[Id] !== document[Id] ? dataDoc[Id] : "",
+                libraryPath: libraryPath ? libraryPath.map(d => d[Id]) : []
                 //collectionDockingView: CollectionDockingView.Instance
             }
         };
@@ -95,12 +96,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
 
     @undoBatch
     @action
-    public OpenFullScreen(docView: DocumentView) {
+    public OpenFullScreen(docView: DocumentView, libraryPath?: Doc[]) {
         const document = Doc.MakeAlias(docView.props.Document);
         const dataDoc = docView.props.DataDoc;
         const newItemStackConfig = {
             type: 'stack',
-            content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)]
+            content: [CollectionDockingView.makeDocumentConfig(document, dataDoc, undefined, libraryPath)]
         };
         const docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
         this._goldenLayout.root.contentItems[0].addChild(docconfig);
@@ -174,12 +175,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     //
     @undoBatch
     @action
-    public static AddRightSplit(document: Doc, dataDoc: Doc | undefined, minimize: boolean = false) {
+    public static AddRightSplit(document: Doc, dataDoc: Doc | undefined, minimize: boolean = false, libraryPath?: Doc[]) {
         if (!CollectionDockingView.Instance) return false;
         const instance = CollectionDockingView.Instance;
         const newItemStackConfig = {
             type: 'stack',
-            content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)]
+            content: [CollectionDockingView.makeDocumentConfig(document, dataDoc, undefined, libraryPath)]
         };
 
         const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
@@ -211,9 +212,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
 
     @undoBatch
     @action
-    public AddTab = (stack: any, document: Doc, dataDocument: Doc | undefined) => {
+    public AddTab = (stack: any, document: Doc, dataDocument: Doc | undefined, libraryPath?: Doc[]) => {
         Doc.GetProto(document).lastOpened = new DateField;
-        const docContentConfig = CollectionDockingView.makeDocumentConfig(document, dataDocument);
+        const docContentConfig = CollectionDockingView.makeDocumentConfig(document, dataDocument, undefined, libraryPath);
         if (stack === undefined) {
             let stack: any = this._goldenLayout.root;
             while (!stack.isStack) {
@@ -427,10 +428,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                             });
                         }}><FontAwesomeIcon icon="file" size="lg" /></span>, dragSpan);
                 ReactDOM.render(<ButtonSelector Document={doc} Stack={stack} />, gearSpan);
-                // ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={(doc, data, where) => {
-                //     where === "onRight" ? CollectionDockingView.AddRightSplit(doc, dataDoc) : CollectionDockingView.Instance.AddTab(stack, doc, dataDoc);
-                //     return true;
-                // }} />, upDiv);
                 tab.reactComponents = [dragSpan, gearSpan, upDiv];
                 tab.element.append(dragSpan);
                 tab.element.append(gearSpan);
@@ -532,11 +529,13 @@ interface DockedFrameProps {
     documentId: FieldId;
     dataDocumentId: FieldId;
     glContainer: any;
+    libraryPath: (FieldId[])
     //collectionDockingView: CollectionDockingView
 }
 @observer
 export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     _mainCont: HTMLDivElement | null = null;
+    @observable private _libraryPath: Doc[] = [];
     @observable private _panelWidth = 0;
     @observable private _panelHeight = 0;
     @observable private _document: Opt<Doc>;
@@ -554,6 +553,14 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                 DocServer.GetRefField(this.props.dataDocumentId).then(action((f: Opt<Field>) => this._dataDoc = f as Doc));
             }
         }));
+        this.props.libraryPath && this.setupLibraryPath();
+    }
+
+    async setupLibraryPath() {
+        Promise.all(this.props.libraryPath.map(async docid => {
+            let d = await DocServer.GetRefField(docid);
+            return d instanceof Doc ? d : undefined
+        })).then(action((list: (Doc | undefined)[]) => this._libraryPath = list.filter(d => d).map(d => d as Doc)));
     }
 
     /**
@@ -640,17 +647,17 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     get previewPanelCenteringOffset() { return this.nativeWidth() && !this.layoutDoc!.ignoreAspect ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
     get widthpercent() { return this.nativeWidth() && !this.layoutDoc!.ignoreAspect ? `${(this.nativeWidth() * this.contentScaling()) / this.panelWidth() * 100}%` : undefined; }
 
-    addDocTab = (doc: Doc, dataDoc: Opt<Doc>, location: string) => {
+    addDocTab = (doc: Doc, dataDoc: Opt<Doc>, location: string, libraryPath?: Doc[]) => {
         SelectionManager.DeselectAll();
         if (doc.dockingConfig) {
             MainView.Instance.openWorkspace(doc);
             return true;
         } else if (location === "onRight") {
-            return CollectionDockingView.AddRightSplit(doc, dataDoc);
+            return CollectionDockingView.AddRightSplit(doc, dataDoc, undefined, libraryPath);
         } else if (location === "close") {
             return CollectionDockingView.CloseRightSplit(doc);
         } else {
-            return CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc);
+            return CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc, libraryPath);
         }
     }
 
@@ -659,6 +666,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         const document = this._document;
         const resolvedDataDoc = document.layout instanceof Doc ? document : this._dataDoc;
         return <DocumentView key={document[Id]}
+            LibraryPath={this._libraryPath}
             Document={document}
             DataDoc={resolvedDataDoc}
             bringToFront={emptyFunction}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index f336eaf75..8169e9e2b 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -141,6 +141,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             <ContentFittingDocumentView
                 Document={layoutDoc}
                 DataDocument={this.previewDocument !== this.props.DataDoc ? this.props.DataDoc : undefined}
+                LibraryPath={this.props.LibraryPath}
                 childDocs={this.childDocs}
                 renderDepth={this.props.renderDepth}
                 ruleProvider={this.props.Document.isRuleProvider && layoutDoc && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 955fcda80..9f01de08b 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -168,6 +168,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         return <ContentFittingDocumentView
             Document={doc}
             DataDocument={dataDoc}
+            LibraryPath={this.props.LibraryPath}
             showOverlays={this.overlays}
             renderDepth={this.props.renderDepth}
             ruleProvider={this.props.Document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 95503147a..219d56607 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -33,6 +33,7 @@ import { CurrentUserUtils } from '../../../server/authentication/models/current_
 export interface TreeViewProps {
     document: Doc;
     dataDoc?: Doc;
+    libraryPath: Doc[] | undefined;
     containingCollection: Doc;
     prevSibling?: Doc;
     renderDepth: number;
@@ -40,7 +41,7 @@ export interface TreeViewProps {
     ruleProvider: Doc | undefined;
     moveDocument: DragManager.MoveFunction;
     dropAction: "alias" | "copy" | undefined;
-    addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => boolean;
+    addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string, libraryPath?: Doc[]) => boolean;
     pinToPres: (document: Doc) => void;
     panelWidth: () => number;
     panelHeight: () => number;
@@ -95,8 +96,8 @@ class TreeView extends React.Component<TreeViewProps> {
     @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.document.maxEmbedHeight, 300); }
     @computed get dataDoc() { return this.templateDataDoc ? this.templateDataDoc : this.props.document; }
     @computed get fieldKey() {
-        const splits = StrCast(Doc.LayoutField(this.props.document)).split("fieldKey={\"");
-        return splits.length > 1 ? splits[1].split("\"")[0] : "data";
+        const splits = StrCast(Doc.LayoutField(this.props.document)).split("fieldKey={\'");
+        return splits.length > 1 ? splits[1].split("\'")[0] : "data";
     }
     childDocList(field: string) {
         const layout = Doc.LayoutField(this.props.document) instanceof Doc ? Doc.LayoutField(this.props.document) as Doc : undefined;
@@ -120,7 +121,7 @@ class TreeView extends React.Component<TreeViewProps> {
     }
 
     @undoBatch delete = () => this.props.deleteDoc(this.props.document);
-    @undoBatch openRight = () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight");
+    @undoBatch openRight = () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight", this.props.libraryPath);
     @undoBatch indent = () => this.props.addDocument(this.props.document) && this.delete();
     @undoBatch move = (doc: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => {
         return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc);
@@ -194,8 +195,8 @@ class TreeView extends React.Component<TreeViewProps> {
                 ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.GetProto(CurrentUserUtils.UserDocument.recentlyClosed as Doc).data = new List<Doc>(), icon: "plus" });
             } else if (this.props.document !== CurrentUserUtils.UserDocument.workspaces) {
                 ContextMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.document), icon: "tv" });
-                ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "inTab"), icon: "folder" });
-                ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight"), icon: "caret-square-right" });
+                ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "inTab", this.props.libraryPath), icon: "folder" });
+                ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight", this.props.libraryPath), icon: "caret-square-right" });
                 if (DocumentManager.Instance.getDocumentViews(this.dataDoc).length) {
                     ContextMenu.Instance.addItem({ description: "Focus", event: () => (view => view && view.props.focus(this.props.document, true))(DocumentManager.Instance.getFirstDocumentView(this.dataDoc)), icon: "camera" });
                 }
@@ -286,7 +287,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     DocListCast(contents), this.props.treeViewId, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                     this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
                     this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.hideHeaderFields, this.props.preventTreeViewOpen,
-                    [...this.props.renderedIds, doc[Id]]);
+                    [...this.props.renderedIds, doc[Id]], this.props.libraryPath);
             } else {
                 contentElement = <EditableView
                     key="editableView"
@@ -331,7 +332,7 @@ class TreeView extends React.Component<TreeViewProps> {
                         this.templateDataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                         this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
                         this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.hideHeaderFields, this.props.preventTreeViewOpen,
-                        [...this.props.renderedIds, this.props.document[Id]])}
+                        [...this.props.renderedIds, this.props.document[Id]], this.props.libraryPath)}
             </ul >;
         } else if (this.treeViewExpandedView === "fields") {
             return <ul><div ref={this._dref} style={{ display: "inline-block" }} key={this.props.document[Id] + this.props.document.title}>
@@ -343,6 +344,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 <ContentFittingDocumentView
                     Document={layoutDoc}
                     DataDocument={this.templateDataDoc}
+                    LibraryPath={[]}
                     renderDepth={this.props.renderDepth}
                     showOverlays={this.noOverlays}
                     ruleProvider={this.props.document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.document : this.props.ruleProvider}
@@ -445,7 +447,8 @@ class TreeView extends React.Component<TreeViewProps> {
         renderDepth: number,
         hideHeaderFields: () => boolean,
         preventTreeViewOpen: boolean,
-        renderedIds: string[]
+        renderedIds: string[],
+        libraryPath: Doc[] | undefined
     ) {
         const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
         if (viewSpecScript) {
@@ -499,9 +502,9 @@ class TreeView extends React.Component<TreeViewProps> {
             }
 
             const indent = i === 0 ? undefined : () => {
-                if (StrCast(docs[i - 1].layout).indexOf("fieldKey") !== -1) {
-                    const fieldKeysub = StrCast(docs[i - 1].layout).split("fieldKey")[1];
-                    const fieldKey = fieldKeysub.split("\"")[1];
+                if (StrCast(docs[i - 1].layout).indexOf('fieldKey') !== -1) {
+                    const fieldKeysub = StrCast(docs[i - 1].layout).split('fieldKey')[1];
+                    const fieldKey = fieldKeysub.split("\'")[1];
                     if (fieldKey && Cast(docs[i - 1][fieldKey], listSpec(Doc)) !== undefined) {
                         Doc.AddDocToList(docs[i - 1], fieldKey, child);
                         docs[i - 1].treeViewOpen = true;
@@ -510,9 +513,9 @@ class TreeView extends React.Component<TreeViewProps> {
                 }
             };
             const outdent = !parentCollectionDoc ? undefined : () => {
-                if (StrCast(parentCollectionDoc.layout).indexOf("fieldKey") !== -1) {
-                    const fieldKeysub = StrCast(parentCollectionDoc.layout).split("fieldKey")[1];
-                    const fieldKey = fieldKeysub.split("\"")[1];
+                if (StrCast(parentCollectionDoc.layout).indexOf('fieldKey') !== -1) {
+                    const fieldKeysub = StrCast(parentCollectionDoc.layout).split('fieldKey')[1];
+                    const fieldKey = fieldKeysub.split("\'")[1];
                     Doc.AddDocToList(parentCollectionDoc, fieldKey, child, parentPrevSibling, false);
                     parentCollectionDoc.treeViewOpen = true;
                     remove(child);
@@ -529,6 +532,7 @@ class TreeView extends React.Component<TreeViewProps> {
             return !(child instanceof Doc) ? (null) : <TreeView
                 document={pair.layout}
                 dataDoc={pair.data}
+                libraryPath={libraryPath ? [...libraryPath, containingCollection] : undefined}
                 containingCollection={containingCollection}
                 prevSibling={docs[i]}
                 treeViewId={treeViewId}
@@ -648,7 +652,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
                         TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
                             moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
                             this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => BoolCast(this.props.Document.hideHeaderFields),
-                            BoolCast(this.props.Document.preventTreeViewOpen), [])
+                            BoolCast(this.props.Document.preventTreeViewOpen), [], this.props.LibraryPath)
                     }
                 </ul>
             </div >
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c69b264c4..c3e064da5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -627,6 +627,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             ...this.props,
             DataDoc: childData,
             Document: childLayout,
+            LibraryPath: this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : [],
             layoutKey: undefined,
             ruleProvider: this.Document.isRuleProvider && childLayout.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider, //bcz: hack! - currently ruleProviders apply to documents in nested colleciton, not direct children of themselves
             onClick: undefined, // this.props.onClick,  // bcz: check this out -- I don't think we want to inherit click handlers, or we at least need a way to ignore them
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index efc907f9b..86fab0ba0 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -17,6 +17,7 @@ import { CollectionView } from "../collections/CollectionView";
 interface ContentFittingDocumentViewProps {
     Document?: Doc;
     DataDocument?: Doc;
+    LibraryPath: Doc[];
     childDocs?: Doc[];
     renderDepth: number;
     fitToBox?: boolean;
@@ -85,8 +86,9 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
                         width: `${100 * (this.props.PanelWidth() - this.centeringOffset * 2) / this.props.PanelWidth()}%`
                     }}>
                     <DocumentView {...this.props}
-                        DataDoc={this.props.DataDocument}
                         Document={this.props.Document}
+                        DataDoc={this.props.DataDocument}
+                        LibraryPath={this.props.LibraryPath}
                         fitToBox={this.props.fitToBox}
                         onClick={this.props.onClick}
                         ruleProvider={this.props.ruleProvider}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 63ce6233c..fef365bf3 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -54,6 +54,7 @@ export interface DocumentViewProps {
     ContainingCollectionDoc: Opt<Doc>;
     Document: Doc;
     DataDoc?: Doc;
+    LibraryPath: Doc[];
     fitToBox?: boolean;
     onClick?: ScriptField;
     addDocument?: (doc: Doc) => boolean;
@@ -70,7 +71,7 @@ export interface DocumentViewProps {
     parentActive: (outsideReaction: boolean) => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     bringToFront: (doc: Doc, sendToBack?: boolean) => void;
-    addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => boolean;
+    addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string, libraryPath?: Doc[]) => boolean;
     pinToPres: (document: Doc) => void;
     zoomToScale: (scale: number) => void;
     backgroundColor: (doc: Doc) => string | undefined;
@@ -401,9 +402,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
         const cm = ContextMenu.Instance;
         const subitems: ContextMenuProps[] = [];
-        subitems.push({ description: "Open Full Screen", event: () => CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this), icon: "desktop" });
-        subitems.push({ description: "Open Tab        ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab"), icon: "folder" });
-        subitems.push({ description: "Open Right      ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "onRight"), icon: "caret-square-right" });
+        subitems.push({ description: "Open Full Screen", event: () => CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this, this.props.LibraryPath), icon: "desktop" });
+        subitems.push({ description: "Open Tab        ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab", this.props.LibraryPath), icon: "folder" });
+        subitems.push({ description: "Open Right      ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "onRight", this.props.LibraryPath), icon: "caret-square-right" });
         subitems.push({ description: "Open Alias Tab  ", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "inTab"), icon: "folder" });
         subitems.push({ description: "Open Alias Right", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "onRight"), icon: "caret-square-right" });
         subitems.push({ description: "Open Fields     ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" });
@@ -527,6 +528,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 SelectionManager.SelectDoc(this, false);
             }
         });
+        let path = this.props.LibraryPath.reduce((p: string, d: Doc) => p + "/" + (Doc.AreProtosEqual(d, (Doc.UserDoc().LibraryBtn as Doc).sourcePanel as Doc) ? "" : d.title), "");
+        cm.addItem({ description: `path: ${path}`, event: () => { }, icon: "check" })
     }
 
     // does Document set a layout prop 
@@ -552,6 +555,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             ContainingCollectionDoc={this.props.ContainingCollectionDoc}
             Document={this.props.Document}
             fitToBox={this.props.fitToBox}
+            LibraryPath={this.props.LibraryPath}
             addDocument={this.props.addDocument}
             removeDocument={this.props.removeDocument}
             moveDocument={this.props.moveDocument}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 048960c5e..ce1c468ad 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -30,6 +30,7 @@ export interface FieldViewProps {
     ruleProvider: Doc | undefined;
     Document: Doc;
     DataDoc?: Doc;
+    LibraryPath: Doc[];
     onClick?: ScriptField;
     isSelected: (outsideReaction?: boolean) => boolean;
     select: (isCtrlPressed: boolean) => void;
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 2ec30e3b3..9f1dd4aec 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -177,8 +177,9 @@ export class FormattedTextBoxComment {
                             } catch (e) { }
                             if (target) {
                                 ReactDOM.render(<ContentFittingDocumentView
-                                    fitToBox={true}
                                     Document={target}
+                                    LibraryPath={[]}
+                                    fitToBox={true}
                                     moveDocument={returnFalse}
                                     getTransform={Transform.Identity}
                                     active={returnFalse}
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 47f876ac2..cfb9319bb 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -170,8 +170,9 @@ export class PresElementBox extends DocComponent<FieldViewProps, PresDocument>(P
                 width: propDocWidth === 0 ? "auto" : propDocWidth * scale(),
             }}>
                 <ContentFittingDocumentView
-                    fitToBox={StrCast(this.targetDoc.type).indexOf(DocumentType.COL) !== -1}
                     Document={this.targetDoc}
+                    LibraryPath={[]}
+                    fitToBox={StrCast(this.targetDoc.type).indexOf(DocumentType.COL) !== -1}
                     addDocument={returnFalse}
                     removeDocument={returnFalse}
                     ruleProvider={undefined}
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 6ffff0562..0be583358 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -159,8 +159,9 @@ export class SearchItem extends React.Component<SearchItemProps> {
                 onPointerEnter={action(() => this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE))}
                 onPointerLeave={action(() => this._displayDim = 50)} >
                 <DocumentView
-                    fitToBox={StrCast(this.props.doc.type).indexOf(DocumentType.COL) !== -1}
                     Document={this.props.doc}
+                    LibraryPath={[]}
+                    fitToBox={StrCast(this.props.doc.type).indexOf(DocumentType.COL) !== -1}
                     addDocument={returnFalse}
                     removeDocument={returnFalse}
                     ruleProvider={undefined}
-- 
cgit v1.2.3-70-g09d2


From 71ebece4a97547ac9bec32cd30957d38c96fb755 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 6 Dec 2019 21:19:25 -0500
Subject: performance fixes for sidebar.   stop re-rendering docking view when
 sidebar expands.  fixed lightbox.

---
 src/client/views/MainView.scss                     | 17 ++++-
 src/client/views/MainView.tsx                      | 77 +++++++++++-----------
 .../views/collections/CollectionDockingView.tsx    |  2 +
 src/client/views/collections/CollectionView.tsx    | 14 +++-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../views/nodes/ContentFittingDocumentView.tsx     |  1 +
 6 files changed, 68 insertions(+), 45 deletions(-)

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

diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index 0ee30f117..a8924c6b1 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -7,11 +7,18 @@
     width: 100%;
 }
 
+.mainContent-div {
+    position: relative;
+    width:100%;
+    height:100%;
+}
+
 // add nodes menu. Note that the + button is actually an input label, not an actual button.
 .mainView-docButtons {
     position: absolute;
     bottom: 20px;
-    left: 250px;
+    left: calc(100% + 5px);
+    z-index: 1;
 }
 
 #mainView-container {
@@ -27,13 +34,13 @@
     width: 100%;
     height: 100%;
     position: absolute;
+    display: flex;
 }
 
 .mainView-flyoutContainer {
     display: flex;
     flex-direction: column;
-    position: absolute;
-    width: 100%;
+    position: relative;
     height: 100%;
 
     .documentView-node-topmost {
@@ -59,9 +66,11 @@
 
 .mainView-libraryFlyout {
     height: 100%;
+    width:100%;
     position: absolute;
     display: flex;
     flex-direction: column;
+    z-index: 1;
 }
 
 .mainView-expandFlyoutButton {
@@ -73,6 +82,7 @@
 
 .mainView-libraryHandle {
     width: 20px;
+    left: calc(100% - 10px);
     height: 40px;
     top: 50%;
     border: 1px solid black;
@@ -80,6 +90,7 @@
     position: absolute;
     z-index: 1;
     touch-action: none;
+    cursor: ew-resize;
 }
 
 .mainView-workspace {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 29719a6eb..175af1abc 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -39,6 +39,7 @@ import MarqueeOptionsMenu from './collections/collectionFreeForm/MarqueeOptionsM
 import InkSelectDecorations from './InkSelectDecorations';
 import { Scripting } from '../util/Scripting';
 import { AudioBox } from './nodes/AudioBox';
+import { TraceMobx } from '../../new_fields/util';
 
 @observer
 export class MainView extends React.Component {
@@ -260,38 +261,41 @@ export class MainView extends React.Component {
     getPHeight = () => this._panelHeight;
     getContentsHeight = () => this._panelHeight - this._buttonBarHeight;
 
+    _emptyPath = [];
+    @computed get mainDocView() {
+        return <DocumentView Document={this.mainContainer!}
+            DataDoc={undefined}
+            LibraryPath={this._emptyPath}
+            addDocument={undefined}
+            addDocTab={this.addDocTabFunc}
+            pinToPres={emptyFunction}
+            onClick={undefined}
+            ruleProvider={undefined}
+            removeDocument={undefined}
+            ScreenToLocalTransform={Transform.Identity}
+            ContentScaling={returnOne}
+            PanelWidth={this.getPWidth}
+            PanelHeight={this.getPHeight}
+            renderDepth={0}
+            backgroundColor={returnEmptyString}
+            focus={emptyFunction}
+            parentActive={returnTrue}
+            whenActiveChanged={emptyFunction}
+            bringToFront={emptyFunction}
+            ContainingCollectionView={undefined}
+            ContainingCollectionDoc={undefined}
+            zoomToScale={emptyFunction}
+            getScale={returnOne}
+        />
+    }
     @computed get dockingContent() {
+        TraceMobx();
         const mainContainer = this.mainContainer;
-        const flyoutWidth = this.flyoutWidth; // bcz: need to be here because Measure messes with observables.
-        const flyoutTranslate = this._flyoutTranslate;
+        const width = this.flyoutWidth;
         return <Measure offset onResize={this.onResize}>
             {({ measureRef }) =>
-                <div ref={measureRef} id="mainContent-div" style={{ width: `calc(100% - ${flyoutTranslate ? flyoutWidth : 0}px`, transform: `translate(${flyoutTranslate ? flyoutWidth : 0}px, 0px)` }} onDrop={this.onDrop}>
-                    {!mainContainer ? (null) :
-                        <DocumentView Document={mainContainer}
-                            DataDoc={undefined}
-                            LibraryPath={[]}
-                            addDocument={undefined}
-                            addDocTab={this.addDocTabFunc}
-                            pinToPres={emptyFunction}
-                            onClick={undefined}
-                            ruleProvider={undefined}
-                            removeDocument={undefined}
-                            ScreenToLocalTransform={Transform.Identity}
-                            ContentScaling={returnOne}
-                            PanelWidth={this.getPWidth}
-                            PanelHeight={this.getPHeight}
-                            renderDepth={0}
-                            backgroundColor={returnEmptyString}
-                            focus={emptyFunction}
-                            parentActive={returnTrue}
-                            whenActiveChanged={emptyFunction}
-                            bringToFront={emptyFunction}
-                            ContainingCollectionView={undefined}
-                            ContainingCollectionDoc={undefined}
-                            zoomToScale={emptyFunction}
-                            getScale={returnOne}
-                        />}
+                <div ref={measureRef} className="mainContent-div" onDrop={this.onDrop} style={{ width: `calc(100% - ${width}px)` }}>
+                    {!mainContainer ? (null) : this.mainDocView}
                 </div>
             }
         </Measure>;
@@ -416,6 +420,7 @@ export class MainView extends React.Component {
                     {CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}
                 </button>
             </div>
+            {this.docButtons}
         </div>;
     }
 
@@ -423,10 +428,9 @@ export class MainView extends React.Component {
         const sidebar = this.userDoc && this.userDoc.sidebarContainer;
         return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
             <div className="mainView-mainContent" >
-                <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger}>
-                    <div className="mainView-libraryHandle"
-                        style={{ cursor: "ew-resize", left: `${(this.flyoutWidth * (this._flyoutTranslate ? 1 : 0)) - 10}px`, backgroundColor: `${StrCast(sidebar.backgroundColor, "lightGray")}` }}
-                        onPointerDown={this.onPointerDown} onPointerOver={this.pointerOverDragger}>
+                <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger} style={{ width: this.flyoutWidth }}>
+                    <div className="mainView-libraryHandle" onPointerDown={this.onPointerDown} onPointerOver={this.pointerOverDragger}
+                        style={{ backgroundColor: `${StrCast(sidebar.backgroundColor, "lightGray")}` }} >
                         <span title="library View Dragger" style={{
                             width: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "3vw",
                             height: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "100vh",
@@ -435,11 +439,9 @@ export class MainView extends React.Component {
                         }} />
                     </div>
                     <div className="mainView-libraryFlyout" style={{
-                        width: `${this.flyoutWidth}px`,
-                        zIndex: 1,
-                        transformOrigin: this._flyoutTranslate ? "" : "left center",
+                        //transformOrigin: this._flyoutTranslate ? "" : "left center",
                         transition: this._flyoutTranslate ? "" : "width .5s",
-                        transform: `scale(${this._flyoutTranslate ? 1 : 0.8})`,
+                        //transform: `scale(${this._flyoutTranslate ? 1 : 0.8})`,
                         boxShadow: this._flyoutTranslate ? "" : "rgb(156, 147, 150) 0.2vw 0.2vw 0.8vw"
                     }}>
                         {this.flyout}
@@ -471,7 +473,7 @@ export class MainView extends React.Component {
     @computed get docButtons() {
         if (CurrentUserUtils.UserDocument?.expandingButtons instanceof Doc) {
             return <div className="mainView-docButtons" ref={this._docBtnRef}
-                style={{ left: (this._flyoutTranslate ? this.flyoutWidth : 0) + 20, height: !CurrentUserUtils.UserDocument.expandingButtons.isExpanded ? "42px" : undefined }} >
+                style={{ height: !CurrentUserUtils.UserDocument.expandingButtons.isExpanded ? "42px" : undefined }} >
                 <MainViewNotifs />
                 <CollectionLinearView
                     Document={CurrentUserUtils.UserDocument.expandingButtons}
@@ -515,7 +517,6 @@ export class MainView extends React.Component {
             {this.mainContent}
             <PreviewCursor />
             <ContextMenu />
-            {this.docButtons}
             <PDFMenu />
             <MarqueeOptionsMenu />
             <OverlayView />
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 4374cde3c..ffcb3e9ce 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -32,6 +32,7 @@ import React = require("react");
 import { ButtonSelector } from './ParentDocumentSelector';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { ComputedField } from '../../../new_fields/ScriptField';
+import { TraceMobx } from '../../../new_fields/util';
 library.add(faFile);
 const _global = (window /* browser */ || global /* node */) as any;
 
@@ -662,6 +663,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     }
 
     @computed get docView() {
+        TraceMobx();
         if (!this._document) return (null);
         const document = this._document;
         const resolvedDataDoc = document.layout instanceof Doc ? document : this._dataDoc;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 54f5a2c42..cd546696f 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -32,6 +32,7 @@ import { SelectionManager } from '../../util/SelectionManager';
 import './CollectionView.scss';
 import { FieldViewProps, FieldView } from '../nodes/FieldView';
 import { Touchable } from '../Touchable';
+const path = require('path');
 library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy);
 
 export enum CollectionViewType {
@@ -234,10 +235,17 @@ export class CollectionView extends Touchable<FieldViewProps> {
     }
 
     lightbox = (images: string[]) => {
+        if (!images.length) return (null);
+        const mainPath = path.extname(images[this._curLightboxImg]);
+        const nextPath = path.extname(images[(this._curLightboxImg + 1) % images.length]);
+        const prevPath = path.extname(images[(this._curLightboxImg + images.length - 1) % images.length]);
+        let main = images[this._curLightboxImg].replace(mainPath, "_o" + mainPath);
+        let next = images[(this._curLightboxImg + 1) % images.length].replace(nextPath, "_o" + nextPath);
+        let prev = images[(this._curLightboxImg + images.length - 1) % images.length].replace(prevPath, "_o" + prevPath);
         return !this._isLightboxOpen ? (null) : (<Lightbox key="lightbox"
-            mainSrc={images[this._curLightboxImg]}
-            nextSrc={images[(this._curLightboxImg + 1) % images.length]}
-            prevSrc={images[(this._curLightboxImg + images.length - 1) % images.length]}
+            mainSrc={main}
+            nextSrc={next}
+            prevSrc={prev}
             onCloseRequest={action(() => this._isLightboxOpen = false)}
             onMovePrevRequest={action(() => this._curLightboxImg = (this._curLightboxImg + images.length - 1) % images.length)}
             onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 0d3748ded..81d8d467b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -711,7 +711,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             case "pivot": computedElementData = this.doPivotLayout(this._layoutPoolData); break;
             default: computedElementData = this.doFreeformLayout(this._layoutPoolData); break;
         }
-        this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).forEach(pair =>
+        this.childLayoutPairs.filter((pair, i) => this.isCurrent(pair.layout)).forEach(pair =>
             computedElementData.elements.push({
                 ele: <CollectionFreeFormDocumentView key={pair.layout[Id]} dataProvider={this.childDataProvider}
                     ruleProvider={this.Document.isRuleProvider ? this.props.Document : this.props.ruleProvider}
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 660a5bb7e..6d0ea33fa 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -45,6 +45,7 @@ interface ContentFittingDocumentViewProps {
 
 @observer
 export class ContentFittingDocumentView extends React.Component<ContentFittingDocumentViewProps>{
+    public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive
     private get layoutDoc() { return this.props.Document && Doc.Layout(this.props.Document); }
     private get nativeWidth() { return NumCast(this.layoutDoc!.nativeWidth, this.props.PanelWidth()); }
     private get nativeHeight() { return NumCast(this.layoutDoc!.nativeHeight, this.props.PanelHeight()); }
-- 
cgit v1.2.3-70-g09d2


From 600d06a162b4489fd52d5809eb9f5a69f28618fc Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 6 Dec 2019 23:11:51 -0500
Subject: performance tuning for sidebar

---
 src/Utils.ts                                         |  2 ++
 src/client/views/MainView.tsx                        | 11 +++++------
 .../views/collections/CollectionStackingView.tsx     | 20 +++++++++++++-------
 .../CollectionStackingViewFieldColumn.tsx            |  6 +++---
 src/client/views/collections/CollectionView.tsx      |  2 ++
 src/client/views/nodes/DocumentContentsView.tsx      |  3 +++
 6 files changed, 28 insertions(+), 16 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 7401ef981..2b15ad0f2 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -362,6 +362,8 @@ export function returnZero() { return 0; }
 
 export function returnEmptyString() { return ""; }
 
+export let emptyPath = [];
+
 export function emptyFunction() { }
 
 export function unimplementedFunction() { throw new Error("This function is not implemented, but should be."); }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 175af1abc..dacc9226e 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -15,7 +15,7 @@ import { List } from '../../new_fields/List';
 import { listSpec } from '../../new_fields/Schema';
 import { Cast, FieldValue, StrCast } from '../../new_fields/Types';
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
-import { emptyFunction, returnEmptyString, returnFalse, returnOne, returnTrue, Utils } from '../../Utils';
+import { emptyFunction, returnEmptyString, returnFalse, returnOne, returnTrue, Utils, emptyPath } from '../../Utils';
 import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
 import { Docs, DocumentOptions } from '../documents/Documents';
@@ -261,11 +261,10 @@ export class MainView extends React.Component {
     getPHeight = () => this._panelHeight;
     getContentsHeight = () => this._panelHeight - this._buttonBarHeight;
 
-    _emptyPath = [];
     @computed get mainDocView() {
         return <DocumentView Document={this.mainContainer!}
             DataDoc={undefined}
-            LibraryPath={this._emptyPath}
+            LibraryPath={emptyPath}
             addDocument={undefined}
             addDocTab={this.addDocTabFunc}
             pinToPres={emptyFunction}
@@ -367,7 +366,7 @@ export class MainView extends React.Component {
                 <DocumentView
                     Document={sidebarButtonsDoc}
                     DataDoc={undefined}
-                    LibraryPath={[]}
+                    LibraryPath={emptyPath}
                     addDocument={undefined}
                     addDocTab={this.addDocTabFunc}
                     pinToPres={emptyFunction}
@@ -394,7 +393,7 @@ export class MainView extends React.Component {
                 <DocumentView
                     Document={sidebarContent}
                     DataDoc={undefined}
-                    LibraryPath={[]}
+                    LibraryPath={emptyPath}
                     addDocument={undefined}
                     addDocTab={this.addDocTabFunc}
                     pinToPres={emptyFunction}
@@ -478,7 +477,7 @@ export class MainView extends React.Component {
                 <CollectionLinearView
                     Document={CurrentUserUtils.UserDocument.expandingButtons}
                     DataDoc={undefined}
-                    LibraryPath={[]}
+                    LibraryPath={emptyPath}
                     fieldKey={"data"}
                     annotationsKey={""}
                     select={emptyFunction}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 9f01de08b..ff3417b77 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -24,6 +24,7 @@ import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
 import { ScriptBox } from "../ScriptBox";
 import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow";
+import { TraceMobx } from "../../../new_fields/util";
 
 @observer
 export class CollectionStackingView extends CollectionSubView(doc => doc) {
@@ -383,17 +384,22 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         }
     }
 
+    @computed get renderedSections() {
+        TraceMobx();
+        let sections = [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]];
+        if (this.sectionFilter) {
+            const entries = Array.from(this.Sections.entries());
+            sections = entries.sort(this.sortFunc);
+        }
+        return sections.map(section => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1]));
+    }
     render() {
+        TraceMobx();
         const editableViewProps = {
             GetValue: () => "",
             SetValue: this.addGroup,
             contents: "+ ADD A GROUP"
         };
-        let sections = [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]];
-        if (this.sectionFilter) {
-            const entries = Array.from(this.Sections.entries());
-            sections = entries.sort(this.sortFunc);
-        }
         return (
             <div className="collectionStackingMasonry-cont" >
                 <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"}
@@ -401,8 +407,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                     onScroll={action((e: React.UIEvent<HTMLDivElement>) => this._scroll = e.currentTarget.scrollTop)}
                     onDrop={this.onDrop.bind(this)}
                     onContextMenu={this.onContextMenu}
-                    onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
-                    {sections.map(section => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1]))}
+                    onWheel={e => e.stopPropagation()} >
+                    {this.renderedSections}
                     {!this.showAddAGroup ? (null) :
                         <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton"
                             style={{ width: !this.isStackingView ? "100%" : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 80dc482af..ca3b93bf8 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -17,6 +17,7 @@ import { anchorPoints, Flyout } from "../DocumentDecorations";
 import { EditableView } from "../EditableView";
 import { CollectionStackingView } from "./CollectionStackingView";
 import "./CollectionStackingView.scss";
+import { TraceMobx } from "../../../new_fields/util";
 
 library.add(faPalette);
 
@@ -252,13 +253,12 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     @observable private collapsed: boolean = false;
 
-    private toggleVisibility = action(() => {
-        this.collapsed = !this.collapsed;
-    });
+    private toggleVisibility = action(() => this.collapsed = !this.collapsed);
 
     @observable _headingsHack: number = 1;
 
     render() {
+        TraceMobx();
         const cols = this.props.cols();
         const key = StrCast(this.props.parent.props.Document.sectionFilter);
         let templatecols = "";
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index cd546696f..411040332 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -32,6 +32,7 @@ import { SelectionManager } from '../../util/SelectionManager';
 import './CollectionView.scss';
 import { FieldViewProps, FieldView } from '../nodes/FieldView';
 import { Touchable } from '../Touchable';
+import { TraceMobx } from '../../../new_fields/util';
 const path = require('path');
 library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy);
 
@@ -251,6 +252,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
             onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />);
     }
     render() {
+        TraceMobx();
         const props: CollectionRenderProps = {
             addDocument: this.addDocument,
             removeDocument: this.removeDocument,
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 13fd3cde1..1bbc82119 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -32,6 +32,7 @@ import { VideoBox } from "./VideoBox";
 import { WebBox } from "./WebBox";
 import { InkingStroke } from "../InkingStroke";
 import React = require("react");
+import { TraceMobx } from "../../../new_fields/util";
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 type BindingProps = Without<FieldViewProps, 'fieldKey'>;
@@ -57,6 +58,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     hideOnLeave?: boolean
 }> {
     @computed get layout(): string {
+        TraceMobx();
         if (!this.layoutDoc) return "<p>awaiting layout</p>";
         const layout = Cast(this.layoutDoc[this.props.layoutKey], "string");
         if (layout === undefined) {
@@ -92,6 +94,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     }
 
     render() {
+        TraceMobx();
         return (this.props.renderDepth > 7 || !this.layout) ? (null) :
             <ObserverJsxParser
                 blacklistedAttrs={[]}
-- 
cgit v1.2.3-70-g09d2


From b4bc5f534146968a0923c04927be66130f36f463 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 8 Dec 2019 11:10:05 -0500
Subject: minor exception fix

---
 src/client/views/MainView.tsx | 22 ++++++++++------------
 1 file changed, 10 insertions(+), 12 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index dacc9226e..bcdc3a453 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -57,6 +57,7 @@ export class MainView extends React.Component {
     @computed private get userDoc() { return CurrentUserUtils.UserDocument; }
     @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; }
     @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
+    @computed public get sidebarButtonsDoc() { return Cast(CurrentUserUtils.UserDocument.sidebarButtons, Doc) as Doc; }
 
     public isPointerDown = false;
 
@@ -206,7 +207,7 @@ export class MainView extends React.Component {
     }
 
     @action
-    openWorkspace = async (doc: Doc, fromHistory = false) => {
+    openWorkspace = (doc: Doc, fromHistory = false) => {
         CurrentUserUtils.MainDocId = doc[Id];
 
         if (doc) {  // this has the side-effect of setting the main container since we're assigning the active/guest workspace
@@ -316,6 +317,7 @@ export class MainView extends React.Component {
     pointerOverDragger = () => {
         if (this.flyoutWidth === 0) {
             this.flyoutWidth = 250;
+            this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30;
             this._flyoutTranslate = false;
         }
     }
@@ -331,26 +333,22 @@ export class MainView extends React.Component {
     @action
     onPointerMove = (e: PointerEvent) => {
         this.flyoutWidth = Math.max(e.clientX, 0);
+        this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30;
     }
     @action
     onPointerUp = (e: PointerEvent) => {
         if (Math.abs(e.clientX - this._flyoutSizeOnDown) < 4) {
             this.flyoutWidth = this.flyoutWidth < 5 ? 250 : 0;
+            this.flyoutWidth && (this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30);
         }
         document.removeEventListener("pointermove", this.onPointerMove);
         document.removeEventListener("pointerup", this.onPointerUp);
     }
     flyoutWidthFunc = () => this.flyoutWidth;
-    addDocTabFunc = (doc: Doc, data: Opt<Doc>, where: string, libraryPath?: Doc[]) => {
-        if (where === "close") {
-            return CollectionDockingView.CloseRightSplit(doc);
-        }
-        if (doc.dockingConfig) {
-            this.openWorkspace(doc);
-            return true;
-        } else {
-            return CollectionDockingView.AddRightSplit(doc, undefined, undefined, libraryPath);
-        }
+    addDocTabFunc = (doc: Doc, data: Opt<Doc>, where: string, libraryPath?: Doc[]): boolean => {
+        return where === "close" ? CollectionDockingView.CloseRightSplit(doc) :
+            doc.dockingConfig ? this.openWorkspace(doc) :
+                CollectionDockingView.AddRightSplit(doc, undefined, undefined, libraryPath);
     }
     mainContainerXf = () => new Transform(0, -this._buttonBarHeight, 1);
 
@@ -360,7 +358,6 @@ export class MainView extends React.Component {
             return (null);
         }
         const sidebarButtonsDoc = Cast(CurrentUserUtils.UserDocument.sidebarButtons, Doc) as Doc;
-        sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30;
         return <div className="mainView-flyoutContainer" >
             <div className="mainView-tabButtons" style={{ height: `${this._buttonBarHeight}px` }}>
                 <DocumentView
@@ -454,6 +451,7 @@ export class MainView extends React.Component {
     public static expandFlyout = action(() => {
         MainView.Instance._flyoutTranslate = true;
         MainView.Instance.flyoutWidth = (MainView.Instance.flyoutWidth || 250);
+        MainView.Instance.sidebarButtonsDoc.columnWidth = MainView.Instance.flyoutWidth / 3 - 30;
     });
 
     @computed get expandButton() {
-- 
cgit v1.2.3-70-g09d2


From 78a65a6d871ce89f3dfed76035e379df631757a8 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 9 Dec 2019 15:55:17 -0500
Subject: fixed up bulleted list formatting.  disabled auto-flyout of sidebar.

---
 src/client/util/RichTextSchema.tsx           | 12 ++----
 src/client/views/MainView.tsx                | 14 +++---
 src/client/views/nodes/FormattedTextBox.scss | 64 +++++++++++++++++-----------
 src/client/views/nodes/FormattedTextBox.tsx  | 57 ++++++++++++++-----------
 4 files changed, 80 insertions(+), 67 deletions(-)

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

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 2102ea85b..9e95314a9 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -249,16 +249,13 @@ export const nodes: { [index: string]: NodeSpec } = {
             bulletStyle: { default: 0 },
             mapStyle: { default: "decimal" },
             setFontSize: { default: undefined },
-            setFontFamily: { default: undefined },
+            setFontFamily: { default: "inherit" },
             inheritedFontSize: { default: undefined },
             visibility: { default: true }
         },
         toDOM(node: Node<any>) {
-            const bs = node.attrs.bulletStyle;
             if (node.attrs.mapStyle === "bullet") return ['ul', 0];
-            const decMap = bs ? "decimal" + bs : "";
-            const multiMap = bs === 1 ? "decimal1" : bs === 2 ? "upper-alpha" : bs === 3 ? "lower-roman" : bs === 4 ? "lower-alpha" : "";
-            const map = node.attrs.mapStyle === "decimal" ? decMap : multiMap;
+            const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : "";
             const fsize = node.attrs.setFontSize ? node.attrs.setFontSize : node.attrs.inheritedFontSize;
             const ffam = node.attrs.setFontFamily;
             return node.attrs.visibility ? ['ol', { class: `${map}-ol`, style: `list-style: none; font-size: ${fsize}; font-family: ${ffam}` }, 0] :
@@ -285,10 +282,7 @@ export const nodes: { [index: string]: NodeSpec } = {
         ...listItem,
         content: 'paragraph block*',
         toDOM(node: any) {
-            const bs = node.attrs.bulletStyle;
-            const decMap = bs ? "decimal" + bs : "";
-            const multiMap = bs === 1 ? "decimal1" : bs === 2 ? "upper-alpha" : bs === 3 ? "lower-roman" : bs === 4 ? "lower-alpha" : "";
-            const map = node.attrs.mapStyle === "decimal" ? decMap : node.attrs.mapStyle === "multi" ? multiMap : "";
+            const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : "";
             return node.attrs.visibility ? ["li", { class: `${map}` }, 0] : ["li", { class: `${map}` }, "..."];
             //return ["li", { class: `${map}` }, 0];
         }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index bcdc3a453..01cd7957c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -315,11 +315,11 @@ export class MainView extends React.Component {
 
     @action
     pointerOverDragger = () => {
-        if (this.flyoutWidth === 0) {
-            this.flyoutWidth = 250;
-            this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30;
-            this._flyoutTranslate = false;
-        }
+        // if (this.flyoutWidth === 0) {
+        //     this.flyoutWidth = 250;
+        //     this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30;
+        //     this._flyoutTranslate = false;
+        // }
     }
 
     @action
@@ -338,7 +338,7 @@ export class MainView extends React.Component {
     @action
     onPointerUp = (e: PointerEvent) => {
         if (Math.abs(e.clientX - this._flyoutSizeOnDown) < 4) {
-            this.flyoutWidth = this.flyoutWidth < 5 ? 250 : 0;
+            this.flyoutWidth = this.flyoutWidth < 15 ? 250 : 0;
             this.flyoutWidth && (this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30);
         }
         document.removeEventListener("pointermove", this.onPointerMove);
@@ -429,7 +429,7 @@ export class MainView extends React.Component {
                         style={{ backgroundColor: `${StrCast(sidebar.backgroundColor, "lightGray")}` }} >
                         <span title="library View Dragger" style={{
                             width: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "3vw",
-                            height: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "100vh",
+                            //height: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "100vh",
                             position: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "absolute" : "fixed",
                             top: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "" : "0"
                         }} />
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index baf758c0e..8256cffac 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -225,33 +225,47 @@ footnote::after {
 
 .ProseMirror {
     touch-action: none;
+    span {
+        font-family: inherit;
+    }
 
+    ol, ul {
+        counter-reset: deci1 0 multi1 0;
+        padding-left: 1em;
+        font-family: inherit;
+    }
     ol {
-        counter-reset: deci1 0;
-        padding-left: 0px;
+        margin-left: 1em;
+        font-family: inherit;
     }
-
-    .decimal1-ol { counter-reset: deci1; p { display: inline };                     ul, ol { padding-left: 30px; } }
-    .decimal2-ol { counter-reset: deci2; p { display: inline }; font-size: smaller; ul, ol { padding-left: 30px; } }
-    .decimal3-ol { counter-reset: deci3; p { display: inline }; font-size: smaller; ul, ol { padding-left: 30px; } }
-    .decimal4-ol { counter-reset: deci4; p { display: inline }; font-size: smaller; ul, ol { padding-left: 30px; } }
-    .decimal5-ol { counter-reset: deci5; p { display: inline }; font-size: smaller; ul, ol { padding-left: 30px; } }
-    .decimal6-ol { counter-reset: deci6; p { display: inline }; font-size: smaller; ul, ol { padding-left: 30px; } }
-    .decimal7-ol { counter-reset: deci7; p { display: inline }; font-size: smaller; ul, ol { padding-left: 30px; } }
-
-    .upper-alpha-ol { counter-reset: ualph;  p { display: inline}; font-size: smaller; }
-    .lower-roman-ol { counter-reset: lroman; p { display: inline}; font-size: smaller; }
-    .lower-alpha-ol { counter-reset: lalpha; p { display: inline}; font-size: smaller; }
-
-    .decimal1:before { counter-increment: deci1; display: inline-block; content: counter(deci1) ". ";  }
-    .decimal2:before { counter-increment: deci2; display: inline-block; content: counter(deci1) "."counter(deci2) ". ";  }
-    .decimal3:before { counter-increment: deci3; display: inline-block; content: counter(deci1) "."counter(deci2) "."counter(deci3) ". "; }
-    .decimal4:before { counter-increment: deci4; display: inline-block; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) ". "; }
-    .decimal5:before { counter-increment: deci5; display: inline-block; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) ". "; }
-    .decimal6:before { counter-increment: deci5; display: inline-block; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) "."counter(deci6) ". "; }
-    .decimal7:before { counter-increment: deci5; display: inline-block; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) "."counter(deci6) "."counter(deci7) ". "; }
+    p { 
+        display: inline;
+        font-family: inherit;
+     }
+
+    .decimal1-ol { counter-reset: deci1; margin-left: 0; }
+    .decimal2-ol { counter-reset: deci2; font-size: smaller; padding-left: 1em;}
+    .decimal3-ol { counter-reset: deci3; font-size: smaller; padding-left: 2em;}
+    .decimal4-ol { counter-reset: deci4; font-size: smaller; padding-left: 3em;}
+    .decimal5-ol { counter-reset: deci5; font-size: smaller; }
+    .decimal6-ol { counter-reset: deci6; font-size: smaller; }
+    .decimal7-ol { counter-reset: deci7; font-size: smaller; }
+
+    .multi1-ol { counter-reset: multi1; margin-left: 0;     padding-left: 1.2em }
+    .multi2-ol { counter-reset: multi2; font-size: smaller; padding-left: 1.4em;}
+    .multi3-ol { counter-reset: multi3; font-size: smaller; padding-left: 2em;}
+    .multi4-ol { counter-reset: multi4; font-size: smaller; padding-left: 3.4em;}
+
+    .decimal1:before { transition: 0.5s;counter-increment: deci1; display: inline-block; margin-left: -1em;   width:  1em;   content: counter(deci1) ". ";  }
+    .decimal2:before { transition: 0.5s;counter-increment: deci2; display: inline-block; margin-left: -2.1em; width: 2.1em;  content: counter(deci1) "."counter(deci2) ". ";  }
+    .decimal3:before { transition: 0.5s;counter-increment: deci3; display: inline-block; margin-left: -2.85em;width: 2.85em; content: counter(deci1) "."counter(deci2) "."counter(deci3) ". "; }
+    .decimal4:before { transition: 0.5s;counter-increment: deci4; display: inline-block; margin-left: -3.85em;width: 3.85em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) ". "; }
+    .decimal5:before { transition: 0.5s;counter-increment: deci5; display: inline-block; margin-left: -2em;   width:  5em;   content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) ". "; }
+    .decimal6:before { transition: 0.5s;counter-increment: deci6; display: inline-block; margin-left: -2em;   width:  6em;   content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) "."counter(deci6) ". "; }
+    .decimal7:before { transition: 0.5s;counter-increment: deci7; display: inline-block; margin-left: -2em;   width:  7em;   content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) "."counter(deci6) "."counter(deci7) ". "; }
     
-    .upper-alpha:before { counter-increment: ualph;  display: inline-block; content: counter(deci1) "."counter(ualph, upper-alpha) ". "; }
-    .lower-roman:before { counter-increment: lroman; display: inline-block; content: counter(deci1) "."counter(ualph, upper-alpha) "."counter(lroman, lower-roman) ". "; }
-    .lower-alpha:before { counter-increment: lalpha; display: inline-block; content: counter(deci1) "."counter(ualph, upper-alpha) "."counter(lroman, lower-roman) "."counter(lalpha, lower-alpha) ". "; }
+    .multi1:before { transition: 0.5s;counter-increment: multi1; display: inline-block; margin-left: -1em;    width: 1.2em;  content: counter(multi1, upper-alpha) ". ";  }
+    .multi2:before { transition: 0.5s;counter-increment: multi2; display: inline-block; margin-left: -2em;    width:   2em;  content: counter(multi1, upper-alpha) "."counter(multi2, decimal) ". "; }
+    .multi3:before { transition: 0.5s;counter-increment: multi3; display: inline-block; margin-left: -2.85em; width:2.85em;  content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) ". "; }
+    .multi4:before { transition: 0.5s;counter-increment: multi4; display: inline-block; margin-left:   -4.2em;  width: 4.2em;    content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) "."counter(multi4, lower-roman) ". "; }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 3ed010e8f..3197ce79f 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -962,37 +962,42 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         //     }
         // }
 
-        this.hitBulletTargets(e.clientX, e.clientY, e.nativeEvent.offsetX, e.shiftKey);
+        this.hitBulletTargets(e.clientX, e.clientY, e.shiftKey, false)
         if (this._recording) setTimeout(() => { this.stopDictation(true); setTimeout(() => this.recordDictation(), 500); }, 500);
     }
 
     // this hackiness handles clicking on the list item bullets to do expand/collapse.  the bullets are ::before pseudo elements so there's no real way to hit test against them.
-    hitBulletTargets(x: number, y: number, offsetX: number, select: boolean, highlightOnly = false) {
+    hitBulletTargets(x: number, y: number, select: boolean, highlightOnly: boolean) {
         clearStyleSheetRules(FormattedTextBox._bulletStyleSheet);
-        if (this.props.isSelected(true) && offsetX < 40) {
-            const pos = this._editorView!.posAtCoords({ left: x, top: y });
-            if (pos && pos.pos > 0) {
-                const node = this._editorView!.state.doc.nodeAt(pos.pos);
-                const node2 = node?.type === schema.nodes.paragraph ? this._editorView!.state.doc.nodeAt(pos.pos - 1) : undefined;
-                if ((node === this._nodeClicked || highlightOnly) && (node2?.type === schema.nodes.ordered_list || node2?.type === schema.nodes.list_item)) {
-                    const hit = this._editorView!.domAtPos(pos.pos).node as any;   // let beforeEle = document.querySelector("." + hit.className) as Element;
-                    const before = hit ? window.getComputedStyle(hit, ':before') : undefined;
-                    const beforeWidth = before ? Number(before.getPropertyValue('width').replace("px", "")) : undefined;
-                    if (beforeWidth && offsetX < beforeWidth * .9) {
-                        const ol = this._editorView!.state.doc.nodeAt(pos.pos - 2) ? this._editorView!.state.doc.nodeAt(pos.pos - 2) : undefined;
-                        if (ol?.type === schema.nodes.ordered_list && select) {
-                            if (!highlightOnly) {
-                                this._editorView!.dispatch(this._editorView!.state.tr.setSelection(new NodeSelection(this._editorView!.state.doc.resolve(pos.pos - 2))));
-                            }
-                            addStyleSheetRule(FormattedTextBox._bulletStyleSheet, hit.className + ":before", { background: "lightgray" });
-                        } else {
-                            if (highlightOnly) {
-                                addStyleSheetRule(FormattedTextBox._bulletStyleSheet, hit.className + ":before", { background: "lightgray" });
-                            } else {
-                                this._editorView!.dispatch(this._editorView!.state.tr.setNodeMarkup(pos.pos - 1, node2.type, { ...node2.attrs, visibility: !node2.attrs.visibility }));
-                            }
-                        }
+        const pos = this._editorView!.posAtCoords({ left: x, top: y });
+        if (pos && this.props.isSelected(true)) {
+            // let beforeEle = document.querySelector("." + hit.className) as Element; // const before = hit ? window.getComputedStyle(hit, ':before') : undefined;
+            //const node = this._editorView!.state.doc.nodeAt(pos.pos);
+            let $pos = this._editorView!.state.doc.resolve(pos.pos);
+            let list_node = $pos.node().type === schema.nodes.list_item ? $pos.node() : undefined;
+            if ($pos.node().type === schema.nodes.ordered_list) {
+                for (let off = 1; off < 100; off++) {
+                    const pos = this._editorView!.posAtCoords({ left: x + off, top: y });
+                    const node = pos && this._editorView!.state.doc.nodeAt(pos.pos);
+                    if (node?.type === schema.nodes.list_item) {
+                        list_node = node;
+                        break;
+                    }
+                }
+            }
+            if (list_node && this._editorView!.state.doc.nodeAt(pos.inside)!.attrs.bulletStyle === list_node.attrs.bulletStyle) {
+                if (select) {
+                    let $olist_pos = this._editorView!.state.doc.resolve($pos.pos - $pos.parentOffset - 1);
+                    if (!highlightOnly) {
+                        this._editorView!.dispatch(this._editorView!.state.tr.setSelection(new NodeSelection($olist_pos)));
+                    }
+                    addStyleSheetRule(FormattedTextBox._bulletStyleSheet, list_node.attrs.mapStyle + list_node.attrs.bulletStyle + ":hover:before", { background: "lightgray" });
+                } else if (Math.abs(pos.pos - pos.inside) < 2) {
+                    if (!highlightOnly) {
+                        this._editorView!.dispatch(this._editorView!.state.tr.setNodeMarkup(pos.inside, list_node.type, { ...list_node.attrs, visibility: !list_node.attrs.visibility }));
+                        this._editorView!.dispatch(this._editorView!.state.tr.setSelection(TextSelection.create(this._editorView!.state.doc, pos.inside)));
                     }
+                    addStyleSheetRule(FormattedTextBox._bulletStyleSheet, list_node.attrs.mapStyle + list_node.attrs.bulletStyle + ":hover:before", { background: "lightgray" });
                 }
             }
         }
@@ -1113,7 +1118,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                 onKeyDown={this.onKeyPress}
                 onFocus={this.onFocused}
                 onClick={this.onClick}
-                onPointerMove={e => this.hitBulletTargets(e.clientX, e.clientY, e.nativeEvent.offsetX, e.shiftKey, true)}
+                onPointerMove={e => this.hitBulletTargets(e.clientX, e.clientY, e.shiftKey, true)}
                 onBlur={this.onBlur}
                 onPointerUp={this.onPointerUp}
                 onPointerDown={this.onPointerDown}
-- 
cgit v1.2.3-70-g09d2


From 9d8845fb64c08729b446f12206aa5ed215228f4e Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 11 Dec 2019 17:00:58 -0500
Subject: cleaned up toottipmenu and richtextschema a bit.  fixed some problems
 with text styles.  fixed warnings.

---
 logs/npm-2-Wed, 11 Dec 2019 08:44:28 GMT.log    | 274 ----------------
 logs/server_pids.txt                            |   1 -
 src/client/util/ProsemirrorExampleTransfer.ts   |   2 +-
 src/client/util/RichTextRules.ts                | 162 +++++-----
 src/client/util/RichTextSchema.tsx              |  68 ++--
 src/client/util/TooltipTextMenu.tsx             | 414 +++++-------------------
 src/client/views/DocumentDecorations.tsx        |   4 +-
 src/client/views/EditableView.tsx               |   2 +-
 src/client/views/MainView.tsx                   |   2 +-
 src/client/views/collections/CollectionView.tsx |   6 +-
 src/client/views/nodes/DocumentView.tsx         |   2 +-
 src/client/views/nodes/FormattedTextBox.tsx     |  32 +-
 12 files changed, 228 insertions(+), 741 deletions(-)
 delete mode 100644 logs/npm-2-Wed, 11 Dec 2019 08:44:28 GMT.log
 delete mode 100644 logs/server_pids.txt

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

diff --git a/logs/npm-2-Wed, 11 Dec 2019 08:44:28 GMT.log b/logs/npm-2-Wed, 11 Dec 2019 08:44:28 GMT.log
deleted file mode 100644
index 37e232d48..000000000
--- a/logs/npm-2-Wed, 11 Dec 2019 08:44:28 GMT.log	
+++ /dev/null
@@ -1,274 +0,0 @@
-
-> dash@1.0.0 start-spawn /Users/swilkinss2012/Documents/GitHub/Dash-Web
-> cross-env SPAWNED=true NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev -- src/server/index.ts
-
-Using ts-node version 7.0.1, typescript version 3.7.2
-objc[9678]: Class GNotificationCenterDelegate is implemented in both /Users/swilkinss2012/Documents/GitHub/Dash-Web/node_modules/sharp/vendor/lib/libgio-2.0.0.dylib (0x10838d578) and /Users/swilkinss2012/Documents/GitHub/Dash-Web/node_modules/canvas/build/Release/libgio-2.0.0.dylib (0x10afd9578). One of the two will be used. Which one is undefined.
-[34m
-starting execution of preliminary functions...[0m
-(node:9678) DeprecationWarning: current URL string parser is deprecated, and will be removed in a future version. To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.
-[34mcompleted preliminary functions
-.[0m
-Starting type checking and linting service...
-Using 1 worker with 2048MB memory limit
-
-running server in [33mdevelopment[39m mode
-[33m[39m
-[33mregistering server routes...[39m
-[32mall server routes have been successfully registered:[39m
-[36m/[39m
-[36m/activity[39m
-[36m/buxton[39m
-[36m/delete[39m
-[36m/deleteAll[39m
-[36m/deleteWithAux[39m
-[36m/deleteWithGoogleCredentials[39m
-[36m/doc/:docId[39m
-[36m/downloadId/:docId[39m
-[36m/environment/:key[39m
-[36m/getCurrentUser[39m
-[36m/getUserDocumentId[39m
-[36m/getUsers[39m
-[36m/googleDocs/:sector/:action[39m
-[36m/googlePhotosMediaDownload[39m
-[36m/googlePhotosMediaUpload[39m
-[36m/home[39m
-[36m/imageHierarchyExport/:docId[39m
-[36m/inspectImage[39m
-[36m/persist[39m
-[36m/pull[39m
-[36m/readGoogleAccessToken[39m
-[36m/search[39m
-[36m/serializeDoc/:docId[39m
-[36m/serverHeartbeat[39m
-[36m/shutdown[39m
-[36m/solr/:action[39m
-[36m/textsearch[39m
-[36m/thumbnail/:filename[39m
-[36m/upload[39m
-[36m/uploadDoc[39m
-[36m/uploadURI[39m
-[36m/version[39m
-[36m/writeGoogleAccessToken[39m
-
-websocket listening on port [33m4321[39m
-server listening on port [33m1050[39m
-
-[34mℹ[39m [90m｢wdm｣[39m: wait until bundle finished: /serverHeartbeat
-[32muser samuel_wilkins@brown.edu has connected to the web socket[39m
-[32muser samuel_wilkins@brown.edu has connected to the web socket[39m
-[34mℹ[39m [90m｢wdm｣[39m: wait until bundle finished: /serverHeartbeat
-[34mℹ[39m [90m｢wdm｣[39m: wait until bundle finished: /serverHeartbeat
-[34mℹ[39m [90m｢wdm｣[39m: wait until bundle finished: /serverHeartbeat
-[34mℹ[39m [90m｢wdm｣[39m: wait until bundle finished: /serverHeartbeat
-[32muser samuel_wilkins@brown.edu has connected to the web socket[39m
-[32muser samuel_wilkins@brown.edu has connected to the web socket[39m
-Type checking and linting in progress...
-webpack built 8f6b743d91fd3862683b in 47419ms
-[33m⚠[39m [90m｢wdm｣[39m: Hash: [1m8f6b743d91fd3862683b[39m[22m
-Version: webpack [1m4.36.1[39m[22m
-Time: [1m47419[39m[22mms
-Built at: 12/11/2019 [1m3:45:31 AM[39m[22m
-                               [1mAsset[39m[22m       [1mSize[39m[22m               [1mChunks[39m[22m  [1m[39m[22m           [1m[39m[22m[1mChunk Names[39m[22m
-[1m[32m275711e56bd1bc79fdff544a3d7dbfae.png[39m[22m  289 bytes                     [1m[39m[22m  [1m[32m[emitted][39m[22m  
-[1m[32m32f1593298e6e7bee5673bf647328d72.png[39m[22m  429 bytes                     [1m[39m[22m  [1m[32m[emitted][39m[22m  
-[1m[32m718c914a99a2136c01c84e01f63e505a.png[39m[22m  829 bytes                     [1m[39m[22m  [1m[32m[emitted][39m[22m  
-[1m[32m906f1a1816c2a03b5c7612f6aa2ceece.png[39m[22m  281 bytes                     [1m[39m[22m  [1m[32m[emitted][39m[22m  
-                [1m[32massets/downarrow.png[39m[22m   3.28 KiB                     [1m[39m[22m  [1m[32m[emitted][39m[22m  
-                     [1m[32massets/env.json[39m[22m  360 bytes                     [1m[39m[22m  [1m[32m[emitted][39m[22m  
-            [1m[32massets/google_photos.png[39m[22m    114 KiB                     [1m[39m[22m  [1m[32m[emitted][39m[22m  
-              [1m[32massets/google_tags.png[39m[22m    7.9 KiB                     [1m[39m[22m  [1m[32m[emitted][39m[22m  
-                  [1m[32massets/loading.gif[39m[22m    112 KiB                     [1m[39m[22m  [1m[32m[emitted][39m[22m  
-                [1m[32massets/pdf.worker.js[39m[22m   1.55 MiB                     [1m[39m[22m  [1m[32m[emitted][39m[22m  
-                           [1m[32mbundle.js[39m[22m   20.8 MiB               [1mbundle[39m[22m  [1m[32m[emitted][39m[22m  bundle
-                       [1m[32mbundle.js.map[39m[22m   23.3 MiB               [1mbundle[39m[22m  [1m[32m[emitted][39m[22m  bundle
-                     [1m[32mdebug/repl.html[39m[22m  348 bytes                     [1m[39m[22m  [1m[32m[emitted][39m[22m  
-                     [1m[32mdebug/test.html[39m[22m  245 bytes                     [1m[39m[22m  [1m[32m[emitted][39m[22m  
-                   [1m[32mdebug/viewer.html[39m[22m  357 bytes                     [1m[39m[22m  [1m[32m[emitted][39m[22m  
-[1m[32me7a34b49f3c49ca0c25c76b30cd09e12.png[39m[22m  445 bytes                     [1m[39m[22m  [1m[32m[emitted][39m[22m  
-                      [1m[32mimageUpload.js[39m[22m   20.8 MiB          [1mimageUpload[39m[22m  [1m[32m[emitted][39m[22m  imageUpload
-                  [1m[32mimageUpload.js.map[39m[22m   23.3 MiB          [1mimageUpload[39m[22m  [1m[32m[emitted][39m[22m  imageUpload
-                          [1m[32mindex.html[39m[22m  593 bytes                     [1m[39m[22m  [1m[32m[emitted][39m[22m  
-                      [1m[32minkControls.js[39m[22m    116 KiB          [1minkControls[39m[22m  [1m[32m[emitted][39m[22m  inkControls
-                  [1m[32minkControls.js.map[39m[22m    122 KiB          [1minkControls[39m[22m  [1m[32m[emitted][39m[22m  inkControls
-                   [1m[32mmobile/image.html[39m[22m  333 bytes                     [1m[39m[22m  [1m[32m[emitted][39m[22m  
-                     [1m[32mmobile/ink.html[39m[22m  252 bytes                     [1m[39m[22m  [1m[32m[emitted][39m[22m  
-                             [1m[32mrepl.js[39m[22m   9.31 MiB                 [1mrepl[39m[22m  [1m[32m[emitted][39m[22m  repl
-                         [1m[32mrepl.js.map[39m[22m   10.5 MiB                 [1mrepl[39m[22m  [1m[32m[emitted][39m[22m  repl
-                             [1m[32mtest.js[39m[22m    1.2 MiB                 [1mtest[39m[22m  [1m[32m[emitted][39m[22m  test
-                         [1m[32mtest.js.map[39m[22m   1.42 MiB                 [1mtest[39m[22m  [1m[32m[emitted][39m[22m  test
-                            [1m[32mtest.pdf[39m[22m   53.6 KiB                     [1m[39m[22m  [1m[32m[emitted][39m[22m  
-              [1m[32mvendors~pdfjsWorker.js[39m[22m   1.55 MiB  [1mvendors~pdfjsWorker[39m[22m  [1m[32m[emitted][39m[22m  vendors~pdfjsWorker
-          [1m[32mvendors~pdfjsWorker.js.map[39m[22m   1.87 MiB  [1mvendors~pdfjsWorker[39m[22m  [1m[32m[emitted][39m[22m  vendors~pdfjsWorker
-                           [1m[32mviewer.js[39m[22m   9.47 MiB               [1mviewer[39m[22m  [1m[32m[emitted][39m[22m  viewer
-                       [1m[32mviewer.js.map[39m[22m   10.7 MiB               [1mviewer[39m[22m  [1m[32m[emitted][39m[22m  viewer
-Entrypoint [1mbundle[39m[22m = [1m[32mbundle.js[39m[22m [1m[32mbundle.js.map[39m[22m
-Entrypoint [1mviewer[39m[22m = [1m[32mviewer.js[39m[22m [1m[32mviewer.js.map[39m[22m
-Entrypoint [1mrepl[39m[22m = [1m[32mrepl.js[39m[22m [1m[32mrepl.js.map[39m[22m
-Entrypoint [1mtest[39m[22m = [1m[32mtest.js[39m[22m [1m[32mtest.js.map[39m[22m
-Entrypoint [1minkControls[39m[22m = [1m[32minkControls.js[39m[22m [1m[32minkControls.js.map[39m[22m
-Entrypoint [1mimageUpload[39m[22m = [1m[32mimageUpload.js[39m[22m [1m[32mimageUpload.js.map[39m[22m
-[19] [1mmulti ./src/client/views/Main.tsx webpack-hot-middleware/client?reload=true[39m[22m 40 bytes {[1m[33mbundle[39m[22m}[1m[32m [built][39m[22m
-[20] [1mmulti ./src/debug/Viewer.tsx webpack-hot-middleware/client?reload=true[39m[22m 40 bytes {[1m[33mviewer[39m[22m}[1m[32m [built][39m[22m
-[21] [1mmulti ./src/debug/Repl.tsx webpack-hot-middleware/client?reload=true[39m[22m 40 bytes {[1m[33mrepl[39m[22m}[1m[32m [built][39m[22m
-[22] [1mmulti ./src/debug/Test.tsx webpack-hot-middleware/client?reload=true[39m[22m 40 bytes {[1m[33mtest[39m[22m}[1m[32m [built][39m[22m
-[23] [1mmulti ./src/mobile/InkControls.tsx webpack-hot-middleware/client?reload=true[39m[22m 40 bytes {[1m[33minkControls[39m[22m}[1m[32m [built][39m[22m
-[24] [1mmulti ./src/mobile/ImageUpload.tsx webpack-hot-middleware/client?reload=true[39m[22m 40 bytes {[1m[33mimageUpload[39m[22m}[1m[32m [built][39m[22m
- [[1m./node_modules/mobx-react/index.module.js[39m[22m] 48.8 KiB {[1m[33mbundle[39m[22m} {[1m[33mviewer[39m[22m} {[1m[33mrepl[39m[22m} {[1m[33mimageUpload[39m[22m}[1m[32m [built][39m[22m
- [[1m./node_modules/mobx/lib/mobx.module.js[39m[22m] 175 KiB {[1m[33mbundle[39m[22m} {[1m[33mviewer[39m[22m} {[1m[33mrepl[39m[22m} {[1m[33mimageUpload[39m[22m}[1m[32m [built][39m[22m
- [./node_modules/webpack-hot-middleware/client.js?reload=true] [1m(webpack)-hot-middleware/client.js?reload=true[39m[22m 7.68 KiB {[1m[33mbundle[39m[22m} {[1m[33mviewer[39m[22m} {[1m[33mrepl[39m[22m} {[1m[33mtest[39m[22m} {[1m[33minkControls[39m[22m} {[1m[33mimageUpload[39m[22m}[1m[32m [built][39m[22m
- [[1m./src/client/views/Main.tsx[39m[22m] 4.03 KiB {[1m[33mbundle[39m[22m}[1m[32m [built][39m[22m
- [[1m./src/debug/Repl.tsx[39m[22m] 6.87 KiB {[1m[33mrepl[39m[22m}[1m[32m [built][39m[22m
- [[1m./src/debug/Test.tsx[39m[22m] 1.02 KiB {[1m[33mtest[39m[22m}[1m[32m [built][39m[22m
- [[1m./src/debug/Viewer.tsx[39m[22m] 12.1 KiB {[1m[33mviewer[39m[22m}[1m[32m [built][39m[22m
- [[1m./src/mobile/ImageUpload.tsx[39m[22m] 9.97 KiB {[1m[33mimageUpload[39m[22m}[1m[32m [built][39m[22m
- [[1m./src/mobile/InkControls.tsx[39m[22m] 14 bytes {[1m[33minkControls[39m[22m}[1m[32m [built][39m[22m
-    + 1494 hidden modules
-
-[1m[33mWARNING in ./node_modules/typescript/lib/typescript.js 5121:41-60
-Critical dependency: the request of a dependency is an expression
- @ ./src/client/util/Scripting.ts
- @ ./src/debug/Viewer.tsx
- @ multi ./src/debug/Viewer.tsx webpack-hot-middleware/client?reload=true[39m[22m
-[34mℹ[39m [90m｢wdm｣[39m: Compiled with warnings.
-[34mℹ[39m [90m｢wdm｣[39m: Compiling...
-webpack building...
-[34mℹ[39m [90m｢wdm｣[39m: wait until bundle finished: /login
-[34mℹ[39m [90m｢wdm｣[39m: wait until bundle finished: /login
-[34mℹ[39m [90m｢wdm｣[39m: wait until bundle finished: /login
-[34mℹ[39m [90m｢wdm｣[39m: wait until bundle finished: /login
-[34mℹ[39m [90m｢wdm｣[39m: wait until bundle finished: /login
-Type checking and linting in progress...
-webpack built 8f6b743d91fd3862683b in 615ms
-[33m⚠[39m [90m｢wdm｣[39m: Hash: [1m8f6b743d91fd3862683b[39m[22m
-Version: webpack [1m4.36.1[39m[22m
-Time: [1m615[39m[22mms
-Built at: 12/11/2019 [1m3:45:33 AM[39m[22m
-                               [1mAsset[39m[22m       [1mSize[39m[22m               [1mChunks[39m[22m  [1m[39m[22m[1m[39m[22m[1mChunk Names[39m[22m
-[1m[32m275711e56bd1bc79fdff544a3d7dbfae.png[39m[22m  289 bytes                     [1m[39m[22m  [1m[32m[39m[22m
-[1m[32m32f1593298e6e7bee5673bf647328d72.png[39m[22m  429 bytes                     [1m[39m[22m  [1m[32m[39m[22m
-[1m[32m718c914a99a2136c01c84e01f63e505a.png[39m[22m  829 bytes                     [1m[39m[22m  [1m[32m[39m[22m
-[1m[32m906f1a1816c2a03b5c7612f6aa2ceece.png[39m[22m  281 bytes                     [1m[39m[22m  [1m[32m[39m[22m
-                           [1m[32mbundle.js[39m[22m   20.8 MiB               [1mbundle[39m[22m  [1m[32m[39m[22mbundle
-                       [1m[32mbundle.js.map[39m[22m   23.3 MiB               [1mbundle[39m[22m  [1m[32m[39m[22mbundle
-[1m[32me7a34b49f3c49ca0c25c76b30cd09e12.png[39m[22m  445 bytes                     [1m[39m[22m  [1m[32m[39m[22m
-                      [1m[32mimageUpload.js[39m[22m   20.8 MiB          [1mimageUpload[39m[22m  [1m[32m[39m[22mimageUpload
-                  [1m[32mimageUpload.js.map[39m[22m   23.3 MiB          [1mimageUpload[39m[22m  [1m[32m[39m[22mimageUpload
-                      [1m[32minkControls.js[39m[22m    116 KiB          [1minkControls[39m[22m  [1m[32m[39m[22minkControls
-                  [1m[32minkControls.js.map[39m[22m    122 KiB          [1minkControls[39m[22m  [1m[32m[39m[22minkControls
-                             [1m[32mrepl.js[39m[22m   9.31 MiB                 [1mrepl[39m[22m  [1m[32m[39m[22mrepl
-                         [1m[32mrepl.js.map[39m[22m   10.5 MiB                 [1mrepl[39m[22m  [1m[32m[39m[22mrepl
-                             [1m[32mtest.js[39m[22m    1.2 MiB                 [1mtest[39m[22m  [1m[32m[39m[22mtest
-                         [1m[32mtest.js.map[39m[22m   1.42 MiB                 [1mtest[39m[22m  [1m[32m[39m[22mtest
-              [1m[32mvendors~pdfjsWorker.js[39m[22m   1.55 MiB  [1mvendors~pdfjsWorker[39m[22m  [1m[32m[39m[22mvendors~pdfjsWorker
-          [1m[32mvendors~pdfjsWorker.js.map[39m[22m   1.87 MiB  [1mvendors~pdfjsWorker[39m[22m  [1m[32m[39m[22mvendors~pdfjsWorker
-                           [1m[32mviewer.js[39m[22m   9.47 MiB               [1mviewer[39m[22m  [1m[32m[39m[22mviewer
-                       [1m[32mviewer.js.map[39m[22m   10.7 MiB               [1mviewer[39m[22m  [1m[32m[39m[22mviewer
-Entrypoint [1mbundle[39m[22m = [1m[32mbundle.js[39m[22m [1m[32mbundle.js.map[39m[22m
-Entrypoint [1mviewer[39m[22m = [1m[32mviewer.js[39m[22m [1m[32mviewer.js.map[39m[22m
-Entrypoint [1mrepl[39m[22m = [1m[32mrepl.js[39m[22m [1m[32mrepl.js.map[39m[22m
-Entrypoint [1mtest[39m[22m = [1m[32mtest.js[39m[22m [1m[32mtest.js.map[39m[22m
-Entrypoint [1minkControls[39m[22m = [1m[32minkControls.js[39m[22m [1m[32minkControls.js.map[39m[22m
-Entrypoint [1mimageUpload[39m[22m = [1m[32mimageUpload.js[39m[22m [1m[32mimageUpload.js.map[39m[22m
-[19] [1mmulti ./src/client/views/Main.tsx webpack-hot-middleware/client?reload=true[39m[22m 40 bytes {[1m[33mbundle[39m[22m}
-[20] [1mmulti ./src/debug/Viewer.tsx webpack-hot-middleware/client?reload=true[39m[22m 40 bytes {[1m[33mviewer[39m[22m}
-[21] [1mmulti ./src/debug/Repl.tsx webpack-hot-middleware/client?reload=true[39m[22m 40 bytes {[1m[33mrepl[39m[22m}
-[22] [1mmulti ./src/debug/Test.tsx webpack-hot-middleware/client?reload=true[39m[22m 40 bytes {[1m[33mtest[39m[22m}
-[23] [1mmulti ./src/mobile/InkControls.tsx webpack-hot-middleware/client?reload=true[39m[22m 40 bytes {[1m[33minkControls[39m[22m}
-[24] [1mmulti ./src/mobile/ImageUpload.tsx webpack-hot-middleware/client?reload=true[39m[22m 40 bytes {[1m[33mimageUpload[39m[22m}
- [[1m./node_modules/mobx-react/index.module.js[39m[22m] 48.8 KiB {[1m[33mbundle[39m[22m} {[1m[33mviewer[39m[22m} {[1m[33mrepl[39m[22m} {[1m[33mimageUpload[39m[22m}
- [[1m./node_modules/mobx/lib/mobx.module.js[39m[22m] 175 KiB {[1m[33mbundle[39m[22m} {[1m[33mviewer[39m[22m} {[1m[33mrepl[39m[22m} {[1m[33mimageUpload[39m[22m}
- [./node_modules/webpack-hot-middleware/client.js?reload=true] [1m(webpack)-hot-middleware/client.js?reload=true[39m[22m 7.68 KiB {[1m[33mbundle[39m[22m} {[1m[33mviewer[39m[22m} {[1m[33mrepl[39m[22m} {[1m[33mtest[39m[22m} {[1m[33minkControls[39m[22m} {[1m[33mimageUpload[39m[22m}
- [[1m./src/client/views/Main.tsx[39m[22m] 4.03 KiB {[1m[33mbundle[39m[22m}
- [[1m./src/debug/Repl.tsx[39m[22m] 6.87 KiB {[1m[33mrepl[39m[22m}
- [[1m./src/debug/Test.tsx[39m[22m] 1.02 KiB {[1m[33mtest[39m[22m}
- [[1m./src/debug/Viewer.tsx[39m[22m] 12.1 KiB {[1m[33mviewer[39m[22m}
- [[1m./src/mobile/ImageUpload.tsx[39m[22m] 9.97 KiB {[1m[33mimageUpload[39m[22m}
- [[1m./src/mobile/InkControls.tsx[39m[22m] 14 bytes {[1m[33minkControls[39m[22m}
-    + 1494 hidden modules
-
-[1m[33mWARNING in ./node_modules/typescript/lib/typescript.js 5121:41-60
-Critical dependency: the request of a dependency is an expression
- @ ./src/client/util/Scripting.ts
- @ ./src/debug/Viewer.tsx
- @ multi ./src/debug/Viewer.tsx webpack-hot-middleware/client?reload=true[39m[22m
-[34mℹ[39m [90m｢wdm｣[39m: Compiled with warnings.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(81,21):
-prefer-const: Identifier 'marks' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(83,25):
-prefer-const: Identifier 'tr' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(86,21):
-prefer-const: Identifier 'isValidColor' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(87,25):
-prefer-const: Identifier 's' is never reassigned; use 'const' instead of 'var'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(89,36):
-triple-equals: == should be ===
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(90,18):
-semicolon: Missing semicolon
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(99,21):
-prefer-const: Identifier 'tr' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(100,21):
-prefer-const: Identifier 'marks' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(126,25):
-prefer-const: Identifier 'node' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(141,25):
-prefer-const: Identifier 'node' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(154,66):
-no-unnecessary-type-assertion: This assertion is unnecessary since it does not change the type of the expression.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(155,25):
-prefer-const: Identifier 'node' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(155,33):
-no-unnecessary-type-assertion: This assertion is unnecessary since it does not change the type of the expression.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(160,25):
-prefer-const: Identifier 'node' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(192,29):
-prefer-const: Identifier 'doc' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextSchema.tsx(640,134):
-semicolon: Missing semicolon
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextSchema.tsx(649,21):
-prefer-const: Identifier 'expand' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextSchema.tsx(650,21):
-prefer-const: Identifier 'tr' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextSchema.tsx(654,138):
-semicolon: Missing semicolon
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextSchema.tsx(658,10):
-semicolon: Missing semicolon
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionView.tsx(243,13):
-prefer-const: Identifier 'main' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionView.tsx(244,13):
-prefer-const: Identifier 'next' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionView.tsx(245,13):
-prefer-const: Identifier 'prev' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/DocumentDecorations.tsx(88,21):
-prefer-const: Identifier 'selectionTitleFieldKey' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/DocumentDecorations.tsx(95,8):
-semicolon: Missing semicolon
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/EditableView.tsx(123,13):
-prefer-const: Identifier 'wasFocused' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/MainView.tsx(289,11):
-semicolon: Missing semicolon
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/DocumentView.tsx(151,25):
-prefer-const: Identifier 'any' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(910,13):
-prefer-const: Identifier 'prosediv' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(911,13):
-prefer-const: Identifier 'keeplocation' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(913,13):
-prefer-const: Identifier 'pos' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(928,17):
-prefer-const: Identifier 'pcords' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(929,17):
-prefer-const: Identifier 'node' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(935,21):
-prefer-const: Identifier 'lastNode' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(976,71):
-semicolon: Missing semicolon
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(987,17):
-prefer-const: Identifier '$pos' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(1001,25):
-prefer-const: Identifier '$olist_pos' is never reassigned; use 'const' instead of 'let'.
-WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(1103,17):
-prefer-const: Identifier 'newHeight' is never reassigned; use 'const' instead of 'let'.
-No type errors found
-Version: typescript 3.7.2, tslint 5.18.0
-Time: 5473ms
diff --git a/logs/server_pids.txt b/logs/server_pids.txt
deleted file mode 100644
index 2aa143f24..000000000
--- a/logs/server_pids.txt
+++ /dev/null
@@ -1 +0,0 @@
-9675 created at Wed, 11 Dec 2019 08:44:28 GMT
diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts
index f1fa6f11d..3324d8abe 100644
--- a/src/client/util/ProsemirrorExampleTransfer.ts
+++ b/src/client/util/ProsemirrorExampleTransfer.ts
@@ -105,7 +105,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
         return true;
     });
 
-    bind("Mod-s", TooltipTextMenu.insertStar);
+    bind("Mod-s", TooltipTextMenu.insertSummarizer);
 
     bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
         const ref = state.selection;
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index 22b2a8204..364c85165 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -62,8 +62,9 @@ export const inpRules = {
             }
         ),
 
+        // set the font size using #<font-size> 
         new InputRule(
-            new RegExp(/^#([0-9]+)\s$/),
+            new RegExp(/^%([0-9]+)\s$/),
             (state, match, start, end) => {
                 const size = Number(match[1]);
                 const ruleProvider = FormattedTextBox.FocusedBox!.props.ruleProvider;
@@ -74,131 +75,123 @@ export const inpRules = {
                 }
                 return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontSize.create({ fontSize: size }));
             }),
+
+        // make current selection a hyperlink portal (assumes % was used to initiate an EnteringStyle mode)
+        new InputRule(
+            new RegExp(/@$/),
+            (state, match, start, end) => {
+                if (state.selection.to === state.selection.from || !(schema as any).EnteringStyle) return null;
+
+                const value = state.doc.textBetween(start, end);
+                if (value) {
+                    DocServer.GetRefField(value).then(docx => {
+                        const doc = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value);
+                        DocUtils.Publish(doc, value, returnFalse, returnFalse);
+                    });
+                    const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + value), location: "onRight", title: value });
+                    return state.tr.addMark(start, end, link);
+                }
+                return state.tr;
+            }),
+
+        // activate a style by name using prefix '%'
         new InputRule(
             new RegExp(/%[a-z]+$/),
             (state, match, start, end) => {
                 const color = match[0].substring(1, match[0].length);
-                let marks = TooltipTextMenuManager.Instance._brushMap.get(color);
+                const marks = TooltipTextMenuManager.Instance._brushMap.get(color);
                 if (marks) {
-                    let tr = state.tr.deleteRange(start, end);
+                    const tr = state.tr.deleteRange(start, end);
                     return marks ? Array.from(marks).reduce((tr, m) => tr.addStoredMark(m), tr) : tr;
                 }
-                let isValidColor = (strColor: string) => {
-                    var s = new Option().style;
+                const isValidColor = (strColor: string) => {
+                    const s = new Option().style;
                     s.color = strColor;
-                    return s.color == strColor.toLowerCase(); // 'false' if color wasn't assigned
-                }
+                    return s.color === strColor.toLowerCase(); // 'false' if color wasn't assigned
+                };
                 if (isValidColor(color)) {
                     return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontColor.create({ color: color }));
                 }
                 return null;
             }),
+        // stop using active style
         new InputRule(
             new RegExp(/%%$/),
             (state, match, start, end) => {
-                let tr = state.tr.deleteRange(start, end);
-                let marks = state.tr.selection.$anchor.nodeBefore?.marks;
+                const tr = state.tr.deleteRange(start, end);
+                const marks = state.tr.selection.$anchor.nodeBefore?.marks;
                 return marks ? Array.from(marks).filter(m => m !== state.schema.marks.user_mark).reduce((tr, m) => tr.removeStoredMark(m), tr) : tr;
             }),
+
+        // set the Todo user-tag on the current selection (assumes % was used to initiate an EnteringStyle mode)
         new InputRule(
-            new RegExp(/t$/),
-            (state, match, start, end) => {
-                if (state.selection.to === state.selection.from && !(state as any).EnteringStyle) return null;
-                const node = (state.doc.resolve(start) as any).nodeAfter;
-                if (node?.marks.findIndex((m: any) => m.type === schema.marks.user_tag) !== -1) return state.tr.removeMark(start, end, schema.marks.user_tag);
-                return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: "todo", modified: Math.round(Date.now() / 1000 / 60) })) : state.tr;
-            }),
-        new InputRule(
-            new RegExp(/i$/),
+            new RegExp(/[ti!x]$/),
             (state, match, start, end) => {
-                if (state.selection.to === state.selection.from && !(state as any).EnteringStyle) return null;
+                if (state.selection.to === state.selection.from || !(schema as any).EnteringStyle) return null;
+                const tag = match[0] === "t" ? "todo" : match[0] === "i" ? "ignore" : match[0] === "x" ? "disagree" : match[0] === "!" ? "important" : "??";
                 const node = (state.doc.resolve(start) as any).nodeAfter;
                 if (node?.marks.findIndex((m: any) => m.type === schema.marks.user_tag) !== -1) return state.tr.removeMark(start, end, schema.marks.user_tag);
-                return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: "ignore", modified: Math.round(Date.now() / 1000 / 60) })) : state.tr;
+                return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: tag, modified: Math.round(Date.now() / 1000 / 60) })) : state.tr;
             }),
+
+        // set the First-line indent node type for the selection's paragraph (assumes % was used to initiate an EnteringStyle mode)
         new InputRule(
-            new RegExp(/d$/),
+            new RegExp(/(%d|d)$/),
             (state, match, start, end) => {
-                if (state.selection.to === state.selection.from) return null;
+                if (!match[0].startsWith("%") && !(schema as any).EnteringStyle) return null;
                 const pos = (state.doc.resolve(start) as any);
-                let depth = pos.path.length / 3 - 1;
-                for (; depth >= 0; depth--) {
-                    let node = pos.node(depth);
+                for (let depth = pos.path.length / 3 - 1; depth >= 0; depth--) {
+                    const node = pos.node(depth);
                     if (node.type === schema.nodes.paragraph) {
                         const replaced = state.tr.setNodeMarkup(pos.pos - pos.parentOffset - 1, node.type, { ...node.attrs, indent: node.attrs.indent === 25 ? undefined : 25 });
-                        return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
+                        const result = replaced.setSelection(new TextSelection(replaced.doc.resolve(start)));
+                        return match[0].startsWith("%") ? result.deleteRange(start, end) : result;
                     }
                 }
                 return null;
             }),
+
+        // set the Hanging indent node type for the current selection's paragraph (assumes % was used to initiate an EnteringStyle mode)
         new InputRule(
-            new RegExp(/h$/),
+            new RegExp(/(%h|h)$/),
             (state, match, start, end) => {
-                if (state.selection.to === state.selection.from) return null;
+                if (!match[0].startsWith("%") && !(schema as any).EnteringStyle) return null;
                 const pos = (state.doc.resolve(start) as any);
-                let depth = pos.path.length / 3 - 1;
-                for (; depth >= 0; depth--) {
-                    let node = pos.node(depth);
+                for (let depth = pos.path.length / 3 - 1; depth >= 0; depth--) {
+                    const node = pos.node(depth);
                     if (node.type === schema.nodes.paragraph) {
                         const replaced = state.tr.setNodeMarkup(pos.pos - pos.parentOffset - 1, node.type, { ...node.attrs, indent: node.attrs.indent === -25 ? undefined : -25 });
-                        return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
+                        const result = replaced.setSelection(new TextSelection(replaced.doc.resolve(start)));
+                        return match[0].startsWith("%") ? result.deleteRange(start, end) : result;
                     }
                 }
                 return null;
             }),
+        // set the Quoted indent node type for the current selection's paragraph (assumes % was used to initiate an EnteringStyle mode)
         new InputRule(
-            new RegExp(/q$/),
+            new RegExp(/(%q|q)$/),
             (state, match, start, end) => {
-                if (state.selection.to === state.selection.from) return null;
+                if (!match[0].startsWith("%") && !(schema as any).EnteringStyle) return null;
                 const pos = (state.doc.resolve(start) as any);
-                if (state.selection instanceof NodeSelection && (state.selection as NodeSelection).node.type === schema.nodes.ordered_list) {
-                    let node = (state.selection as NodeSelection).node;
+                if (state.selection instanceof NodeSelection && state.selection.node.type === schema.nodes.ordered_list) {
+                    const node = state.selection.node;
                     return state.tr.setNodeMarkup(pos.pos, node.type, { ...node.attrs, indent: node.attrs.indent === 30 ? undefined : 30 });
                 }
-                let depth = pos.path.length / 3 - 1;
-                for (; depth >= 0; depth--) {
-                    let node = pos.node(depth);
+                for (let depth = pos.path.length / 3 - 1; depth >= 0; depth--) {
+                    const node = pos.node(depth);
                     if (node.type === schema.nodes.paragraph) {
                         const replaced = state.tr.setNodeMarkup(pos.pos - pos.parentOffset - 1, node.type, { ...node.attrs, inset: node.attrs.inset === 30 ? undefined : 30 });
-                        return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
+                        const result = replaced.setSelection(new TextSelection(replaced.doc.resolve(start)));
+                        return match[0].startsWith("%") ? result.deleteRange(start, end) : result;
                     }
                 }
                 return null;
             }),
-        new InputRule(
-            new RegExp(/!$/),
-            (state, match, start, end) => {
-                if (state.selection.to === state.selection.from && !(state as any).EnteringStyle) return null;
-                const node = (state.doc.resolve(start) as any).nodeAfter;
-                if (node?.marks.findIndex((m: any) => m.type === schema.marks.user_tag) !== -1) return state.tr.removeMark(start, end, schema.marks.user_tag);
-                return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: "important", modified: Math.round(Date.now() / 1000 / 60) })) : state.tr;
-            }),
-        new InputRule(
-            new RegExp(/x$/),
-            (state, match, start, end) => {
-                if (state.selection.to === state.selection.from && !(state as any).EnteringStyle) return null;
-                const node = (state.doc.resolve(start) as any).nodeAfter;
-                if (node?.marks.findIndex((m: any) => m.type === schema.marks.user_tag) !== -1) return state.tr.removeMark(start, end, schema.marks.user_tag);
-                return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: "disagree", modified: Math.round(Date.now() / 1000 / 60) })) : state.tr;
-            }),
-        new InputRule(
-            new RegExp(/@$/),
-            (state, match, start, end) => {
-                if (state.selection.to === state.selection.from) return null;
 
-                const value = state.doc.textBetween(start, end);
-                if (value) {
-                    DocServer.GetRefField(value).then(docx => {
-                        let doc = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value);
-                        DocUtils.Publish(doc, value, returnFalse, returnFalse);
-                    });
-                    const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + value), location: "onRight", title: value });
-                    return state.tr.addMark(start, end, link);
-                }
-                return state.tr;
-            }),
+
+        // center justify text
         new InputRule(
-            new RegExp(/^\^\^\s$/),
+            new RegExp(/%\^$/),
             (state, match, start, end) => {
                 const node = (state.doc.resolve(start) as any).nodeAfter;
                 const sm = state.storedMarks || undefined;
@@ -212,8 +205,9 @@ export const inpRules = {
                     state.tr;
                 return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
             }),
+        // left justify text
         new InputRule(
-            new RegExp(/^\[\[\s$/),
+            new RegExp(/%\[$/),
             (state, match, start, end) => {
                 const node = (state.doc.resolve(start) as any).nodeAfter;
                 const sm = state.storedMarks || undefined;
@@ -227,8 +221,9 @@ export const inpRules = {
                     state.tr;
                 return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
             }),
+        // right justify text
         new InputRule(
-            new RegExp(/^\]\]\s$/),
+            new RegExp(/%\]$/),
             (state, match, start, end) => {
                 const node = (state.doc.resolve(start) as any).nodeAfter;
                 const sm = state.storedMarks || undefined;
@@ -243,7 +238,7 @@ export const inpRules = {
                 return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
             }),
         new InputRule(
-            new RegExp(/##\s$/),
+            new RegExp(/%#$/),
             (state, match, start, end) => {
                 const target = Docs.Create.TextDocument({ width: 75, height: 35, backgroundColor: "yellow", autoHeight: true, fontSize: 9, title: "inline comment" });
                 const node = (state.doc.resolve(start) as any).nodeAfter;
@@ -255,26 +250,25 @@ export const inpRules = {
                 return replaced;//.setSelection(new NodeSelection(replaced.doc.resolve(end)));
             }),
         new InputRule(
-            new RegExp(/\(\(/),
+            new RegExp(/%\(/),
             (state, match, start, end) => {
                 const node = (state.doc.resolve(start) as any).nodeAfter;
                 const sm = state.storedMarks || undefined;
-                const mark = state.schema.marks.highlight.create();
+                const mark = state.schema.marks.summarizeInclusive.create();
                 const selected = state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).addMark(start, end, mark);
                 const content = selected.selection.content();
                 const replaced = node ? selected.replaceRangeWith(start, start,
-                    schema.nodes.star.create({ visibility: true, text: content, textslice: content.toJSON() })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
+                    schema.nodes.summary.create({ visibility: true, text: content, textslice: content.toJSON() })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
                     state.tr;
                 return replaced.setSelection(new TextSelection(replaced.doc.resolve(end + 1)));
             }),
         new InputRule(
-            new RegExp(/\)\)/),
+            new RegExp(/%\)/),
             (state, match, start, end) => {
-                const mark = state.schema.marks.highlight.create();
-                return state.tr.removeStoredMark(mark);
+                return state.tr.removeStoredMark(state.schema.marks.summarizeInclusive.create());
             }),
         new InputRule(
-            new RegExp(/\^f\s$/),
+            new RegExp(/%f\$/),
             (state, match, start, end) => {
                 const newNode = schema.nodes.footnote.create({});
                 const tr = state.tr;
@@ -283,9 +277,5 @@ export const inpRules = {
                     tr.doc.resolve(  // get the location of the footnote node by subtracting the nodesize of the footnote from the current insertion point anchor (which will be immediately after the footnote node)
                         tr.selection.anchor - tr.selection.$anchor.nodeBefore!.nodeSize)));
             }),
-        // let newNode = schema.nodes.footnote.create({});
-        // if (dispatch && state.selection.from === state.selection.to) {
-        //     return true;
-        // }
     ]
 };
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index f9251fb7e..543f45731 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -109,7 +109,7 @@ export const nodes: { [index: string]: NodeSpec } = {
         },
     },
 
-    star: {
+    summary: {
         inline: true,
         attrs: {
             visibility: { default: false },
@@ -121,15 +121,6 @@ export const nodes: { [index: string]: NodeSpec } = {
             const attrs = { style: `width: 40px` };
             return ["span", { ...node.attrs, ...attrs }];
         },
-        // parseDOM: [{
-        //     tag: "star", getAttrs(dom: any) {
-        //         return {
-        //             visibility: dom.getAttribute("visibility"),
-        //             oldtext: dom.getAttribute("oldtext"),
-        //             oldtextlen: dom.getAttribute("oldtextlen"),
-        //         }
-        //     }
-        // }]
     },
 
     // :: NodeSpec An inline image (`<img>`) node. Supports `src`,
@@ -228,6 +219,7 @@ export const nodes: { [index: string]: NodeSpec } = {
             mapStyle: { default: "decimal" },
             setFontSize: { default: undefined },
             setFontFamily: { default: "inherit" },
+            setFontColor: { default: "inherit" },
             inheritedFontSize: { default: undefined },
             visibility: { default: true },
             indent: { default: undefined }
@@ -237,8 +229,10 @@ export const nodes: { [index: string]: NodeSpec } = {
             const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : "";
             const fsize = node.attrs.setFontSize ? node.attrs.setFontSize : node.attrs.inheritedFontSize;
             const ffam = node.attrs.setFontFamily;
-            return node.attrs.visibility ? ['ol', { class: `${map}-ol`, style: `list-style: none; font-size: ${fsize}; font-family: ${ffam}; margin-left: ${node.attrs.indent}` }, 0] :
-                ['ol', { class: `${map}-ol`, style: `list-style: none; font-size: ${fsize}; font-family: ${ffam}` }];
+            const color = node.attrs.setFontColor;
+            return node.attrs.visibility ?
+                ['ol', { class: `${map}-ol`, style: `list-style: none; font-size: ${fsize}; font-family: ${ffam}; color:${color}; margin-left: ${node.attrs.indent}` }, 0] :
+                ['ol', { class: `${map}-ol`, style: `list-style: none;` }];
         }
     },
 
@@ -318,7 +312,7 @@ export const marks: { [index: string]: MarkSpec } = {
         attrs: {
             highlight: { default: "transparent" }
         },
-        inclusive: false,
+        inclusive: true,
         parseDOM: [{
             tag: "span", getAttrs(dom: any) {
                 return { highlight: dom.getAttribute("backgroundColor") };
@@ -401,7 +395,7 @@ export const marks: { [index: string]: MarkSpec } = {
         }
     },
 
-    highlight: {
+    summarizeInclusive: {
         parseDOM: [
             {
                 tag: "span",
@@ -410,7 +404,7 @@ export const marks: { [index: string]: MarkSpec } = {
                         const style = getComputedStyle(p);
                         if (style.textDecoration === "underline") return null;
                         if (p.parentElement.outerHTML.indexOf("text-decoration: underline") !== -1 &&
-                            p.parentElement.outerHTML.indexOf("text-decoration-style: dotted") !== -1) {
+                            p.parentElement.outerHTML.indexOf("text-decoration-style: solid") !== -1) {
                             return null;
                         }
                     }
@@ -419,6 +413,31 @@ export const marks: { [index: string]: MarkSpec } = {
             },
         ],
         inclusive: true,
+        toDOM() {
+            return ['span', {
+                style: 'text-decoration: underline; text-decoration-style: solid; text-decoration-color: rgba(204, 206, 210, 0.92)'
+            }];
+        }
+    },
+
+    summarize: {
+        inclusive: false,
+        parseDOM: [
+            {
+                tag: "span",
+                getAttrs: (p: any) => {
+                    if (typeof (p) !== "string") {
+                        const style = getComputedStyle(p);
+                        if (style.textDecoration === "underline") return null;
+                        if (p.parentElement.outerHTML.indexOf("text-decoration: underline") !== -1 &&
+                            p.parentElement.outerHTML.indexOf("text-decoration-style: dotted") !== -1) {
+                            return null;
+                        }
+                    }
+                    return false;
+                }
+            },
+        ],
         toDOM() {
             return ['span', {
                 style: 'text-decoration: underline; text-decoration-style: dotted; text-decoration-color: rgba(204, 206, 210, 0.92)'
@@ -637,7 +656,7 @@ export class DashDocCommentView {
             }
             const dashDoc = view.state.schema.nodes.dashDoc.create({ width: 75, height: 35, title: "dashDoc", docid: node.attrs.docid, float: "right" });
             view.dispatch(view.state.tr.insert(getPos() + 1, dashDoc));
-            setTimeout(() => { try { view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.tr.doc, getPos() + 2))) } catch (e) { } }, 0);
+            setTimeout(() => { try { view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.tr.doc, getPos() + 2))); } catch (e) { } }, 0);
             return undefined;
         };
         this._collapsed.onpointerdown = (e: any) => {
@@ -646,16 +665,16 @@ export class DashDocCommentView {
         this._collapsed.onpointerup = (e: any) => {
             const target = targetNode();
             if (target) {
-                let expand = target.hidden;
-                let tr = view.state.tr.setNodeMarkup(target.pos, undefined, { ...target.node.attrs, hidden: target.node.attrs.hidden ? false : true });
+                const expand = target.hidden;
+                const tr = view.state.tr.setNodeMarkup(target.pos, undefined, { ...target.node.attrs, hidden: target.node.attrs.hidden ? false : true });
                 view.dispatch(tr.setSelection(TextSelection.create(tr.doc, getPos() + (expand ? 2 : 1)))); // update the attrs 
                 setTimeout(() => {
                     expand && DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc));
-                    try { view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.tr.doc, getPos() + (expand ? 2 : 1)))) } catch (e) { }
+                    try { view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.tr.doc, getPos() + (expand ? 2 : 1)))); } catch (e) { }
                 }, 0);
             }
             e.stopPropagation();
-        }
+        };
         this._collapsed.onpointerenter = (e: any) => {
             DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc));
             e.preventDefault();
@@ -908,7 +927,7 @@ export class FootnoteView {
     ignoreMutation() { return true; }
 }
 
-export class SummarizedView {
+export class SummaryView {
     _collapsed: HTMLElement;
     _view: any;
     constructor(node: any, view: any, getPos: any) {
@@ -946,7 +965,8 @@ export class SummarizedView {
     className = (visible: boolean) => "formattedTextBox-summarizer" + (visible ? "" : "-collapsed");
 
     updateSummarizedText(start?: any) {
-        const mark = this._view.state.schema.marks.highlight.create();
+        const mtype = this._view.state.schema.marks.summarize;
+        const mtypeInc = this._view.state.schema.marks.summarizeInclusive;
         let endPos = start;
 
         const visited = new Set();
@@ -954,7 +974,7 @@ export class SummarizedView {
             let skip = false;
             this._view.state.doc.nodesBetween(start, i, (node: Node, pos: number, parent: Node, index: number) => {
                 if (node.isLeaf && !visited.has(node) && !skip) {
-                    if (node.marks.find((m: any) => m.type === mark.type)) {
+                    if (node.marks.find((m: any) => m.type === mtype || m.type === mtypeInc)) {
                         visited.add(node);
                         endPos = i + node.nodeSize - 1;
                     }
@@ -979,7 +999,7 @@ const fromJson = schema.nodeFromJSON;
 
 schema.nodeFromJSON = (json: any) => {
     const node = fromJson(json);
-    if (json.type === "star") {
+    if (json.type === schema.marks.summarize.name) {
         node.attrs.text = Slice.fromJSON(schema, node.attrs.textslice);
     }
     return node;
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index f29dbf2e4..483ab40a7 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -1,4 +1,3 @@
-import { action } from "mobx";
 import { Dropdown, icons, MenuItem } from "prosemirror-menu"; //no import css
 import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos, Schema } from "prosemirror-model";
 import { wrapInList } from 'prosemirror-schema-list';
@@ -10,8 +9,6 @@ import { Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
 import { FieldViewProps } from "../views/nodes/FieldView";
 import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox";
-import { DocumentManager } from "./DocumentManager";
-import { DragManager } from "./DragManager";
 import { LinkManager } from "./LinkManager";
 import { schema } from "./RichTextSchema";
 import "./TooltipTextMenu.scss";
@@ -20,13 +17,11 @@ import { updateBullets } from './ProsemirrorExampleTransfer';
 import { DocumentDecorations } from '../views/DocumentDecorations';
 import { SelectionManager } from './SelectionManager';
 import { PastelSchemaPalette, DarkPastelSchemaPalette } from '../../new_fields/SchemaHeaderField';
-import { Keys } from "../views/search/FilterBox";
 const { toggleMark, setBlockType } = require("prosemirror-commands");
 const { openPrompt, TextField } = require("./ProsemirrorCopy/prompt.js");
 
 //appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc.
 export class TooltipTextMenu {
-
     public static Toolbar: HTMLDivElement | undefined;
 
     // editor state properties
@@ -48,10 +43,9 @@ export class TooltipTextMenu {
     // editor button doms
     private colorDom?: Node;
     private colorDropdownDom?: Node;
-    private highlightDom?: Node;
-    private highlightDropdownDom?: Node;
+    private highighterDom?: Node;
+    private highlighterDropdownDom?: Node;
     private linkEditor?: HTMLDivElement;
-    private linkText?: HTMLDivElement;
     private linkDrag?: HTMLImageElement;
     private _linkDropdownDom?: Node;
     private _brushdom?: Node;
@@ -94,7 +88,6 @@ export class TooltipTextMenu {
             { command: toggleMark(schema.marks.strikethrough), dom: this.svgIcon("strikethrough", "Strikethrough", "M496 224H293.9l-87.17-26.83A43.55 43.55 0 0 1 219.55 112h66.79A49.89 49.89 0 0 1 331 139.58a16 16 0 0 0 21.46 7.15l42.94-21.47a16 16 0 0 0 7.16-21.46l-.53-1A128 128 0 0 0 287.51 32h-68a123.68 123.68 0 0 0-123 135.64c2 20.89 10.1 39.83 21.78 56.36H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h480a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm-180.24 96A43 43 0 0 1 336 356.45 43.59 43.59 0 0 1 292.45 400h-66.79A49.89 49.89 0 0 1 181 372.42a16 16 0 0 0-21.46-7.15l-42.94 21.47a16 16 0 0 0-7.16 21.46l.53 1A128 128 0 0 0 224.49 480h68a123.68 123.68 0 0 0 123-135.64 114.25 114.25 0 0 0-5.34-24.36z") },
             { command: toggleMark(schema.marks.superscript), dom: this.svgIcon("superscript", "Superscript", "M496 160h-16V16a16 16 0 0 0-16-16h-48a16 16 0 0 0-14.29 8.83l-16 32A16 16 0 0 0 400 64h16v96h-16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h96a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM336 64h-67a16 16 0 0 0-13.14 6.87l-79.9 115-79.9-115A16 16 0 0 0 83 64H16A16 16 0 0 0 0 80v48a16 16 0 0 0 16 16h33.48l77.81 112-77.81 112H16a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h67a16 16 0 0 0 13.14-6.87l79.9-115 79.9 115A16 16 0 0 0 269 448h67a16 16 0 0 0 16-16v-48a16 16 0 0 0-16-16h-33.48l-77.81-112 77.81-112H336a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16z") },
             { command: toggleMark(schema.marks.subscript), dom: this.svgIcon("subscript", "Subscript", "M496 448h-16V304a16 16 0 0 0-16-16h-48a16 16 0 0 0-14.29 8.83l-16 32A16 16 0 0 0 400 352h16v96h-16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h96a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM336 64h-67a16 16 0 0 0-13.14 6.87l-79.9 115-79.9-115A16 16 0 0 0 83 64H16A16 16 0 0 0 0 80v48a16 16 0 0 0 16 16h33.48l77.81 112-77.81 112H16a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h67a16 16 0 0 0 13.14-6.87l79.9-115 79.9 115A16 16 0 0 0 269 448h67a16 16 0 0 0 16-16v-48a16 16 0 0 0-16-16h-33.48l-77.81-112 77.81-112H336a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16z") },
-            // { command: toggleMark(schema.marks.highlight), dom: this.icon("H", 'blue', 'Blue') }
         ];
 
         // add menu items
@@ -123,20 +116,15 @@ export class TooltipTextMenu {
                 if (dom.contains(e.target as Node)) {
                     e.stopPropagation();
                     command(this.view.state, this.view.dispatch, this.view);
-                    // if (this.view.state.selection.empty) {
-                    //     if (dom.style.color === "white") { dom.style.color = "greenyellow"; }
-                    //     else { dom.style.color = "white"; }
-                    // }
                 }
             });
-
         });
 
-        // highlight menu
-        this.highlightDom = this.createHighlightTool().render(this.view).dom;
-        this.highlightDropdownDom = this.createHighlightDropdown().render(this.view).dom;
-        this.tooltip.appendChild(this.highlightDom);
-        this.tooltip.appendChild(this.highlightDropdownDom);
+        // summarize menu
+        this.highighterDom = this.createHighlightTool().render(this.view).dom;
+        this.highlighterDropdownDom = this.createHighlightDropdown().render(this.view).dom;
+        this.tooltip.appendChild(this.highighterDom);
+        this.tooltip.appendChild(this.highlighterDropdownDom);
 
         // color menu
         this.colorDom = this.createColorTool().render(this.view).dom;
@@ -166,7 +154,7 @@ export class TooltipTextMenu {
         this.tooltip.appendChild(this._brushDropdownDom);
 
         // star
-        this.tooltip.appendChild(this.createStar().render(this.view).dom);
+        this.tooltip.appendChild(this.createSummarizer().render(this.view).dom);
 
         // list types dropdown
         this.updateListItemDropdown(":", this.listTypeBtnDom);
@@ -289,8 +277,6 @@ export class TooltipTextMenu {
             // stop moving when mouse button is released:
             document.onpointerup = null;
             document.onpointermove = null;
-            //self.highlightSearchTerms(self.state, ["hello"]);
-            //FormattedTextBox.Instance.unhighlightSearchTerms();
         }
     }
 
@@ -302,11 +288,10 @@ export class TooltipTextMenu {
             fontSizeBtns.push(this.dropdownFontSizeBtn(String(mark.attrs.fontSize), "color: black; width: 50px;", mark, this.view, this.changeToFontSize));
         });
 
-        const newfontSizeDom = (new Dropdown(fontSizeBtns, {
-            label: label,
-            css: "color:black; min-width: 60px;"
-        }) as MenuItem).render(this.view).dom;
-        if (this.fontSizeDom) { this.tooltip.replaceChild(newfontSizeDom, this.fontSizeDom); }
+        const newfontSizeDom = (new Dropdown(fontSizeBtns, { label: label, css: "color:black; min-width: 60px;" }) as MenuItem).render(this.view).dom;
+        if (this.fontSizeDom) {
+            this.tooltip.replaceChild(newfontSizeDom, this.fontSizeDom);
+        }
         else {
             this.tooltip.appendChild(newfontSizeDom);
         }
@@ -321,11 +306,10 @@ export class TooltipTextMenu {
             fontBtns.push(this.dropdownFontFamilyBtn(mark.attrs.family, "color: black; font-family: " + mark.attrs.family + ", sans-serif; width: 125px;", mark, this.view, this.changeToFontFamily));
         });
 
-        const newfontStyleDom = (new Dropdown(fontBtns, {
-            label: label,
-            css: "color:black; width: 125px;"
-        }) as MenuItem).render(this.view).dom;
-        if (this.fontStyleDom) { this.tooltip.replaceChild(newfontStyleDom, this.fontStyleDom); }
+        const newfontStyleDom = (new Dropdown(fontBtns, { label: label, css: "color:black; width: 125px;" }) as MenuItem).render(this.view).dom;
+        if (this.fontStyleDom) {
+            this.tooltip.replaceChild(newfontStyleDom, this.fontStyleDom);
+        }
         else {
             this.tooltip.appendChild(newfontStyleDom);
         }
@@ -333,94 +317,16 @@ export class TooltipTextMenu {
     }
 
     updateLinkMenu() {
-        if (!this.linkEditor || !this.linkText) {
-            this.linkEditor = document.createElement("div");
-            this.linkEditor.className = "ProseMirror-icon menuicon";
-            this.linkText = document.createElement("div");
-            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(); };
-            const linkBtn = document.createElement("div");
-            linkBtn.textContent = ">>";
-            linkBtn.style.width = "10px";
-            linkBtn.style.height = "10px";
-            linkBtn.style.color = "white";
-            linkBtn.style.cssFloat = "left";
-            linkBtn.onpointerdown = (e: PointerEvent) => {
-                const node = this.view.state.selection.$from.nodeAfter;
-                const link = node && node.marks.find(m => m.type.name === "link");
-                if (link) {
-                    const href: string = link.attrs.href;
-                    if (href.indexOf(Utils.prepend("/doc/")) === 0) {
-                        const docid = href.replace(Utils.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, false);
-                                }
-                                else this.editorProps && this.editorProps.addDocTab(f, undefined, "onRight");
-                            }
-                        }));
-                    }
-                    // TODO This should have an else to handle external links
-                    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 = "15px";
-            this.linkDrag.style.height = "15px";
-            this.linkDrag.title = "Drag to create link";
-            this.linkDrag.id = "link-drag";
-            this.linkDrag.onpointerdown = (e: PointerEvent) => {
-                if (!this.editorProps) return;
-                const dragData = new DragManager.LinkDragData(this.editorProps.Document);
-                dragData.dontClearTextBox = true;
-                // hack to get source context -sy
-                const docView = DocumentManager.Instance.getDocumentView(this.editorProps.Document);
-                e.stopPropagation();
-                const ctrlKey = e.ctrlKey;
-                DragManager.StartLinkDrag(this.linkDrag!, dragData, e.clientX, e.clientY,
-                    {
-                        handlers: {
-                            dragComplete: action(() => {
-                                if (dragData.linkDocument) {
-                                    const linkDoc = dragData.linkDocument;
-                                    const proto = Doc.GetProto(linkDoc);
-                                    if (proto && docView) {
-                                        proto.sourceContext = docView.props.ContainingCollectionDoc;
-                                    }
-                                    const text = this.makeLink(linkDoc, StrCast(linkDoc.anchor2.title), ctrlKey ? "onRight" : "inTab");
-                                    if (linkDoc instanceof Doc && linkDoc.anchor2 instanceof Doc) {
-                                        proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODODO open to more descriptive descriptions of following in text link
-                                    }
-                                }
-                            }),
-                        },
-                        hideSource: false
-                    });
-                e.stopPropagation();
-                e.preventDefault();
-            };
-            this.linkEditor.appendChild(this.linkDrag);
-            this.tooltip.appendChild(this.linkEditor);
-        }
-
-        const node = this.view.state.selection.$from.nodeAfter;
-        const link = node && node.marks.find(m => m.type.name === "link");
-        this.linkText.textContent = link ? link.attrs.href : "-empty-";
-
-        this.linkText.onkeydown = (e: KeyboardEvent) => {
-            if (e.key === "Enter") {
-                // this.makeLink(this.linkText!.textContent!);
-                e.stopPropagation();
-                e.preventDefault();
-            }
-        };
+        this.linkEditor = document.createElement("div");
+        this.linkEditor.className = "ProseMirror-icon menuicon";
+        this.linkDrag = document.createElement("img");
+        this.linkDrag.src = "https://seogurusnyc.com/wp-content/uploads/2016/12/link-1.png";
+        this.linkDrag.style.width = "15px";
+        this.linkDrag.style.height = "15px";
+        this.linkDrag.title = "Click to set link target";
+        this.linkDrag.id = "link-btn";
+        this.linkEditor.appendChild(this.linkDrag);
+        this.tooltip.appendChild(this.linkEditor);
     }
 
     async getTextLinkTargetTitle() {
@@ -485,9 +391,7 @@ export class TooltipTextMenu {
                 return div;
             },
             enable() { return false; },
-            run(p1, p2, p3, event) {
-                event.stopPropagation();
-            }
+            run(p1, p2, p3, event) { event.stopPropagation(); }
         });
 
         // menu item to update/apply the hyperlink to the selected text
@@ -584,24 +488,6 @@ export class TooltipTextMenu {
         }
     }
 
-    deleteLinkItem() {
-        const icon = {
-            height: 16, width: 16,
-            path: "M15.898,4.045c-0.271-0.272-0.713-0.272-0.986,0l-4.71,4.711L5.493,4.045c-0.272-0.272-0.714-0.272-0.986,0s-0.272,0.714,0,0.986l4.709,4.711l-4.71,4.711c-0.272,0.271-0.272,0.713,0,0.986c0.136,0.136,0.314,0.203,0.492,0.203c0.179,0,0.357-0.067,0.493-0.203l4.711-4.711l4.71,4.711c0.137,0.136,0.314,0.203,0.494,0.203c0.178,0,0.355-0.067,0.492-0.203c0.273-0.273,0.273-0.715,0-0.986l-4.711-4.711l4.711-4.711C16.172,4.759,16.172,4.317,15.898,4.045z"
-        };
-        return new MenuItem({
-            title: "Delete Link",
-            label: "X",
-            icon: icon,
-            css: "color: red",
-            class: "summarize",
-            execEvent: "",
-            run: (state, dispatch) => {
-                this.deleteLink();
-            }
-        });
-    }
-
     createLink() {
         const markType = schema.marks.link;
         return new MenuItem({
@@ -655,22 +541,19 @@ export class TooltipTextMenu {
         //Make a dropdown of all list types
         const toAdd: MenuItem[] = [];
         this.listTypeToIcon.forEach((icon, type) => {
-            toAdd.push(this.dropdownNodeBtn(icon, "color: black; width: 40px;", type, this.view, this.listTypes, this.changeToNodeType));
+            toAdd.push(this.dropdownBulletBtn(icon, "color: black; width: 40px;", type, this.view, this.listTypes, this.changeBulletType));
         });
         //option to remove the list formatting
-        toAdd.push(this.dropdownNodeBtn("X", "color: black; width: 40px;", undefined, this.view, this.listTypes, this.changeToNodeType));
+        toAdd.push(this.dropdownBulletBtn("X", "color: black; width: 40px;", undefined, this.view, this.listTypes, this.changeBulletType));
 
-        listTypeBtn = (new Dropdown(toAdd, {
-            label: label,
-            css: "color:black; width: 40px;"
-        }) as MenuItem).render(this.view).dom;
+        listTypeBtn = (new Dropdown(toAdd, { label: label, css: "color:black; width: 40px;" }) as MenuItem).render(this.view).dom;
 
         //add this new button and return it
         this.tooltip.appendChild(listTypeBtn);
         return listTypeBtn;
     }
 
-    createStar() {
+    createSummarizer() {
         return new MenuItem({
             title: "Summarize",
             label: "Summarize",
@@ -678,31 +561,17 @@ export class TooltipTextMenu {
             css: "color:white;",
             class: "menuicon",
             execEvent: "",
-            run: (state, dispatch) => {
-                TooltipTextMenu.insertStar(this.view.state, this.view.dispatch);
-            }
-
+            run: (state, dispatch) => TooltipTextMenu.insertSummarizer(state, dispatch)
         });
     }
 
-    public static insertStar(state: EditorState<any>, dispatch: any) {
+    public static insertSummarizer(state: EditorState<any>, dispatch: any) {
         if (state.selection.empty) return false;
-        const mark = state.schema.marks.highlight.create();
+        const mark = state.schema.marks.summarize.create();
         const tr = state.tr;
         tr.addMark(state.selection.from, state.selection.to, mark);
         const content = tr.selection.content();
-        const newNode = state.schema.nodes.star.create({ visibility: false, text: content, textslice: content.toJSON() });
-        dispatch && dispatch(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark));
-        return true;
-    }
-
-    public static insertComment(state: EditorState<any>, dispatch: any) {
-        if (state.selection.empty) return false;
-        const mark = state.schema.marks.highlight.create();
-        const tr = state.tr;
-        tr.addMark(state.selection.from, state.selection.to, mark);
-        const content = tr.selection.content();
-        const newNode = state.schema.nodes.star.create({ visibility: false, text: content, textslice: content.toJSON() });
+        const newNode = state.schema.nodes.summary.create({ visibility: false, text: content, textslice: content.toJSON() });
         dispatch && dispatch(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark));
         return true;
     }
@@ -722,7 +591,7 @@ export class TooltipTextMenu {
 
                 const color = document.createElement("div");
                 color.className = "buttonColor";
-                color.style.backgroundColor = TooltipTextMenuManager.Instance.highlight.toString();
+                color.style.backgroundColor = TooltipTextMenuManager.Instance.highlighter.toString();
 
                 const wrapper = document.createElement("div");
                 wrapper.id = "colorPicker";
@@ -730,17 +599,14 @@ export class TooltipTextMenu {
                 wrapper.appendChild(color);
                 return wrapper;
             },
-            run: (state, dispatch) => {
-                TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlight, this.view.state, this.view.dispatch);
-            }
+            run: (state, dispatch) => TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlighter, state, dispatch)
         });
     }
 
     public static insertHighlight(color: String, state: EditorState<any>, dispatch: any) {
         if (state.selection.empty) return false;
 
-        const highlightMark = state.schema.mark(state.schema.marks.marker, { highlight: color });
-        dispatch(state.tr.addMark(state.selection.from, state.selection.to, highlightMark));
+        toggleMark(state.schema.marks.marker, { highlight: color })(state, dispatch);
     }
 
     createHighlightDropdown() {
@@ -773,22 +639,22 @@ export class TooltipTextMenu {
 
                 colors.forEach(color => {
                     const button = document.createElement("button");
-                    button.className = color === TooltipTextMenuManager.Instance.highlight ? "colorPicker active" : "colorPicker";
+                    button.className = color === TooltipTextMenuManager.Instance.highlighter ? "colorPicker active" : "colorPicker";
                     if (color) {
                         button.style.backgroundColor = color;
                         button.textContent = color === "transparent" ? "X" : "";
                         button.onclick = e => {
-                            TooltipTextMenuManager.Instance.highlight = color;
+                            TooltipTextMenuManager.Instance.highlighter = color;
 
-                            TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlight, self.view.state, self.view.dispatch);
+                            TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlighter, self.view.state, self.view.dispatch);
 
                             // update color menu
                             const highlightDom = self.createHighlightTool().render(self.view).dom;
                             const highlightDropdownDom = self.createHighlightDropdown().render(self.view).dom;
-                            self.highlightDom && self.tooltip.replaceChild(highlightDom, self.highlightDom);
-                            self.highlightDropdownDom && self.tooltip.replaceChild(highlightDropdownDom, self.highlightDropdownDom);
-                            self.highlightDom = highlightDom;
-                            self.highlightDropdownDom = highlightDropdownDom;
+                            self.highighterDom && self.tooltip.replaceChild(highlightDom, self.highighterDom);
+                            self.highlighterDropdownDom && self.tooltip.replaceChild(highlightDropdownDom, self.highlighterDropdownDom);
+                            self.highighterDom = highlightDom;
+                            self.highlighterDropdownDom = highlightDropdownDom;
                         };
                     }
                     colorsWrapper.appendChild(button);
@@ -832,19 +698,18 @@ export class TooltipTextMenu {
                 wrapper.appendChild(color);
                 return wrapper;
             },
-            run: (state, dispatch) => {
-                TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, this.view.state, this.view.dispatch);
-            }
+            run: (state, dispatch) => this.insertColor(TooltipTextMenuManager.Instance.color, state, dispatch)
         });
     }
 
-    public static insertColor(color: String, state: EditorState<any>, dispatch: any) {
+    public insertColor(color: String, state: EditorState<any>, dispatch: any) {
         const colorMark = state.schema.mark(state.schema.marks.pFontColor, { color: color });
         if (state.selection.empty) {
             dispatch(state.tr.addStoredMark(colorMark));
             return false;
         }
-        dispatch(state.tr.addMark(state.selection.from, state.selection.to, colorMark));
+        this.setMark(colorMark, state, dispatch);
+        toggleMark(colorMark.type, { color: color })(state, dispatch);
     }
 
     createColorDropdown() {
@@ -883,7 +748,7 @@ export class TooltipTextMenu {
                         button.onclick = e => {
                             TooltipTextMenuManager.Instance.color = color;
 
-                            TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, self.view.state, self.view.dispatch);
+                            self.insertColor(TooltipTextMenuManager.Instance.color, self.view.state, self.view.dispatch);
 
                             // update color menu
                             const colorDom = self.createColorTool().render(self.view).dom;
@@ -903,13 +768,10 @@ export class TooltipTextMenu {
                 return div;
             },
             enable() { return false; },
-            run(p1, p2, p3, event) {
-                event.stopPropagation();
-            }
+            run(p1, p2, p3, event) { event.stopPropagation(); }
         });
 
-        const colorDropdown = new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem;
-        return colorDropdown;
+        return new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem;
     }
 
     createBrush(active: boolean = false) {
@@ -933,9 +795,7 @@ export class TooltipTextMenu {
                 self._brushDropdownDom && self.tooltip.replaceChild(newBrushDropdowndom, self._brushDropdownDom);
                 self._brushDropdownDom = newBrushDropdowndom;
             },
-            active: (state) => {
-                return true;
-            }
+            active: (state) => true
         });
     }
 
@@ -959,8 +819,7 @@ export class TooltipTextMenu {
                     if (TooltipTextMenuManager.Instance._brushMarks && to - from > 0) {
                         this.view.dispatch(this.view.state.tr.removeMark(from, to));
                         Array.from(TooltipTextMenuManager.Instance._brushMarks).filter(m => m.type !== schema.marks.user_mark).forEach((mark: Mark) => {
-                            const markType = mark.type;
-                            this.changeToMarkInGroup(markType, this.view, []);
+                            this.setMark(mark, this.view.state, this.view.dispatch);
                         });
                     }
                 }
@@ -995,9 +854,7 @@ export class TooltipTextMenu {
             class: "button-setting-disabled",
             css: "",
             enable() { return false; },
-            run(p1, p2, p3, event) {
-                event.stopPropagation();
-            }
+            run(p1, p2, p3, event) { event.stopPropagation(); }
         });
 
         const self = this;
@@ -1056,71 +913,26 @@ export class TooltipTextMenu {
         });
 
         const hasMarks = TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMarks.size > 0;
-        const brushDom = new Dropdown(hasMarks ? [brushInfo, clearBrush] : [brushInfo], { class: "buttonSettings-dropdown" }) as MenuItem;
-        return brushDom;
+        return new Dropdown(hasMarks ? [brushInfo, clearBrush] : [brushInfo], { class: "buttonSettings-dropdown" }) as MenuItem;
     }
-    //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected textchangeToMarkInGroup = (markType: MarkType | undefined, view: EditorView, fontMarks: MarkType[]) => {
-    changeToMarkInGroup = (markType: MarkType | undefined, view: EditorView, fontMarks: MarkType[]) => {
-        const { $cursor, ranges } = view.state.selection as TextSelection;
-        const state = view.state;
-        const dispatch = view.dispatch;
-
-        //remove all other active font marks
-        fontMarks.forEach((type) => {
-            if (dispatch) {
-                if ($cursor) {
-                    if (type.isInSet(state.storedMarks || $cursor.marks())) {
-                        dispatch(state.tr.removeStoredMark(type));
-                    }
-                } else {
-                    let has = false;
-                    for (let i = 0; !has && i < ranges.length; i++) {
-                        const { $from, $to } = ranges[i];
-                        has = state.doc.rangeHasMark($from.pos, $to.pos, type);
-                    }
-                    for (const i of ranges) {
-                        if (has) {
-                            toggleMark(type)(view.state, view.dispatch, view);
-                        }
-                    }
-                }
-            }
-        });
 
-        if (markType) {
-            //actually apply font
-            if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
-                const status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
-                    { ...(view.state.selection as NodeSelection).node.attrs, setFontFamily: markType.name, setFontSize: Number(markType.name.replace(/p/, "")) }), view.state.schema);
-                view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
+    setMark = (mark: Mark, state: EditorState<any>, dispatch: any) => {
+        if (mark) {
+            const node = (state.selection as NodeSelection).node;
+            if (node?.type === schema.nodes.ordered_list) {
+                let attrs = node.attrs;
+                if (mark.type === schema.marks.pFontFamily) attrs = { ...attrs, setFontFamily: mark.attrs.family };
+                if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, setFontSize: mark.attrs.fontSize };
+                if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, setFontColor: mark.attrs.color };
+                const tr = updateBullets(state.tr.setNodeMarkup(state.selection.from, node.type, attrs), state.schema);
+                dispatch(tr.setSelection(new NodeSelection(tr.doc.resolve(state.selection.from))));
+            } else {
+                toggleMark(mark.type, mark.attrs)(state, dispatch);
             }
-            else toggleMark(markType)(view.state, view.dispatch, view);
         }
     }
 
     changeToFontFamily = (mark: Mark, view: EditorView) => {
-        const { $cursor, ranges } = view.state.selection as TextSelection;
-        const state = view.state;
-        const dispatch = view.dispatch;
-
-        //remove all other active font marks
-        if ($cursor) {
-            if (view.state.schema.marks.pFontFamily.isInSet(state.storedMarks || $cursor.marks())) {
-                dispatch(state.tr.removeStoredMark(view.state.schema.marks.pFontFamily));
-            }
-        } else {
-            let has = false;
-            for (let i = 0; !has && i < ranges.length; i++) {
-                const { $from, $to } = ranges[i];
-                has = state.doc.rangeHasMark($from.pos, $to.pos, view.state.schema.marks.pFontFamily);
-            }
-            for (const i of ranges) {
-                if (has) {
-                    toggleMark(view.state.schema.marks.pFontFamily)(view.state, view.dispatch, view);
-                }
-            }
-        }
-
         const fontName = mark.attrs.family;
         if (fontName) { this.updateFontStyleDropdown(fontName); }
         if (this.editorProps) {
@@ -1130,39 +942,10 @@ export class TooltipTextMenu {
                 ruleProvider["ruleFont_" + heading] = fontName;
             }
         }
-        //actually apply font
-        if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
-            const status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
-                { ...(view.state.selection as NodeSelection).node.attrs, setFontFamily: fontName }), view.state.schema);
-            view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
-        }
-        else view.dispatch(view.state.tr.addMark(view.state.selection.from, view.state.selection.to, view.state.schema.marks.pFontFamily.create({ family: fontName })));
-        view.state.storedMarks = [...(view.state.storedMarks || []), view.state.schema.marks.pFontFamily.create({ family: fontName })];
+        this.setMark(view.state.schema.marks.pFontFamily.create({ family: fontName }), view.state, view.dispatch);
     }
 
     changeToFontSize = (mark: Mark, view: EditorView) => {
-        const { $cursor, ranges } = view.state.selection as TextSelection;
-        const state = view.state;
-        const dispatch = view.dispatch;
-
-        //remove all other active font marks
-        if ($cursor) {
-            if (view.state.schema.marks.pFontSize.isInSet(state.storedMarks || $cursor.marks())) {
-                dispatch(state.tr.removeStoredMark(view.state.schema.marks.pFontSize));
-            }
-        } else {
-            let has = false;
-            for (let i = 0; !has && i < ranges.length; i++) {
-                const { $from, $to } = ranges[i];
-                has = state.doc.rangeHasMark($from.pos, $to.pos, view.state.schema.marks.pFontSize);
-            }
-            for (const i of ranges) {
-                if (has) {
-                    toggleMark(view.state.schema.marks.pFontSize)(view.state, view.dispatch, view);
-                }
-            }
-        }
-
         const size = mark.attrs.fontSize;
         if (size) { this.updateFontSizeDropdown(String(size) + " pt"); }
         if (this.editorProps) {
@@ -1172,18 +955,11 @@ export class TooltipTextMenu {
                 ruleProvider["ruleSize_" + heading] = size;
             }
         }
-        //actually apply font
-        if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
-            const status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
-                { ...(view.state.selection as NodeSelection).node.attrs, setFontSize: size }), view.state.schema);
-            view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
-        }
-        else view.dispatch(view.state.tr.addMark(view.state.selection.from, view.state.selection.to, view.state.schema.marks.pFontSize.create({ fontSize: size })));
-        view.state.storedMarks = [...(view.state.storedMarks || []), view.state.schema.marks.pFontSize.create({ fontSize: size })];
+        this.setMark(view.state.schema.marks.pFontSize.create({ fontSize: size }), view.state, view.dispatch);
     }
 
     //remove all node typeand apply the passed-in one to the selected text
-    changeToNodeType = (nodeType: NodeType | undefined) => {
+    changeBulletType = (nodeType: NodeType | undefined) => {
         //remove oldif (nodeType) { //add new
         const view = this.view;
         if (nodeType === schema.nodes.bullet_list) {
@@ -1211,46 +987,40 @@ export class TooltipTextMenu {
     //css is the style you want applied to the button
     dropdownFontFamilyBtn(label: string, css: string, mark: Mark, view: EditorView, changeFontFamily: (mark: Mark<any>, view: EditorView) => any) {
         return new MenuItem({
-            title: "",
+            title: "Set Font Family",
             label: label,
             execEvent: "",
             class: "dropdown-item",
             css: css,
             enable() { return true; },
-            run() {
-                changeFontFamily(mark, view);
-            }
+            run() { changeFontFamily(mark, view); }
         });
     }
     //makes a button for the drop down FOR MARKS
     //css is the style you want applied to the button
     dropdownFontSizeBtn(label: string, css: string, mark: Mark, view: EditorView, changeFontSize: (markType: Mark<any>, view: EditorView) => any) {
         return new MenuItem({
-            title: "",
+            title: "Set Font Size",
             label: label,
             execEvent: "",
             class: "dropdown-item",
             css: css,
             enable() { return true; },
-            run() {
-                changeFontSize(mark, view);
-            }
+            run() { changeFontSize(mark, view); }
         });
     }
 
     //makes a button for the drop down FOR NODE TYPES
     //css is the style you want applied to the button
-    dropdownNodeBtn(label: string, css: string, nodeType: NodeType | undefined, view: EditorView, groupNodes: NodeType[], changeToNodeInGroup: (nodeType: NodeType<any> | undefined, view: EditorView, groupNodes: NodeType[]) => any) {
+    dropdownBulletBtn(label: string, css: string, nodeType: NodeType | undefined, view: EditorView, groupNodes: NodeType[], changeToNodeInGroup: (nodeType: NodeType<any> | undefined, view: EditorView, groupNodes: NodeType[]) => any) {
         return new MenuItem({
-            title: "",
+            title: "Set Bullet Style",
             label: label,
             execEvent: "",
             class: "dropdown-item",
             css: css,
             enable() { return true; },
-            run() {
-                changeToNodeInGroup(nodeType, view, groupNodes);
-            }
+            run() { changeToNodeInGroup(nodeType, view, groupNodes); }
         });
     }
 
@@ -1325,14 +1095,7 @@ export class TooltipTextMenu {
     getMarksInSelection(state: EditorState<any>) {
         const found = new Set<Mark>();
         const { from, to } = state.selection as TextSelection;
-        state.doc.nodesBetween(from, to, (node) => {
-            const marks = node.marks;
-            if (marks) {
-                marks.forEach(m => {
-                    found.add(m);
-                });
-            }
-        });
+        state.doc.nodesBetween(from, to, (node) => node.marks?.forEach(m => found.add(m)));
         return found;
     }
 
@@ -1362,12 +1125,6 @@ export class TooltipTextMenu {
 
         this.reset_mark_doms();
 
-        // Hide the tooltip if the selection is empty
-        if (state.selection.empty) {
-            //this.tooltip.style.display = "none";
-            //return;
-        }
-
         // update link dropdown
         const linkDropdown = await this.createLinkDropdown();
         const newLinkDropdowndom = linkDropdown.render(this.view).dom;
@@ -1473,7 +1230,6 @@ export class TooltipTextMenu {
                     }
                     const mark = state.schema.mark(mark_type);
                     return ref_node.marks.includes(mark);
-                    return false;
                 });
             }
             else {
@@ -1518,19 +1274,19 @@ export class TooltipTextMenu {
 
 export class TooltipTextMenuManager {
     private static _instance: TooltipTextMenuManager;
+    private _isPinned: boolean = false;
 
     public pinnedX: number = 0;
     public pinnedY: number = 0;
     public unpinnedX: number = 0;
     public unpinnedY: number = 0;
-    private _isPinned: boolean = false;
 
     public _brushMarks: Set<Mark> | undefined;
     public _brushMap: Map<string, Set<Mark>> = new Map();
     public _brushIsEmpty: boolean = true;
 
     public color: String = "#000";
-    public highlight: String = "transparent";
+    public highlighter: String = "transparent";
 
     public activeMenu: TooltipTextMenu | undefined;
 
@@ -1541,11 +1297,7 @@ export class TooltipTextMenuManager {
         return TooltipTextMenuManager._instance;
     }
 
-    public get isPinned() {
-        return this._isPinned;
-    }
+    public get isPinned() { return this._isPinned; }
 
-    public toggleIsPinned() {
-        this._isPinned = !this._isPinned;
-    }
+    public toggleIsPinned() { this._isPinned = !this._isPinned; }
 }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index dd3b740fb..f366a3677 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -85,14 +85,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             if (this._accumulatedTitle.startsWith("#") || this._accumulatedTitle.startsWith("=")) {
                 this._titleControlString = this._accumulatedTitle;
             } else if (this._titleControlString.startsWith("#")) {
-                let selectionTitleFieldKey = this._titleControlString.substring(1);
+                const selectionTitleFieldKey = this._titleControlString.substring(1);
                 selectionTitleFieldKey === "title" && (SelectionManager.SelectedDocuments()[0].props.Document.customTitle = !this._accumulatedTitle.startsWith("-"));
                 selectionTitleFieldKey && SelectionManager.SelectedDocuments().forEach(d =>
                     Doc.SetInPlace(d.props.Document, selectionTitleFieldKey, typeof d.props.Document[selectionTitleFieldKey] === "number" ? +this._accumulatedTitle : this._accumulatedTitle, true)
                 );
             }
         }
-    }))
+    }));
 
     @action titleEntered = (e: any) => {
         const key = e.keyCode || e.which;
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index d0cecf03d..54def38b5 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -120,7 +120,7 @@ export class EditableView extends React.Component<EditableProps> {
 
     @action
     setIsFocused = (value: boolean) => {
-        let wasFocused = this._editing;
+        const wasFocused = this._editing;
         this._editing = value;
         return wasFocused !== this._editing;
     }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 01cd7957c..25b7dc5ec 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -286,7 +286,7 @@ export class MainView extends React.Component {
             ContainingCollectionDoc={undefined}
             zoomToScale={emptyFunction}
             getScale={returnOne}
-        />
+        />;
     }
     @computed get dockingContent() {
         TraceMobx();
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 411040332..15eec37de 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -240,9 +240,9 @@ export class CollectionView extends Touchable<FieldViewProps> {
         const mainPath = path.extname(images[this._curLightboxImg]);
         const nextPath = path.extname(images[(this._curLightboxImg + 1) % images.length]);
         const prevPath = path.extname(images[(this._curLightboxImg + images.length - 1) % images.length]);
-        let main = images[this._curLightboxImg].replace(mainPath, "_o" + mainPath);
-        let next = images[(this._curLightboxImg + 1) % images.length].replace(nextPath, "_o" + nextPath);
-        let prev = images[(this._curLightboxImg + images.length - 1) % images.length].replace(prevPath, "_o" + prevPath);
+        const main = images[this._curLightboxImg].replace(mainPath, "_o" + mainPath);
+        const next = images[(this._curLightboxImg + 1) % images.length].replace(nextPath, "_o" + nextPath);
+        const prev = images[(this._curLightboxImg + images.length - 1) % images.length].replace(prevPath, "_o" + prevPath);
         return !this._isLightboxOpen ? (null) : (<Lightbox key="lightbox"
             mainSrc={main}
             nextSrc={next}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 24cc0b5ea..3620ccb34 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -148,7 +148,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             else if (!this._titleRef.current.setIsFocused(true)) { // if focus didn't change, focus on interior text...
                 {
                     this._titleRef.current?.setIsFocused(false);
-                    let any = (this._mainCont.current?.getElementsByClassName("ProseMirror")?.[0] as any);
+                    const any = (this._mainCont.current?.getElementsByClassName("ProseMirror")?.[0] as any);
                     any.keeplocation = true;
                     any?.focus();
                 }
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 9318142e2..ec9e0ce5b 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -27,7 +27,7 @@ import { DictationManager } from '../../util/DictationManager';
 import { DragManager } from "../../util/DragManager";
 import buildKeymap from "../../util/ProsemirrorExampleTransfer";
 import { inpRules } from "../../util/RichTextRules";
-import { DashDocCommentView, FootnoteView, ImageResizeView, DashDocView, OrderedListView, schema, SummarizedView } from "../../util/RichTextSchema";
+import { DashDocCommentView, FootnoteView, ImageResizeView, DashDocView, OrderedListView, schema, SummaryView } from "../../util/RichTextSchema";
 import { SelectionManager } from "../../util/SelectionManager";
 import { TooltipLinkingMenu } from "../../util/TooltipLinkingMenu";
 import { TooltipTextMenu } from "../../util/TooltipTextMenu";
@@ -822,7 +822,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                     dashComment(node, view, getPos) { return new DashDocCommentView(node, view, getPos); },
                     dashDoc(node, view, getPos) { return new DashDocView(node, view, getPos, self); },
                     image(node, view, getPos) { return new ImageResizeView(node, view, getPos, self.props.addDocTab); },
-                    star(node, view, getPos) { return new SummarizedView(node, view, getPos); },
+                    summary(node, view, getPos) { return new SummaryView(node, view, getPos); },
                     ordered_list(node, view, getPos) { return new OrderedListView(); },
                     footnote(node, view, getPos) { return new FootnoteView(node, view, getPos); }
                 },
@@ -907,10 +907,10 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         this.tryUpdateHeight();
 
         // see if we need to preserve the insertion point
-        let prosediv = this._proseRef?.children?.[0] as any;
-        let keeplocation = prosediv?.keeplocation;
+        const prosediv = this._proseRef?.children?.[0] as any;
+        const keeplocation = prosediv?.keeplocation;
         prosediv && (prosediv.keeplocation = undefined);
-        let pos = this._editorView?.state.selection.$from.pos || 1;
+        const pos = this._editorView?.state.selection.$from.pos || 1;
         keeplocation && setTimeout(() => this._editorView?.dispatch(this._editorView?.state.tr.setSelection(TextSelection.create(this._editorView.state.doc, pos))));
     }
     onPointerWheel = (e: React.WheelEvent): void => {
@@ -925,14 +925,14 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
 
     onClick = (e: React.MouseEvent): void => {
         if ((this._editorView!.root as any).getSelection().isCollapsed) { // this is a hack to allow the cursor to be placed at the end of a document when the document ends in an inline dash comment.  Apparently Chrome on Windows has a bug/feature which breaks this when clicking after the end of the text.
-            let pcords = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY });
-            let node = pcords && this._editorView!.state.doc.nodeAt(pcords.pos); // get what prosemirror thinks the clicked node is (if it's null, then we didn't click on any text)
+            const pcords = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY });
+            const node = pcords && this._editorView!.state.doc.nodeAt(pcords.pos); // get what prosemirror thinks the clicked node is (if it's null, then we didn't click on any text)
             if (pcords && node?.type === this._editorView!.state.schema.nodes.dashComment) {
                 this._editorView!.dispatch(this._editorView!.state.tr.setSelection(TextSelection.create(this._editorView!.state.doc, pcords.pos + 2)));
                 e.preventDefault();
             }
             if (!node && this._proseRef) {
-                let lastNode = this._proseRef.children[this._proseRef.children.length - 1].children[this._proseRef.children[this._proseRef.children.length - 1].children.length - 1]; // get the last prosemirror div
+                const lastNode = this._proseRef.children[this._proseRef.children.length - 1].children[this._proseRef.children[this._proseRef.children.length - 1].children.length - 1]; // get the last prosemirror div
                 if (e.clientY > lastNode.getBoundingClientRect().bottom) { // if we clicked below the last prosemirror div, then set the selection to be the end of the document
                     this._editorView!.dispatch(this._editorView!.state.tr.setSelection(TextSelection.create(this._editorView!.state.doc, this._editorView!.state.doc.content.size)));
                 }
@@ -973,7 +973,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         //     }
         // }
 
-        this.hitBulletTargets(e.clientX, e.clientY, e.shiftKey, false)
+        this.hitBulletTargets(e.clientX, e.clientY, e.shiftKey, false);
         if (this._recording) setTimeout(() => { this.stopDictation(true); setTimeout(() => this.recordDictation(), 500); }, 500);
     }
 
@@ -984,7 +984,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         if (pos && this.props.isSelected(true)) {
             // let beforeEle = document.querySelector("." + hit.className) as Element; // const before = hit ? window.getComputedStyle(hit, ':before') : undefined;
             //const node = this._editorView!.state.doc.nodeAt(pos.pos);
-            let $pos = this._editorView!.state.doc.resolve(pos.pos);
+            const $pos = this._editorView!.state.doc.resolve(pos.pos);
             let list_node = $pos.node().type === schema.nodes.list_item ? $pos.node() : undefined;
             if ($pos.node().type === schema.nodes.ordered_list) {
                 for (let off = 1; off < 100; off++) {
@@ -998,7 +998,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             }
             if (list_node && pos.inside >= 0 && this._editorView!.state.doc.nodeAt(pos.inside)!.attrs.bulletStyle === list_node.attrs.bulletStyle) {
                 if (select) {
-                    let $olist_pos = this._editorView!.state.doc.resolve($pos.pos - $pos.parentOffset - 1);
+                    const $olist_pos = this._editorView!.state.doc.resolve($pos.pos - $pos.parentOffset - 1);
                     if (!highlightOnly) {
                         this._editorView!.dispatch(this._editorView!.state.tr.setSelection(new NodeSelection($olist_pos)));
                     }
@@ -1064,14 +1064,14 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             return;
         }
         if (!this._editorView!.state.selection.empty && e.key === "%") {
-            (this._editorView!.state as any).EnteringStyle = true;
+            this._editorView!.state.schema.EnteringStyle = true;
             e.preventDefault();
             e.stopPropagation();
             return;
         }
 
-        if (this._editorView!.state.selection.empty || !(this._editorView!.state as any).EnteringStyle) {
-            (this._editorView!.state as any).EnteringStyle = false;
+        if (this._editorView!.state.selection.empty || !this._editorView!.state.schema.EnteringStyle) {
+            this._editorView!.state.schema.EnteringStyle = false;
         }
         if (e.key === "Escape") {
             SelectionManager.DeselectAll();
@@ -1080,7 +1080,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         if (e.key === "Tab" || e.key === "Enter") {
             e.preventDefault();
         }
-        const mark = e.key !== " " && this._lastTimedMark ? this._lastTimedMark! : schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.round(Date.now() / 1000 / 5) });
+        const mark = e.key !== " " && this._lastTimedMark ? this._lastTimedMark : schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.round(Date.now() / 1000 / 5) });
         this._lastTimedMark = mark;
         this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(mark));
 
@@ -1100,7 +1100,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             getComputedStyle(this._ref.current!.parentElement!).top === "0px") {  // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
             const nh = this.Document.isTemplateField ? 0 : NumCast(this.dataDoc.nativeHeight, 0);
             const dh = NumCast(this.layoutDoc.height, 0);
-            let newHeight = Math.max(10, (nh ? dh / nh * scrollHeight : scrollHeight) + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0));
+            const newHeight = Math.max(10, (nh ? dh / nh * scrollHeight : scrollHeight) + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0));
             if (Math.abs(newHeight - dh) > 1) { // bcz: Argh!  without this, we get into a React crash if the same document is opened in a freeform view and in the treeview.  no idea why, but after dragging the freeform document, selecting it, and selecting text, it will compute to 1 pixel higher than the treeview which causes a cycle
                 this.layoutDoc.height = newHeight;
                 this.dataDoc.nativeHeight = nh ? scrollHeight : undefined;
-- 
cgit v1.2.3-70-g09d2


From 860c7907268874f1754dd86bc3a984066552db12 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 12 Dec 2019 15:02:22 -0500
Subject: major changes to drag and drop

---
 src/client/northstar/dash-nodes/HistogramBox.tsx   |  12 +-
 src/client/util/DocumentManager.ts                 |   4 +-
 src/client/util/DragManager.ts                     | 386 ++++++++-------------
 src/client/util/LinkManager.ts                     |   6 +-
 src/client/views/CollectionLinearView.tsx          |   2 +-
 src/client/views/DocComponent.tsx                  |   2 +-
 src/client/views/DocumentButtonBar.tsx             |  37 +-
 src/client/views/DocumentDecorations.tsx           |   2 +-
 src/client/views/MainView.tsx                      |   2 +-
 src/client/views/OverlayView.tsx                   |   2 -
 src/client/views/TemplateMenu.tsx                  |  11 +-
 .../views/collections/CollectionDockingView.tsx    |  20 +-
 .../collections/CollectionMasonryViewFieldRow.tsx  |  14 +-
 .../views/collections/CollectionSchemaCells.tsx    |  21 +-
 .../CollectionSchemaMovableTableHOC.tsx            |  33 +-
 .../views/collections/CollectionSchemaView.tsx     |   2 +-
 .../views/collections/CollectionStackingView.tsx   |  24 +-
 .../CollectionStackingViewFieldColumn.tsx          |   8 +-
 src/client/views/collections/CollectionSubView.tsx |  31 +-
 .../views/collections/CollectionTreeView.tsx       |  28 +-
 src/client/views/collections/CollectionView.tsx    |   4 +-
 .../views/collections/CollectionViewChromes.tsx    |   6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  33 +-
 src/client/views/linking/LinkMenu.scss             |  85 -----
 src/client/views/linking/LinkMenuGroup.tsx         |  28 +-
 src/client/views/linking/LinkMenuItem.scss         |  87 +++++
 src/client/views/linking/LinkMenuItem.tsx          |  24 +-
 src/client/views/nodes/ButtonBox.tsx               |   9 +-
 .../views/nodes/ContentFittingDocumentView.tsx     |   7 +-
 src/client/views/nodes/DocuLinkBox.tsx             |   4 +-
 src/client/views/nodes/DocumentView.tsx            |  33 +-
 src/client/views/nodes/FieldView.tsx               |   2 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  14 +-
 src/client/views/nodes/ImageBox.tsx                |  10 +-
 src/client/views/pdf/PDFViewer.tsx                 |  12 +-
 src/client/views/search/SearchBox.tsx              |   2 -
 src/client/views/search/SearchItem.tsx             |  10 +-
 37 files changed, 446 insertions(+), 571 deletions(-)
 create mode 100644 src/client/views/linking/LinkMenuItem.scss

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

diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index 854135648..8fee53fb9 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -46,8 +46,8 @@ export class HistogramBox extends React.Component<FieldViewProps> {
 
     @action
     dropX = (e: Event, de: DragManager.DropEvent) => {
-        if (de.data instanceof DragManager.DocumentDragData) {
-            let h = Cast(de.data.draggedDocuments[0].data, HistogramField);
+        if (de.complete.docDragData) {
+            let h = Cast(de.complete.docDragData.draggedDocuments[0].data, HistogramField);
             if (h) {
                 this.HistoOp.X = h.HistoOp.X;
             }
@@ -57,8 +57,8 @@ export class HistogramBox extends React.Component<FieldViewProps> {
     }
     @action
     dropY = (e: Event, de: DragManager.DropEvent) => {
-        if (de.data instanceof DragManager.DocumentDragData) {
-            let h = Cast(de.data.draggedDocuments[0].data, HistogramField);
+        if (de.complete.docDragData) {
+            let h = Cast(de.complete.docDragData.draggedDocuments[0].data, HistogramField);
             if (h) {
                 this.HistoOp.Y = h.HistoOp.X;
             }
@@ -78,10 +78,10 @@ export class HistogramBox extends React.Component<FieldViewProps> {
 
     componentDidMount() {
         if (this._dropXRef.current) {
-            this._dropXDisposer = DragManager.MakeDropTarget(this._dropXRef.current, { handlers: { drop: this.dropX.bind(this) } });
+            this._dropXDisposer = DragManager.MakeDropTarget(this._dropXRef.current, this.dropX.bind(this));
         }
         if (this._dropYRef.current) {
-            this._dropYDisposer = DragManager.MakeDropTarget(this._dropYRef.current, { handlers: { drop: this.dropY.bind(this) } });
+            this._dropYDisposer = DragManager.MakeDropTarget(this._dropYRef.current, this.dropY.bind(this));
         }
         reaction(() => CurrentUserUtils.NorthstarDBCatalog, (catalog?: Catalog) => this.activateHistogramOperation(catalog), { fireImmediately: true });
         reaction(() => [this.VisualBinRanges && this.VisualBinRanges.slice()], () => this.SizeConverter.SetVisualBinRanges(this.VisualBinRanges));
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 07247b7e9..6f65e6c68 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -108,7 +108,7 @@ export class DocumentManager {
                 return init && rest;
             })
         ).reduce((pairs, dv) => {
-            const linksList = LinkManager.Instance.getAllRelatedLinks(dv.props.Document);
+            const linksList = DocListCast(dv.props.Document.links);
             pairs.push(...linksList.reduce((pairs, link) => {
                 const linkToDoc = link && LinkManager.Instance.getOppositeAnchor(link, dv.props.Document);
                 linkToDoc && DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
@@ -176,7 +176,7 @@ export class DocumentManager {
     }
 
     public async FollowLink(link: Doc | undefined, doc: Doc, focus: (doc: Doc, maxLocation: string) => void, zoom: boolean = false, reverse: boolean = false, currentContext?: Doc) {
-        const linkDocs = link ? [link] : LinkManager.Instance.getAllRelatedLinks(doc);
+        const linkDocs = link ? [link] : DocListCast(doc.links);
         SelectionManager.DeselectAll();
         const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc));
         const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc));
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 9e6224d97..95529311a 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,5 +1,4 @@
-import { action, runInAction } from "mobx";
-import { Doc, Field } from "../../new_fields/Doc";
+import { Doc, Field, DocListCast } from "../../new_fields/Doc";
 import { Cast, ScriptCast } from "../../new_fields/Types";
 import { emptyFunction } from "../../Utils";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
@@ -22,7 +21,7 @@ export function SetupDrag(
     docFunc: () => Doc | Promise<Doc>,
     moveFunc?: DragManager.MoveFunction,
     dropAction?: dropActionType,
-    options?: any,
+    treeViewId?: string,
     dontHideOnDrop?: boolean,
     dragStarted?: () => void
 ) {
@@ -36,7 +35,7 @@ export function SetupDrag(
         const dragData = new DragManager.DocumentDragData([doc]);
         dragData.dropAction = dropAction;
         dragData.moveDocument = moveFunc;
-        dragData.options = options;
+        dragData.treeViewId = treeViewId;
         dragData.dontHideOnDrop = dontHideOnDrop;
         DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y);
         dragStarted && dragStarted();
@@ -65,62 +64,9 @@ export function SetupDrag(
     return onItemDown;
 }
 
-function moveLinkedDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
-    const document = SelectionManager.SelectedDocuments()[0];
-    document && document.props.removeDocument && document.props.removeDocument(doc);
-    addDocument(doc);
-    return true;
-}
-
-export async function DragLinkAsDocument(dragEle: HTMLElement, x: number, y: number, linkDoc: Doc, sourceDoc: Doc) {
-    const draggeddoc = LinkManager.Instance.getOppositeAnchor(linkDoc, sourceDoc);
-    if (draggeddoc) {
-        const moddrag = await Cast(draggeddoc.annotationOn, Doc);
-        const dragdocs = moddrag ? [moddrag] : [draggeddoc];
-        const dragData = new DragManager.DocumentDragData(dragdocs);
-        dragData.moveDocument = moveLinkedDocument;
-        DragManager.StartLinkedDocumentDrag([dragEle], dragData, x, y, {
-            handlers: {
-                dragComplete: action(emptyFunction),
-            },
-            hideSource: false
-        });
-    }
-}
-
-export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Doc, singleLink?: Doc) {
-    const srcTarg = sourceDoc.proto;
-    let draggedDocs: Doc[] = [];
-
-    if (srcTarg) {
-        const linkDocs = singleLink ? [singleLink] : LinkManager.Instance.getAllRelatedLinks(srcTarg);
-        if (linkDocs) {
-            draggedDocs = linkDocs.map(link => {
-                const opp = LinkManager.Instance.getOppositeAnchor(link, sourceDoc);
-                if (opp) return opp;
-            }) as Doc[];
-        }
-    }
-    if (draggedDocs.length) {
-        const moddrag: Doc[] = [];
-        for (const draggedDoc of draggedDocs) {
-            const doc = await Cast(draggedDoc.annotationOn, Doc);
-            if (doc) moddrag.push(doc);
-        }
-        const dragdocs = moddrag.length ? moddrag : draggedDocs;
-        const dragData = new DragManager.DocumentDragData(dragdocs);
-        dragData.moveDocument = moveLinkedDocument;
-        DragManager.StartLinkedDocumentDrag([dragEle], dragData, x, y, {
-            handlers: {
-                dragComplete: action(emptyFunction),
-            },
-            hideSource: false
-        });
-    }
-}
-
-
 export namespace DragManager {
+    let dragDiv: HTMLDivElement;
+
     export function Root() {
         const root = document.getElementById("root");
         if (!root) {
@@ -128,79 +74,45 @@ export namespace DragManager {
         }
         return root;
     }
+    export let AbortDrag: () => void = emptyFunction;
+    export type MoveFunction = (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
 
-    let dragDiv: HTMLDivElement;
-
-    export enum DragButtons {
-        Left = 1,
-        Right = 2,
-        Both = Left | Right
-    }
-
-    interface DragOptions {
-        handlers: DragHandlers;
-
-        hideSource: boolean | (() => boolean);
-
-        dragHasStarted?: () => void;
-
-        withoutShiftDrag?: boolean;
-
-        finishDrag?: (dropData: { [id: string]: any }) => void;
-
-        offsetX?: number;
-
+    export interface DragDropDisposer { (): void; }
+    export interface DragOptions {
+        dragComplete?: (e: DragCompleteEvent) => void; // function to invoke when drag has completed
+        hideSource?: boolean;  // hide source document during drag
+        offsetX?: number;      // offset of top left of source drag visual from cursor
         offsetY?: number;
     }
 
-    export interface DragDropDisposer {
-        (): void;
-    }
-
-    export class DragCompleteEvent { }
-
-    export interface DragHandlers {
-        dragComplete: (e: DragCompleteEvent) => void;
-    }
-
-    export interface DropOptions {
-        handlers: DropHandlers;
-    }
+    // event called when the drag operation results in a drop action
     export class DropEvent {
         constructor(
             readonly x: number,
             readonly y: number,
-            readonly data: { [id: string]: any },
-            readonly mods: string
+            readonly complete: DragCompleteEvent,
+            readonly altKey: boolean,
+            readonly metaKey: boolean,
+            readonly ctrlKey: boolean
         ) { }
     }
 
-    export interface DropHandlers {
-        drop: (e: Event, de: DropEvent) => void;
-    }
-
-    export function MakeDropTarget(
-        element: HTMLElement,
-        options: DropOptions
-    ): DragDropDisposer {
-        if ("canDrop" in element.dataset) {
-            throw new Error(
-                "Element is already droppable, can't make it droppable again"
-            );
+    // event called when the drag operation has completed (aborted or completed a drop) -- this will be after any drop event has been generated
+    export class DragCompleteEvent {
+        constructor(aborted: boolean, dragData: { [id: string]: any }) {
+            this.aborted = aborted;
+            this.docDragData = dragData instanceof DocumentDragData ? dragData as DocumentDragData : undefined;
+            this.annoDragData = dragData instanceof PdfAnnoDragData ? dragData as PdfAnnoDragData : undefined;
+            this.linkDragData = dragData instanceof LinkDragData ? dragData as LinkDragData : undefined;
+            this.columnDragData = dragData instanceof ColumnDragData ? dragData as ColumnDragData : undefined;
         }
-        element.dataset.canDrop = "true";
-        const handler = (e: Event) => {
-            const ce = e as CustomEvent<DropEvent>;
-            options.handlers.drop(e, ce.detail);
-        };
-        element.addEventListener("dashOnDrop", handler);
-        return () => {
-            element.removeEventListener("dashOnDrop", handler);
-            delete element.dataset.canDrop;
-        };
+        aborted: boolean;
+        docDragData?: DocumentDragData;
+        annoDragData?: PdfAnnoDragData;
+        linkDragData?: LinkDragData;
+        columnDragData?: ColumnDragData;
     }
 
-    export type MoveFunction = (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
     export class DocumentDragData {
         constructor(dragDoc: Doc[]) {
             this.draggedDocuments = dragDoc;
@@ -210,6 +122,8 @@ export namespace DragManager {
         draggedDocuments: Doc[];
         droppedDocuments: Doc[];
         dragDivName?: string;
+        treeViewId?: string;
+        dontHideOnDrop?: boolean;
         offset: number[];
         dropAction: dropActionType;
         userDropAction: dropActionType;
@@ -217,16 +131,34 @@ export namespace DragManager {
         moveDocument?: MoveFunction;
         isSelectionMove?: boolean; // indicates that an explicitly selected Document is being dragged.  this will suppress onDragStart scripts
         applyAsTemplate?: boolean;
+    }
+    export class LinkDragData {
+        constructor(linkSourceDoc: Doc) {
+            this.linkSourceDocument = linkSourceDoc;
+        }
+        droppedDocuments: Doc[] = [];
+        linkSourceDocument: Doc;
+        dontClearTextBox?: boolean;
+        linkDocument?: Doc;
         [id: string]: any;
     }
-
-    export class AnnotationDragData {
+    export class ColumnDragData {
+        constructor(colKey: SchemaHeaderField) {
+            this.colKey = colKey;
+        }
+        colKey: SchemaHeaderField;
+        [id: string]: any;
+    }
+    // used by PDFs to conditionally (if the drop completes) create a text annotation when dragging from the PDF toolbar when a text region has been selected.
+    // this is pretty clunky and should be rethought out using linkDrag or DocumentDrag
+    export class PdfAnnoDragData {
         constructor(dragDoc: Doc, annotationDoc: Doc, dropDoc: Doc) {
             this.dragDocument = dragDoc;
             this.dropDocument = dropDoc;
             this.annotationDocument = annotationDoc;
             this.offset = [0, 0];
         }
+        linkedToDoc?: boolean;
         targetContext: Doc | undefined;
         dragDocument: Doc;
         annotationDocument: Doc;
@@ -236,98 +168,103 @@ export namespace DragManager {
         userDropAction: dropActionType;
     }
 
-    export let StartDragFunctions: (() => void)[] = [];
+    export function MakeDropTarget(
+        element: HTMLElement,
+        dropFunc: (e: Event, de: DropEvent) => void
+    ): DragDropDisposer {
+        if ("canDrop" in element.dataset) {
+            throw new Error(
+                "Element is already droppable, can't make it droppable again"
+            );
+        }
+        element.dataset.canDrop = "true";
+        const handler = (e: Event) => dropFunc(e, (e as CustomEvent<DropEvent>).detail);
+        element.addEventListener("dashOnDrop", handler);
+        return () => {
+            element.removeEventListener("dashOnDrop", handler);
+            delete element.dataset.canDrop;
+        };
+    }
 
+    // drag a document and drop it (or make an alias/copy on drop)
     export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) {
-        runInAction(() => StartDragFunctions.map(func => func()));
+        const finishDrag = (e: DragCompleteEvent) => {
+            e.docDragData && (e.docDragData.droppedDocuments =
+                dragData.draggedDocuments.map(d => !dragData.isSelectionMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? ScriptCast(d.onDragStart).script.run({ this: d }).result :
+                    dragData.userDropAction === "alias" || (!dragData.userDropAction && dragData.dropAction === "alias") ? Doc.MakeAlias(d) :
+                        dragData.userDropAction === "copy" || (!dragData.userDropAction && dragData.dropAction === "copy") ? Doc.MakeCopy(d, true) : d)
+            );
+            e.docDragData?.droppedDocuments.forEach((drop: Doc, i: number) =>
+                Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), []).map(prop => drop[prop] = undefined));
+        };
         dragData.draggedDocuments.map(d => d.dragFactory); // does this help?  trying to make sure the dragFactory Doc is loaded
-        StartDrag(eles, dragData, downX, downY, options, options && options.finishDrag ? options.finishDrag :
-            (dropData: { [id: string]: any }) => {
-                (dropData.droppedDocuments =
-                    dragData.draggedDocuments.map(d => !dragData.isSelectionMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? ScriptCast(d.onDragStart).script.run({ this: d }).result :
-                        dragData.userDropAction === "alias" || (!dragData.userDropAction && dragData.dropAction === "alias") ? Doc.MakeAlias(d) :
-                            dragData.userDropAction === "copy" || (!dragData.userDropAction && dragData.dropAction === "copy") ? Doc.MakeCopy(d, true) : d)
-                );
-                dropData.droppedDocuments.forEach((drop: Doc, i: number) =>
-                    Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), []).map(prop => drop[prop] = undefined));
-            });
+        StartDrag(eles, dragData, downX, downY, options, finishDrag);
     }
 
+    // drag a button template and drop a new button 
     export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) {
-        const dragData = new DragManager.DocumentDragData([]);
-        runInAction(() => StartDragFunctions.map(func => func()));
-        StartDrag(eles, dragData, downX, downY, options, options && options.finishDrag ? options.finishDrag :
-            (dropData: { [id: string]: any }) => {
-                const bd = Docs.Create.ButtonDocument({ width: 150, height: 50, title: title });
-                bd.onClick = ScriptField.MakeScript(script);
-                params.map(p => Object.keys(vars).indexOf(p) !== -1 && (Doc.GetProto(bd)[p] = new PrefetchProxy(vars[p] as Doc)));
-                initialize && initialize(bd);
-                bd.buttonParams = new List<string>(params);
-                dropData.droppedDocuments = [bd];
-            });
+        const finishDrag = (e: DragCompleteEvent) => {
+            const bd = Docs.Create.ButtonDocument({ width: 150, height: 50, title: title });
+            bd.onClick = ScriptField.MakeScript(script);
+            params.map(p => Object.keys(vars).indexOf(p) !== -1 && (Doc.GetProto(bd)[p] = new PrefetchProxy(vars[p] as Doc)));
+            initialize && initialize(bd);
+            bd.buttonParams = new List<string>(params);
+            e.docDragData && (e.docDragData.droppedDocuments = [bd]);
+        };
+        StartDrag(eles, new DragManager.DocumentDragData([]), downX, downY, options, finishDrag);
     }
 
-    export function StartLinkedDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) {
-        dragData.moveDocument = moveLinkedDocument;
+    // drag links and drop link targets (aliasing them if needed)
+    export async function StartLinkTargetsDrag(dragEle: HTMLElement, downX: number, downY: number, sourceDoc: Doc, specificLinks?: Doc[]) {
+        let draggedDocs = (specificLinks ? specificLinks : DocListCast(sourceDoc.links)).map(link => LinkManager.Instance.getOppositeAnchor(link, sourceDoc)).filter(l => l) as Doc[];
 
-        runInAction(() => StartDragFunctions.map(func => func()));
-        StartDrag(eles, dragData, downX, downY, options,
-            (dropData: { [id: string]: any }) => {
-                const droppedDocuments: Doc[] = dragData.draggedDocuments.reduce((droppedDocs: Doc[], d) => {
-                    const dvs = DocumentManager.Instance.getDocumentViews(d);
-                    if (dvs.length) {
-                        const containingView = SelectionManager.SelectedDocuments()[0] ? SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView : undefined;
-                        const inContext = dvs.filter(dv => dv.props.ContainingCollectionView === containingView);
-                        if (inContext.length) {
-                            inContext.forEach(dv => droppedDocs.push(dv.props.Document));
+        if (draggedDocs.length) {
+            const moddrag: Doc[] = [];
+            for (const draggedDoc of draggedDocs) {
+                const doc = await Cast(draggedDoc.annotationOn, Doc);
+                if (doc) moddrag.push(doc);
+            }
+
+            let dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs);
+            dragData.moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (doc: Doc) => boolean): boolean => {
+                const document = SelectionManager.SelectedDocuments()[0];
+                document && document.props.removeDocument && document.props.removeDocument(doc);
+                addDocument(doc);
+                return true;
+            };
+            const containingView = SelectionManager.SelectedDocuments()[0] ? SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView : undefined;
+            const finishDrag = (e: DragCompleteEvent) =>
+                e.docDragData && (e.docDragData.droppedDocuments =
+                    dragData.draggedDocuments.reduce((droppedDocs, d) => {
+                        const dvs = DocumentManager.Instance.getDocumentViews(d).filter(dv => dv.props.ContainingCollectionView === containingView);
+                        if (dvs.length) {
+                            dvs.forEach(dv => droppedDocs.push(dv.props.Document));
                         } else {
                             droppedDocs.push(Doc.MakeAlias(d));
                         }
-                    } else {
-                        droppedDocs.push(Doc.MakeAlias(d));
-                    }
-                    return droppedDocs;
-                }, []);
-                dropData.droppedDocuments = droppedDocuments;
-            });
-    }
-
-    export function StartAnnotationDrag(eles: HTMLElement[], dragData: AnnotationDragData, downX: number, downY: number, options?: DragOptions) {
-        StartDrag(eles, dragData, downX, downY, options);
-    }
+                        return droppedDocs;
+                    }, [] as Doc[]));
 
-    export class LinkDragData {
-        constructor(linkSourceDoc: Doc, blacklist: Doc[] = []) {
-            this.linkSourceDocument = linkSourceDoc;
-            this.blacklist = blacklist;
+            StartDrag([dragEle], dragData, downX, downY, undefined, finishDrag);
         }
-        droppedDocuments: Doc[] = [];
-        linkSourceDocument: Doc;
-        blacklist: Doc[];
-        dontClearTextBox?: boolean;
-        [id: string]: any;
     }
 
-    // for column dragging in schema view
-    export class ColumnDragData {
-        constructor(colKey: SchemaHeaderField) {
-            this.colKey = colKey;
-        }
-        colKey: SchemaHeaderField;
-        [id: string]: any;
+    // drag&drop the pdf annotation anchor which will create a text note  on drop via a dropCompleted() DragOption 
+    export function StartPdfAnnoDrag(eles: HTMLElement[], dragData: PdfAnnoDragData, downX: number, downY: number, options?: DragOptions) {
+        StartDrag(eles, dragData, downX, downY, options);
     }
 
-    export function StartLinkDrag(ele: HTMLElement, dragData: LinkDragData, downX: number, downY: number, options?: DragOptions) {
-        StartDrag([ele], dragData, downX, downY, options);
+    // drags a linker button and creates a link on drop
+    export function StartLinkDrag(ele: HTMLElement, sourceDoc: Doc, downX: number, downY: number, options?: DragOptions) {
+        StartDrag([ele], new DragManager.LinkDragData(sourceDoc), downX, downY, options);
     }
 
+    // drags a column from a schema view
     export function StartColumnDrag(ele: HTMLElement, dragData: ColumnDragData, downX: number, downY: number, options?: DragOptions) {
         StartDrag([ele], dragData, downX, downY, options);
     }
 
-    export let AbortDrag: () => void = emptyFunction;
-
-    function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: { [id: string]: any }) => void) {
+    function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) {
         eles = eles.filter(e => e);
         if (!dragDiv) {
             dragDiv = document.createElement("div");
@@ -341,33 +278,29 @@ export namespace DragManager {
         const xs: number[] = [];
         const ys: number[] = [];
 
-        const docs = dragData instanceof DocumentDragData ? dragData.draggedDocuments :
-            dragData instanceof AnnotationDragData ? [dragData.dragDocument] : [];
+        const docs = dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof PdfAnnoDragData ? [dragData.dragDocument] : [];
         const dragElements = eles.map(ele => {
-            const w = ele.offsetWidth,
-                h = ele.offsetHeight;
+            if (!ele.parentNode) dragDiv.appendChild(ele);
+            const dragElement = ele.parentNode === dragDiv ? ele : ele.cloneNode(true) as HTMLElement;
             const rect = ele.getBoundingClientRect();
-            const scaleX = rect.width / w,
-                scaleY = rect.height / h;
-            const x = rect.left,
-                y = rect.top;
-            xs.push(x);
-            ys.push(y);
+            const scaleX = rect.width / ele.offsetWidth,
+                scaleY = rect.height / ele.offsetHeight;
+            xs.push(rect.left);
+            ys.push(rect.top);
             scaleXs.push(scaleX);
             scaleYs.push(scaleY);
-            const dragElement = ele.cloneNode(true) as HTMLElement;
             dragElement.style.opacity = "0.7";
-            dragElement.style.borderRadius = getComputedStyle(ele).borderRadius;
             dragElement.style.position = "absolute";
             dragElement.style.margin = "0";
             dragElement.style.top = "0";
             dragElement.style.bottom = "";
             dragElement.style.left = "0";
-            dragElement.style.transition = "none";
             dragElement.style.color = "black";
+            dragElement.style.transition = "none";
             dragElement.style.transformOrigin = "0 0";
+            dragElement.style.borderRadius = getComputedStyle(ele).borderRadius;
             dragElement.style.zIndex = globalCssVariables.contextMenuZindex;// "1000";
-            dragElement.style.transform = `translate(${x + (options?.offsetX || 0)}px, ${y + (options?.offsetY || 0)}px) scale(${scaleX}, ${scaleY})`;
+            dragElement.style.transform = `translate(${rect.left + (options?.offsetX || 0)}px, ${rect.top + (options?.offsetY || 0)}px) scale(${scaleX}, ${scaleY})`;
             dragElement.style.width = `${rect.width / scaleX}px`;
             dragElement.style.height = `${rect.height / scaleY}px`;
 
@@ -385,30 +318,18 @@ export namespace DragManager {
                     Array.from(pdfView).map((v, i) => v.scrollTo({ top: tops[i] }));
                 }, 0);
             }
-            const set = dragElement.getElementsByTagName('*');
             if (dragElement.hasAttribute("style")) (dragElement as any).style.pointerEvents = "none";
+            const set = dragElement.getElementsByTagName('*');
             // tslint:disable-next-line: prefer-for-of
             for (let i = 0; i < set.length; i++) {
-                if (set[i].hasAttribute("style")) {
-                    const s = set[i];
-                    (s as any).style.pointerEvents = "none";
-                }
+                set[i].hasAttribute("style") && ((set[i] as any).style.pointerEvents = "none");
             }
 
-
             dragDiv.appendChild(dragElement);
             return dragElement;
         });
 
-        let hideSource = false;
-        if (options) {
-            if (typeof options.hideSource === "boolean") {
-                hideSource = options.hideSource;
-            } else {
-                hideSource = options.hideSource();
-            }
-        }
-
+        const hideSource = options?.hideSource ? true : false;
         eles.map(ele => ele.parentElement && ele.parentElement?.className === dragData.dragDivName ? (ele.parentElement.hidden = hideSource) : (ele.hidden = hideSource));
 
         let lastX = downX;
@@ -418,9 +339,9 @@ export namespace DragManager {
             if (dragData instanceof DocumentDragData) {
                 dragData.userDropAction = e.ctrlKey ? "alias" : undefined;
             }
-            if (((options && !options.withoutShiftDrag) || !options) && e.shiftKey && CollectionDockingView.Instance) {
+            if (e.shiftKey && CollectionDockingView.Instance) {
                 AbortDrag();
-                finishDrag && finishDrag(dragData);
+                finishDrag?.(new DragCompleteEvent(true, dragData));
                 CollectionDockingView.Instance.StartOtherDrag({
                     pageX: e.pageX,
                     pageY: e.pageY,
@@ -445,45 +366,40 @@ export namespace DragManager {
         const endDrag = () => {
             document.removeEventListener("pointermove", moveHandler, true);
             document.removeEventListener("pointerup", upHandler);
-            if (options) {
-                options.handlers.dragComplete({});
-            }
         };
 
         AbortDrag = () => {
             hideDragShowOriginalElements();
             SelectionManager.SetIsDragging(false);
+            options?.dragComplete?.(new DragCompleteEvent(true, dragData));
             endDrag();
         };
         const upHandler = (e: PointerEvent) => {
             hideDragShowOriginalElements();
             dispatchDrag(eles, e, dragData, options, finishDrag);
             SelectionManager.SetIsDragging(false);
+            options?.dragComplete?.(new DragCompleteEvent(false, dragData));
             endDrag();
         };
         document.addEventListener("pointermove", moveHandler, true);
         document.addEventListener("pointerup", upHandler);
     }
 
-    function dispatchDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (dragData: { [index: string]: any }) => void) {
+    function dispatchDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (e: DragCompleteEvent) => void) {
         const removed = dragData.dontHideOnDrop ? [] : dragEles.map(dragEle => {
-            // let parent = dragEle.parentElement;
-            // if (parent) parent.removeChild(dragEle);
-            const ret = [dragEle, dragEle.style.width, dragEle.style.height];
+            const ret = { ele: dragEle, w: dragEle.style.width, h: dragEle.style.height };
             dragEle.style.width = "0";
             dragEle.style.height = "0";
             return ret;
         });
         const target = document.elementFromPoint(e.x, e.y);
         removed.map(r => {
-            const 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);
+            r.ele.style.width = r.w;
+            r.ele.style.height = r.h;
         });
         if (target) {
-            finishDrag && finishDrag(dragData);
+            const complete = new DragCompleteEvent(false, dragData);
+            finishDrag?.(complete);
 
             target.dispatchEvent(
                 new CustomEvent<DropEvent>("dashOnDrop", {
@@ -491,8 +407,10 @@ export namespace DragManager {
                     detail: {
                         x: e.x,
                         y: e.y,
-                        data: dragData,
-                        mods: e.altKey ? "AltKey" : e.ctrlKey ? "CtrlKey" : e.metaKey ? "MetaKey" : ""
+                        complete: complete,
+                        altKey: e.altKey,
+                        metaKey: e.metaKey,
+                        ctrlKey: e.ctrlKey
                     }
                 })
             );
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index fb6f27478..5f3667acc 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -70,7 +70,7 @@ export class LinkManager {
     }
 
     // finds all links that contain the given anchor
-    public getAllRelatedLinks(anchor: Doc): Doc[] {//List<Doc> {
+    public getAllRelatedLinks(anchor: Doc): Doc[] {
         const related = LinkManager.Instance.getAllLinks().filter(link => {
             const protomatch1 = Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, null));
             const protomatch2 = Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, null));
@@ -244,7 +244,5 @@ export class LinkManager {
         if (Doc.AreProtosEqual(anchor, linkDoc)) return linkDoc;
     }
 }
-Scripting.addGlobal(function links(doc: any) {
-    return new List(LinkManager.Instance.getAllRelatedLinks(doc));
-});
 
+Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); });
\ No newline at end of file
diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx
index 293a4feea..5ca861f71 100644
--- a/src/client/views/CollectionLinearView.tsx
+++ b/src/client/views/CollectionLinearView.tsx
@@ -39,7 +39,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
     protected createDropTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
         this._dropDisposer && this._dropDisposer();
         if (ele) {
-            this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+            this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
         }
     }
 
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 27ee9f122..c7ddee7ea 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -71,7 +71,7 @@ export function DocAnnotatableComponent<P extends DocAnnotatableProps, T>(schema
         // if the moved document is already in this overlay collection nothing needs to be done.
         // otherwise, if the document can be removed from where it was, it will then be added to this document's overlay collection. 
         @action.bound
-        moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
+        moveDocument(doc: Doc, targetCollection: Doc | undefined, addDocument: (doc: Doc) => boolean): boolean {
             return Doc.AreProtosEqual(this.props.Document, targetCollection) ? true : this.removeDocument(doc) ? addDocument(doc) : false;
         }
         @action.bound
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 7f125dd34..f014c1329 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -3,13 +3,12 @@ import { faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCloudUploadA
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, observable, runInAction, computed } from "mobx";
 import { observer } from "mobx-react";
-import { Doc } from "../../new_fields/Doc";
+import { Doc, DocListCast } from "../../new_fields/Doc";
 import { RichTextField } from '../../new_fields/RichTextField';
-import { NumCast, StrCast } from "../../new_fields/Types";
+import { NumCast, StrCast, Cast } from "../../new_fields/Types";
 import { emptyFunction } from "../../Utils";
 import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
 import { DragManager } from "../util/DragManager";
-import { LinkManager } from '../util/LinkManager';
 import { UndoManager } from "../util/UndoManager";
 import './DocumentButtonBar.scss';
 import './collections/ParentDocumentSelector.scss';
@@ -107,27 +106,21 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[],
         if (this._linkButton.current !== null && (Math.abs(e.clientX - this._downX) > 3 || Math.abs(e.clientY - this._downY) > 3)) {
             document.removeEventListener("pointermove", this.onLinkButtonMoved);
             document.removeEventListener("pointerup", this.onLinkButtonUp);
-            const docView = this.props.views[0];
-            const container = docView.props.ContainingCollectionDoc?.proto;
-            const dragData = new DragManager.LinkDragData(docView.props.Document, container ? [container] : []);
             const linkDrag = UndoManager.StartBatch("Drag Link");
-            DragManager.StartLinkDrag(this._linkButton.current, dragData, e.pageX, e.pageY, {
-                handlers: {
-                    dragComplete: () => {
-                        const tooltipmenu = FormattedTextBox.ToolTipTextMenu;
-                        const linkDoc = dragData.linkDocument;
-                        if (linkDoc && tooltipmenu) {
-                            const proto = Doc.GetProto(linkDoc);
-                            if (proto && docView) {
-                                proto.sourceContext = docView.props.ContainingCollectionDoc;
-                            }
-                            const text = tooltipmenu.makeLink(linkDoc, StrCast(linkDoc.anchor2.title), e.ctrlKey ? "onRight" : "inTab");
-                            if (linkDoc instanceof Doc && linkDoc.anchor2 instanceof Doc) {
-                                proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODODO open to more descriptive descriptions of following in text link
-                            }
+            DragManager.StartLinkDrag(this._linkButton.current, this.props.views[0].props.Document, e.pageX, e.pageY, {
+                dragComplete: dropEv => {
+                    const linkDoc = dropEv.linkDragData?.linkDocument; // equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop
+                    if (linkDoc && FormattedTextBox.ToolTipTextMenu) {
+                        const proto = Doc.GetProto(linkDoc);
+                        proto.sourceContext = this.props.views[0].props.ContainingCollectionDoc;
+
+                        const anchor2Title = linkDoc.anchor2 instanceof Doc ? StrCast((linkDoc.anchor2 as Doc).title) : "-untitled-";
+                        const text = FormattedTextBox.ToolTipTextMenu.makeLink(linkDoc, anchor2Title, e.ctrlKey ? "onRight" : "inTab");
+                        if (linkDoc.anchor2 instanceof Doc) {
+                            proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODO open to more descriptive descriptions of following in text link
                         }
-                        linkDrag && linkDrag.end();
                     }
+                    linkDrag?.end();
                 },
                 hideSource: false
             });
@@ -200,7 +193,7 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[],
 
     @computed
     get linkButton() {
-        const linkCount = LinkManager.Instance.getAllRelatedLinks(this.props.views[0].props.Document).length;
+        const linkCount = DocListCast(this.props.views[0].props.Document.links).length;
         return <div title="Drag(create link) Tap(view links)" className="documentButtonBar-linkFlyout" ref={this._linkButton}>
             <Flyout anchorPoint={anchorPoints.RIGHT_TOP}
                 content={<LinkMenu docView={this.props.views[0]} addDocTab={this.props.views[0].props.addDocTab} changeFlyout={emptyFunction} />}>
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index f366a3677..fdaca87a9 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -199,7 +199,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         document.removeEventListener("pointermove", this.onTitleMove);
         document.removeEventListener("pointerup", this.onTitleUp);
         DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(documentView => documentView.ContentDiv!), dragData, e.x, e.y, {
-            handlers: { dragComplete: action(() => this._hidden = this.Interacting = false) },
+            dragComplete: action(e => this._hidden = this.Interacting = false),
             hideSource: true
         });
         e.stopPropagation();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 25b7dc5ec..db2a3c298 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -460,7 +460,7 @@ export class MainView extends React.Component {
 
     addButtonDoc = (doc: Doc) => Doc.AddDocToList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", doc);
     remButtonDoc = (doc: Doc) => Doc.RemoveDocFromList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", doc);
-    moveButtonDoc = (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => this.remButtonDoc(doc) && addDocument(doc);
+    moveButtonDoc = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => this.remButtonDoc(doc) && addDocument(doc);
 
     buttonBarXf = () => {
         if (!this._docBtnRef.current) return Transform.Identity();
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index cd330d492..350a75d29 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -9,8 +9,6 @@ import { DocListCast, Doc } from "../../new_fields/Doc";
 import { Id } from "../../new_fields/FieldSymbols";
 import { DocumentView } from "./nodes/DocumentView";
 import { Transform } from "../util/Transform";
-import { CollectionFreeFormDocumentView } from "./nodes/CollectionFreeFormDocumentView";
-import { DocumentContentsView } from "./nodes/DocumentContentsView";
 import { NumCast } from "../../new_fields/Types";
 import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView";
 
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 8d9dc4cf7..f7cfc4022 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -66,7 +66,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
         const ex = e.target.getBoundingClientRect().left;
         const ey = e.target.getBoundingClientRect().top;
         const de = new DragManager.DocumentDragData([topDoc]);
-        de.dragDivName =  topDocView.props.dragDivName;
+        de.dragDivName = topDocView.props.dragDivName;
         de.moveDocument = topDocView.props.moveDocument;
         undoBatch(action(() => topDoc.z = topDoc.z ? 0 : 1))();
         setTimeout(() => {
@@ -74,11 +74,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
             if (newDocView) {
                 const contentDiv = newDocView.ContentDiv!;
                 const xf = contentDiv.getBoundingClientRect();
-                DragManager.StartDocumentDrag([contentDiv], de, ex, ey, {
-                    offsetX: ex - xf.left, offsetY: ey - xf.top,
-                    handlers: { dragComplete: () => { }, },
-                    hideSource: true
-                });
+                DragManager.StartDocumentDrag([contentDiv], de, ex, ey, { offsetX: ex - xf.left, offsetY: ey - xf.top, hideSource: true });
             }
         }, 0);
     }
@@ -148,9 +144,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
             DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, {
                 offsetX: dragData.offset[0],
                 offsetY: dragData.offset[1],
-                handlers: {
-                    dragComplete: action(emptyFunction),
-                },
                 hideSource: false
             });
         }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index ffcb3e9ce..08b9fd216 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -20,7 +20,7 @@ import { emptyFunction, returnEmptyString, returnFalse, returnOne, returnTrue, U
 import { DocServer } from "../../DocServer";
 import { Docs } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
-import { DragLinksAsDocuments, DragManager } from "../../util/DragManager";
+import { DragManager } from "../../util/DragManager";
 import { SelectionManager } from '../../util/SelectionManager';
 import { Transform } from '../../util/Transform';
 import { undoBatch } from "../../util/UndoManager";
@@ -346,7 +346,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             const docid = (e.target as any).DashDocId;
             const tab = (e.target as any).parentElement as HTMLElement;
             DocServer.GetRefField(docid).then(action(async (sourceDoc: Opt<Field>) =>
-                (sourceDoc instanceof Doc) && DragLinksAsDocuments(tab, x, y, sourceDoc)));
+                (sourceDoc instanceof Doc) && DragManager.StartLinkTargetsDrag(tab, x, y, sourceDoc)));
         }
         if (className === "lm_drag_handle" || className === "lm_close" || className === "lm_maximise" || className === "lm_minimise" || className === "lm_close_tab") {
             this._flush = true;
@@ -419,15 +419,13 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                 };
                 ReactDOM.render(<span title="Drag as document"
                     className="collectionDockingView-dragAsDocument"
-                    onPointerDown={
-                        e => {
-                            e.preventDefault();
-                            e.stopPropagation();
-                            DragManager.StartDocumentDrag([dragSpan], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY, {
-                                handlers: { dragComplete: emptyFunction },
-                                hideSource: false
-                            });
-                        }}><FontAwesomeIcon icon="file" size="lg" /></span>, dragSpan);
+                    onPointerDown={e => {
+                        e.preventDefault();
+                        e.stopPropagation();
+                        DragManager.StartDocumentDrag([dragSpan], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY);
+                    }}>
+                    <FontAwesomeIcon icon="file" size="lg" />
+                </span>, dragSpan);
                 ReactDOM.render(<ButtonSelector Document={doc} Stack={stack} />, gearSpan);
                 tab.reactComponents = [dragSpan, gearSpan, upDiv];
                 tab.element.append(dragSpan);
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index d697e721b..80752303c 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -56,7 +56,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     createRowDropRef = (ele: HTMLDivElement | null) => {
         this._dropDisposer && this._dropDisposer();
         if (ele) {
-            this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.rowDrop.bind(this) } });
+            this._dropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this));
         }
     }
 
@@ -74,12 +74,12 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     @undoBatch
     rowDrop = action((e: Event, de: DragManager.DropEvent) => {
         this._createAliasSelected = false;
-        if (de.data instanceof DragManager.DocumentDragData) {
+        if (de.complete.docDragData) {
             (this.props.parent.Document.dropConverter instanceof ScriptField) &&
-                this.props.parent.Document.dropConverter.script.run({ dragData: de.data });
+                this.props.parent.Document.dropConverter.script.run({ dragData: de.complete.docDragData });
             const key = StrCast(this.props.parent.props.Document.sectionFilter);
             const castedValue = this.getValue(this._heading);
-            de.data.droppedDocuments.forEach(d => d[key] = castedValue);
+            de.complete.docDragData.droppedDocuments.forEach(d => d[key] = castedValue);
             this.props.parent.drop(e, de);
             e.stopPropagation();
         }
@@ -171,10 +171,8 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
             const script = `return doc.${key} === ${value}`;
             const compiled = CompileScript(script, { params: { doc: Doc.name } });
             if (compiled.compiled) {
-                const scriptField = new ScriptField(compiled);
-                alias.viewSpecScript = scriptField;
-                const dragData = new DragManager.DocumentDragData([alias]);
-                DragManager.StartDocumentDrag([this._headerRef.current!], dragData, e.clientX, e.clientY);
+                alias.viewSpecScript = new ScriptField(compiled);
+                DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([alias]), e.clientX, e.clientY);
             }
 
             e.stopPropagation();
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 171dc4606..79a34bc00 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -37,7 +37,7 @@ export interface CellProps {
     renderDepth: number;
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     pinToPres: (document: Doc) => void;
-    moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
+    moveDocument: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
     isFocused: boolean;
     changeFocusedCellByIndex: (row: number, col: number) => void;
     setIsEditing: (isEditing: boolean) => void;
@@ -105,13 +105,13 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
     }
 
     private drop = (e: Event, de: DragManager.DropEvent) => {
-        if (de.data instanceof DragManager.DocumentDragData) {
+        if (de.complete.docDragData) {
             const fieldKey = this.props.rowProps.column.id as string;
-            if (de.data.draggedDocuments.length === 1) {
-                this._document[fieldKey] = de.data.draggedDocuments[0];
+            if (de.complete.docDragData.draggedDocuments.length === 1) {
+                this._document[fieldKey] = de.complete.docDragData.draggedDocuments[0];
             }
             else {
-                const coll = Docs.Create.SchemaDocument([new SchemaHeaderField("title", "#f1efeb")], de.data.draggedDocuments, {});
+                const coll = Docs.Create.SchemaDocument([new SchemaHeaderField("title", "#f1efeb")], de.complete.docDragData.draggedDocuments, {});
                 this._document[fieldKey] = coll;
             }
             e.stopPropagation();
@@ -121,7 +121,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
     private dropRef = (ele: HTMLElement | null) => {
         this._dropDisposer && this._dropDisposer();
         if (ele) {
-            this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+            this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
         }
     }
 
@@ -167,11 +167,10 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
         const fieldIsDoc = (type === "document" && typeof field === "object") || (typeof field === "object" && doc);
 
         const onItemDown = (e: React.PointerEvent) => {
-            if (fieldIsDoc) {
-                SetupDrag(this._focusRef, () => this._document[props.fieldKey] instanceof Doc ? this._document[props.fieldKey] : this._document,
-                    this._document[props.fieldKey] instanceof Doc ? (doc: Doc, target: Doc, addDoc: (newDoc: Doc) => any) => addDoc(doc) : this.props.moveDocument,
-                    this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e);
-            }
+            fieldIsDoc && SetupDrag(this._focusRef,
+                () => this._document[props.fieldKey] instanceof Doc ? this._document[props.fieldKey] : this._document,
+                this._document[props.fieldKey] instanceof Doc ? (doc: Doc, target: Doc | undefined, addDoc: (newDoc: Doc) => any) => addDoc(doc) : this.props.moveDocument,
+                this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e);
         };
         const onPointerEnter = (e: React.PointerEvent): void => {
             if (e.buttons === 1 && SelectionManager.GetIsDragging() && (type === "document" || type === undefined)) {
diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index 90320df82..153bbd410 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -56,7 +56,7 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
     createColDropTarget = (ele: HTMLDivElement) => {
         this._colDropDisposer && this._colDropDisposer();
         if (ele) {
-            this._colDropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.colDrop.bind(this) } });
+            this._colDropDisposer = DragManager.MakeDropTarget(ele, this.colDrop.bind(this));
         }
     }
 
@@ -66,8 +66,8 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
         const rect = this._header!.current!.getBoundingClientRect();
         const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + ((rect.right - rect.left) / 2), rect.top);
         const before = x[0] < bounds[0];
-        if (de.data instanceof DragManager.ColumnDragData) {
-            this.props.reorderColumns(de.data.colKey, this.props.columnValue, before, this.props.allColumns);
+        if (de.complete.columnDragData) {
+            this.props.reorderColumns(de.complete.columnDragData.colKey, this.props.columnValue, before, this.props.allColumns);
             return true;
         }
         return false;
@@ -165,7 +165,7 @@ export class MovableRow extends React.Component<MovableRowProps> {
     createRowDropTarget = (ele: HTMLDivElement) => {
         this._rowDropDisposer && this._rowDropDisposer();
         if (ele) {
-            this._rowDropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.rowDrop.bind(this) } });
+            this._rowDropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this));
         }
     }
 
@@ -178,16 +178,17 @@ export class MovableRow extends React.Component<MovableRowProps> {
         const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
         const before = x[1] < bounds[1];
 
-        if (de.data instanceof DragManager.DocumentDragData) {
+        const docDragData = de.complete.docDragData;
+        if (docDragData) {
             e.stopPropagation();
-            if (de.data.draggedDocuments[0] === rowDoc) return true;
+            if (docDragData.draggedDocuments[0] === rowDoc) return true;
             const addDocument = (doc: Doc) => this.props.addDoc(doc, rowDoc, before);
-            const movedDocs = de.data.draggedDocuments;
-            return (de.data.dropAction || de.data.userDropAction) ?
-                de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before) || added, false)
-                : (de.data.moveDocument) ?
-                    movedDocs.reduce((added: boolean, d) => de.data.moveDocument(d, rowDoc, addDocument) || added, false)
-                    : de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before), false);
+            const movedDocs = docDragData.draggedDocuments;
+            return (docDragData.dropAction || docDragData.userDropAction) ?
+                docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before) || added, false)
+                : (docDragData.moveDocument) ?
+                    movedDocs.reduce((added: boolean, d) => docDragData.moveDocument?.(d, rowDoc, addDocument) || added, false)
+                    : docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before), false);
         }
         return false;
     }
@@ -199,12 +200,12 @@ export class MovableRow extends React.Component<MovableRowProps> {
 
     @undoBatch
     @action
-    move: DragManager.MoveFunction = (doc: Doc, target: Doc, addDoc) => {
-        const targetView = DocumentManager.Instance.getDocumentView(target);
+    move: DragManager.MoveFunction = (doc: Doc, targetCollection: Doc | undefined, addDoc) => {
+        const targetView = targetCollection && DocumentManager.Instance.getDocumentView(targetCollection);
         if (targetView && targetView.props.ContainingCollectionDoc) {
-            return doc !== target && doc !== targetView.props.ContainingCollectionDoc && this.props.removeDoc(doc) && addDoc(doc);
+            return doc !== targetCollection && doc !== targetView.props.ContainingCollectionDoc && this.props.removeDoc(doc) && addDoc(doc);
         }
-        return doc !== target && this.props.removeDoc(doc) && addDoc(doc);
+        return doc !== targetCollection && this.props.removeDoc(doc) && addDoc(doc);
     }
 
     render() {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 8169e9e2b..bb706e528 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -224,7 +224,7 @@ export interface SchemaTableProps {
     renderDepth: number;
     deleteDocument: (document: Doc) => boolean;
     addDocument: (document: Doc) => boolean;
-    moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
+    moveDocument: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
     ScreenToLocalTransform: () => Transform;
     active: (outsideReaction: boolean) => boolean;
     onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index ff3417b77..6a23920f3 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -147,7 +147,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
 
     @action
-    moveDocument = (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean): boolean => {
+    moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean): boolean => {
         return this.props.removeDocument(doc) && addDocument(doc);
     }
     createRef = (ele: HTMLDivElement | null) => {
@@ -243,7 +243,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         const where = [de.x, de.y];
         let targInd = -1;
         let plusOne = false;
-        if (de.data instanceof DragManager.DocumentDragData) {
+        if (de.complete.docDragData) {
             this._docXfs.map((cd, i) => {
                 const pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
                 const pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height());
@@ -252,16 +252,16 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                     plusOne = (where[1] > (pos[1] + pos1[1]) / 2 ? 1 : 0) ? true : false;
                 }
             });
-        }
-        if (super.drop(e, de)) {
-            const newDoc = de.data.droppedDocuments[0];
-            const docs = this.childDocList;
-            if (docs) {
-                if (targInd === -1) targInd = docs.length;
-                else targInd = docs.indexOf(this.filteredChildren[targInd]);
-                const srcInd = docs.indexOf(newDoc);
-                docs.splice(srcInd, 1);
-                docs.splice((targInd > srcInd ? targInd - 1 : targInd) + (plusOne ? 1 : 0), 0, newDoc);
+            if (super.drop(e, de)) {
+                const newDoc = de.complete.docDragData.droppedDocuments[0];
+                const docs = this.childDocList;
+                if (docs) {
+                    if (targInd === -1) targInd = docs.length;
+                    else targInd = docs.indexOf(this.filteredChildren[targInd]);
+                    const srcInd = docs.indexOf(newDoc);
+                    docs.splice(srcInd, 1);
+                    docs.splice((targInd > srcInd ? targInd - 1 : targInd) + (plusOne ? 1 : 0), 0, newDoc);
+                }
             }
         }
         return false;
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index ca3b93bf8..39b4e4e1d 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -51,21 +51,21 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         this._dropRef = ele;
         this.dropDisposer && this.dropDisposer();
         if (ele) {
-            this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.columnDrop.bind(this) } });
+            this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this));
         }
     }
 
     @undoBatch
     columnDrop = action((e: Event, de: DragManager.DropEvent) => {
         this._createAliasSelected = false;
-        if (de.data instanceof DragManager.DocumentDragData) {
+        if (de.complete.docDragData) {
             const key = StrCast(this.props.parent.props.Document.sectionFilter);
             const castedValue = this.getValue(this._heading);
             if (castedValue) {
-                de.data.droppedDocuments.forEach(d => d[key] = castedValue);
+                de.complete.docDragData.droppedDocuments.forEach(d => d[key] = castedValue);
             }
             else {
-                de.data.droppedDocuments.forEach(d => d[key] = undefined);
+                de.complete.docDragData.droppedDocuments.forEach(d => d[key] = undefined);
             }
             this.props.parent.drop(e, de);
             e.stopPropagation();
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 9fa244546..5c7794cc0 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -27,7 +27,7 @@ import { Networking } from "../../Network";
 export interface CollectionViewProps extends FieldViewProps {
     addDocument: (document: Doc) => boolean;
     removeDocument: (document: Doc) => boolean;
-    moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
+    moveDocument: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
     PanelWidth: () => number;
     PanelHeight: () => number;
     VisibleHeight?: () => number;
@@ -51,7 +51,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
         protected createDropTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
             this.dropDisposer && this.dropDisposer();
             if (ele) {
-                this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+                this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
             }
         }
         protected CreateDropTarget(ele: HTMLDivElement) { //used in schema view
@@ -132,32 +132,33 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
         @undoBatch
         @action
         protected drop(e: Event, de: DragManager.DropEvent): boolean {
+            const docDragData = de.complete.docDragData;
             (this.props.Document.dropConverter instanceof ScriptField) &&
-                this.props.Document.dropConverter.script.run({ dragData: de.data });
-            if (de.data instanceof DragManager.DocumentDragData && !de.data.applyAsTemplate) {
-                if (de.mods === "AltKey" && de.data.draggedDocuments.length) {
+                this.props.Document.dropConverter.script.run({ dragData: docDragData }); /// bcz: check this 
+            if (docDragData && !docDragData.applyAsTemplate) {
+                if (de.altKey && docDragData.draggedDocuments.length) {
                     this.childDocs.map(doc =>
-                        Doc.ApplyTemplateTo(de.data.draggedDocuments[0], doc, "layoutFromParent"));
+                        Doc.ApplyTemplateTo(docDragData.draggedDocuments[0], doc, "layoutFromParent"));
                     e.stopPropagation();
                     return true;
                 }
                 let added = false;
-                if (de.data.dropAction || de.data.userDropAction) {
-                    added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
-                } else if (de.data.moveDocument) {
-                    const movedDocs = de.data.draggedDocuments;
+                if (docDragData.dropAction || docDragData.userDropAction) {
+                    added = docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
+                } else if (docDragData.moveDocument) {
+                    const movedDocs = docDragData.draggedDocuments;
                     added = movedDocs.reduce((added: boolean, d, i) =>
-                        de.data.droppedDocuments[i] !== d ? this.props.addDocument(de.data.droppedDocuments[i]) :
-                            de.data.moveDocument(d, this.props.Document, this.props.addDocument) || added, false);
+                        docDragData.droppedDocuments[i] !== d ? this.props.addDocument(docDragData.droppedDocuments[i]) :
+                            docDragData.moveDocument?.(d, this.props.Document, this.props.addDocument) || added, false);
                 } else {
-                    added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
+                    added = docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
                 }
                 e.stopPropagation();
                 return added;
             }
-            else if (de.data instanceof DragManager.AnnotationDragData) {
+            else if (de.complete.annoDragData) {
                 e.stopPropagation();
-                return this.props.addDocument(de.data.dropDocument);
+                return this.props.addDocument(de.complete.annoDragData.dropDocument);
             }
             return false;
         }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ec1e7409f..0a22a897c 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -123,7 +123,7 @@ class TreeView extends React.Component<TreeViewProps> {
     @undoBatch delete = () => this.props.deleteDoc(this.props.document);
     @undoBatch openRight = () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight", this.props.libraryPath);
     @undoBatch indent = () => this.props.addDocument(this.props.document) && this.delete();
-    @undoBatch move = (doc: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => {
+    @undoBatch move = (doc: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => {
         return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc);
     }
     @undoBatch @action remove = (document: Document, key: string) => {
@@ -132,7 +132,7 @@ class TreeView extends React.Component<TreeViewProps> {
 
     protected createTreeDropTarget = (ele: HTMLDivElement) => {
         this._treedropDisposer && this._treedropDisposer();
-        ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.treeDrop.bind(this) } }));
+        ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this)));
     }
 
     onPointerDown = (e: React.PointerEvent) => e.stopPropagation();
@@ -220,25 +220,25 @@ class TreeView extends React.Component<TreeViewProps> {
         const rect = this._header!.current!.getBoundingClientRect();
         const before = pt[1] < rect.top + rect.height / 2;
         const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && DocListCast(this.dataDoc[this.fieldKey]).length);
-        if (de.data instanceof DragManager.LinkDragData) {
-            const sourceDoc = de.data.linkSourceDocument;
+        if (de.complete.linkDragData) {
+            const sourceDoc = de.complete.linkDragData.linkSourceDocument;
             const destDoc = this.props.document;
             DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc });
             e.stopPropagation();
         }
-        if (de.data instanceof DragManager.DocumentDragData) {
+        if (de.complete.docDragData) {
             e.stopPropagation();
-            if (de.data.draggedDocuments[0] === this.props.document) return true;
+            if (de.complete.docDragData.draggedDocuments[0] === this.props.document) return true;
             let addDoc = (doc: Doc) => this.props.addDocument(doc, undefined, before);
             if (inside) {
                 addDoc = (doc: Doc) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) || addDoc(doc);
             }
-            const movedDocs = (de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments);
-            return ((de.data.dropAction && (de.data.options !== this.props.treeViewId)) || de.data.userDropAction) ?
-                de.data.droppedDocuments.reduce((added, d) => addDoc(d) || added, false)
-                : de.data.moveDocument ?
-                    movedDocs.reduce((added, d) => de.data.moveDocument(d, undefined, addDoc) || added, false)
-                    : de.data.droppedDocuments.reduce((added, d) => addDoc(d), false);
+            const movedDocs = (de.complete.docDragData.treeViewId === this.props.treeViewId ? de.complete.docDragData.draggedDocuments : de.complete.docDragData.droppedDocuments);
+            return ((de.complete.docDragData.dropAction && (de.complete.docDragData.treeViewId !== this.props.treeViewId)) || de.complete.docDragData.userDropAction) ?
+                de.complete.docDragData.droppedDocuments.reduce((added, d) => addDoc(d) || added, false)
+                : de.complete.docDragData.moveDocument ?
+                    movedDocs.reduce((added, d) => de.complete.docDragData?.moveDocument?.(d, undefined, addDoc) || added, false)
+                    : de.complete.docDragData.droppedDocuments.reduce((added, d) => addDoc(d), false);
         }
         return false;
     }
@@ -571,7 +571,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
     protected createTreeDropTarget = (ele: HTMLDivElement) => {
         this.treedropDisposer && this.treedropDisposer();
         if (this._mainEle = ele) {
-            this.treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+            this.treedropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
         }
     }
 
@@ -624,7 +624,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
     render() {
         const dropAction = StrCast(this.props.Document.dropAction) as dropActionType;
         const addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before, false, false, false);
-        const moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
+        const moveDoc = (d: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
         return !this.childDocs ? (null) : (
             <div className="collectionTreeView-dropTarget" id="body"
                 style={{ background: StrCast(this.props.Document.backgroundColor, "lightgray"), paddingTop: `${NumCast(this.props.Document.yMargin, 20)}px` }}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 15eec37de..9bd07966c 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -68,7 +68,7 @@ export namespace CollectionViewType {
 export interface CollectionRenderProps {
     addDocument: (document: Doc) => boolean;
     removeDocument: (document: Doc) => boolean;
-    moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
+    moveDocument: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
     active: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
 }
@@ -150,7 +150,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
     // otherwise, the document being moved must be able to be removed from its container before
     // moving it into the target.  
     @action.bound
-    moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
+    moveDocument(doc: Doc, targetCollection: Doc | undefined, addDocument: (doc: Doc) => boolean): boolean {
         if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
             return true;
         }
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 4161e5d6e..a870b6043 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -288,15 +288,15 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
     protected createDropTarget = (ele: HTMLDivElement) => {
         this.dropDisposer && this.dropDisposer();
         if (ele) {
-            this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+            this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
         }
     }
 
     @undoBatch
     @action
     protected drop(e: Event, de: DragManager.DropEvent): boolean {
-        if (de.data instanceof DragManager.DocumentDragData && de.data.draggedDocuments.length) {
-            this._buttonizableCommands.filter(c => c.title === this._currentKey).map(c => c.immediate(de.data.draggedDocuments));
+        if (de.complete.docDragData && de.complete.docDragData.draggedDocuments.length) {
+            this._buttonizableCommands.filter(c => c.title === this._currentKey).map(c => c.immediate(de.complete.docDragData?.draggedDocuments || []));
             e.stopPropagation();
         }
         return true;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ddfb52ba9..89c1df598 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -139,15 +139,15 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         const [xp, yp] = xf.transformPoint(de.x, de.y);
         const [xpo, ypo] = xfo.transformPoint(de.x, de.y);
         if (super.drop(e, de)) {
-            if (de.data instanceof DragManager.DocumentDragData) {
-                if (de.data.droppedDocuments.length) {
-                    const firstDoc = de.data.droppedDocuments[0];
+            if (de.complete.docDragData) {
+                if (de.complete.docDragData.droppedDocuments.length) {
+                    const firstDoc = de.complete.docDragData.droppedDocuments[0];
                     const z = NumCast(firstDoc.z);
-                    const x = (z ? xpo : xp) - de.data.offset[0];
-                    const y = (z ? ypo : yp) - de.data.offset[1];
+                    const x = (z ? xpo : xp) - de.complete.docDragData.offset[0];
+                    const y = (z ? ypo : yp) - de.complete.docDragData.offset[1];
                     const dropX = NumCast(firstDoc.x);
                     const dropY = NumCast(firstDoc.y);
-                    de.data.droppedDocuments.forEach(action((d: Doc) => {
+                    de.complete.docDragData.droppedDocuments.forEach(action((d: Doc) => {
                         const layoutDoc = Doc.Layout(d);
                         d.x = x + NumCast(d.x) - dropX;
                         d.y = y + NumCast(d.y) - dropY;
@@ -162,19 +162,19 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                         this.bringToFront(d);
                     }));
 
-                    de.data.droppedDocuments.length === 1 && this.updateCluster(de.data.droppedDocuments[0]);
+                    de.complete.docDragData.droppedDocuments.length === 1 && this.updateCluster(de.complete.docDragData.droppedDocuments[0]);
                 }
             }
-            else if (de.data instanceof DragManager.AnnotationDragData) {
-                if (de.data.dropDocument) {
-                    const dragDoc = de.data.dropDocument;
-                    const x = xp - de.data.offset[0];
-                    const y = yp - de.data.offset[1];
+            else if (de.complete.annoDragData) {
+                if (de.complete.annoDragData.dropDocument) {
+                    const dragDoc = de.complete.annoDragData.dropDocument;
+                    const x = xp - de.complete.annoDragData.offset[0];
+                    const y = yp - de.complete.annoDragData.offset[1];
                     const dropX = NumCast(dragDoc.x);
                     const dropY = NumCast(dragDoc.y);
                     dragDoc.x = x + NumCast(dragDoc.x) - dropX;
                     dragDoc.y = y + NumCast(dragDoc.y) - dropY;
-                    de.data.targetContext = this.props.Document; // dropped a PDF annotation, so we need to set the targetContext on the dragData which the PDF view uses at the end of the drop operation
+                    de.complete.annoDragData.targetContext = this.props.Document; // dropped a PDF annotation, so we need to set the targetContext on the dragData which the PDF view uses at the end of the drop operation
                     this.bringToFront(dragDoc);
                 }
             }
@@ -205,10 +205,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                 const [left, top] = clusterDocs[0].props.ScreenToLocalTransform().scale(clusterDocs[0].props.ContentScaling()).inverse().transformPoint(0, 0);
                 de.offset = this.getTransform().transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
                 de.dropAction = e.ctrlKey || e.altKey ? "alias" : undefined;
-                DragManager.StartDocumentDrag(clusterDocs.map(v => v.ContentDiv!), de, ptsParent.clientX, ptsParent.clientY, {
-                    handlers: { dragComplete: action(emptyFunction) },
-                    hideSource: !de.dropAction
-                });
+                DragManager.StartDocumentDrag(clusterDocs.map(v => v.ContentDiv!), de, ptsParent.clientX, ptsParent.clientY, { hideSource: !de.dropAction });
                 return true;
             }
         }
@@ -896,7 +893,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         // otherwise, they are stored in fieldKey.  All annotations to this document are stored in the extension document
         return !this.extensionDoc ? (null) :
             <div className={"collectionfreeformview-container"} ref={this.createDropTarget} onWheel={this.onPointerWheel}//pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined,
-                style={{ height: this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight() }}
+                style={{ pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined, height: this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight() }}
                 onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} onTouchStart={this.onTouchStart}>
                 <MarqueeView {...this.props} extensionDoc={this.extensionDoc} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} addDocument={this.addDocument}
                     addLiveTextDocument={this.addLiveTextBox} getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} isAnnotationOverlay={this.isAnnotationOverlay}>
diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss
index a4018bd2d..7dee22f66 100644
--- a/src/client/views/linking/LinkMenu.scss
+++ b/src/client/views/linking/LinkMenu.scss
@@ -48,90 +48,5 @@
     }
 }
 
-.linkMenu-item {
-    // border-top: 0.5px solid $main-accent; 
-    position: relative;
-    display: flex;
-    font-size: 12px;
-    
-
-    .link-name {
-        position: relative;
-
-        p {
-            padding: 4px 6px;
-            line-height: 12px;
-            border-radius: 5px;
-            overflow-wrap: break-word;
-        }
-    }
-
-    .linkMenu-item-content {
-        width: 100%;
-    }
-    
-    .link-metadata {
-        padding: 0 10px 0 16px;
-        margin-bottom: 4px;
-        color: $main-accent;
-        font-style: italic;
-        font-size: 10.5px;
-    }
-
-    &:hover {
-        .linkMenu-item-buttons {
-            display: flex;
-        }
-        .linkMenu-item-content {
-            &.expand-two p {
-                width: calc(100% - 52px);
-                background-color: lightgray;
-            }
-            &.expand-three p {
-                width: calc(100% - 84px);
-                background-color: lightgray;
-            }
-        }
-    }
-}
-
-.linkMenu-item-buttons {
-    display: none;
-    position: absolute;
-    top: 50%;
-    right: 0;
-    transform: translateY(-50%);
-
-    .button {
-        width: 20px;
-        height: 20px;
-        margin: 0;
-        margin-right: 6px;
-        border-radius: 50%;
-        cursor: pointer;
-        pointer-events: auto;
-        background-color: $dark-color;
-        color: $light-color;
-        font-size: 65%;
-        transition: transform 0.2s;
-        text-align: center;
-        position: relative;
-
-        .fa-icon {
-            position: absolute;
-            top: 50%;
-            left: 50%;
-            transform: translate(-50%, -50%);
-        }
-
-        &:last-child {
-            margin-right: 0;
-        }
-        &:hover {
-            background: $main-accent;
-        }
-    }
-}
-
 
 
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index 15aacbbc9..da56435f2 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -4,11 +4,9 @@ import { observer } from "mobx-react";
 import { Doc } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
-import { emptyFunction } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
 import { DragManager, SetupDrag } from "../../util/DragManager";
 import { LinkManager } from "../../util/LinkManager";
-import { UndoManager } from "../../util/UndoManager";
 import { DocumentView } from "../nodes/DocumentView";
 import './LinkMenu.scss';
 import { LinkMenuItem } from "./LinkMenuItem";
@@ -21,7 +19,6 @@ interface LinkMenuGroupProps {
     showEditor: (linkDoc: Doc) => void;
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     docView: DocumentView;
-
 }
 
 @observer
@@ -44,27 +41,14 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
         e.stopPropagation();
     }
 
-
     onLinkButtonMoved = async (e: PointerEvent) => {
-        UndoManager.RunInBatch(() => {
-            if (this._drag.current !== null && (e.movementX > 1 || e.movementY > 1)) {
-                document.removeEventListener("pointermove", this.onLinkButtonMoved);
-                document.removeEventListener("pointerup", this.onLinkButtonUp);
+        if (this._drag.current && (e.movementX > 1 || e.movementY > 1)) {
+            document.removeEventListener("pointermove", this.onLinkButtonMoved);
+            document.removeEventListener("pointerup", this.onLinkButtonUp);
 
-                const draggedDocs = this.props.group.map(linkDoc => {
-                    const opp = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc);
-                    if (opp) return opp;
-                }) as Doc[];
-                const dragData = new DragManager.DocumentDragData(draggedDocs);
-
-                DragManager.StartLinkedDocumentDrag([this._drag.current], dragData, e.x, e.y, {
-                    handlers: {
-                        dragComplete: action(emptyFunction),
-                    },
-                    hideSource: false
-                });
-            }
-        }, "drag links");
+            let targets = this.props.group.map(l => LinkManager.Instance.getOppositeAnchor(l, this.props.sourceDoc)).filter(d => d) as Doc[];
+            DragManager.StartLinkTargetsDrag(this._drag.current!, e.x, e.y, this.props.sourceDoc, targets);
+        }
         e.stopPropagation();
     }
 
diff --git a/src/client/views/linking/LinkMenuItem.scss b/src/client/views/linking/LinkMenuItem.scss
new file mode 100644
index 000000000..fd0954f65
--- /dev/null
+++ b/src/client/views/linking/LinkMenuItem.scss
@@ -0,0 +1,87 @@
+@import "../globalCssVariables";
+
+.linkMenu-item {
+    // border-top: 0.5px solid $main-accent; 
+    position: relative;
+    display: flex;
+    font-size: 12px;
+    
+
+    .linkMenu-name {
+        position: relative;
+
+        p {
+            padding: 4px 6px;
+            line-height: 12px;
+            border-radius: 5px;
+            overflow-wrap: break-word;
+            user-select: none;
+        }
+    }
+
+    .linkMenu-item-content {
+        width: 100%;
+    }
+    
+    .link-metadata {
+        padding: 0 10px 0 16px;
+        margin-bottom: 4px;
+        color: $main-accent;
+        font-style: italic;
+        font-size: 10.5px;
+    }
+
+    &:hover {
+        .linkMenu-item-buttons {
+            display: flex;
+        }
+        .linkMenu-item-content {
+            &.expand-two p {
+                width: calc(100% - 52px);
+                background-color: lightgray;
+            }
+            &.expand-three p {
+                width: calc(100% - 84px);
+                background-color: lightgray;
+            }
+        }
+    }
+}
+
+.linkMenu-item-buttons {
+    display: none;
+    position: absolute;
+    top: 50%;
+    right: 0;
+    transform: translateY(-50%);
+
+    .button {
+        width: 20px;
+        height: 20px;
+        margin: 0;
+        margin-right: 6px;
+        border-radius: 50%;
+        cursor: pointer;
+        pointer-events: auto;
+        background-color: $dark-color;
+        color: $light-color;
+        font-size: 65%;
+        transition: transform 0.2s;
+        text-align: center;
+        position: relative;
+
+        .fa-icon {
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+        }
+
+        &:last-child {
+            margin-right: 0;
+        }
+        &:hover {
+            background: $main-accent;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index edf5e9c26..b7d27ee30 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -5,11 +5,11 @@ import { action, observable } from 'mobx';
 import { observer } from "mobx-react";
 import { Doc } from '../../../new_fields/Doc';
 import { Cast, StrCast } from '../../../new_fields/Types';
-import { DragLinkAsDocument } from '../../util/DragManager';
+import { DragManager } from '../../util/DragManager';
 import { LinkManager } from '../../util/LinkManager';
 import { ContextMenu } from '../ContextMenu';
 import { LinkFollowBox } from './LinkFollowBox';
-import './LinkMenu.scss';
+import './LinkMenuItem.scss';
 import React = require("react");
 library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp);
 
@@ -26,6 +26,9 @@ interface LinkMenuItemProps {
 @observer
 export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
     private _drag = React.createRef<HTMLDivElement>();
+    private _downX = 0;
+    private _downY = 0;
+    private _eleClone: any;
     @observable private _showMore: boolean = false;
     @action toggleShowMore() { this._showMore = !this._showMore; }
 
@@ -55,6 +58,9 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
     }
 
     onLinkButtonDown = (e: React.PointerEvent): void => {
+        this._downX = e.clientX;
+        this._downY = e.clientY;
+        this._eleClone = this._drag.current!.cloneNode(true);
         e.stopPropagation();
         document.removeEventListener("pointermove", this.onLinkButtonMoved);
         document.addEventListener("pointermove", this.onLinkButtonMoved);
@@ -75,11 +81,12 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
     }
 
     onLinkButtonMoved = async (e: PointerEvent) => {
-        if (this._drag.current !== null && (e.movementX > 1 || e.movementY > 1)) {
+        if (this._drag.current !== null && Math.abs((e.clientX - this._downX) * (e.clientX - this._downX) + (e.clientY - this._downY) * (e.clientY - this._downY)) > 5) {
             document.removeEventListener("pointermove", this.onLinkButtonMoved);
             document.removeEventListener("pointerup", this.onLinkButtonUp);
 
-            DragLinkAsDocument(this._drag.current, e.x, e.y, this.props.linkDoc, this.props.sourceDoc);
+            this._eleClone.style.transform = `translate(${e.x}px, ${e.y}px)`;
+            DragManager.StartLinkTargetsDrag(this._eleClone, e.x, e.y, this.props.sourceDoc, [this.props.linkDoc]);
         }
         e.stopPropagation();
     }
@@ -109,20 +116,21 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
     }
 
     render() {
-
         const keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType);
         const canExpand = keys ? keys.length > 0 : false;
 
         return (
             <div className="linkMenu-item">
                 <div className={canExpand ? "linkMenu-item-content expand-three" : "linkMenu-item-content expand-two"}>
-                    <div className="link-name">
-                        <p ref={this._drag} onPointerDown={this.onLinkButtonDown}>{StrCast(this.props.destinationDoc.title)}</p>
+                    <div ref={this._drag} className="linkMenu-name" title="drag to view target. click to customize." onPointerDown={this.onLinkButtonDown}>
+                        <p >{StrCast(this.props.destinationDoc.title)}</p>
                         <div className="linkMenu-item-buttons">
                             {canExpand ? <div title="Show more" className="button" onPointerDown={() => this.toggleShowMore()}>
                                 <FontAwesomeIcon className="fa-icon" icon={this._showMore ? "chevron-up" : "chevron-down"} size="sm" /></div> : <></>}
                             <div title="Edit link" className="button" onPointerDown={this.onEdit}><FontAwesomeIcon className="fa-icon" icon="edit" size="sm" /></div>
-                            <div title="Follow link" className="button" onClick={this.followDefault} onContextMenu={this.onContextMenu}><FontAwesomeIcon className="fa-icon" icon="arrow-right" size="sm" /></div>
+                            <div title="Follow link" className="button" onClick={this.followDefault} onContextMenu={this.onContextMenu}>
+                                <FontAwesomeIcon className="fa-icon" icon="arrow-right" size="sm" />
+                            </div>
                         </div>
                     </div>
                     {this._showMore ? this.renderMetadata() : <></>}
diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx
index 34151a311..d1272c266 100644
--- a/src/client/views/nodes/ButtonBox.tsx
+++ b/src/client/views/nodes/ButtonBox.tsx
@@ -46,7 +46,7 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt
             this.dropDisposer();
         }
         if (ele) {
-            this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+            this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
         }
     }
 
@@ -65,9 +65,10 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
-        if (de.data instanceof DragManager.DocumentDragData && e.target) {
-            this.props.Document[(e.target as any).textContent] = new List<Doc>(de.data.droppedDocuments.map((d, i) =>
-                d.onDragStart ? de.data.draggedDocuments[i] : d));
+        const docDragData = de.complete.docDragData;
+        if (docDragData && e.target) {
+            this.props.Document[(e.target as any).textContent] = new List<Doc>(docDragData.droppedDocuments.map((d, i) =>
+                d.onDragStart ? docDragData.draggedDocuments[i] : d));
             e.stopPropagation();
         }
     }
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 6d0ea33fa..5e3306a11 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -32,7 +32,7 @@ interface ContentFittingDocumentViewProps {
     onClick?: ScriptField;
     getTransform: () => Transform;
     addDocument: (document: Doc) => boolean;
-    moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean;
+    moveDocument: (document: Doc, target: Doc | undefined, addDoc: ((doc: Doc) => boolean)) => boolean;
     removeDocument: (document: Doc) => boolean;
     active: (outsideReaction: boolean) => boolean;
     whenActiveChanged: (isActive: boolean) => void;
@@ -60,11 +60,12 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
-        if (de.data instanceof DragManager.DocumentDragData) {
+        const docDragData = de.complete.docDragData;
+        if (docDragData) {
             this.props.childDocs && this.props.childDocs.map(otherdoc => {
                 const target = Doc.GetProto(otherdoc);
                 target.layout = ComputedField.MakeFunction("this.image_data[0]");
-                target.layoutCustom = Doc.MakeDelegate(de.data.draggedDocuments[0]);
+                target.layoutCustom = Doc.MakeDelegate(docDragData.draggedDocuments[0]);
             });
             e.stopPropagation();
         }
diff --git a/src/client/views/nodes/DocuLinkBox.tsx b/src/client/views/nodes/DocuLinkBox.tsx
index a22472e9e..3e2e74c67 100644
--- a/src/client/views/nodes/DocuLinkBox.tsx
+++ b/src/client/views/nodes/DocuLinkBox.tsx
@@ -5,7 +5,7 @@ import { makeInterface } from "../../../new_fields/Schema";
 import { NumCast, StrCast, Cast } from "../../../new_fields/Types";
 import { Utils } from '../../../Utils';
 import { DocumentManager } from "../../util/DocumentManager";
-import { DragLinksAsDocuments } from "../../util/DragManager";
+import { DragManager } from "../../util/DragManager";
 import { DocComponent } from "../DocComponent";
 import "./DocuLinkBox.scss";
 import { FieldView, FieldViewProps } from "./FieldView";
@@ -43,7 +43,7 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
             const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY));
             const dragdist = Math.sqrt((pt[0] - this._downx) * (pt[0] - this._downx) + (pt[1] - this._downy) * (pt[1] - this._downy));
             if (separation > 100) {
-                DragLinksAsDocuments(this._ref.current!, pt[0], pt[1], Cast(this.props.Document[this.props.fieldKey], Doc) as Doc, this.props.Document); // Containging collection is the document, not a collection... hack.
+                DragManager.StartLinkTargetsDrag(this._ref.current!, pt[0], pt[1], Cast(this.props.Document[this.props.fieldKey], Doc) as Doc, [this.props.Document]); // Containging collection is the document, not a collection... hack.
                 document.removeEventListener("pointermove", this.onPointerMove);
                 document.removeEventListener("pointerup", this.onPointerUp);
             } else if (dragdist > separation) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1b2d92027..b096d68fc 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -19,7 +19,6 @@ import { DocumentType } from '../../documents/DocumentTypes';
 import { ClientUtils } from '../../util/ClientUtils';
 import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager, dropActionType } from "../../util/DragManager";
-import { LinkManager } from '../../util/LinkManager';
 import { Scripting } from '../../util/Scripting';
 import { SelectionManager } from "../../util/SelectionManager";
 import SharingManager from '../../util/SharingManager';
@@ -60,7 +59,7 @@ export interface DocumentViewProps {
     dragDivName?: string;
     addDocument?: (doc: Doc) => boolean;
     removeDocument?: (doc: Doc) => boolean;
-    moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
+    moveDocument?: (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
     ScreenToLocalTransform: () => Transform;
     renderDepth: number;
     showOverlays?: (doc: Doc) => { title?: string, caption?: string };
@@ -105,7 +104,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @action
     componentDidMount() {
-        this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } }));
+        this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this)));
 
         !this.props.dontRegisterView && DocumentManager.Instance.DocumentViews.push(this);
     }
@@ -113,7 +112,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @action
     componentDidUpdate() {
         this._dropDisposer && this._dropDisposer();
-        this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } }));
+        this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this)));
     }
 
     @action
@@ -132,12 +131,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             dragData.moveDocument = this.Document.onDragStart ? undefined : this.props.moveDocument;
             dragData.applyAsTemplate = applyAsTemplate;
             dragData.dragDivName = this.props.dragDivName;
-            DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, {
-                handlers: {
-                    dragComplete: action((emptyFunction))
-                },
-                hideSource: !dropAction && !this.Document.onDragStart
-            });
+            DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: !dropAction && !this.Document.onDragStart });
         }
     }
 
@@ -190,7 +184,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     buttonClick = async (altKey: boolean, ctrlKey: boolean) => {
         const maximizedDocs = await DocListCastAsync(this.Document.maximizedDocs);
         const summarizedDocs = await DocListCastAsync(this.Document.summarizedDocs);
-        const linkDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document);
+        const linkDocs = DocListCast(this.props.Document.links);
         let expandedDocs: Doc[] = [];
         expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs;
         expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs;
@@ -324,23 +318,24 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @undoBatch
     @action
     drop = async (e: Event, de: DragManager.DropEvent) => {
-        if (de.data instanceof DragManager.AnnotationDragData) {
+        if (de.complete.annoDragData) {
             /// this whole section for handling PDF annotations looks weird.  Need to rethink this to make it cleaner
             e.stopPropagation();
-            (de.data as any).linkedToDoc = true;
+            de.complete.annoDragData.linkedToDoc = true;
 
-            DocUtils.MakeLink({ doc: de.data.annotationDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, `Link from ${StrCast(de.data.annotationDocument.title)}`);
+            DocUtils.MakeLink({ doc: de.complete.annoDragData.annotationDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc },
+                `Link from ${StrCast(de.complete.annoDragData.annotationDocument.title)}`);
         }
-        if (de.data instanceof DragManager.DocumentDragData && de.data.applyAsTemplate) {
-            Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document, "layoutCustom");
+        if (de.complete.docDragData && de.complete.docDragData.applyAsTemplate) {
+            Doc.ApplyTemplateTo(de.complete.docDragData.draggedDocuments[0], this.props.Document, "layoutCustom");
             e.stopPropagation();
         }
-        if (de.data instanceof DragManager.LinkDragData) {
+        if (de.complete.linkDragData) {
             e.stopPropagation();
             // const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true);
             // const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView);
-            de.data.linkSourceDocument !== this.props.Document &&
-                (de.data.linkDocument = DocUtils.MakeLink({ doc: de.data.linkSourceDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, "in-text link being created")); // TODODO this is where in text links get passed
+            de.complete.linkDragData.linkSourceDocument !== this.props.Document &&
+                (de.complete.linkDragData.linkDocument = DocUtils.MakeLink({ doc: de.complete.linkDragData.linkSourceDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, "in-text link being created")); // TODODO this is where in text links get passed
         }
     }
 
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index ce1c468ad..c56fde186 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -39,7 +39,7 @@ export interface FieldViewProps {
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     removeDocument?: (document: Doc) => boolean;
-    moveDocument?: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
+    moveDocument?: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
     ScreenToLocalTransform: () => Transform;
     active: (outsideReaction?: boolean) => boolean;
     whenActiveChanged: (isActive: boolean) => void;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 7d404b28f..af09e57f4 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -246,22 +246,22 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     protected createDropTarget = (ele: HTMLDivElement) => {
         this._proseRef = ele;
         this.dropDisposer && this.dropDisposer();
-        ele && (this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }));
+        ele && (this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)));
     }
 
     @undoBatch
     @action
     drop = async (e: Event, de: DragManager.DropEvent) => {
-        if (de.data instanceof DragManager.DocumentDragData) {
-            const draggedDoc = de.data.draggedDocuments.length && de.data.draggedDocuments[0];
+        if (de.complete.docDragData) {
+            const draggedDoc = de.complete.docDragData.draggedDocuments.length && de.complete.docDragData.draggedDocuments[0];
             // replace text contents whend dragging with Alt
-            if (draggedDoc && draggedDoc.type === DocumentType.TEXT && !Doc.AreProtosEqual(draggedDoc, this.props.Document) && de.mods === "AltKey") {
+            if (draggedDoc && draggedDoc.type === DocumentType.TEXT && !Doc.AreProtosEqual(draggedDoc, this.props.Document) && de.altKey) {
                 if (draggedDoc.data instanceof RichTextField) {
                     Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new RichTextField(draggedDoc.data.Data, draggedDoc.data.Text);
                     e.stopPropagation();
                 }
                 // apply as template when dragging with Meta
-            } else if (draggedDoc && draggedDoc.type === DocumentType.TEXT && !Doc.AreProtosEqual(draggedDoc, this.props.Document) && de.mods === "MetaKey") {
+            } else if (draggedDoc && draggedDoc.type === DocumentType.TEXT && !Doc.AreProtosEqual(draggedDoc, this.props.Document) && de.metaKey) {
                 draggedDoc.isTemplateDoc = true;
                 let newLayout = Doc.Layout(draggedDoc);
                 if (typeof (draggedDoc.layout) === "string") {
@@ -272,8 +272,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                 this.Document.layoutKey = "layoutCustom";
                 e.stopPropagation();
                 // embed document when dragging with a userDropAction or an embedDoc flag set
-            } else if (de.data.userDropAction || de.data.embedDoc) {
-                const target = de.data.droppedDocuments[0];
+            } else if (de.complete.docDragData.userDropAction || de.complete.docDragData.embedDoc) {
+                const target = de.complete.docDragData.droppedDocuments[0];
                 // const link = DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: target }, "Embedded Doc:" + target.title);
                 // if (link) {
                 target.fitToBox = true;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 4b3da3dae..f6aa47f15 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -67,18 +67,18 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
 
     protected createDropTarget = (ele: HTMLDivElement) => {
         this._dropDisposer && this._dropDisposer();
-        ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }));
+        ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)));
     }
 
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
-        if (de.data instanceof DragManager.DocumentDragData) {
-            if (de.mods === "AltKey" && de.data.draggedDocuments.length && de.data.draggedDocuments[0].data instanceof ImageField) {
-                Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new ImageField(de.data.draggedDocuments[0].data.url);
+        if (de.complete.docDragData) {
+            if (de.altKey && de.complete.docDragData.draggedDocuments.length && de.complete.docDragData.draggedDocuments[0].data instanceof ImageField) {
+                Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new ImageField(de.complete.docDragData.draggedDocuments[0].data.url);
                 e.stopPropagation();
             }
-            de.mods === "MetaKey" && de.data.droppedDocuments.forEach(action((drop: Doc) => {
+            de.metaKey && de.complete.docDragData.droppedDocuments.forEach(action((drop: Doc) => {
                 this.extensionDoc && Doc.AddDocToList(Doc.GetProto(this.extensionDoc), "Alternates", drop);
                 e.stopPropagation();
             }));
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index b114cd7ed..c85350264 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -409,6 +409,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
         if ((this.Document.scale || 1) !== 1) return;
         if ((e.button !== 0 || e.altKey) && this.active(true)) {
             this._setPreviewCursor && this._setPreviewCursor(e.clientX, e.clientY, true);
+            e.stopPropagation();
         }
         this._marqueeing = false;
         if (!e.altKey && e.button === 0 && this.active(true)) {
@@ -559,14 +560,9 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
         const targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "Note linked to " + this.props.Document.title });
         const annotationDoc = this.highlight("rgba(146, 245, 95, 0.467)"); // yellowish highlight color when dragging out a text selection
         if (annotationDoc) {
-            const dragData = new DragManager.AnnotationDragData(this.props.Document, annotationDoc, targetDoc);
-            DragManager.StartAnnotationDrag([ele], dragData, e.pageX, e.pageY, {
-                handlers: {
-                    dragComplete: () => !(dragData as any).linkedToDoc &&
-                        DocUtils.MakeLink({ doc: annotationDoc }, { doc: dragData.dropDocument, ctx: dragData.targetContext }, `Annotation from ${this.Document.title}`, "link from PDF")
-
-                },
-                hideSource: false
+            DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.Document, annotationDoc, targetDoc), e.pageX, e.pageY, {
+                dragComplete: e => !e.aborted && e.annoDragData && !e.annoDragData.linkedToDoc &&
+                    DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument, ctx: e.annoDragData.targetContext }, `Annotation from ${this.Document.title}`, "link from PDF")
             });
         }
     }
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index fe21c29c3..515c248f7 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -12,13 +12,11 @@ import { Utils } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
 import { SetupDrag } from '../../util/DragManager';
 import { SearchUtil } from '../../util/SearchUtil';
-import { MainView } from '../MainView';
 import { FilterBox } from './FilterBox';
 import "./FilterBox.scss";
 import "./SearchBox.scss";
 import { SearchItem } from './SearchItem';
 import { IconBar } from './IconBar';
-import { string } from 'prop-types';
 
 library.add(faTimes);
 
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index cdb6dcf7c..8cd4b3caa 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -4,14 +4,13 @@ import { faCaretUp, faChartBar, faFile, faFilePdf, faFilm, faFingerprint, faGlob
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, DocListCast } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils, emptyPath } from "../../../Utils";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager, SetupDrag } from "../../util/DragManager";
-import { LinkManager } from "../../util/LinkManager";
 import { SearchUtil } from "../../util/SearchUtil";
 import { Transform } from "../../util/Transform";
 import { SEARCH_THUMBNAIL_SIZE } from "../../views/globalCssVariables.scss";
@@ -211,7 +210,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
     }
 
     @computed
-    get linkCount() { return LinkManager.Instance.getAllRelatedLinks(this.props.doc).length; }
+    get linkCount() { return DocListCast(this.props.doc.links).length; }
 
     @action
     pointerDown = (e: React.PointerEvent) => { e.preventDefault(); e.button === 0 && SearchBox.Instance.openSearch(e); }
@@ -267,10 +266,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
     onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
         e.stopPropagation();
         const doc = Doc.IsPrototype(this.props.doc) ? Doc.MakeDelegate(this.props.doc) : this.props.doc;
-        DragManager.StartDocumentDrag([e.currentTarget], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY, {
-            handlers: { dragComplete: emptyFunction },
-            hideSource: false,
-        });
+        DragManager.StartDocumentDrag([e.currentTarget], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY);
     }
 
     render() {
-- 
cgit v1.2.3-70-g09d2


From 80d4bf70cb8e29f15d8e51b1e0bd378a26d68fcf Mon Sep 17 00:00:00 2001
From: vellichora <fangrui_tong@brown.edu>
Date: Tue, 7 Jan 2020 03:07:18 -0500
Subject: converted tool tip text menu to react component with basic marks and
 dropdown

---
 .bash_profile                               |    3 +
 package-lock.json                           | 7079 ++++++++++++++-------------
 src/client/util/ProseMirrorEditorView.tsx   |   74 +
 src/client/util/RichTextMenu.tsx            |  337 ++
 src/client/views/DocumentDecorations.tsx    |    6 +
 src/client/views/MainView.tsx               |    2 +
 src/client/views/nodes/FormattedTextBox.tsx |    9 +-
 src/client/views/pdf/PDFMenu.tsx            |    2 +-
 8 files changed, 3979 insertions(+), 3533 deletions(-)
 create mode 100644 .bash_profile
 create mode 100644 src/client/util/ProseMirrorEditorView.tsx
 create mode 100644 src/client/util/RichTextMenu.tsx

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

diff --git a/.bash_profile b/.bash_profile
new file mode 100644
index 000000000..d54c83f48
--- /dev/null
+++ b/.bash_profile
@@ -0,0 +1,3 @@
+export PATH="/usr/local/bin:$PATH"
+export PATH="$PATH:/usr/local/mongodb/bin"
+
diff --git a/package-lock.json b/package-lock.json
index 2590d607b..677bd5de1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,7 @@
             "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
             "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==",
             "requires": {
-                "@babel/highlight": "7.5.0"
+                "@babel/highlight": "^7.0.0"
             }
         },
         "@babel/helper-module-imports": {
@@ -17,7 +17,7 @@
             "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.7.4.tgz",
             "integrity": "sha512-dGcrX6K9l8258WFjyDLJwuVKxR4XZfU0/vTUgOQYWEnRD8mgr+p4d6fCUMq/ys0h4CCt/S5JhbvtyErjWouAUQ==",
             "requires": {
-                "@babel/types": "7.7.4"
+                "@babel/types": "^7.7.4"
             }
         },
         "@babel/highlight": {
@@ -25,9 +25,9 @@
             "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz",
             "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==",
             "requires": {
-                "chalk": "2.4.2",
-                "esutils": "2.0.2",
-                "js-tokens": "4.0.0"
+                "chalk": "^2.0.0",
+                "esutils": "^2.0.2",
+                "js-tokens": "^4.0.0"
             },
             "dependencies": {
                 "ansi-styles": {
@@ -35,7 +35,7 @@
                     "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
                     "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
                     "requires": {
-                        "color-convert": "1.9.3"
+                        "color-convert": "^1.9.0"
                     }
                 },
                 "chalk": {
@@ -43,9 +43,9 @@
                     "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
                     "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
                     "requires": {
-                        "ansi-styles": "3.2.1",
-                        "escape-string-regexp": "1.0.5",
-                        "supports-color": "5.5.0"
+                        "ansi-styles": "^3.2.1",
+                        "escape-string-regexp": "^1.0.5",
+                        "supports-color": "^5.3.0"
                     }
                 },
                 "supports-color": {
@@ -53,7 +53,7 @@
                     "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
                     "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
                     "requires": {
-                        "has-flag": "3.0.0"
+                        "has-flag": "^3.0.0"
                     }
                 }
             }
@@ -63,7 +63,7 @@
             "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.5.5.tgz",
             "integrity": "sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==",
             "requires": {
-                "regenerator-runtime": "0.13.3"
+                "regenerator-runtime": "^0.13.2"
             }
         },
         "@babel/types": {
@@ -71,9 +71,9 @@
             "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz",
             "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==",
             "requires": {
-                "esutils": "2.0.2",
-                "lodash": "4.17.15",
-                "to-fast-properties": "2.0.0"
+                "esutils": "^2.0.2",
+                "lodash": "^4.17.13",
+                "to-fast-properties": "^2.0.0"
             }
         },
         "@emotion/cache": {
@@ -106,7 +106,7 @@
                 "@emotion/memoize": "0.7.3",
                 "@emotion/unitless": "0.7.4",
                 "@emotion/utils": "0.11.2",
-                "csstype": "2.6.6"
+                "csstype": "^2.5.7"
             }
         },
         "@emotion/sheet": {
@@ -144,7 +144,7 @@
             "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free-solid/-/fontawesome-free-solid-5.0.13.tgz",
             "integrity": "sha512-b+krVnqkdDt52Yfev0x0ZZgtxBQsLw00Zfa3uaVWIDzpNZVtrEXuxldUSUaN/ihgGhSNi8VpvDAdNPVgCKOSxw==",
             "requires": {
-                "@fortawesome/fontawesome-common-types": "0.1.7"
+                "@fortawesome/fontawesome-common-types": "^0.1.7"
             }
         },
         "@fortawesome/fontawesome-svg-core": {
@@ -152,7 +152,7 @@
             "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.19.tgz",
             "integrity": "sha512-D4ICXg9oU08eF9o7Or392gPpjmwwgJu8ecCFusthbID95CLVXOgIyd4mOKD9Nud5Ckz+Ty59pqkNtThDKR0erA==",
             "requires": {
-                "@fortawesome/fontawesome-common-types": "0.2.19"
+                "@fortawesome/fontawesome-common-types": "^0.2.19"
             },
             "dependencies": {
                 "@fortawesome/fontawesome-common-types": {
@@ -167,7 +167,7 @@
             "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.9.0.tgz",
             "integrity": "sha512-sOz1wFyslaHUak8tY6IEhSAV1mAWbCLssBR8yFQV6f065k8nUCkjyrcxW4RVl9+wiLXmeG1CJUABUJV9DiW+7Q==",
             "requires": {
-                "@fortawesome/fontawesome-common-types": "0.2.19"
+                "@fortawesome/fontawesome-common-types": "^0.2.19"
             },
             "dependencies": {
                 "@fortawesome/fontawesome-common-types": {
@@ -182,7 +182,7 @@
             "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.9.0.tgz",
             "integrity": "sha512-6ZO0jLhk/Yrso0u5pXeYYSfZiHCNoCF7SgtqStdlEX8WtWD4IOfAB1N+MlSnMo12P5KR4cmucX/K0NCOPrhJwg==",
             "requires": {
-                "@fortawesome/fontawesome-common-types": "0.2.19"
+                "@fortawesome/fontawesome-common-types": "^0.2.19"
             },
             "dependencies": {
                 "@fortawesome/fontawesome-common-types": {
@@ -197,7 +197,7 @@
             "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.9.0.tgz",
             "integrity": "sha512-U8YXPfWcSozsCW0psCtlRGKjjRs5+Am5JJwLOUmVHFZbIEWzaz4YbP84EoPwUsVmSAKrisu3QeNcVOtmGml0Xw==",
             "requires": {
-                "@fortawesome/fontawesome-common-types": "0.2.19"
+                "@fortawesome/fontawesome-common-types": "^0.2.19"
             },
             "dependencies": {
                 "@fortawesome/fontawesome-common-types": {
@@ -212,8 +212,8 @@
             "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.7.2"
+                "humps": "^2.0.1",
+                "prop-types": "^15.5.10"
             }
         },
         "@hig/flyout": {
@@ -221,10 +221,10 @@
             "resolved": "https://registry.npmjs.org/@hig/flyout/-/flyout-1.2.0.tgz",
             "integrity": "sha512-/maJI6COH1UyUX3LCa0muG2VmPiXBtacyz8RAdLLcDIgeTpmyvxpb0FFZI/ib2HUbO2iILBN7qnXiMJ4YNJ/bQ==",
             "requires": {
-                "@hig/utils": "0.4.0",
-                "emotion": "10.0.23",
-                "prop-types": "15.7.2",
-                "react-transition-group": "2.9.0"
+                "@hig/utils": "^0.4.0",
+                "emotion": "^10.0.0",
+                "prop-types": "^15.7.1",
+                "react-transition-group": "^2.3.1"
             }
         },
         "@hig/theme-context": {
@@ -232,8 +232,8 @@
             "resolved": "https://registry.npmjs.org/@hig/theme-context/-/theme-context-2.1.3.tgz",
             "integrity": "sha512-c0Ju+Z8C532ZZtjwOLzN+XeO+pL3kqUawu6ZG3J084MH5RM9W8JCKyMf4D9Qr38jFWoiX6u8yiSxxjV/mz9Sqw==",
             "requires": {
-                "create-react-context": "0.2.3",
-                "prop-types": "15.7.2"
+                "create-react-context": "^0.2.3",
+                "prop-types": "^15.6.1"
             }
         },
         "@hig/theme-data": {
@@ -241,7 +241,7 @@
             "resolved": "https://registry.npmjs.org/@hig/theme-data/-/theme-data-2.8.0.tgz",
             "integrity": "sha512-wH82aJXlFTAE0HZrjCsRfVA8yDHjAve9Sr9lADQcQ4UQTjDHJVGN5Ed7FcPEWqV6kriCSK7JYuRhi52bbDOflw==",
             "requires": {
-                "tinycolor2": "1.4.1"
+                "tinycolor2": "^1.4.1"
             }
         },
         "@hig/utils": {
@@ -249,8 +249,8 @@
             "resolved": "https://registry.npmjs.org/@hig/utils/-/utils-0.4.0.tgz",
             "integrity": "sha512-EQnMGZKdPh9UJaBUKLKXp92sSoCo+PTpgrGNd8q+71uRFdD0udMu/+yeVekTEtNOJcCk1gnKfyg1rRvIbTcpRw==",
             "requires": {
-                "emotion": "10.0.23",
-                "lodash.memoize": "4.1.2"
+                "emotion": "^10.0.0",
+                "lodash.memoize": "^4.1.2"
             }
         },
         "@icons/material": {
@@ -268,12 +268,12 @@
             "resolved": "https://registry.npmjs.org/@react-bootstrap/react-popper/-/react-popper-1.2.1.tgz",
             "integrity": "sha512-4l3q7LcZEhrSkI4d3Ie3g4CdrXqqTexXX4PFT45CB0z5z2JUbaxgRwKNq7r5j2bLdVpZm+uvUGqxJw8d9vgbJQ==",
             "requires": {
-                "babel-runtime": "6.26.0",
-                "create-react-context": "0.2.3",
-                "popper.js": "1.15.0",
-                "prop-types": "15.7.2",
-                "typed-styles": "0.0.5",
-                "warning": "3.0.0"
+                "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"
             }
         },
         "@restart/context": {
@@ -291,8 +291,8 @@
             "resolved": "https://registry.npmjs.org/@trendmicro/react-buttons/-/react-buttons-1.3.1.tgz",
             "integrity": "sha512-9zvt/fdkqCb9kxUdZnvTZKmbmykM2wDQ3VEJFtztGcKAkm4Wkq4oZOQLJXKfUQ1vX3w+YDJob18LkNOzaHI1UQ==",
             "requires": {
-                "classnames": "2.2.6",
-                "prop-types": "15.7.2"
+                "classnames": "^2.2.5",
+                "prop-types": "^15.5.8"
             }
         },
         "@trendmicro/react-dropdown": {
@@ -300,13 +300,13 @@
             "resolved": "https://registry.npmjs.org/@trendmicro/react-dropdown/-/react-dropdown-1.3.0.tgz",
             "integrity": "sha512-KwL0ksEZPay7qNsiGcPQ3aGmyfJCcUuIjiD9HZs6L66ScwSRoFkDlAjMTlRVLFcYVNhpuyUH4pPiFlKQQzDHGQ==",
             "requires": {
-                "@trendmicro/react-buttons": "1.3.1",
-                "chained-function": "0.5.0",
-                "classnames": "2.2.6",
-                "dom-helpers": "3.4.0",
-                "prop-types": "15.7.2",
-                "uncontrollable": "5.1.0",
-                "warning": "3.0.0"
+                "@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"
             }
         },
         "@types/adm-zip": {
@@ -314,7 +314,7 @@
             "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.4.32.tgz",
             "integrity": "sha512-hv1O7ySn+XvP5OeDQcJFWwVb2v+GFGO1A9aMTQ5B/bzxb7WW21O8iRhVdsKKr8QwuiagzGmPP+gsUAYZ6bRddQ==",
             "requires": {
-                "@types/node": "10.14.13"
+                "@types/node": "*"
             }
         },
         "@types/animejs": {
@@ -332,7 +332,7 @@
             "resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-3.0.0.tgz",
             "integrity": "sha512-orghAMOF+//wSg4ru2znk6jt0eIPvKTtMVLH7XcYcjbcRyAXRClDlh27QVdqnAvVM37yu9xDP6Nh7egRhNr8tQ==",
             "requires": {
-                "@types/glob": "7.1.1"
+                "@types/glob": "*"
             }
         },
         "@types/async": {
@@ -350,7 +350,7 @@
             "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.5.tgz",
             "integrity": "sha512-xH2e58elpj1X4ynnKp9qSnWlsRTIs6n3tgLGNfwAGHwePw0mulHQllV34n0T25uYSu1k0hRKkWXF890B1yS47w==",
             "requires": {
-                "@types/babel-types": "7.0.7"
+                "@types/babel-types": "*"
             }
         },
         "@types/bcrypt-nodejs": {
@@ -368,8 +368,8 @@
             "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz",
             "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==",
             "requires": {
-                "@types/connect": "3.4.32",
-                "@types/node": "10.14.13"
+                "@types/connect": "*",
+                "@types/node": "*"
             }
         },
         "@types/bson": {
@@ -377,7 +377,7 @@
             "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.0.tgz",
             "integrity": "sha512-pq/rqJwJWkbS10crsG5bgnrisL8pML79KlMKQMoQwLUjlPAkrUHMvHJ3oGwE7WHR61Lv/nadMwXVAD2b+fpD8Q==",
             "requires": {
-                "@types/node": "10.14.13"
+                "@types/node": "*"
             }
         },
         "@types/caseless": {
@@ -401,7 +401,7 @@
             "resolved": "https://registry.npmjs.org/@types/color/-/color-3.0.0.tgz",
             "integrity": "sha512-5qqtNia+m2I0/85+pd2YzAXaTyKO8j+svirO5aN+XaQJ5+eZ8nx0jPtEWZLxCi50xwYsX10xUHetFzfb1WEs4Q==",
             "requires": {
-                "@types/color-convert": "1.9.0"
+                "@types/color-convert": "*"
             }
         },
         "@types/color-convert": {
@@ -409,7 +409,7 @@
             "resolved": "https://registry.npmjs.org/@types/color-convert/-/color-convert-1.9.0.tgz",
             "integrity": "sha512-OKGEfULrvSL2VRbkl/gnjjgbbF7ycIlpSsX7Nkab4MOWi5XxmgBYvuiQ7lcCFY5cPDz7MUNaKgxte2VRmtr4Fg==",
             "requires": {
-                "@types/color-name": "1.1.1"
+                "@types/color-name": "*"
             }
         },
         "@types/color-name": {
@@ -422,7 +422,7 @@
             "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz",
             "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==",
             "requires": {
-                "@types/node": "10.14.13"
+                "@types/node": "*"
             }
         },
         "@types/connect-flash": {
@@ -430,7 +430,7 @@
             "resolved": "https://registry.npmjs.org/@types/connect-flash/-/connect-flash-0.0.34.tgz",
             "integrity": "sha512-QC93TwnTZ0sk//bfT81o7U4GOedbOZAcgvqi0v1vJqCESC8tqIVnhzB1CHiAUBUWFjoxG5JQF0TYaNa6DMb6Ig==",
             "requires": {
-                "@types/express": "4.17.0"
+                "@types/express": "*"
             }
         },
         "@types/cookie-parser": {
@@ -438,7 +438,7 @@
             "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.1.tgz",
             "integrity": "sha512-iJY6B3ZGufLiDf2OCAgiAAQuj1sMKC/wz/7XCEjZ+/MDuultfFJuSwrBKcLSmJ5iYApLzCCYBYJZs0Ws8GPmwA==",
             "requires": {
-                "@types/express": "4.17.0"
+                "@types/express": "*"
             }
         },
         "@types/cookie-session": {
@@ -446,8 +446,8 @@
             "resolved": "https://registry.npmjs.org/@types/cookie-session/-/cookie-session-2.0.37.tgz",
             "integrity": "sha512-h8uZLDGyfAgER6kHbHlYWm1g/P/7zCBMOW6yT5/fQydVJxByJD4tohSvHBzJrGoLVmQJefQdfwuNkKb23cq29Q==",
             "requires": {
-                "@types/express": "4.17.0",
-                "@types/keygrip": "1.0.1"
+                "@types/express": "*",
+                "@types/keygrip": "*"
             }
         },
         "@types/d3-format": {
@@ -460,7 +460,7 @@
             "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.1.tgz",
             "integrity": "sha512-ftQl3DtBvqHl9L16tpqqzA4YzCSXZfi7g8cQceTz5rOlYtk/IZbFjAv3mLOQlNIgOaylCQWQoBdDQHPgEBJPHg==",
             "requires": {
-                "@types/node": "10.14.13"
+                "@types/node": "*"
             }
         },
         "@types/events": {
@@ -473,7 +473,7 @@
             "resolved": "https://registry.npmjs.org/@types/exif/-/exif-0.6.0.tgz",
             "integrity": "sha512-TyXIoevHn10FjPnCbNfpFlgb44c5KPsCbdWaNf59T76fKOl6YWfBQTmlt84kI7GtY4VuG9aW0qlEEmMuNDldoQ==",
             "requires": {
-                "@types/node": "10.14.13"
+                "@types/node": "*"
             }
         },
         "@types/express": {
@@ -481,9 +481,9 @@
             "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.0.tgz",
             "integrity": "sha512-CjaMu57cjgjuZbh9DpkloeGxV45CnMGlVd+XpG7Gm9QgVrd7KFq+X4HY0vM+2v0bczS48Wg7bvnMY5TN+Xmcfw==",
             "requires": {
-                "@types/body-parser": "1.17.0",
-                "@types/express-serve-static-core": "4.16.7",
-                "@types/serve-static": "1.13.2"
+                "@types/body-parser": "*",
+                "@types/express-serve-static-core": "*",
+                "@types/serve-static": "*"
             }
         },
         "@types/express-flash": {
@@ -491,8 +491,8 @@
             "resolved": "https://registry.npmjs.org/@types/express-flash/-/express-flash-0.0.0.tgz",
             "integrity": "sha512-zs1xXRIZOjghUBriJPSnhPmfDpqf/EQxT21ggi/9XZ9/RHYrUi+5vK2jnQrP2pD1abbuZvm7owLICiNCLBQzEQ==",
             "requires": {
-                "@types/connect-flash": "0.0.34",
-                "@types/express": "4.17.0"
+                "@types/connect-flash": "*",
+                "@types/express": "*"
             }
         },
         "@types/express-serve-static-core": {
@@ -500,8 +500,8 @@
             "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.7.tgz",
             "integrity": "sha512-847KvL8Q1y3TtFLRTXcVakErLJQgdpFSaq+k043xefz9raEf0C7HalpSY7OW5PyjCnY8P7bPW5t/Co9qqp+USg==",
             "requires": {
-                "@types/node": "10.14.13",
-                "@types/range-parser": "1.2.3"
+                "@types/node": "*",
+                "@types/range-parser": "*"
             }
         },
         "@types/express-session": {
@@ -509,8 +509,8 @@
             "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.15.13.tgz",
             "integrity": "sha512-BLRzO/ZfjTTLSRakUJxB0p5I5NmBHuyHkXDyh8sezdCMYxpqXrvMljKwle81I9AeCAzdq6nfz6qafmYLQ/rU9A==",
             "requires": {
-                "@types/express": "4.17.0",
-                "@types/node": "10.14.13"
+                "@types/express": "*",
+                "@types/node": "*"
             }
         },
         "@types/express-validator": {
@@ -518,7 +518,7 @@
             "resolved": "https://registry.npmjs.org/@types/express-validator/-/express-validator-3.0.0.tgz",
             "integrity": "sha512-LusnB0YhTXpBT25PXyGPQlK7leE1e41Vezq1hHEUwjfkopM1Pkv2X2Ppxqh9c+w/HZ6Udzki8AJotKNjDTGdkQ==",
             "requires": {
-                "express-validator": "5.3.1"
+                "express-validator": "*"
             }
         },
         "@types/formidable": {
@@ -526,8 +526,8 @@
             "resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-1.0.31.tgz",
             "integrity": "sha512-dIhM5t8lRP0oWe2HF8MuPvdd1TpPTjhDMAqemcq6oIZQCBQTovhBAdTQ5L5veJB4pdQChadmHuxtB0YzqvfU3Q==",
             "requires": {
-                "@types/events": "3.0.0",
-                "@types/node": "10.14.13"
+                "@types/events": "*",
+                "@types/node": "*"
             }
         },
         "@types/gapi": {
@@ -540,9 +540,9 @@
             "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
             "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==",
             "requires": {
-                "@types/events": "3.0.0",
-                "@types/minimatch": "3.0.3",
-                "@types/node": "10.14.13"
+                "@types/events": "*",
+                "@types/minimatch": "*",
+                "@types/node": "*"
             }
         },
         "@types/jquery": {
@@ -550,7 +550,7 @@
             "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.30.tgz",
             "integrity": "sha512-chB+QbLulamShZAFcTJtl8opZwHFBpDOP6nRLrPGkhC6N1aKWrDXg2Nc71tEg6ny6E8SQpRwbWSi9GdstH5VJA==",
             "requires": {
-                "@types/sizzle": "2.3.2"
+                "@types/sizzle": "*"
             }
         },
         "@types/jquery-awesome-cursor": {
@@ -558,7 +558,7 @@
             "resolved": "https://registry.npmjs.org/@types/jquery-awesome-cursor/-/jquery-awesome-cursor-0.3.0.tgz",
             "integrity": "sha512-tNou39eBTgyQtQGzcynUblExZdZiDqg5xuorANsoIfwBRBZZpHOP8wT/iDSR/qSq2rsu1KuQEfoC8z2L9YSp8A==",
             "requires": {
-                "@types/jquery": "3.3.30"
+                "@types/jquery": "*"
             }
         },
         "@types/jsonwebtoken": {
@@ -566,7 +566,7 @@
             "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.3.2.tgz",
             "integrity": "sha512-Mkjljd9DTpkPlrmGfTJvcP4aBU7yO2QmW7wNVhV4/6AEUxYoacqU7FJU/N0yFEHTsIrE4da3rUrjrR5ejicFmA==",
             "requires": {
-                "@types/node": "10.14.13"
+                "@types/node": "*"
             }
         },
         "@types/keygrip": {
@@ -585,7 +585,7 @@
             "integrity": "sha512-j5AcZo7dbMxHoOimcHEIh0JZe5e1b8q8AqGSpZJrYc7xOgCIP79cIjTdx5jSDLtySnQDwkDTqwlC7Xw7uXw7qg==",
             "dev": true,
             "requires": {
-                "@types/node": "10.14.13"
+                "@types/node": "*"
             }
         },
         "@types/mime": {
@@ -603,7 +603,7 @@
             "resolved": "https://registry.npmjs.org/@types/mobile-detect/-/mobile-detect-1.3.4.tgz",
             "integrity": "sha512-MGBTvT5c7aH8eX6szFYP3dWPryNLt5iGlo31XNaJtt8o6jsg6tjn99eEMq9l8T6cPZymsr+J4Jth8+/G/04ZDw==",
             "requires": {
-                "mobile-detect": "1.4.3"
+                "mobile-detect": "*"
             }
         },
         "@types/mocha": {
@@ -617,8 +617,8 @@
             "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.1.29.tgz",
             "integrity": "sha512-X74BBsFQruQXVJif2oJ08uceUfAVSkb2gl6Zm07fgqKQHnTdxIW3vknHNpQahogezX42EPQv9A+dYG0+CFY8aA==",
             "requires": {
-                "@types/bson": "4.0.0",
-                "@types/node": "10.14.13"
+                "@types/bson": "*",
+                "@types/node": "*"
             }
         },
         "@types/mongoose": {
@@ -626,8 +626,8 @@
             "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.5.9.tgz",
             "integrity": "sha512-KVM8yWVGPc2XD8iov+VzMq/3vyzJ3kqQuiZOJOe3VTVW+U7R4bk5lDfRFvqnnPpQ/pvMPSn6xVVnuYaMUKhZSg==",
             "requires": {
-                "@types/mongodb": "3.1.29",
-                "@types/node": "10.14.13"
+                "@types/mongodb": "*",
+                "@types/node": "*"
             }
         },
         "@types/node": {
@@ -640,7 +640,7 @@
             "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-4.6.8.tgz",
             "integrity": "sha512-IX1P3bxDP1VIdZf6/kIWYNmSejkYm9MOyMEtoDFi4DVzKjJ3kY4GhOcOAKs6lZRjqVVmF9UjPOZXuQczlpZThw==",
             "requires": {
-                "@types/node": "10.14.13"
+                "@types/node": "*"
             }
         },
         "@types/oauth": {
@@ -648,7 +648,7 @@
             "resolved": "https://registry.npmjs.org/@types/oauth/-/oauth-0.9.1.tgz",
             "integrity": "sha512-a1iY62/a3yhZ7qH7cNUsxoI3U/0Fe9+RnuFrpTKr+0WVOzbKlSLojShCKe20aOD1Sppv+i8Zlq0pLDuTJnwS4A==",
             "requires": {
-                "@types/node": "10.14.13"
+                "@types/node": "*"
             }
         },
         "@types/orderedmap": {
@@ -666,7 +666,7 @@
             "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.2.tgz",
             "integrity": "sha512-Pf39AYKf8q+YoONym3150cEwfUD66dtwHJWvbeOzKxnA0GZZ/vAXhNWv9vMhKyRQBQZiQyWQnhYBEBlKW6G8wg==",
             "requires": {
-                "@types/express": "4.17.0"
+                "@types/express": "*"
             }
         },
         "@types/passport-google-oauth20": {
@@ -674,9 +674,9 @@
             "resolved": "https://registry.npmjs.org/@types/passport-google-oauth20/-/passport-google-oauth20-2.0.2.tgz",
             "integrity": "sha512-5Pek3WNGb/Qb466DJMY26VeuT/WSExJYYOSVlk0hWXZRH4hAjTKxVq2ljXv2TLkTlDEgwi8KOdPpiuT67qjWJQ==",
             "requires": {
-                "@types/express": "4.17.0",
-                "@types/passport": "1.0.2",
-                "@types/passport-oauth2": "1.4.8"
+                "@types/express": "*",
+                "@types/passport": "*",
+                "@types/passport-oauth2": "*"
             }
         },
         "@types/passport-local": {
@@ -684,9 +684,9 @@
             "resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.33.tgz",
             "integrity": "sha512-+rn6ZIxje0jZ2+DAiWFI8vGG7ZFKB0hXx2cUdMmudSWsigSq6ES7Emso46r4HJk0qCgrZVfI8sJiM7HIYf4SbA==",
             "requires": {
-                "@types/express": "4.17.0",
-                "@types/passport": "1.0.2",
-                "@types/passport-strategy": "0.2.35"
+                "@types/express": "*",
+                "@types/passport": "*",
+                "@types/passport-strategy": "*"
             }
         },
         "@types/passport-oauth2": {
@@ -694,9 +694,9 @@
             "resolved": "https://registry.npmjs.org/@types/passport-oauth2/-/passport-oauth2-1.4.8.tgz",
             "integrity": "sha512-tlX16wyFE5YJR2pHpZ308dgB1MV9/Ra2wfQh71eWk+/umPoD1Rca2D4N5M27W7nZm1wqUNGTk1I864nHvEgiFA==",
             "requires": {
-                "@types/express": "4.17.0",
-                "@types/oauth": "0.9.1",
-                "@types/passport": "1.0.2"
+                "@types/express": "*",
+                "@types/oauth": "*",
+                "@types/passport": "*"
             }
         },
         "@types/passport-strategy": {
@@ -704,8 +704,8 @@
             "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz",
             "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==",
             "requires": {
-                "@types/express": "4.17.0",
-                "@types/passport": "1.0.2"
+                "@types/express": "*",
+                "@types/passport": "*"
             }
         },
         "@types/pdfjs-dist": {
@@ -723,9 +723,9 @@
             "resolved": "https://registry.npmjs.org/@types/prosemirror-commands/-/prosemirror-commands-1.0.1.tgz",
             "integrity": "sha512-GeE12m8VT9N1JrzoY//946IX8ZyQOLNmvryJ+BNQs/HvhmXW9EWOcWUE6OBRtxK7Y8SrzSOwx4XmqSgVmK3tGQ==",
             "requires": {
-                "@types/prosemirror-model": "1.7.2",
-                "@types/prosemirror-state": "1.2.3",
-                "@types/prosemirror-view": "1.9.0"
+                "@types/prosemirror-model": "*",
+                "@types/prosemirror-state": "*",
+                "@types/prosemirror-view": "*"
             }
         },
         "@types/prosemirror-history": {
@@ -733,8 +733,8 @@
             "resolved": "https://registry.npmjs.org/@types/prosemirror-history/-/prosemirror-history-1.0.1.tgz",
             "integrity": "sha512-BYyPJlWDo3VEnWS5X2DCHXrrAKEjdbCe1DUjGL6R/8hmwMFe3iMJGYdBkOXU1FfkTpw7Z+PlwY/pMyeelVydmg==",
             "requires": {
-                "@types/prosemirror-model": "1.7.2",
-                "@types/prosemirror-state": "1.2.3"
+                "@types/prosemirror-model": "*",
+                "@types/prosemirror-state": "*"
             }
         },
         "@types/prosemirror-inputrules": {
@@ -742,8 +742,8 @@
             "resolved": "https://registry.npmjs.org/@types/prosemirror-inputrules/-/prosemirror-inputrules-1.0.2.tgz",
             "integrity": "sha512-bKFneQUPnkZmzCJ1uoitpKH6PFW0hc4q55NsC7mFUCvX0eZl0GRKxyfV47jkJbsbyUQoO/QFv0WwLDz2bo15sA==",
             "requires": {
-                "@types/prosemirror-model": "1.7.2",
-                "@types/prosemirror-state": "1.2.3"
+                "@types/prosemirror-model": "*",
+                "@types/prosemirror-state": "*"
             }
         },
         "@types/prosemirror-keymap": {
@@ -751,8 +751,8 @@
             "resolved": "https://registry.npmjs.org/@types/prosemirror-keymap/-/prosemirror-keymap-1.0.1.tgz",
             "integrity": "sha512-8IjM8ySmoZps9Tn+aKfB4ZR6zoNOjeQfAc9YLQujYXHJB6tdGWV0cbTuoT4QmZOR1iecN1EJ6E9RiRUBk796kQ==",
             "requires": {
-                "@types/prosemirror-state": "1.2.3",
-                "@types/prosemirror-view": "1.9.0"
+                "@types/prosemirror-state": "*",
+                "@types/prosemirror-view": "*"
             }
         },
         "@types/prosemirror-menu": {
@@ -760,9 +760,9 @@
             "resolved": "https://registry.npmjs.org/@types/prosemirror-menu/-/prosemirror-menu-1.0.1.tgz",
             "integrity": "sha512-wVGc6G7uYRvjIuVwV0zKSLwntFH1wanFwM1fDkq2YcUrLhuj4zZ1i7IPe+yqSoPm7JfmjiDEgHXTpafmwLKJrA==",
             "requires": {
-                "@types/prosemirror-model": "1.7.2",
-                "@types/prosemirror-state": "1.2.3",
-                "@types/prosemirror-view": "1.9.0"
+                "@types/prosemirror-model": "*",
+                "@types/prosemirror-state": "*",
+                "@types/prosemirror-view": "*"
             }
         },
         "@types/prosemirror-model": {
@@ -770,7 +770,7 @@
             "resolved": "https://registry.npmjs.org/@types/prosemirror-model/-/prosemirror-model-1.7.2.tgz",
             "integrity": "sha512-2l+yXvidg3AUHN07mO4Jd8Q84fo6ksFsy7LHUurLYrZ74uTahBp2fzcO49AKZMzww2EulXJ40Kl/OFaQ/7A1fw==",
             "requires": {
-                "@types/orderedmap": "1.0.0"
+                "@types/orderedmap": "*"
             }
         },
         "@types/prosemirror-schema-basic": {
@@ -778,7 +778,7 @@
             "resolved": "https://registry.npmjs.org/@types/prosemirror-schema-basic/-/prosemirror-schema-basic-1.0.1.tgz",
             "integrity": "sha512-IOQAYf1urifbH+Zwbq5XfFOUMNCbEnvIqpuSAE8SUt00nDAoH62T/S8Qhu8LuF++KQbyXb7fdMp352zkPW9Hmw==",
             "requires": {
-                "@types/prosemirror-model": "1.7.2"
+                "@types/prosemirror-model": "*"
             }
         },
         "@types/prosemirror-schema-list": {
@@ -786,9 +786,9 @@
             "resolved": "https://registry.npmjs.org/@types/prosemirror-schema-list/-/prosemirror-schema-list-1.0.1.tgz",
             "integrity": "sha512-+iUYq+pj2wVHSThj0MjNDzkkGwq8aDQ6j0UJK8a0cNCL8v44Ftcx1noGPtBIEUJgitH960VnfBNoTWfQoQZfRA==",
             "requires": {
-                "@types/orderedmap": "1.0.0",
-                "@types/prosemirror-model": "1.7.2",
-                "@types/prosemirror-state": "1.2.3"
+                "@types/orderedmap": "*",
+                "@types/prosemirror-model": "*",
+                "@types/prosemirror-state": "*"
             }
         },
         "@types/prosemirror-state": {
@@ -796,9 +796,9 @@
             "resolved": "https://registry.npmjs.org/@types/prosemirror-state/-/prosemirror-state-1.2.3.tgz",
             "integrity": "sha512-6m433Hubix9bx+JgcLW7zzyiZuzwjq5mBdSMYY4Yi5c5ZpV2RiVmg7Cy6f9Thtts8vuztilw+PczJAgDm1Frfw==",
             "requires": {
-                "@types/prosemirror-model": "1.7.2",
-                "@types/prosemirror-transform": "1.1.0",
-                "@types/prosemirror-view": "1.9.0"
+                "@types/prosemirror-model": "*",
+                "@types/prosemirror-transform": "*",
+                "@types/prosemirror-view": "*"
             }
         },
         "@types/prosemirror-transform": {
@@ -806,7 +806,7 @@
             "resolved": "https://registry.npmjs.org/@types/prosemirror-transform/-/prosemirror-transform-1.1.0.tgz",
             "integrity": "sha512-VsPiEj+88Xvw8f0vXHL65z2qHlnrvnybW9GC7w9I9PORcKheDi7hQBgP8JdDwUPG7ttyUYUaSAec0TV6DsdWKg==",
             "requires": {
-                "@types/prosemirror-model": "1.7.2"
+                "@types/prosemirror-model": "*"
             }
         },
         "@types/prosemirror-view": {
@@ -814,9 +814,9 @@
             "resolved": "https://registry.npmjs.org/@types/prosemirror-view/-/prosemirror-view-1.9.0.tgz",
             "integrity": "sha512-57Z7VoQxGdlazRPnRmNqpl9jD8HoNhWu9hpAIyPAvF/4u2Mte0S/LJQQgb9zNmmzug5cbnEk1dBY6gjwDGDeeQ==",
             "requires": {
-                "@types/prosemirror-model": "1.7.2",
-                "@types/prosemirror-state": "1.2.3",
-                "@types/prosemirror-transform": "1.1.0"
+                "@types/prosemirror-model": "*",
+                "@types/prosemirror-state": "*",
+                "@types/prosemirror-transform": "*"
             }
         },
         "@types/pug": {
@@ -834,7 +834,7 @@
             "resolved": "https://registry.npmjs.org/@types/rc-switch/-/rc-switch-1.8.0.tgz",
             "integrity": "sha512-3zvdN04uILIa788Sdl4VVxkkcge/cSIuHgVDeMJ6NxDBPtPiva3CYd8QEVsD6+u1NcNCLVlpn96cGSW6NJcUrQ==",
             "requires": {
-                "@types/react": "16.8.23"
+                "@types/react": "*"
             }
         },
         "@types/react": {
@@ -842,8 +842,8 @@
             "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.23.tgz",
             "integrity": "sha512-abkEOIeljniUN9qB5onp++g0EY38h7atnDHxwKUFz1r3VH1+yG1OKi2sNPTyObL40goBmfKFpdii2lEzwLX1cA==",
             "requires": {
-                "@types/prop-types": "15.7.1",
-                "csstype": "2.6.6"
+                "@types/prop-types": "*",
+                "csstype": "^2.2.0"
             }
         },
         "@types/react-autosuggest": {
@@ -851,7 +851,7 @@
             "resolved": "https://registry.npmjs.org/@types/react-autosuggest/-/react-autosuggest-9.3.9.tgz",
             "integrity": "sha512-MuDqgOZmbcT4Uzj4boMY3icf90dlvPTFZ1nnXHYaRKmk7ZPG7srI/In1lTxUvZsgoS+WAbz2CIEKAktCXfJmwg==",
             "requires": {
-                "@types/react": "16.8.23"
+                "@types/react": "*"
             }
         },
         "@types/react-color": {
@@ -859,7 +859,7 @@
             "resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-2.17.2.tgz",
             "integrity": "sha512-6aa8L1hhxxjEZz7LY45NRMOKUt72dVrB3MWXESv92YZohH3n2jjUi7j1cMeygdSUxZD8qLU5ITA63tRYYu8M2g==",
             "requires": {
-                "@types/react": "16.8.23"
+                "@types/react": "*"
             }
         },
         "@types/react-dom": {
@@ -868,7 +868,7 @@
             "integrity": "sha512-eIRpEW73DCzPIMaNBDP5pPIpK1KXyZwNgfxiVagb5iGiz6da+9A5hslSX6GAQKdO7SayVCS/Fr2kjqprgAvkfA==",
             "dev": true,
             "requires": {
-                "@types/react": "16.8.23"
+                "@types/react": "*"
             }
         },
         "@types/react-measure": {
@@ -876,7 +876,7 @@
             "resolved": "https://registry.npmjs.org/@types/react-measure/-/react-measure-2.0.5.tgz",
             "integrity": "sha512-T1Bpt8FlWbDhoInUaNrjTOiVRpRJmrRcqhFJxLGBq1VjaqBLHCvUPapgdKMWEIX4Oqsa1SSKjtNkNJGy6WAAZg==",
             "requires": {
-                "@types/react": "16.8.23"
+                "@types/react": "*"
             }
         },
         "@types/react-table": {
@@ -884,7 +884,7 @@
             "resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-6.8.5.tgz",
             "integrity": "sha512-ueCsAadG1IwuuAZM+MWf2SoxbccSWweyQa9YG6xGN5cOVK3SayPOJW4MsUHGpY0V/Q+iZWgohpasliiao29O6g==",
             "requires": {
-                "@types/react": "16.8.23"
+                "@types/react": "*"
             }
         },
         "@types/request": {
@@ -892,10 +892,10 @@
             "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.2.tgz",
             "integrity": "sha512-gP+PSFXAXMrd5PcD7SqHeUjdGshAI8vKQ3+AvpQr3ht9iQea+59LOKvKITcQI+Lg+1EIkDP6AFSBUJPWG8GDyA==",
             "requires": {
-                "@types/caseless": "0.12.2",
-                "@types/node": "10.14.13",
-                "@types/tough-cookie": "2.3.5",
-                "form-data": "2.5.0"
+                "@types/caseless": "*",
+                "@types/node": "*",
+                "@types/tough-cookie": "*",
+                "form-data": "^2.5.0"
             }
         },
         "@types/request-promise": {
@@ -903,8 +903,8 @@
             "resolved": "https://registry.npmjs.org/@types/request-promise/-/request-promise-4.1.44.tgz",
             "integrity": "sha512-RId7eFsUKxfal1LirDDIcOp9u3MM3NXFDBcC3sqIMcmu7f4U6DsCEMD8RbLZtnPrQlN5Jc79di/WPsIEDO4keg==",
             "requires": {
-                "@types/bluebird": "3.5.27",
-                "@types/request": "2.48.2"
+                "@types/bluebird": "*",
+                "@types/request": "*"
             }
         },
         "@types/rimraf": {
@@ -912,8 +912,8 @@
             "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-2.0.3.tgz",
             "integrity": "sha512-dZfyfL/u9l/oi984hEXdmAjX3JHry7TLWw43u1HQ8HhPv6KtfxnrZ3T/bleJ0GEvnk9t5sM7eePkgMqz3yBcGg==",
             "requires": {
-                "@types/glob": "7.1.1",
-                "@types/node": "10.14.13"
+                "@types/glob": "*",
+                "@types/node": "*"
             }
         },
         "@types/serve-static": {
@@ -921,8 +921,8 @@
             "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz",
             "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==",
             "requires": {
-                "@types/express-serve-static-core": "4.16.7",
-                "@types/mime": "2.0.1"
+                "@types/express-serve-static-core": "*",
+                "@types/mime": "*"
             }
         },
         "@types/sharp": {
@@ -930,7 +930,7 @@
             "resolved": "https://registry.npmjs.org/@types/sharp/-/sharp-0.22.2.tgz",
             "integrity": "sha512-oH49f42h3nf/qys0weYsaTGiMv67wPB769ynCoPfBAVwjjxFF3QtIPEe3MfhwyNjQAhQhTEfnmMKvVZfcFkhIw==",
             "requires": {
-                "@types/node": "10.14.13"
+                "@types/node": "*"
             }
         },
         "@types/shelljs": {
@@ -938,8 +938,8 @@
             "resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.8.5.tgz",
             "integrity": "sha512-bZgjwIWu9gHCjirKJoOlLzGi5N0QgZ5t7EXEuoqyWCHTuSddURXo3FOBYDyRPNOWzZ6NbkLvZnVkn483Y/tvcQ==",
             "requires": {
-                "@types/glob": "7.1.1",
-                "@types/node": "10.14.13"
+                "@types/glob": "*",
+                "@types/node": "*"
             }
         },
         "@types/sizzle": {
@@ -952,7 +952,7 @@
             "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.2.tgz",
             "integrity": "sha512-Ind+4qMNfQ62llyB4IMs1D8znMEBsMKohZBPqfBUIXqLQ9bdtWIbNTBWwtdcBWJKnokMZGcmWOOKslatni5vtA==",
             "requires": {
-                "@types/node": "10.14.13"
+                "@types/node": "*"
             }
         },
         "@types/socket.io-client": {
@@ -987,7 +987,7 @@
             "resolved": "https://registry.npmjs.org/@types/typescript/-/typescript-2.0.0.tgz",
             "integrity": "sha1-xDNTnJi64oaCswfqp6D9IRW4PCg=",
             "requires": {
-                "typescript": "3.7.2"
+                "typescript": "*"
             }
         },
         "@types/uglify-js": {
@@ -995,7 +995,7 @@
             "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.0.4.tgz",
             "integrity": "sha512-SudIN9TRJ+v8g5pTG8RRCqfqTMNqgWCKKd3vtynhGzkIIjxaicNAMuY5TRadJ6tzDu3Dotf3ngaMILtmOdmWEQ==",
             "requires": {
-                "source-map": "0.6.1"
+                "source-map": "^0.6.1"
             },
             "dependencies": {
                 "source-map": {
@@ -1010,7 +1010,7 @@
             "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.5.tgz",
             "integrity": "sha512-MNL15wC3EKyw1VLF+RoVO4hJJdk9t/Hlv3rt1OL65Qvuadm4BYo6g9ZJQqoq7X8NBFSsQXgAujWciovh2lpVjA==",
             "requires": {
-                "@types/node": "10.14.13"
+                "@types/node": "*"
             }
         },
         "@types/webpack": {
@@ -1018,11 +1018,11 @@
             "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.32.0.tgz",
             "integrity": "sha512-kpz5wHDyG/WEpzX9gcwFp/w0oSsq0n/rmFdJelk/QBMHmNIOZdiTDInV0Lj8itGKBahQrBgJGJRss/6UHgLuKg==",
             "requires": {
-                "@types/anymatch": "1.3.1",
-                "@types/node": "10.14.13",
-                "@types/tapable": "1.0.4",
-                "@types/uglify-js": "3.0.4",
-                "source-map": "0.6.1"
+                "@types/anymatch": "*",
+                "@types/node": "*",
+                "@types/tapable": "*",
+                "@types/uglify-js": "*",
+                "source-map": "^0.6.0"
             },
             "dependencies": {
                 "source-map": {
@@ -1038,10 +1038,10 @@
             "integrity": "sha512-DzNJJ6ah/6t1n8sfAgQyEbZ/OMmFcF9j9P3aesnm7G6/iBFR/qiGin8K89J0RmaWIBzhTMdDg3I5PmKmSv7N9w==",
             "dev": true,
             "requires": {
-                "@types/connect": "3.4.32",
-                "@types/memory-fs": "0.3.2",
-                "@types/webpack": "4.32.0",
-                "loglevel": "1.6.3"
+                "@types/connect": "*",
+                "@types/memory-fs": "*",
+                "@types/webpack": "*",
+                "loglevel": "^1.6.2"
             }
         },
         "@types/webpack-hot-middleware": {
@@ -1050,8 +1050,8 @@
             "integrity": "sha512-41qSQeyRGZkWSi366jMQVsLo5fdLT8EgmvHNoBwcCtwZcHrQk6An6tD+ZfC0zMdNHzVEFlzQvT2mTte8zDxqNw==",
             "dev": true,
             "requires": {
-                "@types/connect": "3.4.32",
-                "@types/webpack": "4.32.0"
+                "@types/connect": "*",
+                "@types/webpack": "*"
             }
         },
         "@types/youtube": {
@@ -1110,7 +1110,7 @@
             "dev": true,
             "requires": {
                 "@webassemblyjs/ast": "1.8.5",
-                "mamacro": "0.0.3"
+                "mamacro": "^0.0.3"
             }
         },
         "@webassemblyjs/helper-wasm-bytecode": {
@@ -1137,7 +1137,7 @@
             "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==",
             "dev": true,
             "requires": {
-                "@xtuc/ieee754": "1.2.0"
+                "@xtuc/ieee754": "^1.2.0"
             }
         },
         "@webassemblyjs/leb128": {
@@ -1263,7 +1263,7 @@
             "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
             "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
             "requires": {
-                "event-target-shim": "5.0.1"
+                "event-target-shim": "^5.0.0"
             }
         },
         "accepts": {
@@ -1271,7 +1271,7 @@
             "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
             "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
             "requires": {
-                "mime-types": "2.1.24",
+                "mime-types": "~2.1.24",
                 "negotiator": "0.6.2"
             }
         },
@@ -1285,7 +1285,7 @@
             "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz",
             "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=",
             "requires": {
-                "acorn": "4.0.13"
+                "acorn": "^4.0.4"
             },
             "dependencies": {
                 "acorn": {
@@ -1300,7 +1300,7 @@
             "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-4.1.1.tgz",
             "integrity": "sha512-JY+iV6r+cO21KtntVvFkD+iqjtdpRUpGqKWgfkCdZq1R+kbreEl8EcdcJR4SmiIgsIQT33s6QzheQ9a275Q8xw==",
             "requires": {
-                "acorn": "5.7.3"
+                "acorn": "^5.0.3"
             }
         },
         "acorn-walk": {
@@ -1324,7 +1324,7 @@
             "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
             "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
             "requires": {
-                "es6-promisify": "5.0.0"
+                "es6-promisify": "^5.0.0"
             }
         },
         "ajv": {
@@ -1332,10 +1332,10 @@
             "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
             "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
             "requires": {
-                "fast-deep-equal": "2.0.1",
-                "fast-json-stable-stringify": "2.0.0",
-                "json-schema-traverse": "0.4.1",
-                "uri-js": "4.2.2"
+                "fast-deep-equal": "^2.0.1",
+                "fast-json-stable-stringify": "^2.0.0",
+                "json-schema-traverse": "^0.4.1",
+                "uri-js": "^4.2.2"
             }
         },
         "ajv-errors": {
@@ -1353,9 +1353,9 @@
             "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
             "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
             "requires": {
-                "kind-of": "3.2.2",
-                "longest": "1.0.1",
-                "repeat-string": "1.6.1"
+                "kind-of": "^3.0.2",
+                "longest": "^1.0.1",
+                "repeat-string": "^1.5.2"
             },
             "dependencies": {
                 "kind-of": {
@@ -1363,7 +1363,7 @@
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
                     "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
-                        "is-buffer": "1.1.6"
+                        "is-buffer": "^1.1.5"
                     }
                 }
             }
@@ -1383,7 +1383,7 @@
             "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz",
             "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=",
             "requires": {
-                "string-width": "2.1.1"
+                "string-width": "^2.0.0"
             },
             "dependencies": {
                 "ansi-regex": {
@@ -1401,8 +1401,8 @@
                     "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
                     "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
                     "requires": {
-                        "is-fullwidth-code-point": "2.0.0",
-                        "strip-ansi": "4.0.0"
+                        "is-fullwidth-code-point": "^2.0.0",
+                        "strip-ansi": "^4.0.0"
                     }
                 },
                 "strip-ansi": {
@@ -1410,7 +1410,7 @@
                     "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
                     "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
                     "requires": {
-                        "ansi-regex": "3.0.0"
+                        "ansi-regex": "^3.0.0"
                     }
                 }
             }
@@ -1426,7 +1426,7 @@
             "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz",
             "integrity": "sha512-dzW9kHxH011uBsidTXd14JXgzye/YLb2LzeKZ4bsgl/Knwx8AtbSFkkGxagdNOoh0DlqHCmfiEjWKBaqjOanVw==",
             "requires": {
-                "array-back": "3.1.0"
+                "array-back": "^3.0.1"
             },
             "dependencies": {
                 "array-back": {
@@ -1462,8 +1462,8 @@
             "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
             "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
             "requires": {
-                "micromatch": "3.1.10",
-                "normalize-path": "2.1.1"
+                "micromatch": "^3.1.4",
+                "normalize-path": "^2.1.1"
             },
             "dependencies": {
                 "normalize-path": {
@@ -1471,7 +1471,7 @@
                     "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
                     "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
                     "requires": {
-                        "remove-trailing-separator": "1.1.0"
+                        "remove-trailing-separator": "^1.0.1"
                     }
                 }
             }
@@ -1486,13 +1486,13 @@
             "resolved": "https://registry.npmjs.org/archiver/-/archiver-3.0.3.tgz",
             "integrity": "sha512-d0W7NUyXoLklozHHfvWnHoHS3dvQk8eB22pv5tBwcu1jEO5eZY8W+gHytkAaJ0R8fU2TnNThrWYxjvFlKvRxpw==",
             "requires": {
-                "archiver-utils": "2.1.0",
-                "async": "2.6.3",
-                "buffer-crc32": "0.2.13",
-                "glob": "7.1.4",
-                "readable-stream": "3.4.0",
-                "tar-stream": "2.1.0",
-                "zip-stream": "2.1.0"
+                "archiver-utils": "^2.1.0",
+                "async": "^2.6.3",
+                "buffer-crc32": "^0.2.1",
+                "glob": "^7.1.4",
+                "readable-stream": "^3.4.0",
+                "tar-stream": "^2.1.0",
+                "zip-stream": "^2.1.0"
             },
             "dependencies": {
                 "bl": {
@@ -1500,7 +1500,7 @@
                     "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.0.tgz",
                     "integrity": "sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A==",
                     "requires": {
-                        "readable-stream": "3.4.0"
+                        "readable-stream": "^3.0.1"
                     }
                 },
                 "readable-stream": {
@@ -1508,9 +1508,9 @@
                     "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
                     "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
                     "requires": {
-                        "inherits": "2.0.3",
-                        "string_decoder": "1.1.1",
-                        "util-deprecate": "1.0.2"
+                        "inherits": "^2.0.3",
+                        "string_decoder": "^1.1.1",
+                        "util-deprecate": "^1.0.1"
                     }
                 },
                 "tar-stream": {
@@ -1518,11 +1518,11 @@
                     "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.0.tgz",
                     "integrity": "sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw==",
                     "requires": {
-                        "bl": "3.0.0",
-                        "end-of-stream": "1.4.1",
-                        "fs-constants": "1.0.0",
-                        "inherits": "2.0.3",
-                        "readable-stream": "3.4.0"
+                        "bl": "^3.0.0",
+                        "end-of-stream": "^1.4.1",
+                        "fs-constants": "^1.0.0",
+                        "inherits": "^2.0.3",
+                        "readable-stream": "^3.1.1"
                     }
                 }
             }
@@ -1532,16 +1532,16 @@
             "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz",
             "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
             "requires": {
-                "glob": "7.1.4",
-                "graceful-fs": "4.2.0",
-                "lazystream": "1.0.0",
-                "lodash.defaults": "4.2.0",
-                "lodash.difference": "4.5.0",
-                "lodash.flatten": "4.4.0",
-                "lodash.isplainobject": "4.0.6",
-                "lodash.union": "4.6.0",
-                "normalize-path": "3.0.0",
-                "readable-stream": "2.3.6"
+                "glob": "^7.1.4",
+                "graceful-fs": "^4.2.0",
+                "lazystream": "^1.0.0",
+                "lodash.defaults": "^4.2.0",
+                "lodash.difference": "^4.5.0",
+                "lodash.flatten": "^4.4.0",
+                "lodash.isplainobject": "^4.0.6",
+                "lodash.union": "^4.6.0",
+                "normalize-path": "^3.0.0",
+                "readable-stream": "^2.0.0"
             }
         },
         "are-we-there-yet": {
@@ -1549,8 +1549,8 @@
             "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
             "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
             "requires": {
-                "delegates": "1.0.0",
-                "readable-stream": "2.3.6"
+                "delegates": "^1.0.0",
+                "readable-stream": "^2.0.6"
             }
         },
         "argparse": {
@@ -1558,7 +1558,7 @@
             "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
             "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
             "requires": {
-                "sprintf-js": "1.0.3"
+                "sprintf-js": "~1.0.2"
             }
         },
         "arr-diff": {
@@ -1581,7 +1581,7 @@
             "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz",
             "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==",
             "requires": {
-                "typical": "2.6.1"
+                "typical": "^2.6.1"
             }
         },
         "array-batcher": {
@@ -1589,11 +1589,11 @@
             "resolved": "https://registry.npmjs.org/array-batcher/-/array-batcher-1.2.3.tgz",
             "integrity": "sha512-/IOrwn4ZJi7YqTZrs3k+wQN5nKhjtTqL5ZKkzB+sKJlPeJzpMnRc3o8T9yt8/ZJiSldd+PwTHjM+//UsaszOOw==",
             "requires": {
-                "@types/node": "12.12.3",
-                "chai": "4.2.0",
-                "mocha": "6.2.2",
-                "request": "2.88.0",
-                "request-promise": "4.2.4"
+                "@types/node": "^12.7.5",
+                "chai": "^4.2.0",
+                "mocha": "^6.2.0",
+                "request": "^2.88.0",
+                "request-promise": "^4.2.4"
             },
             "dependencies": {
                 "@types/node": {
@@ -1616,7 +1616,7 @@
                     "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
                     "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
                     "requires": {
-                        "color-convert": "1.9.3"
+                        "color-convert": "^1.9.0"
                     }
                 },
                 "assert-plus": {
@@ -1644,9 +1644,9 @@
                     "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
                     "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
                     "requires": {
-                        "string-width": "3.1.0",
-                        "strip-ansi": "5.2.0",
-                        "wrap-ansi": "5.1.0"
+                        "string-width": "^3.1.0",
+                        "strip-ansi": "^5.2.0",
+                        "wrap-ansi": "^5.1.0"
                     }
                 },
                 "debug": {
@@ -1654,7 +1654,7 @@
                     "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
                     "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
                     "requires": {
-                        "ms": "2.1.1"
+                        "ms": "^2.1.1"
                     }
                 },
                 "find-up": {
@@ -1662,7 +1662,7 @@
                     "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
                     "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
                     "requires": {
-                        "locate-path": "3.0.0"
+                        "locate-path": "^3.0.0"
                     }
                 },
                 "form-data": {
@@ -1670,9 +1670,9 @@
                     "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
                     "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
                     "requires": {
-                        "asynckit": "0.4.0",
-                        "combined-stream": "1.0.8",
-                        "mime-types": "2.1.24"
+                        "asynckit": "^0.4.0",
+                        "combined-stream": "^1.0.6",
+                        "mime-types": "^2.1.12"
                     }
                 },
                 "get-caller-file": {
@@ -1685,12 +1685,12 @@
                     "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
                     "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
                     "requires": {
-                        "fs.realpath": "1.0.0",
-                        "inflight": "1.0.6",
-                        "inherits": "2.0.3",
-                        "minimatch": "3.0.4",
-                        "once": "1.4.0",
-                        "path-is-absolute": "1.0.1"
+                        "fs.realpath": "^1.0.0",
+                        "inflight": "^1.0.4",
+                        "inherits": "2",
+                        "minimatch": "^3.0.4",
+                        "once": "^1.3.0",
+                        "path-is-absolute": "^1.0.0"
                     }
                 },
                 "har-validator": {
@@ -1698,8 +1698,8 @@
                     "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
                     "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
                     "requires": {
-                        "ajv": "6.10.2",
-                        "har-schema": "2.0.0"
+                        "ajv": "^6.5.5",
+                        "har-schema": "^2.0.0"
                     }
                 },
                 "he": {
@@ -1712,9 +1712,9 @@
                     "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
                     "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
                     "requires": {
-                        "assert-plus": "1.0.0",
-                        "jsprim": "1.4.1",
-                        "sshpk": "1.16.1"
+                        "assert-plus": "^1.0.0",
+                        "jsprim": "^1.2.2",
+                        "sshpk": "^1.7.0"
                     }
                 },
                 "is-fullwidth-code-point": {
@@ -1727,8 +1727,8 @@
                     "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
                     "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
                     "requires": {
-                        "p-locate": "3.0.0",
-                        "path-exists": "3.0.0"
+                        "p-locate": "^3.0.0",
+                        "path-exists": "^3.0.0"
                     }
                 },
                 "mocha": {
@@ -1776,7 +1776,7 @@
                     "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
                     "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
                     "requires": {
-                        "p-limit": "2.2.0"
+                        "p-limit": "^2.0.0"
                     }
                 },
                 "qs": {
@@ -1789,26 +1789,26 @@
                     "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
                     "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
                     "requires": {
-                        "aws-sign2": "0.7.0",
-                        "aws4": "1.9.0",
-                        "caseless": "0.12.0",
-                        "combined-stream": "1.0.8",
-                        "extend": "3.0.2",
-                        "forever-agent": "0.6.1",
-                        "form-data": "2.3.3",
-                        "har-validator": "5.1.3",
-                        "http-signature": "1.2.0",
-                        "is-typedarray": "1.0.0",
-                        "isstream": "0.1.2",
-                        "json-stringify-safe": "5.0.1",
-                        "mime-types": "2.1.24",
-                        "oauth-sign": "0.9.0",
-                        "performance-now": "2.1.0",
-                        "qs": "6.5.2",
-                        "safe-buffer": "5.1.2",
-                        "tough-cookie": "2.4.3",
-                        "tunnel-agent": "0.6.0",
-                        "uuid": "3.3.2"
+                        "aws-sign2": "~0.7.0",
+                        "aws4": "^1.8.0",
+                        "caseless": "~0.12.0",
+                        "combined-stream": "~1.0.6",
+                        "extend": "~3.0.2",
+                        "forever-agent": "~0.6.1",
+                        "form-data": "~2.3.2",
+                        "har-validator": "~5.1.0",
+                        "http-signature": "~1.2.0",
+                        "is-typedarray": "~1.0.0",
+                        "isstream": "~0.1.2",
+                        "json-stringify-safe": "~5.0.1",
+                        "mime-types": "~2.1.19",
+                        "oauth-sign": "~0.9.0",
+                        "performance-now": "^2.1.0",
+                        "qs": "~6.5.2",
+                        "safe-buffer": "^5.1.2",
+                        "tough-cookie": "~2.4.3",
+                        "tunnel-agent": "^0.6.0",
+                        "uuid": "^3.3.2"
                     }
                 },
                 "require-main-filename": {
@@ -1821,9 +1821,9 @@
                     "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
                     "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
                     "requires": {
-                        "emoji-regex": "7.0.3",
-                        "is-fullwidth-code-point": "2.0.0",
-                        "strip-ansi": "5.2.0"
+                        "emoji-regex": "^7.0.1",
+                        "is-fullwidth-code-point": "^2.0.0",
+                        "strip-ansi": "^5.1.0"
                     }
                 },
                 "strip-ansi": {
@@ -1831,7 +1831,7 @@
                     "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
                     "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
                     "requires": {
-                        "ansi-regex": "4.1.0"
+                        "ansi-regex": "^4.1.0"
                     }
                 },
                 "supports-color": {
@@ -1839,7 +1839,7 @@
                     "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz",
                     "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==",
                     "requires": {
-                        "has-flag": "3.0.0"
+                        "has-flag": "^3.0.0"
                     }
                 },
                 "which-module": {
@@ -1852,9 +1852,9 @@
                     "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
                     "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
                     "requires": {
-                        "ansi-styles": "3.2.1",
-                        "string-width": "3.1.0",
-                        "strip-ansi": "5.2.0"
+                        "ansi-styles": "^3.2.0",
+                        "string-width": "^3.0.0",
+                        "strip-ansi": "^5.0.0"
                     }
                 },
                 "y18n": {
@@ -1867,16 +1867,16 @@
                     "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
                     "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==",
                     "requires": {
-                        "cliui": "5.0.0",
-                        "find-up": "3.0.0",
-                        "get-caller-file": "2.0.5",
-                        "require-directory": "2.1.1",
-                        "require-main-filename": "2.0.0",
-                        "set-blocking": "2.0.0",
-                        "string-width": "3.1.0",
-                        "which-module": "2.0.0",
-                        "y18n": "4.0.0",
-                        "yargs-parser": "13.1.1"
+                        "cliui": "^5.0.0",
+                        "find-up": "^3.0.0",
+                        "get-caller-file": "^2.0.1",
+                        "require-directory": "^2.1.1",
+                        "require-main-filename": "^2.0.0",
+                        "set-blocking": "^2.0.0",
+                        "string-width": "^3.0.0",
+                        "which-module": "^2.0.0",
+                        "y18n": "^4.0.0",
+                        "yargs-parser": "^13.1.1"
                     }
                 },
                 "yargs-parser": {
@@ -1884,8 +1884,8 @@
                     "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
                     "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
                     "requires": {
-                        "camelcase": "5.3.1",
-                        "decamelize": "1.2.0"
+                        "camelcase": "^5.0.0",
+                        "decamelize": "^1.2.0"
                     }
                 }
             }
@@ -1912,7 +1912,7 @@
             "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
             "dev": true,
             "requires": {
-                "array-uniq": "1.0.3"
+                "array-uniq": "^1.0.1"
             }
         },
         "array-uniq": {
@@ -1947,7 +1947,7 @@
             "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
             "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
             "requires": {
-                "safer-buffer": "2.1.2"
+                "safer-buffer": "~2.1.0"
             }
         },
         "asn1.js": {
@@ -1955,9 +1955,9 @@
             "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
             "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
             "requires": {
-                "bn.js": "4.11.8",
-                "inherits": "2.0.3",
-                "minimalistic-assert": "1.0.1"
+                "bn.js": "^4.0.0",
+                "inherits": "^2.0.1",
+                "minimalistic-assert": "^1.0.0"
             }
         },
         "assert": {
@@ -1966,7 +1966,7 @@
             "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==",
             "dev": true,
             "requires": {
-                "object-assign": "4.1.1",
+                "object-assign": "^4.1.1",
                 "util": "0.10.3"
             },
             "dependencies": {
@@ -2012,7 +2012,7 @@
             "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
             "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
             "requires": {
-                "lodash": "4.17.15"
+                "lodash": "^4.17.14"
             }
         },
         "async-each": {
@@ -2046,14 +2046,14 @@
             "integrity": "sha512-slv66OAJB8orL+UUaTI3pKlLorwIvS4ARZzYR9iJJyGsEgOqueMfOMdKySWzZ73vIkEe3fcwFgsKMg4d8zyb1g==",
             "dev": true,
             "requires": {
-                "chalk": "2.4.2",
-                "enhanced-resolve": "4.1.0",
-                "loader-utils": "1.2.3",
-                "lodash": "4.17.15",
-                "micromatch": "3.1.10",
-                "mkdirp": "0.5.1",
-                "source-map-support": "0.5.12",
-                "webpack-log": "1.2.0"
+                "chalk": "^2.4.1",
+                "enhanced-resolve": "^4.0.0",
+                "loader-utils": "^1.1.0",
+                "lodash": "^4.17.5",
+                "micromatch": "^3.1.9",
+                "mkdirp": "^0.5.1",
+                "source-map-support": "^0.5.3",
+                "webpack-log": "^1.2.0"
             },
             "dependencies": {
                 "ansi-styles": {
@@ -2062,7 +2062,7 @@
                     "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
                     "dev": true,
                     "requires": {
-                        "color-convert": "1.9.3"
+                        "color-convert": "^1.9.0"
                     }
                 },
                 "chalk": {
@@ -2071,9 +2071,9 @@
                     "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
                     "dev": true,
                     "requires": {
-                        "ansi-styles": "3.2.1",
-                        "escape-string-regexp": "1.0.5",
-                        "supports-color": "5.5.0"
+                        "ansi-styles": "^3.2.1",
+                        "escape-string-regexp": "^1.0.5",
+                        "supports-color": "^5.3.0"
                     }
                 },
                 "supports-color": {
@@ -2082,7 +2082,7 @@
                     "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
                     "dev": true,
                     "requires": {
-                        "has-flag": "3.0.0"
+                        "has-flag": "^3.0.0"
                     }
                 }
             }
@@ -2103,9 +2103,9 @@
             "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
             "dev": true,
             "requires": {
-                "chalk": "1.1.3",
-                "esutils": "2.0.2",
-                "js-tokens": "3.0.2"
+                "chalk": "^1.1.3",
+                "esutils": "^2.0.2",
+                "js-tokens": "^3.0.2"
             },
             "dependencies": {
                 "js-tokens": {
@@ -2121,16 +2121,16 @@
             "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.0.23.tgz",
             "integrity": "sha512-1JiCyXU0t5S2xCbItejCduLGGcKmF3POT0Ujbexog2MI4IlRcIn/kWjkYwCUZlxpON0O5FC635yPl/3slr7cKQ==",
             "requires": {
-                "@babel/helper-module-imports": "7.7.4",
+                "@babel/helper-module-imports": "^7.0.0",
                 "@emotion/hash": "0.7.3",
                 "@emotion/memoize": "0.7.3",
-                "@emotion/serialize": "0.11.14",
-                "babel-plugin-macros": "2.8.0",
-                "babel-plugin-syntax-jsx": "6.18.0",
-                "convert-source-map": "1.7.0",
-                "escape-string-regexp": "1.0.5",
-                "find-root": "1.1.0",
-                "source-map": "0.5.7"
+                "@emotion/serialize": "^0.11.14",
+                "babel-plugin-macros": "^2.0.0",
+                "babel-plugin-syntax-jsx": "^6.18.0",
+                "convert-source-map": "^1.5.0",
+                "escape-string-regexp": "^1.0.5",
+                "find-root": "^1.1.0",
+                "source-map": "^0.5.7"
             }
         },
         "babel-plugin-macros": {
@@ -2138,9 +2138,9 @@
             "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz",
             "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==",
             "requires": {
-                "@babel/runtime": "7.7.6",
-                "cosmiconfig": "6.0.0",
-                "resolve": "1.13.1"
+                "@babel/runtime": "^7.7.2",
+                "cosmiconfig": "^6.0.0",
+                "resolve": "^1.12.0"
             },
             "dependencies": {
                 "@babel/runtime": {
@@ -2148,7 +2148,7 @@
                     "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.6.tgz",
                     "integrity": "sha512-BWAJxpNVa0QlE5gZdWjSxXtemZyZ9RmrmVozxt3NUXeZhVIJ5ANyqmMc0JDrivBZyxUuQvFxlvH4OWWOogGfUw==",
                     "requires": {
-                        "regenerator-runtime": "0.13.3"
+                        "regenerator-runtime": "^0.13.2"
                     }
                 },
                 "resolve": {
@@ -2156,7 +2156,7 @@
                     "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.13.1.tgz",
                     "integrity": "sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w==",
                     "requires": {
-                        "path-parse": "1.0.6"
+                        "path-parse": "^1.0.6"
                     }
                 }
             }
@@ -2171,8 +2171,8 @@
             "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
             "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
             "requires": {
-                "core-js": "2.6.9",
-                "regenerator-runtime": "0.11.1"
+                "core-js": "^2.4.0",
+                "regenerator-runtime": "^0.11.0"
             },
             "dependencies": {
                 "core-js": {
@@ -2192,10 +2192,10 @@
             "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
             "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
             "requires": {
-                "babel-runtime": "6.26.0",
-                "esutils": "2.0.2",
-                "lodash": "4.17.15",
-                "to-fast-properties": "1.0.3"
+                "babel-runtime": "^6.26.0",
+                "esutils": "^2.0.2",
+                "lodash": "^4.17.4",
+                "to-fast-properties": "^1.0.3"
             },
             "dependencies": {
                 "to-fast-properties": {
@@ -2225,13 +2225,13 @@
             "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
             "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
             "requires": {
-                "cache-base": "1.0.1",
-                "class-utils": "0.3.6",
-                "component-emitter": "1.3.0",
-                "define-property": "1.0.0",
-                "isobject": "3.0.1",
-                "mixin-deep": "1.3.2",
-                "pascalcase": "0.1.1"
+                "cache-base": "^1.0.1",
+                "class-utils": "^0.3.5",
+                "component-emitter": "^1.2.1",
+                "define-property": "^1.0.0",
+                "isobject": "^3.0.1",
+                "mixin-deep": "^1.2.0",
+                "pascalcase": "^0.1.1"
             },
             "dependencies": {
                 "define-property": {
@@ -2239,7 +2239,7 @@
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
                     "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
                     "requires": {
-                        "is-descriptor": "1.0.2"
+                        "is-descriptor": "^1.0.0"
                     }
                 },
                 "is-accessor-descriptor": {
@@ -2247,7 +2247,7 @@
                     "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
                     "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
                     "requires": {
-                        "kind-of": "6.0.2"
+                        "kind-of": "^6.0.0"
                     }
                 },
                 "is-data-descriptor": {
@@ -2255,7 +2255,7 @@
                     "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
                     "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
                     "requires": {
-                        "kind-of": "6.0.2"
+                        "kind-of": "^6.0.0"
                     }
                 },
                 "is-descriptor": {
@@ -2263,9 +2263,9 @@
                     "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
                     "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
                     "requires": {
-                        "is-accessor-descriptor": "1.0.0",
-                        "is-data-descriptor": "1.0.0",
-                        "kind-of": "6.0.2"
+                        "is-accessor-descriptor": "^1.0.0",
+                        "is-data-descriptor": "^1.0.0",
+                        "kind-of": "^6.0.2"
                     }
                 }
             }
@@ -2311,7 +2311,7 @@
             "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
             "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
             "requires": {
-                "tweetnacl": "0.14.5"
+                "tweetnacl": "^0.14.3"
             }
         },
         "better-assert": {
@@ -2342,8 +2342,8 @@
             "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
             "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
             "requires": {
-                "readable-stream": "2.3.6",
-                "safe-buffer": "5.1.2"
+                "readable-stream": "^2.3.5",
+                "safe-buffer": "^5.1.1"
             }
         },
         "blob": {
@@ -2356,7 +2356,7 @@
             "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
             "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
             "requires": {
-                "inherits": "2.0.3"
+                "inherits": "~2.0.0"
             }
         },
         "bluebird": {
@@ -2380,15 +2380,15 @@
             "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
             "requires": {
                 "bytes": "3.1.0",
-                "content-type": "1.0.4",
+                "content-type": "~1.0.4",
                 "debug": "2.6.9",
-                "depd": "1.1.2",
+                "depd": "~1.1.2",
                 "http-errors": "1.7.2",
                 "iconv-lite": "0.4.24",
-                "on-finished": "2.3.0",
+                "on-finished": "~2.3.0",
                 "qs": "6.7.0",
                 "raw-body": "2.4.0",
-                "type-is": "1.6.18"
+                "type-is": "~1.6.17"
             }
         },
         "bonjour": {
@@ -2397,12 +2397,12 @@
             "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=",
             "dev": true,
             "requires": {
-                "array-flatten": "2.1.2",
-                "deep-equal": "1.0.1",
-                "dns-equal": "1.0.0",
-                "dns-txt": "2.0.2",
-                "multicast-dns": "6.2.3",
-                "multicast-dns-service-types": "1.1.0"
+                "array-flatten": "^2.1.0",
+                "deep-equal": "^1.0.1",
+                "dns-equal": "^1.0.0",
+                "dns-txt": "^2.0.2",
+                "multicast-dns": "^6.0.1",
+                "multicast-dns-service-types": "^1.1.0"
             },
             "dependencies": {
                 "array-flatten": {
@@ -2443,13 +2443,13 @@
             "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz",
             "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==",
             "requires": {
-                "ansi-align": "2.0.0",
-                "camelcase": "4.1.0",
-                "chalk": "2.4.2",
-                "cli-boxes": "1.0.0",
-                "string-width": "2.1.1",
-                "term-size": "1.2.0",
-                "widest-line": "2.0.1"
+                "ansi-align": "^2.0.0",
+                "camelcase": "^4.0.0",
+                "chalk": "^2.0.1",
+                "cli-boxes": "^1.0.0",
+                "string-width": "^2.0.0",
+                "term-size": "^1.2.0",
+                "widest-line": "^2.0.0"
             },
             "dependencies": {
                 "ansi-regex": {
@@ -2462,7 +2462,7 @@
                     "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
                     "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
                     "requires": {
-                        "color-convert": "1.9.3"
+                        "color-convert": "^1.9.0"
                     }
                 },
                 "chalk": {
@@ -2470,9 +2470,9 @@
                     "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
                     "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
                     "requires": {
-                        "ansi-styles": "3.2.1",
-                        "escape-string-regexp": "1.0.5",
-                        "supports-color": "5.5.0"
+                        "ansi-styles": "^3.2.1",
+                        "escape-string-regexp": "^1.0.5",
+                        "supports-color": "^5.3.0"
                     }
                 },
                 "is-fullwidth-code-point": {
@@ -2485,8 +2485,8 @@
                     "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
                     "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
                     "requires": {
-                        "is-fullwidth-code-point": "2.0.0",
-                        "strip-ansi": "4.0.0"
+                        "is-fullwidth-code-point": "^2.0.0",
+                        "strip-ansi": "^4.0.0"
                     }
                 },
                 "strip-ansi": {
@@ -2494,7 +2494,7 @@
                     "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
                     "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
                     "requires": {
-                        "ansi-regex": "3.0.0"
+                        "ansi-regex": "^3.0.0"
                     }
                 },
                 "supports-color": {
@@ -2502,7 +2502,7 @@
                     "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
                     "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
                     "requires": {
-                        "has-flag": "3.0.0"
+                        "has-flag": "^3.0.0"
                     }
                 }
             }
@@ -2512,7 +2512,7 @@
             "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
             "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
             "requires": {
-                "balanced-match": "1.0.0",
+                "balanced-match": "^1.0.0",
                 "concat-map": "0.0.1"
             }
         },
@@ -2521,16 +2521,16 @@
             "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
             "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
             "requires": {
-                "arr-flatten": "1.1.0",
-                "array-unique": "0.3.2",
-                "extend-shallow": "2.0.1",
-                "fill-range": "4.0.0",
-                "isobject": "3.0.1",
-                "repeat-element": "1.1.3",
-                "snapdragon": "0.8.2",
-                "snapdragon-node": "2.1.1",
-                "split-string": "3.1.0",
-                "to-regex": "3.0.2"
+                "arr-flatten": "^1.1.0",
+                "array-unique": "^0.3.2",
+                "extend-shallow": "^2.0.1",
+                "fill-range": "^4.0.0",
+                "isobject": "^3.0.1",
+                "repeat-element": "^1.1.2",
+                "snapdragon": "^0.8.1",
+                "snapdragon-node": "^2.0.1",
+                "split-string": "^3.0.2",
+                "to-regex": "^3.0.1"
             },
             "dependencies": {
                 "extend-shallow": {
@@ -2538,7 +2538,7 @@
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
                     "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
                     "requires": {
-                        "is-extendable": "0.1.1"
+                        "is-extendable": "^0.1.0"
                     }
                 }
             }
@@ -2564,12 +2564,12 @@
             "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
             "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
             "requires": {
-                "buffer-xor": "1.0.3",
-                "cipher-base": "1.0.4",
-                "create-hash": "1.2.0",
-                "evp_bytestokey": "1.0.3",
-                "inherits": "2.0.3",
-                "safe-buffer": "5.1.2"
+                "buffer-xor": "^1.0.3",
+                "cipher-base": "^1.0.0",
+                "create-hash": "^1.1.0",
+                "evp_bytestokey": "^1.0.3",
+                "inherits": "^2.0.1",
+                "safe-buffer": "^5.0.1"
             }
         },
         "browserify-cipher": {
@@ -2577,9 +2577,9 @@
             "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
             "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
             "requires": {
-                "browserify-aes": "1.2.0",
-                "browserify-des": "1.0.2",
-                "evp_bytestokey": "1.0.3"
+                "browserify-aes": "^1.0.4",
+                "browserify-des": "^1.0.0",
+                "evp_bytestokey": "^1.0.0"
             }
         },
         "browserify-des": {
@@ -2587,10 +2587,10 @@
             "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
             "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
             "requires": {
-                "cipher-base": "1.0.4",
-                "des.js": "1.0.0",
-                "inherits": "2.0.3",
-                "safe-buffer": "5.1.2"
+                "cipher-base": "^1.0.1",
+                "des.js": "^1.0.0",
+                "inherits": "^2.0.1",
+                "safe-buffer": "^5.1.2"
             }
         },
         "browserify-rsa": {
@@ -2598,8 +2598,8 @@
             "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
             "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
             "requires": {
-                "bn.js": "4.11.8",
-                "randombytes": "2.1.0"
+                "bn.js": "^4.1.0",
+                "randombytes": "^2.0.1"
             }
         },
         "browserify-sign": {
@@ -2607,13 +2607,13 @@
             "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
             "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
             "requires": {
-                "bn.js": "4.11.8",
-                "browserify-rsa": "4.0.1",
-                "create-hash": "1.2.0",
-                "create-hmac": "1.1.7",
-                "elliptic": "6.5.0",
-                "inherits": "2.0.3",
-                "parse-asn1": "5.1.4"
+                "bn.js": "^4.1.1",
+                "browserify-rsa": "^4.0.0",
+                "create-hash": "^1.1.0",
+                "create-hmac": "^1.1.2",
+                "elliptic": "^6.0.0",
+                "inherits": "^2.0.1",
+                "parse-asn1": "^5.0.0"
             }
         },
         "browserify-zlib": {
@@ -2622,7 +2622,7 @@
             "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
             "dev": true,
             "requires": {
-                "pako": "1.0.10"
+                "pako": "~1.0.5"
             }
         },
         "bson": {
@@ -2636,9 +2636,9 @@
             "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
             "dev": true,
             "requires": {
-                "base64-js": "1.3.0",
-                "ieee754": "1.1.13",
-                "isarray": "1.0.0"
+                "base64-js": "^1.0.2",
+                "ieee754": "^1.1.4",
+                "isarray": "^1.0.0"
             }
         },
         "buffer-alloc": {
@@ -2646,8 +2646,8 @@
             "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
             "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
             "requires": {
-                "buffer-alloc-unsafe": "1.1.0",
-                "buffer-fill": "1.0.0"
+                "buffer-alloc-unsafe": "^1.1.0",
+                "buffer-fill": "^1.0.0"
             }
         },
         "buffer-alloc-unsafe": {
@@ -2714,19 +2714,19 @@
             "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==",
             "dev": true,
             "requires": {
-                "bluebird": "3.5.5",
-                "chownr": "1.1.2",
-                "glob": "7.1.4",
-                "graceful-fs": "4.2.0",
-                "lru-cache": "4.1.5",
-                "mississippi": "2.0.0",
-                "mkdirp": "0.5.1",
-                "move-concurrently": "1.0.1",
-                "promise-inflight": "1.0.1",
-                "rimraf": "2.7.1",
-                "ssri": "5.3.0",
-                "unique-filename": "1.1.1",
-                "y18n": "4.0.0"
+                "bluebird": "^3.5.1",
+                "chownr": "^1.0.1",
+                "glob": "^7.1.2",
+                "graceful-fs": "^4.1.11",
+                "lru-cache": "^4.1.1",
+                "mississippi": "^2.0.0",
+                "mkdirp": "^0.5.1",
+                "move-concurrently": "^1.0.1",
+                "promise-inflight": "^1.0.1",
+                "rimraf": "^2.6.2",
+                "ssri": "^5.2.4",
+                "unique-filename": "^1.1.0",
+                "y18n": "^4.0.0"
             },
             "dependencies": {
                 "rimraf": {
@@ -2735,7 +2735,7 @@
                     "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
                     "dev": true,
                     "requires": {
-                        "glob": "7.1.4"
+                        "glob": "^7.1.3"
                     }
                 },
                 "y18n": {
@@ -2751,15 +2751,15 @@
             "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
             "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
             "requires": {
-                "collection-visit": "1.0.0",
-                "component-emitter": "1.3.0",
-                "get-value": "2.0.6",
-                "has-value": "1.0.0",
-                "isobject": "3.0.1",
-                "set-value": "2.0.1",
-                "to-object-path": "0.3.0",
-                "union-value": "1.0.1",
-                "unset-value": "1.0.0"
+                "collection-visit": "^1.0.0",
+                "component-emitter": "^1.2.1",
+                "get-value": "^2.0.6",
+                "has-value": "^1.0.0",
+                "isobject": "^3.0.1",
+                "set-value": "^2.0.0",
+                "to-object-path": "^0.3.0",
+                "union-value": "^1.0.0",
+                "unset-value": "^1.0.0"
             }
         },
         "callsite": {
@@ -2782,8 +2782,8 @@
             "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
             "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
             "requires": {
-                "camelcase": "2.1.1",
-                "map-obj": "1.0.1"
+                "camelcase": "^2.0.0",
+                "map-obj": "^1.0.0"
             },
             "dependencies": {
                 "camelcase": {
@@ -2798,9 +2798,9 @@
             "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.5.0.tgz",
             "integrity": "sha512-wwRz2cLMgb9d+rnotOJCoc04Bzj3aJMpWc6JxAD6lP7bYz0ldcn0sKddoZ0vhD5T8HBxrK+XmRDJb68/2VqARw==",
             "requires": {
-                "nan": "2.14.0",
-                "node-pre-gyp": "0.11.0",
-                "simple-get": "3.0.3"
+                "nan": "^2.13.2",
+                "node-pre-gyp": "^0.11.0",
+                "simple-get": "^3.0.3"
             }
         },
         "capture-stack-trace": {
@@ -2818,8 +2818,8 @@
             "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
             "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
             "requires": {
-                "align-text": "0.1.4",
-                "lazy-cache": "1.0.4"
+                "align-text": "^0.1.3",
+                "lazy-cache": "^1.0.3"
             }
         },
         "chai": {
@@ -2827,12 +2827,12 @@
             "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
             "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
             "requires": {
-                "assertion-error": "1.1.0",
-                "check-error": "1.0.2",
-                "deep-eql": "3.0.1",
-                "get-func-name": "2.0.0",
-                "pathval": "1.1.0",
-                "type-detect": "4.0.8"
+                "assertion-error": "^1.1.0",
+                "check-error": "^1.0.2",
+                "deep-eql": "^3.0.1",
+                "get-func-name": "^2.0.0",
+                "pathval": "^1.1.0",
+                "type-detect": "^4.0.5"
             }
         },
         "chained-function": {
@@ -2845,11 +2845,11 @@
             "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
             "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
             "requires": {
-                "ansi-styles": "2.2.1",
-                "escape-string-regexp": "1.0.5",
-                "has-ansi": "2.0.0",
-                "strip-ansi": "3.0.1",
-                "supports-color": "2.0.0"
+                "ansi-styles": "^2.2.1",
+                "escape-string-regexp": "^1.0.2",
+                "has-ansi": "^2.0.0",
+                "strip-ansi": "^3.0.0",
+                "supports-color": "^2.0.0"
             }
         },
         "character-parser": {
@@ -2857,7 +2857,7 @@
             "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz",
             "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=",
             "requires": {
-                "is-regex": "1.0.4"
+                "is-regex": "^1.0.3"
             }
         },
         "check-error": {
@@ -2870,12 +2870,12 @@
             "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz",
             "integrity": "sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==",
             "requires": {
-                "css-select": "1.2.0",
-                "dom-serializer": "0.1.1",
-                "entities": "1.1.2",
-                "htmlparser2": "3.10.1",
-                "lodash": "4.17.15",
-                "parse5": "3.0.3"
+                "css-select": "~1.2.0",
+                "dom-serializer": "~0.1.1",
+                "entities": "~1.1.1",
+                "htmlparser2": "^3.9.1",
+                "lodash": "^4.15.0",
+                "parse5": "^3.0.1"
             },
             "dependencies": {
                 "parse5": {
@@ -2883,7 +2883,7 @@
                     "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz",
                     "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==",
                     "requires": {
-                        "@types/node": "10.14.13"
+                        "@types/node": "*"
                     }
                 }
             }
@@ -2898,18 +2898,18 @@
             "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz",
             "integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==",
             "requires": {
-                "anymatch": "2.0.0",
-                "async-each": "1.0.3",
-                "braces": "2.3.2",
-                "fsevents": "1.2.9",
-                "glob-parent": "3.1.0",
-                "inherits": "2.0.3",
-                "is-binary-path": "1.0.1",
-                "is-glob": "4.0.1",
-                "normalize-path": "3.0.0",
-                "path-is-absolute": "1.0.1",
-                "readdirp": "2.2.1",
-                "upath": "1.1.2"
+                "anymatch": "^2.0.0",
+                "async-each": "^1.0.1",
+                "braces": "^2.3.2",
+                "fsevents": "^1.2.7",
+                "glob-parent": "^3.1.0",
+                "inherits": "^2.0.3",
+                "is-binary-path": "^1.0.0",
+                "is-glob": "^4.0.0",
+                "normalize-path": "^3.0.0",
+                "path-is-absolute": "^1.0.0",
+                "readdirp": "^2.2.1",
+                "upath": "^1.1.1"
             }
         },
         "chownr": {
@@ -2923,7 +2923,7 @@
             "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==",
             "dev": true,
             "requires": {
-                "tslib": "1.10.0"
+                "tslib": "^1.9.0"
             }
         },
         "ci-info": {
@@ -2936,8 +2936,8 @@
             "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
             "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
             "requires": {
-                "inherits": "2.0.3",
-                "safe-buffer": "5.1.2"
+                "inherits": "^2.0.1",
+                "safe-buffer": "^5.0.1"
             }
         },
         "class-transformer": {
@@ -2950,10 +2950,10 @@
             "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
             "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
             "requires": {
-                "arr-union": "3.1.0",
-                "define-property": "0.2.5",
-                "isobject": "3.0.1",
-                "static-extend": "0.1.2"
+                "arr-union": "^3.1.0",
+                "define-property": "^0.2.5",
+                "isobject": "^3.0.0",
+                "static-extend": "^0.1.1"
             },
             "dependencies": {
                 "define-property": {
@@ -2961,7 +2961,7 @@
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
                     "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
                     "requires": {
-                        "is-descriptor": "0.1.6"
+                        "is-descriptor": "^0.1.0"
                     }
                 }
             }
@@ -2976,7 +2976,7 @@
             "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
             "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==",
             "requires": {
-                "source-map": "0.6.1"
+                "source-map": "~0.6.0"
             },
             "dependencies": {
                 "source-map": {
@@ -2996,14 +2996,14 @@
             "resolved": "https://registry.npmjs.org/cliss/-/cliss-0.0.2.tgz",
             "integrity": "sha512-6rj9pgdukjT994Md13JCUAgTk91abAKrygL9sAvmHY4F6AKMOV8ccGaxhUUfcBuyg3sundWnn3JE0Mc9W6ZYqw==",
             "requires": {
-                "command-line-usage": "4.1.0",
-                "deepmerge": "2.2.1",
-                "get-stdin": "5.0.1",
+                "command-line-usage": "^4.0.1",
+                "deepmerge": "^2.0.0",
+                "get-stdin": "^5.0.1",
                 "inspect-parameters-declaration": "0.0.9",
                 "object-to-arguments": "0.0.8",
-                "pipe-functions": "1.3.0",
-                "strip-ansi": "4.0.0",
-                "yargs-parser": "7.0.0"
+                "pipe-functions": "^1.3.0",
+                "strip-ansi": "^4.0.0",
+                "yargs-parser": "^7.0.0"
             },
             "dependencies": {
                 "ansi-regex": {
@@ -3016,7 +3016,7 @@
                     "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
                     "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
                     "requires": {
-                        "ansi-regex": "3.0.0"
+                        "ansi-regex": "^3.0.0"
                     }
                 }
             }
@@ -3026,9 +3026,9 @@
             "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
             "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
             "requires": {
-                "string-width": "1.0.2",
-                "strip-ansi": "3.0.1",
-                "wrap-ansi": "2.1.0"
+                "string-width": "^1.0.1",
+                "strip-ansi": "^3.0.1",
+                "wrap-ansi": "^2.0.0"
             }
         },
         "clj-fuzzy": {
@@ -3042,10 +3042,10 @@
             "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==",
             "dev": true,
             "requires": {
-                "for-own": "1.0.0",
-                "is-plain-object": "2.0.4",
-                "kind-of": "6.0.2",
-                "shallow-clone": "1.0.0"
+                "for-own": "^1.0.0",
+                "is-plain-object": "^2.0.4",
+                "kind-of": "^6.0.0",
+                "shallow-clone": "^1.0.0"
             }
         },
         "code-point-at": {
@@ -3058,8 +3058,8 @@
             "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
             "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
             "requires": {
-                "map-visit": "1.0.0",
-                "object-visit": "1.0.1"
+                "map-visit": "^1.0.0",
+                "object-visit": "^1.0.0"
             }
         },
         "color": {
@@ -3067,8 +3067,8 @@
             "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz",
             "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==",
             "requires": {
-                "color-convert": "1.9.3",
-                "color-string": "1.5.3"
+                "color-convert": "^1.9.1",
+                "color-string": "^1.5.2"
             }
         },
         "color-convert": {
@@ -3089,8 +3089,8 @@
             "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
             "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
             "requires": {
-                "color-name": "1.1.3",
-                "simple-swizzle": "0.2.2"
+                "color-name": "^1.0.0",
+                "simple-swizzle": "^0.2.2"
             }
         },
         "colors": {
@@ -3103,7 +3103,7 @@
             "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
             "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
             "requires": {
-                "delayed-stream": "1.0.0"
+                "delayed-stream": "~1.0.0"
             }
         },
         "command-line-usage": {
@@ -3111,10 +3111,10 @@
             "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-4.1.0.tgz",
             "integrity": "sha512-MxS8Ad995KpdAC0Jopo/ovGIroV/m0KHwzKfXxKag6FHOkGsH8/lv5yjgablcRxCJJC0oJeUMuO/gmaq+Wq46g==",
             "requires": {
-                "ansi-escape-sequences": "4.1.0",
-                "array-back": "2.0.0",
-                "table-layout": "0.4.5",
-                "typical": "2.6.1"
+                "ansi-escape-sequences": "^4.0.0",
+                "array-back": "^2.0.0",
+                "table-layout": "^0.4.2",
+                "typical": "^2.6.1"
             }
         },
         "commander": {
@@ -3133,15 +3133,15 @@
             "resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.8.tgz",
             "integrity": "sha1-NPw2cs0kOT6LtH5wyqApOBH08sU=",
             "requires": {
-                "commander": "2.20.0",
-                "detective": "4.7.1",
-                "glob": "5.0.15",
-                "graceful-fs": "4.2.0",
-                "iconv-lite": "0.4.24",
-                "mkdirp": "0.5.1",
-                "private": "0.1.8",
-                "q": "1.5.1",
-                "recast": "0.11.23"
+                "commander": "^2.5.0",
+                "detective": "^4.3.1",
+                "glob": "^5.0.15",
+                "graceful-fs": "^4.1.2",
+                "iconv-lite": "^0.4.5",
+                "mkdirp": "^0.5.0",
+                "private": "^0.1.6",
+                "q": "^1.1.2",
+                "recast": "^0.11.17"
             },
             "dependencies": {
                 "glob": {
@@ -3149,11 +3149,11 @@
                     "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
                     "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
                     "requires": {
-                        "inflight": "1.0.6",
-                        "inherits": "2.0.3",
-                        "minimatch": "3.0.4",
-                        "once": "1.4.0",
-                        "path-is-absolute": "1.0.1"
+                        "inflight": "^1.0.4",
+                        "inherits": "2",
+                        "minimatch": "2 || 3",
+                        "once": "^1.3.0",
+                        "path-is-absolute": "^1.0.0"
                     }
                 }
             }
@@ -3178,10 +3178,10 @@
             "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-2.0.0.tgz",
             "integrity": "sha512-gnETNngrfsAoLBENM8M0DoiCDJkHwz3OfIg4mBtqKDcRgE4oXNwHxHxgHvwKKlrcD7eZ7BVTy4l8t9xVF7q3FQ==",
             "requires": {
-                "buffer-crc32": "0.2.13",
-                "crc32-stream": "2.0.0",
-                "normalize-path": "3.0.0",
-                "readable-stream": "2.3.6"
+                "buffer-crc32": "^0.2.13",
+                "crc32-stream": "^2.0.0",
+                "normalize-path": "^3.0.0",
+                "readable-stream": "^2.3.6"
             }
         },
         "compressible": {
@@ -3190,7 +3190,7 @@
             "integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==",
             "dev": true,
             "requires": {
-                "mime-db": "1.40.0"
+                "mime-db": ">= 1.40.0 < 2"
             }
         },
         "compression": {
@@ -3199,13 +3199,13 @@
             "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
             "dev": true,
             "requires": {
-                "accepts": "1.3.7",
+                "accepts": "~1.3.5",
                 "bytes": "3.0.0",
-                "compressible": "2.0.17",
+                "compressible": "~2.0.16",
                 "debug": "2.6.9",
-                "on-headers": "1.0.2",
+                "on-headers": "~1.0.2",
                 "safe-buffer": "5.1.2",
-                "vary": "1.1.2"
+                "vary": "~1.1.2"
             },
             "dependencies": {
                 "bytes": {
@@ -3227,10 +3227,10 @@
             "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
             "dev": true,
             "requires": {
-                "buffer-from": "1.1.1",
-                "inherits": "2.0.3",
-                "readable-stream": "2.3.6",
-                "typedarray": "0.0.6"
+                "buffer-from": "^1.0.0",
+                "inherits": "^2.0.3",
+                "readable-stream": "^2.2.2",
+                "typedarray": "^0.0.6"
             }
         },
         "configstore": {
@@ -3238,12 +3238,12 @@
             "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz",
             "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==",
             "requires": {
-                "dot-prop": "4.2.0",
-                "graceful-fs": "4.2.0",
-                "make-dir": "1.3.0",
-                "unique-string": "1.0.0",
-                "write-file-atomic": "2.4.3",
-                "xdg-basedir": "3.0.0"
+                "dot-prop": "^4.1.0",
+                "graceful-fs": "^4.1.2",
+                "make-dir": "^1.0.0",
+                "unique-string": "^1.0.0",
+                "write-file-atomic": "^2.0.0",
+                "xdg-basedir": "^3.0.0"
             }
         },
         "connect-flash": {
@@ -3262,7 +3262,7 @@
             "resolved": "https://registry.npmjs.org/connect-mongo/-/connect-mongo-2.0.3.tgz",
             "integrity": "sha512-Vs+QZ/6X6gbCrP1Ls7Oh/wlyY6pgpbPSrUKF5yRT+zd+4GZPNbjNquxquZ+Clv2+03HBXE7T4lVM0PUcaBhihg==",
             "requires": {
-                "mongodb": "2.2.36"
+                "mongodb": "^2.0.36"
             },
             "dependencies": {
                 "mongodb": {
@@ -3285,13 +3285,13 @@
                     "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz",
                     "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=",
                     "requires": {
-                        "buffer-shims": "1.0.0",
-                        "core-util-is": "1.0.2",
-                        "inherits": "2.0.3",
-                        "isarray": "1.0.0",
-                        "process-nextick-args": "1.0.7",
-                        "string_decoder": "1.0.3",
-                        "util-deprecate": "1.0.2"
+                        "buffer-shims": "~1.0.0",
+                        "core-util-is": "~1.0.0",
+                        "inherits": "~2.0.1",
+                        "isarray": "~1.0.0",
+                        "process-nextick-args": "~1.0.6",
+                        "string_decoder": "~1.0.0",
+                        "util-deprecate": "~1.0.1"
                     }
                 },
                 "string_decoder": {
@@ -3299,7 +3299,7 @@
                     "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
                     "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
                     "requires": {
-                        "safe-buffer": "5.1.2"
+                        "safe-buffer": "~5.1.0"
                     }
                 }
             }
@@ -3310,7 +3310,7 @@
             "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
             "dev": true,
             "requires": {
-                "date-now": "0.1.4"
+                "date-now": "^0.1.4"
             }
         },
         "console-control-strings": {
@@ -3323,10 +3323,10 @@
             "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.2.tgz",
             "integrity": "sha512-yePcBqEFhLOqSBtwYOGGS1exHo/s1xjekXiinh4itpNQGCu4KA1euPh1fg07N2wMITZXQkBz75Ntdt1ctGZouw==",
             "requires": {
-                "@types/babel-types": "7.0.7",
-                "@types/babylon": "6.16.5",
-                "babel-types": "6.26.0",
-                "babylon": "6.18.0"
+                "@types/babel-types": "^7.0.0",
+                "@types/babylon": "^6.16.2",
+                "babel-types": "^6.26.0",
+                "babylon": "^6.18.0"
             }
         },
         "constants-browserify": {
@@ -3353,7 +3353,7 @@
             "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
             "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
             "requires": {
-                "safe-buffer": "5.1.2"
+                "safe-buffer": "~5.1.1"
             }
         },
         "cookie": {
@@ -3377,7 +3377,7 @@
             "requires": {
                 "cookies": "0.7.1",
                 "debug": "3.1.0",
-                "on-headers": "1.0.2",
+                "on-headers": "~1.0.1",
                 "safe-buffer": "5.1.1"
             },
             "dependencies": {
@@ -3406,8 +3406,8 @@
             "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.7.1.tgz",
             "integrity": "sha1-fIphX1SBxhq58WyDNzG8uPZjuZs=",
             "requires": {
-                "depd": "1.1.2",
-                "keygrip": "1.0.3"
+                "depd": "~1.1.1",
+                "keygrip": "~1.0.2"
             }
         },
         "copy-concurrently": {
@@ -3416,12 +3416,12 @@
             "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
             "dev": true,
             "requires": {
-                "aproba": "1.2.0",
-                "fs-write-stream-atomic": "1.0.10",
-                "iferr": "0.1.5",
-                "mkdirp": "0.5.1",
-                "rimraf": "2.7.1",
-                "run-queue": "1.0.3"
+                "aproba": "^1.1.1",
+                "fs-write-stream-atomic": "^1.0.8",
+                "iferr": "^0.1.5",
+                "mkdirp": "^0.5.1",
+                "rimraf": "^2.5.4",
+                "run-queue": "^1.0.0"
             },
             "dependencies": {
                 "rimraf": {
@@ -3430,7 +3430,7 @@
                     "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
                     "dev": true,
                     "requires": {
-                        "glob": "7.1.4"
+                        "glob": "^7.1.3"
                     }
                 }
             }
@@ -3446,14 +3446,14 @@
             "integrity": "sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==",
             "dev": true,
             "requires": {
-                "cacache": "10.0.4",
-                "find-cache-dir": "1.0.0",
-                "globby": "7.1.1",
-                "is-glob": "4.0.1",
-                "loader-utils": "1.2.3",
-                "minimatch": "3.0.4",
-                "p-limit": "1.3.0",
-                "serialize-javascript": "1.7.0"
+                "cacache": "^10.0.4",
+                "find-cache-dir": "^1.0.0",
+                "globby": "^7.1.1",
+                "is-glob": "^4.0.0",
+                "loader-utils": "^1.1.0",
+                "minimatch": "^3.0.4",
+                "p-limit": "^1.0.0",
+                "serialize-javascript": "^1.4.0"
             },
             "dependencies": {
                 "p-limit": {
@@ -3462,7 +3462,7 @@
                     "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
                     "dev": true,
                     "requires": {
-                        "p-try": "1.0.0"
+                        "p-try": "^1.0.0"
                     }
                 }
             }
@@ -3482,11 +3482,11 @@
             "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
             "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
             "requires": {
-                "@types/parse-json": "4.0.0",
-                "import-fresh": "3.2.1",
-                "parse-json": "5.0.0",
-                "path-type": "4.0.0",
-                "yaml": "1.7.2"
+                "@types/parse-json": "^4.0.0",
+                "import-fresh": "^3.1.0",
+                "parse-json": "^5.0.0",
+                "path-type": "^4.0.0",
+                "yaml": "^1.7.2"
             },
             "dependencies": {
                 "path-type": {
@@ -3501,7 +3501,7 @@
             "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz",
             "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==",
             "requires": {
-                "buffer": "5.2.1"
+                "buffer": "^5.1.0"
             },
             "dependencies": {
                 "buffer": {
@@ -3509,8 +3509,8 @@
                     "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz",
                     "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==",
                     "requires": {
-                        "base64-js": "1.3.0",
-                        "ieee754": "1.1.13"
+                        "base64-js": "^1.0.2",
+                        "ieee754": "^1.1.4"
                     }
                 }
             }
@@ -3520,8 +3520,8 @@
             "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-2.0.0.tgz",
             "integrity": "sha1-483TtN8xaN10494/u8t7KX/pCPQ=",
             "requires": {
-                "crc": "3.8.0",
-                "readable-stream": "2.3.6"
+                "crc": "^3.4.4",
+                "readable-stream": "^2.0.0"
             }
         },
         "create-ecdh": {
@@ -3529,8 +3529,8 @@
             "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
             "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==",
             "requires": {
-                "bn.js": "4.11.8",
-                "elliptic": "6.5.0"
+                "bn.js": "^4.1.0",
+                "elliptic": "^6.0.0"
             }
         },
         "create-emotion": {
@@ -3538,8 +3538,8 @@
             "resolved": "https://registry.npmjs.org/create-emotion/-/create-emotion-10.0.14.tgz",
             "integrity": "sha512-5G4naKMxokOur+94eDz7iPKBfwzy4wa/+0isnPhxXyosIQHBq7yvBy4jjdZw/nnRm7G3PM7P9Ug8mUmtoqcaHg==",
             "requires": {
-                "@emotion/cache": "10.0.19",
-                "@emotion/serialize": "0.11.14",
+                "@emotion/cache": "^10.0.14",
+                "@emotion/serialize": "^0.11.8",
                 "@emotion/sheet": "0.9.3",
                 "@emotion/utils": "0.11.2"
             }
@@ -3549,7 +3549,7 @@
             "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
             "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
             "requires": {
-                "capture-stack-trace": "1.0.1"
+                "capture-stack-trace": "^1.0.0"
             }
         },
         "create-hash": {
@@ -3557,11 +3557,11 @@
             "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
             "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
             "requires": {
-                "cipher-base": "1.0.4",
-                "inherits": "2.0.3",
-                "md5.js": "1.3.5",
-                "ripemd160": "2.0.2",
-                "sha.js": "2.4.11"
+                "cipher-base": "^1.0.1",
+                "inherits": "^2.0.1",
+                "md5.js": "^1.3.4",
+                "ripemd160": "^2.0.1",
+                "sha.js": "^2.4.0"
             }
         },
         "create-hmac": {
@@ -3569,12 +3569,12 @@
             "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
             "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
             "requires": {
-                "cipher-base": "1.0.4",
-                "create-hash": "1.2.0",
-                "inherits": "2.0.3",
-                "ripemd160": "2.0.2",
-                "safe-buffer": "5.1.2",
-                "sha.js": "2.4.11"
+                "cipher-base": "^1.0.3",
+                "create-hash": "^1.1.0",
+                "inherits": "^2.0.1",
+                "ripemd160": "^2.0.0",
+                "safe-buffer": "^5.0.1",
+                "sha.js": "^2.4.8"
             }
         },
         "create-react-context": {
@@ -3582,8 +3582,8 @@
             "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.3.tgz",
             "integrity": "sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag==",
             "requires": {
-                "fbjs": "0.8.17",
-                "gud": "1.0.0"
+                "fbjs": "^0.8.0",
+                "gud": "^1.0.0"
             }
         },
         "crel": {
@@ -3597,8 +3597,8 @@
             "integrity": "sha512-jtdNFfFW1hB7sMhr/H6rW1Z45LFqyI431m3qU6bFXcQ3Eh7LtBuG3h74o7ohHZ3crrRkkqHlo4jYHFPcjroANg==",
             "dev": true,
             "requires": {
-                "cross-spawn": "6.0.5",
-                "is-windows": "1.0.2"
+                "cross-spawn": "^6.0.5",
+                "is-windows": "^1.0.0"
             },
             "dependencies": {
                 "cross-spawn": {
@@ -3607,11 +3607,11 @@
                     "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
                     "dev": true,
                     "requires": {
-                        "nice-try": "1.0.5",
-                        "path-key": "2.0.1",
-                        "semver": "5.7.0",
-                        "shebang-command": "1.2.0",
-                        "which": "1.3.1"
+                        "nice-try": "^1.0.4",
+                        "path-key": "^2.0.1",
+                        "semver": "^5.5.0",
+                        "shebang-command": "^1.2.0",
+                        "which": "^1.2.9"
                     }
                 }
             }
@@ -3637,8 +3637,8 @@
             "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
             "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=",
             "requires": {
-                "lru-cache": "4.1.5",
-                "which": "1.3.1"
+                "lru-cache": "^4.0.1",
+                "which": "^1.2.9"
             }
         },
         "crypto-browserify": {
@@ -3646,17 +3646,17 @@
             "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
             "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
             "requires": {
-                "browserify-cipher": "1.0.1",
-                "browserify-sign": "4.0.4",
-                "create-ecdh": "4.0.3",
-                "create-hash": "1.2.0",
-                "create-hmac": "1.1.7",
-                "diffie-hellman": "5.0.3",
-                "inherits": "2.0.3",
-                "pbkdf2": "3.0.17",
-                "public-encrypt": "4.0.3",
-                "randombytes": "2.1.0",
-                "randomfill": "1.0.4"
+                "browserify-cipher": "^1.0.0",
+                "browserify-sign": "^4.0.0",
+                "create-ecdh": "^4.0.0",
+                "create-hash": "^1.1.0",
+                "create-hmac": "^1.1.0",
+                "diffie-hellman": "^5.0.0",
+                "inherits": "^2.0.1",
+                "pbkdf2": "^3.0.3",
+                "public-encrypt": "^4.0.0",
+                "randombytes": "^2.0.0",
+                "randomfill": "^1.0.3"
             }
         },
         "crypto-random-string": {
@@ -3670,17 +3670,17 @@
             "integrity": "sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w==",
             "dev": true,
             "requires": {
-                "camelcase": "5.3.1",
-                "icss-utils": "4.1.1",
-                "loader-utils": "1.2.3",
-                "normalize-path": "3.0.0",
-                "postcss": "7.0.17",
-                "postcss-modules-extract-imports": "2.0.0",
-                "postcss-modules-local-by-default": "2.0.6",
-                "postcss-modules-scope": "2.1.0",
-                "postcss-modules-values": "2.0.0",
-                "postcss-value-parser": "3.3.1",
-                "schema-utils": "1.0.0"
+                "camelcase": "^5.2.0",
+                "icss-utils": "^4.1.0",
+                "loader-utils": "^1.2.3",
+                "normalize-path": "^3.0.0",
+                "postcss": "^7.0.14",
+                "postcss-modules-extract-imports": "^2.0.0",
+                "postcss-modules-local-by-default": "^2.0.6",
+                "postcss-modules-scope": "^2.1.0",
+                "postcss-modules-values": "^2.0.0",
+                "postcss-value-parser": "^3.3.0",
+                "schema-utils": "^1.0.0"
             },
             "dependencies": {
                 "camelcase": {
@@ -3695,9 +3695,9 @@
                     "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
                     "dev": true,
                     "requires": {
-                        "ajv": "6.10.2",
-                        "ajv-errors": "1.0.1",
-                        "ajv-keywords": "3.4.1"
+                        "ajv": "^6.1.0",
+                        "ajv-errors": "^1.0.0",
+                        "ajv-keywords": "^3.1.0"
                     }
                 }
             }
@@ -3707,10 +3707,10 @@
             "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
             "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
             "requires": {
-                "boolbase": "1.0.0",
-                "css-what": "2.1.3",
+                "boolbase": "~1.0.0",
+                "css-what": "2.1",
                 "domutils": "1.5.1",
-                "nth-check": "1.0.2"
+                "nth-check": "~1.0.1"
             }
         },
         "css-what": {
@@ -3736,7 +3736,7 @@
             "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==",
             "dev": true,
             "requires": {
-                "cssom": "0.3.8"
+                "cssom": "0.3.x"
             }
         },
         "csstype": {
@@ -3749,7 +3749,7 @@
             "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
             "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
             "requires": {
-                "array-find-index": "1.0.2"
+                "array-find-index": "^1.0.1"
             }
         },
         "cyclist": {
@@ -3764,8 +3764,8 @@
             "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
             "dev": true,
             "requires": {
-                "es5-ext": "0.10.50",
-                "type": "1.0.1"
+                "es5-ext": "^0.10.50",
+                "type": "^1.0.1"
             }
         },
         "d3-format": {
@@ -3778,7 +3778,7 @@
             "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
             "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
             "requires": {
-                "assert-plus": "1.0.0"
+                "assert-plus": "^1.0.0"
             },
             "dependencies": {
                 "assert-plus": {
@@ -3794,9 +3794,9 @@
             "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==",
             "dev": true,
             "requires": {
-                "abab": "2.0.0",
-                "whatwg-mimetype": "2.3.0",
-                "whatwg-url": "7.0.0"
+                "abab": "^2.0.0",
+                "whatwg-mimetype": "^2.2.0",
+                "whatwg-url": "^7.0.0"
             }
         },
         "date-now": {
@@ -3811,8 +3811,8 @@
             "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=",
             "dev": true,
             "requires": {
-                "get-stdin": "4.0.1",
-                "meow": "3.7.0"
+                "get-stdin": "^4.0.1",
+                "meow": "^3.3.0"
             },
             "dependencies": {
                 "get-stdin": {
@@ -3852,7 +3852,7 @@
             "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
             "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
             "requires": {
-                "mimic-response": "1.0.1"
+                "mimic-response": "^1.0.0"
             }
         },
         "deep-eql": {
@@ -3860,7 +3860,7 @@
             "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
             "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
             "requires": {
-                "type-detect": "4.0.8"
+                "type-detect": "^4.0.0"
             }
         },
         "deep-equal": {
@@ -3891,8 +3891,8 @@
             "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==",
             "dev": true,
             "requires": {
-                "execa": "1.0.0",
-                "ip-regex": "2.1.0"
+                "execa": "^1.0.0",
+                "ip-regex": "^2.1.0"
             },
             "dependencies": {
                 "cross-spawn": {
@@ -3901,11 +3901,11 @@
                     "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
                     "dev": true,
                     "requires": {
-                        "nice-try": "1.0.5",
-                        "path-key": "2.0.1",
-                        "semver": "5.7.0",
-                        "shebang-command": "1.2.0",
-                        "which": "1.3.1"
+                        "nice-try": "^1.0.4",
+                        "path-key": "^2.0.1",
+                        "semver": "^5.5.0",
+                        "shebang-command": "^1.2.0",
+                        "which": "^1.2.9"
                     }
                 },
                 "execa": {
@@ -3914,13 +3914,13 @@
                     "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
                     "dev": true,
                     "requires": {
-                        "cross-spawn": "6.0.5",
-                        "get-stream": "4.1.0",
-                        "is-stream": "1.1.0",
-                        "npm-run-path": "2.0.2",
-                        "p-finally": "1.0.0",
-                        "signal-exit": "3.0.2",
-                        "strip-eof": "1.0.0"
+                        "cross-spawn": "^6.0.0",
+                        "get-stream": "^4.0.0",
+                        "is-stream": "^1.1.0",
+                        "npm-run-path": "^2.0.0",
+                        "p-finally": "^1.0.0",
+                        "signal-exit": "^3.0.0",
+                        "strip-eof": "^1.0.0"
                     }
                 },
                 "get-stream": {
@@ -3929,7 +3929,7 @@
                     "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
                     "dev": true,
                     "requires": {
-                        "pump": "3.0.0"
+                        "pump": "^3.0.0"
                     }
                 },
                 "pump": {
@@ -3938,8 +3938,8 @@
                     "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
                     "dev": true,
                     "requires": {
-                        "end-of-stream": "1.4.1",
-                        "once": "1.4.0"
+                        "end-of-stream": "^1.1.0",
+                        "once": "^1.3.1"
                     }
                 }
             }
@@ -3949,7 +3949,7 @@
             "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
             "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
             "requires": {
-                "object-keys": "1.1.1"
+                "object-keys": "^1.0.12"
             }
         },
         "define-property": {
@@ -3957,8 +3957,8 @@
             "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
             "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
             "requires": {
-                "is-descriptor": "1.0.2",
-                "isobject": "3.0.1"
+                "is-descriptor": "^1.0.2",
+                "isobject": "^3.0.1"
             },
             "dependencies": {
                 "is-accessor-descriptor": {
@@ -3966,7 +3966,7 @@
                     "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
                     "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
                     "requires": {
-                        "kind-of": "6.0.2"
+                        "kind-of": "^6.0.0"
                     }
                 },
                 "is-data-descriptor": {
@@ -3974,7 +3974,7 @@
                     "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
                     "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
                     "requires": {
-                        "kind-of": "6.0.2"
+                        "kind-of": "^6.0.0"
                     }
                 },
                 "is-descriptor": {
@@ -3982,9 +3982,9 @@
                     "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
                     "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
                     "requires": {
-                        "is-accessor-descriptor": "1.0.0",
-                        "is-data-descriptor": "1.0.0",
-                        "kind-of": "6.0.2"
+                        "is-accessor-descriptor": "^1.0.0",
+                        "is-data-descriptor": "^1.0.0",
+                        "kind-of": "^6.0.2"
                     }
                 }
             }
@@ -4000,13 +4000,13 @@
             "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==",
             "dev": true,
             "requires": {
-                "@types/glob": "7.1.1",
-                "globby": "6.1.0",
-                "is-path-cwd": "2.2.0",
-                "is-path-in-cwd": "2.1.0",
-                "p-map": "2.1.0",
-                "pify": "4.0.1",
-                "rimraf": "2.7.1"
+                "@types/glob": "^7.1.1",
+                "globby": "^6.1.0",
+                "is-path-cwd": "^2.0.0",
+                "is-path-in-cwd": "^2.0.0",
+                "p-map": "^2.0.0",
+                "pify": "^4.0.1",
+                "rimraf": "^2.6.3"
             },
             "dependencies": {
                 "globby": {
@@ -4015,11 +4015,11 @@
                     "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
                     "dev": true,
                     "requires": {
-                        "array-union": "1.0.2",
-                        "glob": "7.1.4",
-                        "object-assign": "4.1.1",
-                        "pify": "2.3.0",
-                        "pinkie-promise": "2.0.1"
+                        "array-union": "^1.0.1",
+                        "glob": "^7.0.3",
+                        "object-assign": "^4.0.1",
+                        "pify": "^2.0.0",
+                        "pinkie-promise": "^2.0.0"
                     },
                     "dependencies": {
                         "pify": {
@@ -4042,7 +4042,7 @@
                     "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
                     "dev": true,
                     "requires": {
-                        "glob": "7.1.4"
+                        "glob": "^7.1.3"
                     }
                 }
             }
@@ -4067,8 +4067,8 @@
             "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
             "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
             "requires": {
-                "inherits": "2.0.3",
-                "minimalistic-assert": "1.0.1"
+                "inherits": "^2.0.1",
+                "minimalistic-assert": "^1.0.0"
             }
         },
         "destroy": {
@@ -4098,8 +4098,8 @@
             "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz",
             "integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==",
             "requires": {
-                "acorn": "5.7.3",
-                "defined": "1.0.0"
+                "acorn": "^5.2.1",
+                "defined": "^1.0.0"
             }
         },
         "diff": {
@@ -4112,9 +4112,9 @@
             "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
             "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
             "requires": {
-                "bn.js": "4.11.8",
-                "miller-rabin": "4.0.1",
-                "randombytes": "2.1.0"
+                "bn.js": "^4.1.0",
+                "miller-rabin": "^4.0.0",
+                "randombytes": "^2.0.0"
             }
         },
         "dir-glob": {
@@ -4123,7 +4123,7 @@
             "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==",
             "dev": true,
             "requires": {
-                "path-type": "3.0.0"
+                "path-type": "^3.0.0"
             },
             "dependencies": {
                 "path-type": {
@@ -4132,7 +4132,7 @@
                     "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
                     "dev": true,
                     "requires": {
-                        "pify": "3.0.0"
+                        "pify": "^3.0.0"
                     }
                 },
                 "pify": {
@@ -4155,8 +4155,8 @@
             "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==",
             "dev": true,
             "requires": {
-                "ip": "1.1.5",
-                "safe-buffer": "5.1.2"
+                "ip": "^1.1.0",
+                "safe-buffer": "^5.0.1"
             }
         },
         "dns-txt": {
@@ -4165,7 +4165,7 @@
             "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=",
             "dev": true,
             "requires": {
-                "buffer-indexof": "1.1.1"
+                "buffer-indexof": "^1.0.0"
             }
         },
         "doctypes": {
@@ -4178,7 +4178,7 @@
             "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
             "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==",
             "requires": {
-                "@babel/runtime": "7.5.5"
+                "@babel/runtime": "^7.1.2"
             }
         },
         "dom-serializer": {
@@ -4186,8 +4186,8 @@
             "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
             "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
             "requires": {
-                "domelementtype": "1.3.1",
-                "entities": "1.1.2"
+                "domelementtype": "^1.3.0",
+                "entities": "^1.1.1"
             }
         },
         "domain-browser": {
@@ -4207,7 +4207,7 @@
             "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
             "dev": true,
             "requires": {
-                "webidl-conversions": "4.0.2"
+                "webidl-conversions": "^4.0.2"
             }
         },
         "domhandler": {
@@ -4215,7 +4215,7 @@
             "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
             "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
             "requires": {
-                "domelementtype": "1.3.1"
+                "domelementtype": "1"
             }
         },
         "domutils": {
@@ -4223,8 +4223,8 @@
             "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
             "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
             "requires": {
-                "dom-serializer": "0.1.1",
-                "domelementtype": "1.3.1"
+                "dom-serializer": "0",
+                "domelementtype": "1"
             }
         },
         "dot-prop": {
@@ -4232,7 +4232,7 @@
             "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
             "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==",
             "requires": {
-                "is-obj": "1.0.1"
+                "is-obj": "^1.0.0"
             }
         },
         "dotenv": {
@@ -4251,10 +4251,10 @@
             "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
             "dev": true,
             "requires": {
-                "end-of-stream": "1.4.1",
-                "inherits": "2.0.3",
-                "readable-stream": "2.3.6",
-                "stream-shift": "1.0.0"
+                "end-of-stream": "^1.0.0",
+                "inherits": "^2.0.1",
+                "readable-stream": "^2.0.0",
+                "stream-shift": "^1.0.0"
             }
         },
         "dynamic-dedupe": {
@@ -4263,7 +4263,7 @@
             "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=",
             "dev": true,
             "requires": {
-                "xtend": "4.0.2"
+                "xtend": "^4.0.0"
             }
         },
         "ecc-jsbn": {
@@ -4271,8 +4271,8 @@
             "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
             "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
             "requires": {
-                "jsbn": "0.1.1",
-                "safer-buffer": "2.1.2"
+                "jsbn": "~0.1.0",
+                "safer-buffer": "^2.1.0"
             }
         },
         "ecdsa-sig-formatter": {
@@ -4280,7 +4280,7 @@
             "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
             "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
             "requires": {
-                "safe-buffer": "5.1.2"
+                "safe-buffer": "^5.0.1"
             }
         },
         "ee-first": {
@@ -4298,13 +4298,13 @@
             "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.0.tgz",
             "integrity": "sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg==",
             "requires": {
-                "bn.js": "4.11.8",
-                "brorand": "1.1.0",
-                "hash.js": "1.1.7",
-                "hmac-drbg": "1.0.1",
-                "inherits": "2.0.3",
-                "minimalistic-assert": "1.0.1",
-                "minimalistic-crypto-utils": "1.0.1"
+                "bn.js": "^4.4.0",
+                "brorand": "^1.0.1",
+                "hash.js": "^1.0.0",
+                "hmac-drbg": "^1.0.0",
+                "inherits": "^2.0.1",
+                "minimalistic-assert": "^1.0.0",
+                "minimalistic-crypto-utils": "^1.0.0"
             }
         },
         "emit-logger": {
@@ -4330,8 +4330,8 @@
             "resolved": "https://registry.npmjs.org/emotion/-/emotion-10.0.23.tgz",
             "integrity": "sha512-H/x+5rJUnSvI0rdYsAFyDfuQwE0poZgTMj5TQsKirLzyHVWqs6CiUponsdE86sisXw0vS60j91HAbidJJeDt1g==",
             "requires": {
-                "babel-plugin-emotion": "10.0.23",
-                "create-emotion": "10.0.14"
+                "babel-plugin-emotion": "^10.0.23",
+                "create-emotion": "^10.0.14"
             }
         },
         "encodeurl": {
@@ -4344,7 +4344,7 @@
             "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
             "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
             "requires": {
-                "iconv-lite": "0.4.24"
+                "iconv-lite": "~0.4.13"
             }
         },
         "end-of-stream": {
@@ -4352,7 +4352,7 @@
             "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
             "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
             "requires": {
-                "once": "1.4.0"
+                "once": "^1.4.0"
             }
         },
         "engine.io": {
@@ -4360,12 +4360,12 @@
             "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.3.2.tgz",
             "integrity": "sha512-AsaA9KG7cWPXWHp5FvHdDWY3AMWeZ8x+2pUVLcn71qE5AtAzgGbxuclOytygskw8XGmiQafTmnI9Bix3uihu2w==",
             "requires": {
-                "accepts": "1.3.7",
+                "accepts": "~1.3.4",
                 "base64id": "1.0.0",
                 "cookie": "0.3.1",
-                "debug": "3.1.0",
-                "engine.io-parser": "2.1.3",
-                "ws": "6.1.4"
+                "debug": "~3.1.0",
+                "engine.io-parser": "~2.1.0",
+                "ws": "~6.1.0"
             },
             "dependencies": {
                 "debug": {
@@ -4385,14 +4385,14 @@
             "requires": {
                 "component-emitter": "1.2.1",
                 "component-inherit": "0.0.3",
-                "debug": "3.1.0",
-                "engine.io-parser": "2.1.3",
+                "debug": "~3.1.0",
+                "engine.io-parser": "~2.1.1",
                 "has-cors": "1.1.0",
                 "indexof": "0.0.1",
                 "parseqs": "0.0.5",
                 "parseuri": "0.0.5",
-                "ws": "6.1.4",
-                "xmlhttprequest-ssl": "1.5.5",
+                "ws": "~6.1.0",
+                "xmlhttprequest-ssl": "~1.5.4",
                 "yeast": "0.1.2"
             },
             "dependencies": {
@@ -4417,10 +4417,10 @@
             "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==",
             "requires": {
                 "after": "0.8.2",
-                "arraybuffer.slice": "0.0.7",
+                "arraybuffer.slice": "~0.0.7",
                 "base64-arraybuffer": "0.1.5",
                 "blob": "0.0.5",
-                "has-binary2": "1.0.3"
+                "has-binary2": "~1.0.2"
             }
         },
         "enhanced-resolve": {
@@ -4429,9 +4429,9 @@
             "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==",
             "dev": true,
             "requires": {
-                "graceful-fs": "4.2.0",
-                "memory-fs": "0.4.1",
-                "tapable": "1.1.3"
+                "graceful-fs": "^4.1.2",
+                "memory-fs": "^0.4.0",
+                "tapable": "^1.0.0"
             }
         },
         "entities": {
@@ -4444,8 +4444,8 @@
             "resolved": "https://registry.npmjs.org/envify/-/envify-3.4.1.tgz",
             "integrity": "sha1-1xIjKejfFoi6dxsSUBkXyc5cvOg=",
             "requires": {
-                "jstransform": "11.0.3",
-                "through": "2.3.8"
+                "jstransform": "^11.0.3",
+                "through": "~2.3.4"
             }
         },
         "errno": {
@@ -4454,7 +4454,7 @@
             "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
             "dev": true,
             "requires": {
-                "prr": "1.0.1"
+                "prr": "~1.0.1"
             }
         },
         "error-ex": {
@@ -4462,7 +4462,7 @@
             "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
             "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
             "requires": {
-                "is-arrayish": "0.2.1"
+                "is-arrayish": "^0.2.1"
             }
         },
         "es-abstract": {
@@ -4470,16 +4470,16 @@
             "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz",
             "integrity": "sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==",
             "requires": {
-                "es-to-primitive": "1.2.0",
-                "function-bind": "1.1.1",
-                "has": "1.0.3",
-                "has-symbols": "1.0.0",
-                "is-callable": "1.1.4",
-                "is-regex": "1.0.4",
-                "object-inspect": "1.6.0",
-                "object-keys": "1.1.1",
-                "string.prototype.trimleft": "2.1.0",
-                "string.prototype.trimright": "2.1.0"
+                "es-to-primitive": "^1.2.0",
+                "function-bind": "^1.1.1",
+                "has": "^1.0.3",
+                "has-symbols": "^1.0.0",
+                "is-callable": "^1.1.4",
+                "is-regex": "^1.0.4",
+                "object-inspect": "^1.6.0",
+                "object-keys": "^1.1.1",
+                "string.prototype.trimleft": "^2.1.0",
+                "string.prototype.trimright": "^2.1.0"
             }
         },
         "es-to-primitive": {
@@ -4487,9 +4487,9 @@
             "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
             "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
             "requires": {
-                "is-callable": "1.1.4",
-                "is-date-object": "1.0.1",
-                "is-symbol": "1.0.2"
+                "is-callable": "^1.1.4",
+                "is-date-object": "^1.0.1",
+                "is-symbol": "^1.0.2"
             }
         },
         "es5-ext": {
@@ -4498,9 +4498,9 @@
             "integrity": "sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==",
             "dev": true,
             "requires": {
-                "es6-iterator": "2.0.3",
-                "es6-symbol": "3.1.1",
-                "next-tick": "1.0.0"
+                "es6-iterator": "~2.0.3",
+                "es6-symbol": "~3.1.1",
+                "next-tick": "^1.0.0"
             }
         },
         "es6-iterator": {
@@ -4509,9 +4509,9 @@
             "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
             "dev": true,
             "requires": {
-                "d": "1.0.1",
-                "es5-ext": "0.10.50",
-                "es6-symbol": "3.1.1"
+                "d": "1",
+                "es5-ext": "^0.10.35",
+                "es6-symbol": "^3.1.1"
             }
         },
         "es6-promise": {
@@ -4524,7 +4524,7 @@
             "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
             "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
             "requires": {
-                "es6-promise": "4.2.8"
+                "es6-promise": "^4.0.3"
             },
             "dependencies": {
                 "es6-promise": {
@@ -4540,8 +4540,8 @@
             "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
             "dev": true,
             "requires": {
-                "d": "1.0.1",
-                "es5-ext": "0.10.50"
+                "d": "1",
+                "es5-ext": "~0.10.14"
             }
         },
         "escape-html": {
@@ -4560,11 +4560,11 @@
             "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==",
             "dev": true,
             "requires": {
-                "esprima": "3.1.3",
-                "estraverse": "4.2.0",
-                "esutils": "2.0.2",
-                "optionator": "0.8.2",
-                "source-map": "0.6.1"
+                "esprima": "^3.1.3",
+                "estraverse": "^4.2.0",
+                "esutils": "^2.0.2",
+                "optionator": "^0.8.1",
+                "source-map": "~0.6.1"
             },
             "dependencies": {
                 "esprima": {
@@ -4588,8 +4588,8 @@
             "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
             "dev": true,
             "requires": {
-                "esrecurse": "4.2.1",
-                "estraverse": "4.2.0"
+                "esrecurse": "^4.1.0",
+                "estraverse": "^4.1.1"
             }
         },
         "esprima": {
@@ -4603,7 +4603,7 @@
             "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
             "dev": true,
             "requires": {
-                "estraverse": "4.2.0"
+                "estraverse": "^4.1.0"
             }
         },
         "estraverse": {
@@ -4649,7 +4649,7 @@
             "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==",
             "dev": true,
             "requires": {
-                "original": "1.0.2"
+                "original": "^1.0.0"
             }
         },
         "evp_bytestokey": {
@@ -4657,8 +4657,8 @@
             "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
             "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
             "requires": {
-                "md5.js": "1.3.5",
-                "safe-buffer": "5.1.2"
+                "md5.js": "^1.3.4",
+                "safe-buffer": "^5.1.1"
             }
         },
         "execa": {
@@ -4666,13 +4666,13 @@
             "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
             "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
             "requires": {
-                "cross-spawn": "5.1.0",
-                "get-stream": "3.0.0",
-                "is-stream": "1.1.0",
-                "npm-run-path": "2.0.2",
-                "p-finally": "1.0.0",
-                "signal-exit": "3.0.2",
-                "strip-eof": "1.0.0"
+                "cross-spawn": "^5.0.1",
+                "get-stream": "^3.0.0",
+                "is-stream": "^1.1.0",
+                "npm-run-path": "^2.0.0",
+                "p-finally": "^1.0.0",
+                "signal-exit": "^3.0.0",
+                "strip-eof": "^1.0.0"
             },
             "dependencies": {
                 "cross-spawn": {
@@ -4680,9 +4680,9 @@
                     "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
                     "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
                     "requires": {
-                        "lru-cache": "4.1.5",
-                        "shebang-command": "1.2.0",
-                        "which": "1.3.1"
+                        "lru-cache": "^4.0.1",
+                        "shebang-command": "^1.2.0",
+                        "which": "^1.2.9"
                     }
                 }
             }
@@ -4697,7 +4697,7 @@
             "resolved": "https://registry.npmjs.org/exif/-/exif-0.6.0.tgz",
             "integrity": "sha1-YKYmaAdlQst+T1cZnUrG830sX0o=",
             "requires": {
-                "debug": "2.6.9"
+                "debug": "^2.2"
             }
         },
         "expand-brackets": {
@@ -4705,13 +4705,13 @@
             "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
             "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
             "requires": {
-                "debug": "2.6.9",
-                "define-property": "0.2.5",
-                "extend-shallow": "2.0.1",
-                "posix-character-classes": "0.1.1",
-                "regex-not": "1.0.2",
-                "snapdragon": "0.8.2",
-                "to-regex": "3.0.2"
+                "debug": "^2.3.3",
+                "define-property": "^0.2.5",
+                "extend-shallow": "^2.0.1",
+                "posix-character-classes": "^0.1.0",
+                "regex-not": "^1.0.0",
+                "snapdragon": "^0.8.1",
+                "to-regex": "^3.0.1"
             },
             "dependencies": {
                 "define-property": {
@@ -4719,7 +4719,7 @@
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
                     "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
                     "requires": {
-                        "is-descriptor": "0.1.6"
+                        "is-descriptor": "^0.1.0"
                     }
                 },
                 "extend-shallow": {
@@ -4727,7 +4727,7 @@
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
                     "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
                     "requires": {
-                        "is-extendable": "0.1.1"
+                        "is-extendable": "^0.1.0"
                     }
                 }
             }
@@ -4743,7 +4743,7 @@
             "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
             "dev": true,
             "requires": {
-                "homedir-polyfill": "1.0.3"
+                "homedir-polyfill": "^1.0.1"
             }
         },
         "express": {
@@ -4751,36 +4751,36 @@
             "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
             "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
             "requires": {
-                "accepts": "1.3.7",
+                "accepts": "~1.3.7",
                 "array-flatten": "1.1.1",
                 "body-parser": "1.19.0",
                 "content-disposition": "0.5.3",
-                "content-type": "1.0.4",
+                "content-type": "~1.0.4",
                 "cookie": "0.4.0",
                 "cookie-signature": "1.0.6",
                 "debug": "2.6.9",
-                "depd": "1.1.2",
-                "encodeurl": "1.0.2",
-                "escape-html": "1.0.3",
-                "etag": "1.8.1",
-                "finalhandler": "1.1.2",
+                "depd": "~1.1.2",
+                "encodeurl": "~1.0.2",
+                "escape-html": "~1.0.3",
+                "etag": "~1.8.1",
+                "finalhandler": "~1.1.2",
                 "fresh": "0.5.2",
                 "merge-descriptors": "1.0.1",
-                "methods": "1.1.2",
-                "on-finished": "2.3.0",
-                "parseurl": "1.3.3",
+                "methods": "~1.1.2",
+                "on-finished": "~2.3.0",
+                "parseurl": "~1.3.3",
                 "path-to-regexp": "0.1.7",
-                "proxy-addr": "2.0.5",
+                "proxy-addr": "~2.0.5",
                 "qs": "6.7.0",
-                "range-parser": "1.2.1",
+                "range-parser": "~1.2.1",
                 "safe-buffer": "5.1.2",
                 "send": "0.17.1",
                 "serve-static": "1.14.1",
                 "setprototypeof": "1.1.1",
-                "statuses": "1.5.0",
-                "type-is": "1.6.18",
+                "statuses": "~1.5.0",
+                "type-is": "~1.6.18",
                 "utils-merge": "1.0.1",
-                "vary": "1.1.2"
+                "vary": "~1.1.2"
             },
             "dependencies": {
                 "cookie": {
@@ -4795,7 +4795,7 @@
             "resolved": "https://registry.npmjs.org/express-flash/-/express-flash-0.0.2.tgz",
             "integrity": "sha1-I9GovPP5DXB5KOSJ+Whp7K0KzaI=",
             "requires": {
-                "connect-flash": "0.1.1"
+                "connect-flash": "0.1.x"
             }
         },
         "express-session": {
@@ -4806,11 +4806,11 @@
                 "cookie": "0.3.1",
                 "cookie-signature": "1.0.6",
                 "debug": "2.6.9",
-                "depd": "2.0.0",
-                "on-headers": "1.0.2",
-                "parseurl": "1.3.3",
+                "depd": "~2.0.0",
+                "on-headers": "~1.0.2",
+                "parseurl": "~1.3.3",
                 "safe-buffer": "5.1.2",
-                "uid-safe": "2.1.5"
+                "uid-safe": "~2.1.5"
             },
             "dependencies": {
                 "depd": {
@@ -4825,8 +4825,8 @@
             "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-5.3.1.tgz",
             "integrity": "sha512-g8xkipBF6VxHbO1+ksC7nxUU7+pWif0+OZXjZTybKJ/V0aTVhuCoHbyhIPgSYVldwQLocGExPtB2pE0DqK4jsw==",
             "requires": {
-                "lodash": "4.17.15",
-                "validator": "10.11.0"
+                "lodash": "^4.17.10",
+                "validator": "^10.4.0"
             }
         },
         "expressjs": {
@@ -4844,8 +4844,8 @@
             "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
             "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
             "requires": {
-                "assign-symbols": "1.0.0",
-                "is-extendable": "1.0.1"
+                "assign-symbols": "^1.0.0",
+                "is-extendable": "^1.0.1"
             },
             "dependencies": {
                 "is-extendable": {
@@ -4853,7 +4853,7 @@
                     "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
                     "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
                     "requires": {
-                        "is-plain-object": "2.0.4"
+                        "is-plain-object": "^2.0.4"
                     }
                 }
             }
@@ -4863,14 +4863,14 @@
             "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
             "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
             "requires": {
-                "array-unique": "0.3.2",
-                "define-property": "1.0.0",
-                "expand-brackets": "2.1.4",
-                "extend-shallow": "2.0.1",
-                "fragment-cache": "0.2.1",
-                "regex-not": "1.0.2",
-                "snapdragon": "0.8.2",
-                "to-regex": "3.0.2"
+                "array-unique": "^0.3.2",
+                "define-property": "^1.0.0",
+                "expand-brackets": "^2.1.4",
+                "extend-shallow": "^2.0.1",
+                "fragment-cache": "^0.2.1",
+                "regex-not": "^1.0.0",
+                "snapdragon": "^0.8.1",
+                "to-regex": "^3.0.1"
             },
             "dependencies": {
                 "define-property": {
@@ -4878,7 +4878,7 @@
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
                     "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
                     "requires": {
-                        "is-descriptor": "1.0.2"
+                        "is-descriptor": "^1.0.0"
                     }
                 },
                 "extend-shallow": {
@@ -4886,7 +4886,7 @@
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
                     "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
                     "requires": {
-                        "is-extendable": "0.1.1"
+                        "is-extendable": "^0.1.0"
                     }
                 },
                 "is-accessor-descriptor": {
@@ -4894,7 +4894,7 @@
                     "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
                     "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
                     "requires": {
-                        "kind-of": "6.0.2"
+                        "kind-of": "^6.0.0"
                     }
                 },
                 "is-data-descriptor": {
@@ -4902,7 +4902,7 @@
                     "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
                     "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
                     "requires": {
-                        "kind-of": "6.0.2"
+                        "kind-of": "^6.0.0"
                     }
                 },
                 "is-descriptor": {
@@ -4910,9 +4910,9 @@
                     "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
                     "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
                     "requires": {
-                        "is-accessor-descriptor": "1.0.0",
-                        "is-data-descriptor": "1.0.0",
-                        "kind-of": "6.0.2"
+                        "is-accessor-descriptor": "^1.0.0",
+                        "is-data-descriptor": "^1.0.0",
+                        "kind-of": "^6.0.2"
                     }
                 }
             }
@@ -4949,7 +4949,7 @@
             "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=",
             "dev": true,
             "requires": {
-                "websocket-driver": "0.7.3"
+                "websocket-driver": ">=0.5.1"
             }
         },
         "fbjs": {
@@ -4957,13 +4957,13 @@
             "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
             "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
             "requires": {
-                "core-js": "1.2.7",
-                "isomorphic-fetch": "2.2.1",
-                "loose-envify": "1.4.0",
-                "object-assign": "4.1.1",
-                "promise": "7.3.1",
-                "setimmediate": "1.0.5",
-                "ua-parser-js": "0.7.20"
+                "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": {
@@ -4978,8 +4978,8 @@
             "integrity": "sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==",
             "dev": true,
             "requires": {
-                "loader-utils": "1.2.3",
-                "schema-utils": "1.0.0"
+                "loader-utils": "^1.0.2",
+                "schema-utils": "^1.0.0"
             },
             "dependencies": {
                 "schema-utils": {
@@ -4988,9 +4988,9 @@
                     "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
                     "dev": true,
                     "requires": {
-                        "ajv": "6.10.2",
-                        "ajv-errors": "1.0.1",
-                        "ajv-keywords": "3.4.1"
+                        "ajv": "^6.1.0",
+                        "ajv-errors": "^1.0.0",
+                        "ajv-keywords": "^3.1.0"
                     }
                 }
             }
@@ -5001,7 +5001,7 @@
             "integrity": "sha1-9KGVc1Xdr0Q8zXiolfPVXiPIoDQ=",
             "dev": true,
             "requires": {
-                "debounce": "1.2.0"
+                "debounce": "^1.0.0"
             }
         },
         "fill-range": {
@@ -5009,10 +5009,10 @@
             "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
             "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
             "requires": {
-                "extend-shallow": "2.0.1",
-                "is-number": "3.0.0",
-                "repeat-string": "1.6.1",
-                "to-regex-range": "2.1.1"
+                "extend-shallow": "^2.0.1",
+                "is-number": "^3.0.0",
+                "repeat-string": "^1.6.1",
+                "to-regex-range": "^2.1.0"
             },
             "dependencies": {
                 "extend-shallow": {
@@ -5020,7 +5020,7 @@
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
                     "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
                     "requires": {
-                        "is-extendable": "0.1.1"
+                        "is-extendable": "^0.1.0"
                     }
                 }
             }
@@ -5031,12 +5031,12 @@
             "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
             "requires": {
                 "debug": "2.6.9",
-                "encodeurl": "1.0.2",
-                "escape-html": "1.0.3",
-                "on-finished": "2.3.0",
-                "parseurl": "1.3.3",
-                "statuses": "1.5.0",
-                "unpipe": "1.0.0"
+                "encodeurl": "~1.0.2",
+                "escape-html": "~1.0.3",
+                "on-finished": "~2.3.0",
+                "parseurl": "~1.3.3",
+                "statuses": "~1.5.0",
+                "unpipe": "~1.0.0"
             }
         },
         "find": {
@@ -5044,7 +5044,7 @@
             "resolved": "https://registry.npmjs.org/find/-/find-0.1.7.tgz",
             "integrity": "sha1-yGyHrxqxjyIrvjjeyGy8dg0Wpvs=",
             "requires": {
-                "traverse-chain": "0.1.0"
+                "traverse-chain": "~0.1.0"
             }
         },
         "find-cache-dir": {
@@ -5053,9 +5053,9 @@
             "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=",
             "dev": true,
             "requires": {
-                "commondir": "1.0.1",
-                "make-dir": "1.3.0",
-                "pkg-dir": "2.0.0"
+                "commondir": "^1.0.1",
+                "make-dir": "^1.0.0",
+                "pkg-dir": "^2.0.0"
             }
         },
         "find-in-files": {
@@ -5063,8 +5063,8 @@
             "resolved": "https://registry.npmjs.org/find-in-files/-/find-in-files-0.5.0.tgz",
             "integrity": "sha512-VraTc6HdtdSHmAp0yJpAy20yPttGKzyBWc7b7FPnnsX9TOgmKx0g9xajizpF/iuu4IvNK4TP0SpyBT9zAlwG+g==",
             "requires": {
-                "find": "0.1.7",
-                "q": "1.5.1"
+                "find": "^0.1.5",
+                "q": "^1.0.1"
             }
         },
         "find-root": {
@@ -5077,7 +5077,7 @@
             "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
             "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
             "requires": {
-                "locate-path": "2.0.0"
+                "locate-path": "^2.0.0"
             }
         },
         "findup-sync": {
@@ -5086,10 +5086,10 @@
             "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==",
             "dev": true,
             "requires": {
-                "detect-file": "1.0.0",
-                "is-glob": "4.0.1",
-                "micromatch": "3.1.10",
-                "resolve-dir": "1.0.1"
+                "detect-file": "^1.0.0",
+                "is-glob": "^4.0.0",
+                "micromatch": "^3.0.4",
+                "resolve-dir": "^1.0.1"
             }
         },
         "flat": {
@@ -5097,7 +5097,7 @@
             "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
             "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
             "requires": {
-                "is-buffer": "2.0.4"
+                "is-buffer": "~2.0.3"
             },
             "dependencies": {
                 "is-buffer": {
@@ -5118,8 +5118,8 @@
             "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
             "dev": true,
             "requires": {
-                "inherits": "2.0.3",
-                "readable-stream": "2.3.6"
+                "inherits": "^2.0.3",
+                "readable-stream": "^2.3.6"
             }
         },
         "follow-redirects": {
@@ -5128,7 +5128,7 @@
             "integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==",
             "dev": true,
             "requires": {
-                "debug": "3.2.6"
+                "debug": "^3.2.6"
             },
             "dependencies": {
                 "debug": {
@@ -5137,7 +5137,7 @@
                     "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
                     "dev": true,
                     "requires": {
-                        "ms": "2.1.2"
+                        "ms": "^2.1.1"
                     }
                 },
                 "ms": {
@@ -5158,7 +5158,7 @@
             "resolved": "https://registry.npmjs.org/for-each-property/-/for-each-property-0.0.4.tgz",
             "integrity": "sha1-z6hXrsFCLh0Sb/CHhPz2Jim8g/Y=",
             "requires": {
-                "get-prototype-chain": "1.0.1"
+                "get-prototype-chain": "^1.0.1"
             }
         },
         "for-each-property-deep": {
@@ -5180,7 +5180,7 @@
             "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
             "dev": true,
             "requires": {
-                "for-in": "1.0.2"
+                "for-in": "^1.0.1"
             }
         },
         "forever-agent": {
@@ -5194,14 +5194,14 @@
             "integrity": "sha512-srf43Z3B1hCJNrwCG78DbHmWgKQUqHKsvFbLP182gank28j9s05KJbSZaMKBA0b6Pqi0LBLpAFWeB0JPbc1iLQ==",
             "dev": true,
             "requires": {
-                "babel-code-frame": "6.26.0",
-                "chalk": "2.4.2",
-                "chokidar": "2.1.6",
-                "micromatch": "3.1.10",
-                "minimatch": "3.0.4",
-                "semver": "5.7.0",
-                "tapable": "1.1.3",
-                "worker-rpc": "0.1.1"
+                "babel-code-frame": "^6.22.0",
+                "chalk": "^2.4.1",
+                "chokidar": "^2.0.4",
+                "micromatch": "^3.1.10",
+                "minimatch": "^3.0.4",
+                "semver": "^5.6.0",
+                "tapable": "^1.0.0",
+                "worker-rpc": "^0.1.0"
             },
             "dependencies": {
                 "ansi-styles": {
@@ -5210,7 +5210,7 @@
                     "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
                     "dev": true,
                     "requires": {
-                        "color-convert": "1.9.3"
+                        "color-convert": "^1.9.0"
                     }
                 },
                 "chalk": {
@@ -5219,9 +5219,9 @@
                     "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
                     "dev": true,
                     "requires": {
-                        "ansi-styles": "3.2.1",
-                        "escape-string-regexp": "1.0.5",
-                        "supports-color": "5.5.0"
+                        "ansi-styles": "^3.2.1",
+                        "escape-string-regexp": "^1.0.5",
+                        "supports-color": "^5.3.0"
                     }
                 },
                 "supports-color": {
@@ -5230,7 +5230,7 @@
                     "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
                     "dev": true,
                     "requires": {
-                        "has-flag": "3.0.0"
+                        "has-flag": "^3.0.0"
                     }
                 }
             }
@@ -5240,9 +5240,9 @@
             "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.0.tgz",
             "integrity": "sha512-WXieX3G/8side6VIqx44ablyULoGruSde5PNTxoUyo5CeyAMX6nVWUd0rgist/EuX655cjhUhTo1Fo3tRYqbcA==",
             "requires": {
-                "asynckit": "0.4.0",
-                "combined-stream": "1.0.8",
-                "mime-types": "2.1.24"
+                "asynckit": "^0.4.0",
+                "combined-stream": "^1.0.6",
+                "mime-types": "^2.1.12"
             }
         },
         "formidable": {
@@ -5260,7 +5260,7 @@
             "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
             "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
             "requires": {
-                "map-cache": "0.2.2"
+                "map-cache": "^0.2.2"
             }
         },
         "fresh": {
@@ -5274,8 +5274,8 @@
             "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
             "dev": true,
             "requires": {
-                "inherits": "2.0.3",
-                "readable-stream": "2.3.6"
+                "inherits": "^2.0.1",
+                "readable-stream": "^2.0.0"
             }
         },
         "fs-constants": {
@@ -5293,11 +5293,11 @@
             "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz",
             "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=",
             "requires": {
-                "graceful-fs": "4.2.0",
-                "jsonfile": "2.4.0",
-                "klaw": "1.3.1",
-                "path-is-absolute": "1.0.1",
-                "rimraf": "2.7.1"
+                "graceful-fs": "^4.1.2",
+                "jsonfile": "^2.1.0",
+                "klaw": "^1.0.0",
+                "path-is-absolute": "^1.0.0",
+                "rimraf": "^2.2.8"
             },
             "dependencies": {
                 "rimraf": {
@@ -5305,7 +5305,7 @@
                     "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
                     "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
                     "requires": {
-                        "glob": "7.1.4"
+                        "glob": "^7.1.3"
                     }
                 }
             }
@@ -5315,7 +5315,7 @@
             "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.6.tgz",
             "integrity": "sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ==",
             "requires": {
-                "minipass": "2.3.5"
+                "minipass": "^2.2.1"
             }
         },
         "fs-write-stream-atomic": {
@@ -5324,10 +5324,10 @@
             "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
             "dev": true,
             "requires": {
-                "graceful-fs": "4.2.0",
-                "iferr": "0.1.5",
-                "imurmurhash": "0.1.4",
-                "readable-stream": "2.3.6"
+                "graceful-fs": "^4.1.2",
+                "iferr": "^0.1.5",
+                "imurmurhash": "^0.1.4",
+                "readable-stream": "1 || 2"
             }
         },
         "fs.realpath": {
@@ -5341,8 +5341,8 @@
             "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==",
             "optional": true,
             "requires": {
-                "nan": "2.14.0",
-                "node-pre-gyp": "0.12.0"
+                "nan": "^2.12.1",
+                "node-pre-gyp": "^0.12.0"
             },
             "dependencies": {
                 "abbrev": {
@@ -5352,7 +5352,8 @@
                 },
                 "ansi-regex": {
                     "version": "2.1.1",
-                    "bundled": true
+                    "bundled": true,
+                    "optional": true
                 },
                 "aproba": {
                     "version": "1.2.0",
@@ -5364,19 +5365,21 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "delegates": "1.0.0",
-                        "readable-stream": "2.3.6"
+                        "delegates": "^1.0.0",
+                        "readable-stream": "^2.0.6"
                     }
                 },
                 "balanced-match": {
                     "version": "1.0.0",
-                    "bundled": true
+                    "bundled": true,
+                    "optional": true
                 },
                 "brace-expansion": {
                     "version": "1.1.11",
                     "bundled": true,
+                    "optional": true,
                     "requires": {
-                        "balanced-match": "1.0.0",
+                        "balanced-match": "^1.0.0",
                         "concat-map": "0.0.1"
                     }
                 },
@@ -5387,15 +5390,18 @@
                 },
                 "code-point-at": {
                     "version": "1.1.0",
-                    "bundled": true
+                    "bundled": true,
+                    "optional": true
                 },
                 "concat-map": {
                     "version": "0.0.1",
-                    "bundled": true
+                    "bundled": true,
+                    "optional": true
                 },
                 "console-control-strings": {
                     "version": "1.1.0",
-                    "bundled": true
+                    "bundled": true,
+                    "optional": true
                 },
                 "core-util-is": {
                     "version": "1.0.2",
@@ -5407,7 +5413,7 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "ms": "2.1.1"
+                        "ms": "^2.1.1"
                     }
                 },
                 "deep-extend": {
@@ -5430,7 +5436,7 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "minipass": "2.3.5"
+                        "minipass": "^2.2.1"
                     }
                 },
                 "fs.realpath": {
@@ -5443,14 +5449,14 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "aproba": "1.2.0",
-                        "console-control-strings": "1.1.0",
-                        "has-unicode": "2.0.1",
-                        "object-assign": "4.1.1",
-                        "signal-exit": "3.0.2",
-                        "string-width": "1.0.2",
-                        "strip-ansi": "3.0.1",
-                        "wide-align": "1.1.3"
+                        "aproba": "^1.0.3",
+                        "console-control-strings": "^1.0.0",
+                        "has-unicode": "^2.0.0",
+                        "object-assign": "^4.1.0",
+                        "signal-exit": "^3.0.0",
+                        "string-width": "^1.0.1",
+                        "strip-ansi": "^3.0.1",
+                        "wide-align": "^1.1.0"
                     }
                 },
                 "glob": {
@@ -5458,12 +5464,12 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "fs.realpath": "1.0.0",
-                        "inflight": "1.0.6",
-                        "inherits": "2.0.3",
-                        "minimatch": "3.0.4",
-                        "once": "1.4.0",
-                        "path-is-absolute": "1.0.1"
+                        "fs.realpath": "^1.0.0",
+                        "inflight": "^1.0.4",
+                        "inherits": "2",
+                        "minimatch": "^3.0.4",
+                        "once": "^1.3.0",
+                        "path-is-absolute": "^1.0.0"
                     }
                 },
                 "has-unicode": {
@@ -5476,7 +5482,7 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "safer-buffer": "2.1.2"
+                        "safer-buffer": ">= 2.1.2 < 3"
                     }
                 },
                 "ignore-walk": {
@@ -5484,7 +5490,7 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "minimatch": "3.0.4"
+                        "minimatch": "^3.0.4"
                     }
                 },
                 "inflight": {
@@ -5492,13 +5498,14 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "once": "1.4.0",
-                        "wrappy": "1.0.2"
+                        "once": "^1.3.0",
+                        "wrappy": "1"
                     }
                 },
                 "inherits": {
                     "version": "2.0.3",
-                    "bundled": true
+                    "bundled": true,
+                    "optional": true
                 },
                 "ini": {
                     "version": "1.3.5",
@@ -5508,8 +5515,9 @@
                 "is-fullwidth-code-point": {
                     "version": "1.0.0",
                     "bundled": true,
+                    "optional": true,
                     "requires": {
-                        "number-is-nan": "1.0.1"
+                        "number-is-nan": "^1.0.0"
                     }
                 },
                 "isarray": {
@@ -5520,20 +5528,23 @@
                 "minimatch": {
                     "version": "3.0.4",
                     "bundled": true,
+                    "optional": true,
                     "requires": {
-                        "brace-expansion": "1.1.11"
+                        "brace-expansion": "^1.1.7"
                     }
                 },
                 "minimist": {
                     "version": "0.0.8",
-                    "bundled": true
+                    "bundled": true,
+                    "optional": true
                 },
                 "minipass": {
                     "version": "2.3.5",
                     "bundled": true,
+                    "optional": true,
                     "requires": {
-                        "safe-buffer": "5.1.2",
-                        "yallist": "3.0.3"
+                        "safe-buffer": "^5.1.2",
+                        "yallist": "^3.0.0"
                     }
                 },
                 "minizlib": {
@@ -5541,12 +5552,13 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "minipass": "2.3.5"
+                        "minipass": "^2.2.1"
                     }
                 },
                 "mkdirp": {
                     "version": "0.5.1",
                     "bundled": true,
+                    "optional": true,
                     "requires": {
                         "minimist": "0.0.8"
                     }
@@ -5561,9 +5573,9 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "debug": "4.1.1",
-                        "iconv-lite": "0.4.24",
-                        "sax": "1.2.4"
+                        "debug": "^4.1.0",
+                        "iconv-lite": "^0.4.4",
+                        "sax": "^1.2.4"
                     }
                 },
                 "node-pre-gyp": {
@@ -5571,16 +5583,16 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "detect-libc": "1.0.3",
-                        "mkdirp": "0.5.1",
-                        "needle": "2.3.0",
-                        "nopt": "4.0.1",
-                        "npm-packlist": "1.4.1",
-                        "npmlog": "4.1.2",
-                        "rc": "1.2.8",
-                        "rimraf": "2.6.3",
-                        "semver": "5.7.0",
-                        "tar": "4.4.8"
+                        "detect-libc": "^1.0.2",
+                        "mkdirp": "^0.5.1",
+                        "needle": "^2.2.1",
+                        "nopt": "^4.0.1",
+                        "npm-packlist": "^1.1.6",
+                        "npmlog": "^4.0.2",
+                        "rc": "^1.2.7",
+                        "rimraf": "^2.6.1",
+                        "semver": "^5.3.0",
+                        "tar": "^4"
                     }
                 },
                 "nopt": {
@@ -5588,8 +5600,8 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "abbrev": "1.1.1",
-                        "osenv": "0.1.5"
+                        "abbrev": "1",
+                        "osenv": "^0.1.4"
                     }
                 },
                 "npm-bundled": {
@@ -5602,8 +5614,8 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "ignore-walk": "3.0.1",
-                        "npm-bundled": "1.0.6"
+                        "ignore-walk": "^3.0.1",
+                        "npm-bundled": "^1.0.1"
                     }
                 },
                 "npmlog": {
@@ -5611,15 +5623,16 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "are-we-there-yet": "1.1.5",
-                        "console-control-strings": "1.1.0",
-                        "gauge": "2.7.4",
-                        "set-blocking": "2.0.0"
+                        "are-we-there-yet": "~1.1.2",
+                        "console-control-strings": "~1.1.0",
+                        "gauge": "~2.7.3",
+                        "set-blocking": "~2.0.0"
                     }
                 },
                 "number-is-nan": {
                     "version": "1.0.1",
-                    "bundled": true
+                    "bundled": true,
+                    "optional": true
                 },
                 "object-assign": {
                     "version": "4.1.1",
@@ -5629,8 +5642,9 @@
                 "once": {
                     "version": "1.4.0",
                     "bundled": true,
+                    "optional": true,
                     "requires": {
-                        "wrappy": "1.0.2"
+                        "wrappy": "1"
                     }
                 },
                 "os-homedir": {
@@ -5648,8 +5662,8 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "os-homedir": "1.0.2",
-                        "os-tmpdir": "1.0.2"
+                        "os-homedir": "^1.0.0",
+                        "os-tmpdir": "^1.0.0"
                     }
                 },
                 "path-is-absolute": {
@@ -5667,10 +5681,10 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "deep-extend": "0.6.0",
-                        "ini": "1.3.5",
-                        "minimist": "1.2.0",
-                        "strip-json-comments": "2.0.1"
+                        "deep-extend": "^0.6.0",
+                        "ini": "~1.3.0",
+                        "minimist": "^1.2.0",
+                        "strip-json-comments": "~2.0.1"
                     },
                     "dependencies": {
                         "minimist": {
@@ -5685,13 +5699,13 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "core-util-is": "1.0.2",
-                        "inherits": "2.0.3",
-                        "isarray": "1.0.0",
-                        "process-nextick-args": "2.0.0",
-                        "safe-buffer": "5.1.2",
-                        "string_decoder": "1.1.1",
-                        "util-deprecate": "1.0.2"
+                        "core-util-is": "~1.0.0",
+                        "inherits": "~2.0.3",
+                        "isarray": "~1.0.0",
+                        "process-nextick-args": "~2.0.0",
+                        "safe-buffer": "~5.1.1",
+                        "string_decoder": "~1.1.1",
+                        "util-deprecate": "~1.0.1"
                     }
                 },
                 "rimraf": {
@@ -5699,12 +5713,13 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "glob": "7.1.3"
+                        "glob": "^7.1.3"
                     }
                 },
                 "safe-buffer": {
                     "version": "5.1.2",
-                    "bundled": true
+                    "bundled": true,
+                    "optional": true
                 },
                 "safer-buffer": {
                     "version": "2.1.2",
@@ -5734,10 +5749,11 @@
                 "string-width": {
                     "version": "1.0.2",
                     "bundled": true,
+                    "optional": true,
                     "requires": {
-                        "code-point-at": "1.1.0",
-                        "is-fullwidth-code-point": "1.0.0",
-                        "strip-ansi": "3.0.1"
+                        "code-point-at": "^1.0.0",
+                        "is-fullwidth-code-point": "^1.0.0",
+                        "strip-ansi": "^3.0.0"
                     }
                 },
                 "string_decoder": {
@@ -5745,14 +5761,15 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "safe-buffer": "5.1.2"
+                        "safe-buffer": "~5.1.0"
                     }
                 },
                 "strip-ansi": {
                     "version": "3.0.1",
                     "bundled": true,
+                    "optional": true,
                     "requires": {
-                        "ansi-regex": "2.1.1"
+                        "ansi-regex": "^2.0.0"
                     }
                 },
                 "strip-json-comments": {
@@ -5765,13 +5782,13 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "chownr": "1.1.1",
-                        "fs-minipass": "1.2.5",
-                        "minipass": "2.3.5",
-                        "minizlib": "1.2.1",
-                        "mkdirp": "0.5.1",
-                        "safe-buffer": "5.1.2",
-                        "yallist": "3.0.3"
+                        "chownr": "^1.1.1",
+                        "fs-minipass": "^1.2.5",
+                        "minipass": "^2.3.4",
+                        "minizlib": "^1.1.1",
+                        "mkdirp": "^0.5.0",
+                        "safe-buffer": "^5.1.2",
+                        "yallist": "^3.0.2"
                     }
                 },
                 "util-deprecate": {
@@ -5784,16 +5801,18 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "string-width": "1.0.2"
+                        "string-width": "^1.0.2 || 2"
                     }
                 },
                 "wrappy": {
                     "version": "1.0.2",
-                    "bundled": true
+                    "bundled": true,
+                    "optional": true
                 },
                 "yallist": {
                     "version": "3.0.3",
-                    "bundled": true
+                    "bundled": true,
+                    "optional": true
                 }
             }
         },
@@ -5802,10 +5821,10 @@
             "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
             "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
             "requires": {
-                "graceful-fs": "4.2.0",
-                "inherits": "2.0.3",
-                "mkdirp": "0.5.1",
-                "rimraf": "2.7.1"
+                "graceful-fs": "^4.1.2",
+                "inherits": "~2.0.0",
+                "mkdirp": ">=0.5 0",
+                "rimraf": "2"
             },
             "dependencies": {
                 "rimraf": {
@@ -5813,7 +5832,7 @@
                     "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
                     "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
                     "requires": {
-                        "glob": "7.1.4"
+                        "glob": "^7.1.3"
                     }
                 }
             }
@@ -5828,14 +5847,14 @@
             "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
             "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
             "requires": {
-                "aproba": "1.2.0",
-                "console-control-strings": "1.1.0",
-                "has-unicode": "2.0.1",
-                "object-assign": "4.1.1",
-                "signal-exit": "3.0.2",
-                "string-width": "1.0.2",
-                "strip-ansi": "3.0.1",
-                "wide-align": "1.1.3"
+                "aproba": "^1.0.3",
+                "console-control-strings": "^1.0.0",
+                "has-unicode": "^2.0.0",
+                "object-assign": "^4.1.0",
+                "signal-exit": "^3.0.0",
+                "string-width": "^1.0.1",
+                "strip-ansi": "^3.0.1",
+                "wide-align": "^1.1.0"
             }
         },
         "gaxios": {
@@ -5843,10 +5862,10 @@
             "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.0.1.tgz",
             "integrity": "sha512-c1NXovTxkgRJTIgB2FrFmOFg4YIV6N/bAa4f/FZ4jIw13Ql9ya/82x69CswvotJhbV3DiGnlTZwoq2NVXk2Irg==",
             "requires": {
-                "abort-controller": "3.0.0",
-                "extend": "3.0.2",
-                "https-proxy-agent": "2.2.2",
-                "node-fetch": "2.6.0"
+                "abort-controller": "^3.0.0",
+                "extend": "^3.0.2",
+                "https-proxy-agent": "^2.2.1",
+                "node-fetch": "^2.3.0"
             },
             "dependencies": {
                 "node-fetch": {
@@ -5861,7 +5880,7 @@
             "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
             "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==",
             "requires": {
-                "globule": "1.2.1"
+                "globule": "^1.0.0"
             }
         },
         "gcp-metadata": {
@@ -5869,8 +5888,8 @@
             "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-2.0.1.tgz",
             "integrity": "sha512-nrbLj5O1MurvpLC/doFwzdTfKnmYGDYXlY/v7eQ4tJNVIvQXbOK672J9UFbradbtmuTkyHzjpzD8HD0Djz0LWw==",
             "requires": {
-                "gaxios": "2.0.1",
-                "json-bigint": "0.3.0"
+                "gaxios": "^2.0.0",
+                "json-bigint": "^0.3.0"
             }
         },
         "get-caller-file": {
@@ -5918,7 +5937,7 @@
             "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
             "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
             "requires": {
-                "assert-plus": "1.0.0"
+                "assert-plus": "^1.0.0"
             },
             "dependencies": {
                 "assert-plus": {
@@ -5938,12 +5957,12 @@
             "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
             "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
             "requires": {
-                "fs.realpath": "1.0.0",
-                "inflight": "1.0.6",
-                "inherits": "2.0.3",
-                "minimatch": "3.0.4",
-                "once": "1.4.0",
-                "path-is-absolute": "1.0.1"
+                "fs.realpath": "^1.0.0",
+                "inflight": "^1.0.4",
+                "inherits": "2",
+                "minimatch": "^3.0.4",
+                "once": "^1.3.0",
+                "path-is-absolute": "^1.0.0"
             }
         },
         "glob-parent": {
@@ -5951,8 +5970,8 @@
             "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
             "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
             "requires": {
-                "is-glob": "3.1.0",
-                "path-dirname": "1.0.2"
+                "is-glob": "^3.1.0",
+                "path-dirname": "^1.0.0"
             },
             "dependencies": {
                 "is-glob": {
@@ -5960,7 +5979,7 @@
                     "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
                     "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
                     "requires": {
-                        "is-extglob": "2.1.1"
+                        "is-extglob": "^2.1.0"
                     }
                 }
             }
@@ -5970,7 +5989,7 @@
             "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
             "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
             "requires": {
-                "ini": "1.3.5"
+                "ini": "^1.3.4"
             }
         },
         "global-modules": {
@@ -5979,7 +5998,7 @@
             "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==",
             "dev": true,
             "requires": {
-                "global-prefix": "3.0.0"
+                "global-prefix": "^3.0.0"
             },
             "dependencies": {
                 "global-prefix": {
@@ -5988,9 +6007,9 @@
                     "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
                     "dev": true,
                     "requires": {
-                        "ini": "1.3.5",
-                        "kind-of": "6.0.2",
-                        "which": "1.3.1"
+                        "ini": "^1.3.5",
+                        "kind-of": "^6.0.2",
+                        "which": "^1.3.1"
                     }
                 }
             }
@@ -6001,11 +6020,11 @@
             "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
             "dev": true,
             "requires": {
-                "expand-tilde": "2.0.2",
-                "homedir-polyfill": "1.0.3",
-                "ini": "1.3.5",
-                "is-windows": "1.0.2",
-                "which": "1.3.1"
+                "expand-tilde": "^2.0.2",
+                "homedir-polyfill": "^1.0.1",
+                "ini": "^1.3.4",
+                "is-windows": "^1.0.1",
+                "which": "^1.2.14"
             }
         },
         "globby": {
@@ -6014,12 +6033,12 @@
             "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=",
             "dev": true,
             "requires": {
-                "array-union": "1.0.2",
-                "dir-glob": "2.2.2",
-                "glob": "7.1.4",
-                "ignore": "3.3.10",
-                "pify": "3.0.0",
-                "slash": "1.0.0"
+                "array-union": "^1.0.1",
+                "dir-glob": "^2.0.0",
+                "glob": "^7.1.2",
+                "ignore": "^3.3.5",
+                "pify": "^3.0.0",
+                "slash": "^1.0.0"
             },
             "dependencies": {
                 "pify": {
@@ -6035,9 +6054,9 @@
             "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz",
             "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==",
             "requires": {
-                "glob": "7.1.4",
-                "lodash": "4.17.15",
-                "minimatch": "3.0.4"
+                "glob": "~7.1.1",
+                "lodash": "~4.17.10",
+                "minimatch": "~3.0.2"
             }
         },
         "golden-layout": {
@@ -6045,7 +6064,7 @@
             "resolved": "https://registry.npmjs.org/golden-layout/-/golden-layout-1.5.9.tgz",
             "integrity": "sha1-o5vB9qZ+b4hreXwBbdkk6UJrp38=",
             "requires": {
-                "jquery": "3.4.1"
+                "jquery": "*"
             }
         },
         "google-auth-library": {
@@ -6053,14 +6072,14 @@
             "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-4.2.6.tgz",
             "integrity": "sha512-oJ6tCA9rbsYeIVY+mcLPFHa2hatz3XO6idYIrlI/KhhlMxZrO3tKyU8O2Pxu5KnSBBP7Wj4HtbM1LLKngNFaFw==",
             "requires": {
-                "arrify": "2.0.1",
-                "base64-js": "1.3.0",
-                "fast-text-encoding": "1.0.0",
-                "gaxios": "2.0.1",
-                "gcp-metadata": "2.0.1",
-                "gtoken": "3.0.2",
-                "jws": "3.2.2",
-                "lru-cache": "5.1.1"
+                "arrify": "^2.0.0",
+                "base64-js": "^1.3.0",
+                "fast-text-encoding": "^1.0.0",
+                "gaxios": "^2.0.0",
+                "gcp-metadata": "^2.0.0",
+                "gtoken": "^3.0.0",
+                "jws": "^3.1.5",
+                "lru-cache": "^5.0.0"
             },
             "dependencies": {
                 "arrify": {
@@ -6073,7 +6092,7 @@
                     "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
                     "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
                     "requires": {
-                        "yallist": "3.0.3"
+                        "yallist": "^3.0.2"
                     }
                 }
             }
@@ -6083,7 +6102,7 @@
             "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.1.tgz",
             "integrity": "sha512-6h6x+eBX3k+IDSe/c8dVYmn8Mzr1mUcmKC9MdUSwaBkFAXlqBEnwFWmSFgGC+tcqtsLn73BDP/vUNWEehf1Rww==",
             "requires": {
-                "node-forge": "0.8.5"
+                "node-forge": "^0.8.0"
             },
             "dependencies": {
                 "node-forge": {
@@ -6098,8 +6117,8 @@
             "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-40.0.1.tgz",
             "integrity": "sha512-B6qZVCautOOspEhru9GZ814I+ztkGWyA4ZEUfaXwXHBruX/HAWqedbsuUEx1w3nCECywK/FLTNUdcbH9zpaMaw==",
             "requires": {
-                "google-auth-library": "4.2.6",
-                "googleapis-common": "2.0.4"
+                "google-auth-library": "^4.0.0",
+                "googleapis-common": "^2.0.2"
             }
         },
         "googleapis-common": {
@@ -6107,12 +6126,12 @@
             "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-2.0.4.tgz",
             "integrity": "sha512-8RRkxr24v1jIKCC1onFWA8RGnwFV55m3Qpil9DLX1yLc9e5qvOJsRoDOhhD2e7jFRONYEhT/BzT8vJZANqSr9w==",
             "requires": {
-                "extend": "3.0.2",
-                "gaxios": "2.0.1",
-                "google-auth-library": "4.2.6",
-                "qs": "6.7.0",
-                "url-template": "2.0.8",
-                "uuid": "3.3.2"
+                "extend": "^3.0.2",
+                "gaxios": "^2.0.1",
+                "google-auth-library": "^4.2.5",
+                "qs": "^6.7.0",
+                "url-template": "^2.0.8",
+                "uuid": "^3.3.2"
             }
         },
         "googlephotos": {
@@ -6120,8 +6139,8 @@
             "resolved": "https://registry.npmjs.org/googlephotos/-/googlephotos-0.2.1.tgz",
             "integrity": "sha512-BCDFBGvv3CgceAc4/+AdbLebqVSYZTJx6qjoZTukQXRz86uEnR5GlYgVlVoBYqVypZS92poRRVBMVVdZhItdwg==",
             "requires": {
-                "request": "2.88.0",
-                "request-promise": "4.2.4"
+                "request": "^2.86.0",
+                "request-promise": "^4.2.2"
             },
             "dependencies": {
                 "assert-plus": {
@@ -6144,9 +6163,9 @@
                     "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
                     "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
                     "requires": {
-                        "asynckit": "0.4.0",
-                        "combined-stream": "1.0.8",
-                        "mime-types": "2.1.24"
+                        "asynckit": "^0.4.0",
+                        "combined-stream": "^1.0.6",
+                        "mime-types": "^2.1.12"
                     }
                 },
                 "har-validator": {
@@ -6154,8 +6173,8 @@
                     "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
                     "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
                     "requires": {
-                        "ajv": "6.10.2",
-                        "har-schema": "2.0.0"
+                        "ajv": "^6.5.5",
+                        "har-schema": "^2.0.0"
                     }
                 },
                 "http-signature": {
@@ -6163,9 +6182,9 @@
                     "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
                     "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
                     "requires": {
-                        "assert-plus": "1.0.0",
-                        "jsprim": "1.4.1",
-                        "sshpk": "1.16.1"
+                        "assert-plus": "^1.0.0",
+                        "jsprim": "^1.2.2",
+                        "sshpk": "^1.7.0"
                     }
                 },
                 "oauth-sign": {
@@ -6183,26 +6202,26 @@
                     "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
                     "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
                     "requires": {
-                        "aws-sign2": "0.7.0",
-                        "aws4": "1.9.0",
-                        "caseless": "0.12.0",
-                        "combined-stream": "1.0.8",
-                        "extend": "3.0.2",
-                        "forever-agent": "0.6.1",
-                        "form-data": "2.3.3",
-                        "har-validator": "5.1.3",
-                        "http-signature": "1.2.0",
-                        "is-typedarray": "1.0.0",
-                        "isstream": "0.1.2",
-                        "json-stringify-safe": "5.0.1",
-                        "mime-types": "2.1.24",
-                        "oauth-sign": "0.9.0",
-                        "performance-now": "2.1.0",
-                        "qs": "6.5.2",
-                        "safe-buffer": "5.1.2",
-                        "tough-cookie": "2.4.3",
-                        "tunnel-agent": "0.6.0",
-                        "uuid": "3.3.2"
+                        "aws-sign2": "~0.7.0",
+                        "aws4": "^1.8.0",
+                        "caseless": "~0.12.0",
+                        "combined-stream": "~1.0.6",
+                        "extend": "~3.0.2",
+                        "forever-agent": "~0.6.1",
+                        "form-data": "~2.3.2",
+                        "har-validator": "~5.1.0",
+                        "http-signature": "~1.2.0",
+                        "is-typedarray": "~1.0.0",
+                        "isstream": "~0.1.2",
+                        "json-stringify-safe": "~5.0.1",
+                        "mime-types": "~2.1.19",
+                        "oauth-sign": "~0.9.0",
+                        "performance-now": "^2.1.0",
+                        "qs": "~6.5.2",
+                        "safe-buffer": "^5.1.2",
+                        "tough-cookie": "~2.4.3",
+                        "tunnel-agent": "^0.6.0",
+                        "uuid": "^3.3.2"
                     }
                 }
             }
@@ -6212,17 +6231,17 @@
             "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
             "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
             "requires": {
-                "create-error-class": "3.0.2",
-                "duplexer3": "0.1.4",
-                "get-stream": "3.0.0",
-                "is-redirect": "1.0.0",
-                "is-retry-allowed": "1.1.0",
-                "is-stream": "1.1.0",
-                "lowercase-keys": "1.0.1",
-                "safe-buffer": "5.1.2",
-                "timed-out": "4.0.1",
-                "unzip-response": "2.0.1",
-                "url-parse-lax": "1.0.0"
+                "create-error-class": "^3.0.0",
+                "duplexer3": "^0.1.4",
+                "get-stream": "^3.0.0",
+                "is-redirect": "^1.0.0",
+                "is-retry-allowed": "^1.0.0",
+                "is-stream": "^1.0.0",
+                "lowercase-keys": "^1.0.0",
+                "safe-buffer": "^5.0.1",
+                "timed-out": "^4.0.0",
+                "unzip-response": "^2.0.1",
+                "url-parse-lax": "^1.0.0"
             }
         },
         "graceful-fs": {
@@ -6246,10 +6265,10 @@
             "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-3.0.2.tgz",
             "integrity": "sha512-BOBi6Zz31JfxhSHRZBIDdbwIbOPyux10WxJHdx8wz/FMP1zyN1xFrsAWsgcLe5ww5v/OZu/MePUEZAjgJXSauA==",
             "requires": {
-                "gaxios": "2.0.1",
-                "google-p12-pem": "2.0.1",
-                "jws": "3.2.2",
-                "mime": "2.4.4"
+                "gaxios": "^2.0.0",
+                "google-p12-pem": "^2.0.0",
+                "jws": "^3.1.5",
+                "mime": "^2.2.0"
             },
             "dependencies": {
                 "mime": {
@@ -6280,8 +6299,8 @@
             "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
             "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
             "requires": {
-                "ajv": "6.10.2",
-                "har-schema": "2.0.0"
+                "ajv": "^6.5.5",
+                "har-schema": "^2.0.0"
             }
         },
         "has": {
@@ -6289,7 +6308,7 @@
             "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
             "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
             "requires": {
-                "function-bind": "1.1.1"
+                "function-bind": "^1.1.1"
             }
         },
         "has-ansi": {
@@ -6297,7 +6316,7 @@
             "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
             "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
             "requires": {
-                "ansi-regex": "2.1.1"
+                "ansi-regex": "^2.0.0"
             }
         },
         "has-binary2": {
@@ -6340,9 +6359,9 @@
             "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
             "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
             "requires": {
-                "get-value": "2.0.6",
-                "has-values": "1.0.0",
-                "isobject": "3.0.1"
+                "get-value": "^2.0.6",
+                "has-values": "^1.0.0",
+                "isobject": "^3.0.0"
             }
         },
         "has-values": {
@@ -6350,8 +6369,8 @@
             "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
             "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
             "requires": {
-                "is-number": "3.0.0",
-                "kind-of": "4.0.0"
+                "is-number": "^3.0.0",
+                "kind-of": "^4.0.0"
             },
             "dependencies": {
                 "kind-of": {
@@ -6359,7 +6378,7 @@
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
                     "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
                     "requires": {
-                        "is-buffer": "1.1.6"
+                        "is-buffer": "^1.1.5"
                     }
                 }
             }
@@ -6369,8 +6388,8 @@
             "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
             "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
             "requires": {
-                "inherits": "2.0.3",
-                "safe-buffer": "5.1.2"
+                "inherits": "^2.0.1",
+                "safe-buffer": "^5.0.1"
             }
         },
         "hash.js": {
@@ -6378,8 +6397,8 @@
             "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
             "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
             "requires": {
-                "inherits": "2.0.3",
-                "minimalistic-assert": "1.0.1"
+                "inherits": "^2.0.3",
+                "minimalistic-assert": "^1.0.1"
             }
         },
         "he": {
@@ -6393,9 +6412,9 @@
             "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
             "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
             "requires": {
-                "hash.js": "1.1.7",
-                "minimalistic-assert": "1.0.1",
-                "minimalistic-crypto-utils": "1.0.1"
+                "hash.js": "^1.0.3",
+                "minimalistic-assert": "^1.0.0",
+                "minimalistic-crypto-utils": "^1.0.1"
             }
         },
         "hoist-non-react-statics": {
@@ -6403,7 +6422,7 @@
             "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz",
             "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==",
             "requires": {
-                "react-is": "16.8.6"
+                "react-is": "^16.7.0"
             }
         },
         "homedir-polyfill": {
@@ -6412,7 +6431,7 @@
             "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
             "dev": true,
             "requires": {
-                "parse-passwd": "1.0.0"
+                "parse-passwd": "^1.0.0"
             }
         },
         "hosted-git-info": {
@@ -6431,10 +6450,10 @@
             "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=",
             "dev": true,
             "requires": {
-                "inherits": "2.0.3",
-                "obuf": "1.1.2",
-                "readable-stream": "2.3.6",
-                "wbuf": "1.7.3"
+                "inherits": "^2.0.1",
+                "obuf": "^1.0.0",
+                "readable-stream": "^2.0.1",
+                "wbuf": "^1.1.0"
             }
         },
         "html-encoding-sniffer": {
@@ -6443,7 +6462,7 @@
             "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
             "dev": true,
             "requires": {
-                "whatwg-encoding": "1.0.5"
+                "whatwg-encoding": "^1.0.1"
             }
         },
         "html-entities": {
@@ -6462,12 +6481,12 @@
             "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
             "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
             "requires": {
-                "domelementtype": "1.3.1",
-                "domhandler": "2.4.2",
-                "domutils": "1.5.1",
-                "entities": "1.1.2",
-                "inherits": "2.0.3",
-                "readable-stream": "3.4.0"
+                "domelementtype": "^1.3.1",
+                "domhandler": "^2.3.0",
+                "domutils": "^1.5.1",
+                "entities": "^1.1.1",
+                "inherits": "^2.0.1",
+                "readable-stream": "^3.1.1"
             },
             "dependencies": {
                 "readable-stream": {
@@ -6475,9 +6494,9 @@
                     "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
                     "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
                     "requires": {
-                        "inherits": "2.0.3",
-                        "string_decoder": "1.1.1",
-                        "util-deprecate": "1.0.2"
+                        "inherits": "^2.0.3",
+                        "string_decoder": "^1.1.1",
+                        "util-deprecate": "^1.0.1"
                     }
                 }
             }
@@ -6493,10 +6512,10 @@
             "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
             "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
             "requires": {
-                "depd": "1.1.2",
+                "depd": "~1.1.2",
                 "inherits": "2.0.3",
                 "setprototypeof": "1.1.1",
-                "statuses": "1.5.0",
+                "statuses": ">= 1.5.0 < 2",
                 "toidentifier": "1.0.0"
             }
         },
@@ -6512,9 +6531,9 @@
             "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==",
             "dev": true,
             "requires": {
-                "eventemitter3": "3.1.2",
-                "follow-redirects": "1.7.0",
-                "requires-port": "1.0.0"
+                "eventemitter3": "^3.0.0",
+                "follow-redirects": "^1.0.0",
+                "requires-port": "^1.0.0"
             },
             "dependencies": {
                 "eventemitter3": {
@@ -6531,10 +6550,10 @@
             "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==",
             "dev": true,
             "requires": {
-                "http-proxy": "1.17.0",
-                "is-glob": "4.0.1",
-                "lodash": "4.17.15",
-                "micromatch": "3.1.10"
+                "http-proxy": "^1.17.0",
+                "is-glob": "^4.0.0",
+                "lodash": "^4.17.11",
+                "micromatch": "^3.1.10"
             }
         },
         "http-signature": {
@@ -6542,9 +6561,9 @@
             "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
             "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
             "requires": {
-                "assert-plus": "1.0.0",
-                "jsprim": "1.4.1",
-                "sshpk": "1.16.1"
+                "assert-plus": "^1.0.0",
+                "jsprim": "^1.2.2",
+                "sshpk": "^1.7.0"
             }
         },
         "https-browserify": {
@@ -6558,8 +6577,8 @@
             "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz",
             "integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==",
             "requires": {
-                "agent-base": "4.3.0",
-                "debug": "3.2.6"
+                "agent-base": "^4.3.0",
+                "debug": "^3.1.0"
             },
             "dependencies": {
                 "debug": {
@@ -6567,7 +6586,7 @@
                     "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
                     "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
                     "requires": {
-                        "ms": "2.1.2"
+                        "ms": "^2.1.1"
                     }
                 },
                 "ms": {
@@ -6592,7 +6611,7 @@
             "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
             "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
             "requires": {
-                "safer-buffer": "2.1.2"
+                "safer-buffer": ">= 2.1.2 < 3"
             }
         },
         "icss-replace-symbols": {
@@ -6607,7 +6626,7 @@
             "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==",
             "dev": true,
             "requires": {
-                "postcss": "7.0.17"
+                "postcss": "^7.0.14"
             }
         },
         "ieee754": {
@@ -6637,7 +6656,7 @@
             "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz",
             "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
             "requires": {
-                "minimatch": "3.0.4"
+                "minimatch": "^3.0.4"
             }
         },
         "image-data-uri": {
@@ -6645,10 +6664,10 @@
             "resolved": "https://registry.npmjs.org/image-data-uri/-/image-data-uri-2.0.0.tgz",
             "integrity": "sha512-PhIJxgfSQai/Xy8Nij1lWgK6++Y6x/ga2FKQTd8F71Nz2ArqtFr1F1UAREK0twrfp7mcEqidgGSF06No14/m+Q==",
             "requires": {
-                "fs-extra": "0.26.7",
+                "fs-extra": "^0.26.7",
                 "magicli": "0.0.8",
-                "mime-types": "2.1.24",
-                "request": "2.88.0"
+                "mime-types": "^2.1.18",
+                "request": "^2.88.0"
             },
             "dependencies": {
                 "assert-plus": {
@@ -6671,9 +6690,9 @@
                     "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
                     "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
                     "requires": {
-                        "asynckit": "0.4.0",
-                        "combined-stream": "1.0.8",
-                        "mime-types": "2.1.24"
+                        "asynckit": "^0.4.0",
+                        "combined-stream": "^1.0.6",
+                        "mime-types": "^2.1.12"
                     }
                 },
                 "har-validator": {
@@ -6681,8 +6700,8 @@
                     "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
                     "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
                     "requires": {
-                        "ajv": "6.10.2",
-                        "har-schema": "2.0.0"
+                        "ajv": "^6.5.5",
+                        "har-schema": "^2.0.0"
                     }
                 },
                 "http-signature": {
@@ -6690,9 +6709,9 @@
                     "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
                     "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
                     "requires": {
-                        "assert-plus": "1.0.0",
-                        "jsprim": "1.4.1",
-                        "sshpk": "1.16.1"
+                        "assert-plus": "^1.0.0",
+                        "jsprim": "^1.2.2",
+                        "sshpk": "^1.7.0"
                     }
                 },
                 "oauth-sign": {
@@ -6710,26 +6729,26 @@
                     "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
                     "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
                     "requires": {
-                        "aws-sign2": "0.7.0",
-                        "aws4": "1.9.0",
-                        "caseless": "0.12.0",
-                        "combined-stream": "1.0.8",
-                        "extend": "3.0.2",
-                        "forever-agent": "0.6.1",
-                        "form-data": "2.3.3",
-                        "har-validator": "5.1.3",
-                        "http-signature": "1.2.0",
-                        "is-typedarray": "1.0.0",
-                        "isstream": "0.1.2",
-                        "json-stringify-safe": "5.0.1",
-                        "mime-types": "2.1.24",
-                        "oauth-sign": "0.9.0",
-                        "performance-now": "2.1.0",
-                        "qs": "6.5.2",
-                        "safe-buffer": "5.1.2",
-                        "tough-cookie": "2.4.3",
-                        "tunnel-agent": "0.6.0",
-                        "uuid": "3.3.2"
+                        "aws-sign2": "~0.7.0",
+                        "aws4": "^1.8.0",
+                        "caseless": "~0.12.0",
+                        "combined-stream": "~1.0.6",
+                        "extend": "~3.0.2",
+                        "forever-agent": "~0.6.1",
+                        "form-data": "~2.3.2",
+                        "har-validator": "~5.1.0",
+                        "http-signature": "~1.2.0",
+                        "is-typedarray": "~1.0.0",
+                        "isstream": "~0.1.2",
+                        "json-stringify-safe": "~5.0.1",
+                        "mime-types": "~2.1.19",
+                        "oauth-sign": "~0.9.0",
+                        "performance-now": "^2.1.0",
+                        "qs": "~6.5.2",
+                        "safe-buffer": "^5.1.2",
+                        "tough-cookie": "~2.4.3",
+                        "tunnel-agent": "^0.6.0",
+                        "uuid": "^3.3.2"
                     }
                 }
             }
@@ -6744,7 +6763,7 @@
             "resolved": "https://registry.npmjs.org/imagesloaded/-/imagesloaded-4.1.4.tgz",
             "integrity": "sha512-ltiBVcYpc/TYTF5nolkMNsnREHW+ICvfQ3Yla2Sgr71YFwQ86bDwV9hgpFhFtrGPuwEx5+LqOHIrdXBdoWwwsA==",
             "requires": {
-                "ev-emitter": "1.1.1"
+                "ev-emitter": "^1.0.0"
             }
         },
         "immutable": {
@@ -6757,8 +6776,8 @@
             "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
             "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
             "requires": {
-                "parent-module": "1.0.1",
-                "resolve-from": "4.0.0"
+                "parent-module": "^1.0.0",
+                "resolve-from": "^4.0.0"
             },
             "dependencies": {
                 "resolve-from": {
@@ -6779,8 +6798,8 @@
             "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==",
             "dev": true,
             "requires": {
-                "pkg-dir": "3.0.0",
-                "resolve-cwd": "2.0.0"
+                "pkg-dir": "^3.0.0",
+                "resolve-cwd": "^2.0.0"
             },
             "dependencies": {
                 "find-up": {
@@ -6789,7 +6808,7 @@
                     "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
                     "dev": true,
                     "requires": {
-                        "locate-path": "3.0.0"
+                        "locate-path": "^3.0.0"
                     }
                 },
                 "locate-path": {
@@ -6798,8 +6817,8 @@
                     "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
                     "dev": true,
                     "requires": {
-                        "p-locate": "3.0.0",
-                        "path-exists": "3.0.0"
+                        "p-locate": "^3.0.0",
+                        "path-exists": "^3.0.0"
                     }
                 },
                 "p-locate": {
@@ -6808,7 +6827,7 @@
                     "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
                     "dev": true,
                     "requires": {
-                        "p-limit": "2.2.0"
+                        "p-limit": "^2.0.0"
                     }
                 },
                 "pkg-dir": {
@@ -6817,7 +6836,7 @@
                     "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
                     "dev": true,
                     "requires": {
-                        "find-up": "3.0.0"
+                        "find-up": "^3.0.0"
                     }
                 }
             }
@@ -6837,7 +6856,7 @@
             "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
             "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
             "requires": {
-                "repeating": "2.0.1"
+                "repeating": "^2.0.0"
             }
         },
         "indexes-of": {
@@ -6856,8 +6875,8 @@
             "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
             "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
             "requires": {
-                "once": "1.4.0",
-                "wrappy": "1.0.2"
+                "once": "^1.3.0",
+                "wrappy": "1"
             }
         },
         "infobox-parser": {
@@ -6865,7 +6884,7 @@
             "resolved": "https://registry.npmjs.org/infobox-parser/-/infobox-parser-3.3.1.tgz",
             "integrity": "sha512-Aj1uF/taawGhet8cazhXz2uEDFMOqH8hnuw720wvi7Zw6bJWmA45Ta2FI9xMG5wvvo4CB6GR9S1/RUFtC6EtAg==",
             "requires": {
-                "camelcase": "4.1.0"
+                "camelcase": "^4.1.0"
             }
         },
         "inherits": {
@@ -6910,10 +6929,10 @@
                     "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz",
                     "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=",
                     "requires": {
-                        "commander": "2.20.0",
-                        "get-stdin": "5.0.1",
-                        "inspect-function": "0.2.2",
-                        "pipe-functions": "1.3.0"
+                        "commander": "^2.9.0",
+                        "get-stdin": "^5.0.1",
+                        "inspect-function": "^0.2.1",
+                        "pipe-functions": "^1.2.0"
                     }
                 }
             }
@@ -6925,7 +6944,7 @@
             "requires": {
                 "for-each-property": "0.0.4",
                 "for-each-property-deep": "0.0.3",
-                "inspect-function": "0.3.4"
+                "inspect-function": "^0.3.1"
             },
             "dependencies": {
                 "inspect-function": {
@@ -6972,10 +6991,10 @@
                             "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz",
                             "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=",
                             "requires": {
-                                "commander": "2.20.0",
-                                "get-stdin": "5.0.1",
-                                "inspect-function": "0.2.2",
-                                "pipe-functions": "1.3.0"
+                                "commander": "^2.9.0",
+                                "get-stdin": "^5.0.1",
+                                "inspect-function": "^0.2.1",
+                                "pipe-functions": "^1.2.0"
                             }
                         },
                         "split-skip": {
@@ -6998,8 +7017,8 @@
             "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==",
             "dev": true,
             "requires": {
-                "default-gateway": "4.2.0",
-                "ipaddr.js": "1.9.0"
+                "default-gateway": "^4.2.0",
+                "ipaddr.js": "^1.9.0"
             }
         },
         "interpret": {
@@ -7012,7 +7031,7 @@
             "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
             "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
             "requires": {
-                "loose-envify": "1.4.0"
+                "loose-envify": "^1.0.0"
             }
         },
         "invert-kv": {
@@ -7056,7 +7075,7 @@
             "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
             "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
             "requires": {
-                "kind-of": "3.2.2"
+                "kind-of": "^3.0.2"
             },
             "dependencies": {
                 "kind-of": {
@@ -7064,7 +7083,7 @@
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
                     "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
-                        "is-buffer": "1.1.6"
+                        "is-buffer": "^1.1.5"
                     }
                 }
             }
@@ -7079,7 +7098,7 @@
             "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
             "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
             "requires": {
-                "binary-extensions": "1.13.1"
+                "binary-extensions": "^1.0.0"
             }
         },
         "is-buffer": {
@@ -7097,7 +7116,7 @@
             "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz",
             "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==",
             "requires": {
-                "ci-info": "1.6.0"
+                "ci-info": "^1.5.0"
             }
         },
         "is-data-descriptor": {
@@ -7105,7 +7124,7 @@
             "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
             "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
             "requires": {
-                "kind-of": "3.2.2"
+                "kind-of": "^3.0.2"
             },
             "dependencies": {
                 "kind-of": {
@@ -7113,7 +7132,7 @@
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
                     "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
-                        "is-buffer": "1.1.6"
+                        "is-buffer": "^1.1.5"
                     }
                 }
             }
@@ -7128,9 +7147,9 @@
             "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
             "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
             "requires": {
-                "is-accessor-descriptor": "0.1.6",
-                "is-data-descriptor": "0.1.4",
-                "kind-of": "5.1.0"
+                "is-accessor-descriptor": "^0.1.6",
+                "is-data-descriptor": "^0.1.4",
+                "kind-of": "^5.0.0"
             },
             "dependencies": {
                 "kind-of": {
@@ -7145,8 +7164,8 @@
             "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz",
             "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=",
             "requires": {
-                "acorn": "4.0.13",
-                "object-assign": "4.1.1"
+                "acorn": "~4.0.2",
+                "object-assign": "^4.0.1"
             },
             "dependencies": {
                 "acorn": {
@@ -7171,7 +7190,7 @@
             "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
             "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
             "requires": {
-                "number-is-nan": "1.0.1"
+                "number-is-nan": "^1.0.0"
             }
         },
         "is-fullwidth-code-point": {
@@ -7179,7 +7198,7 @@
             "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
             "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
             "requires": {
-                "number-is-nan": "1.0.1"
+                "number-is-nan": "^1.0.0"
             }
         },
         "is-glob": {
@@ -7187,7 +7206,7 @@
             "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
             "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
             "requires": {
-                "is-extglob": "2.1.1"
+                "is-extglob": "^2.1.1"
             }
         },
         "is-installed-globally": {
@@ -7195,8 +7214,8 @@
             "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz",
             "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=",
             "requires": {
-                "global-dirs": "0.1.1",
-                "is-path-inside": "1.0.1"
+                "global-dirs": "^0.1.0",
+                "is-path-inside": "^1.0.0"
             }
         },
         "is-npm": {
@@ -7209,7 +7228,7 @@
             "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
             "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
             "requires": {
-                "kind-of": "3.2.2"
+                "kind-of": "^3.0.2"
             },
             "dependencies": {
                 "kind-of": {
@@ -7217,7 +7236,7 @@
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
                     "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
-                        "is-buffer": "1.1.6"
+                        "is-buffer": "^1.1.5"
                     }
                 }
             }
@@ -7239,7 +7258,7 @@
             "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==",
             "dev": true,
             "requires": {
-                "is-path-inside": "2.1.0"
+                "is-path-inside": "^2.1.0"
             },
             "dependencies": {
                 "is-path-inside": {
@@ -7248,7 +7267,7 @@
                     "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==",
                     "dev": true,
                     "requires": {
-                        "path-is-inside": "1.0.2"
+                        "path-is-inside": "^1.0.2"
                     }
                 }
             }
@@ -7258,7 +7277,7 @@
             "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
             "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
             "requires": {
-                "path-is-inside": "1.0.2"
+                "path-is-inside": "^1.0.1"
             }
         },
         "is-plain-object": {
@@ -7266,7 +7285,7 @@
             "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
             "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
             "requires": {
-                "isobject": "3.0.1"
+                "isobject": "^3.0.1"
             }
         },
         "is-promise": {
@@ -7284,7 +7303,7 @@
             "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
             "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
             "requires": {
-                "has": "1.0.3"
+                "has": "^1.0.1"
             }
         },
         "is-retry-allowed": {
@@ -7302,7 +7321,7 @@
             "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
             "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
             "requires": {
-                "has-symbols": "1.0.0"
+                "has-symbols": "^1.0.0"
             }
         },
         "is-typedarray": {
@@ -7346,8 +7365,8 @@
             "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
             "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
             "requires": {
-                "node-fetch": "1.7.3",
-                "whatwg-fetch": "3.0.0"
+                "node-fetch": "^1.0.1",
+                "whatwg-fetch": ">=0.10.0"
             }
         },
         "isstream": {
@@ -7360,8 +7379,8 @@
             "resolved": "https://registry.npmjs.org/its-set/-/its-set-1.2.3.tgz",
             "integrity": "sha512-UQc+xLLn+0a8KKRXRj3OS2kERK8G7zcayPpPULqZnPwuJ1hGWEO8+j0T5eycu7DKXYjezw3pyF8oV1fJkJxV5w==",
             "requires": {
-                "babel-runtime": "6.26.0",
-                "lodash.get": "4.4.2"
+                "babel-runtime": "6.x.x",
+                "lodash.get": "^4.4.2"
             }
         },
         "jquery": {
@@ -7374,7 +7393,7 @@
             "resolved": "https://registry.npmjs.org/jquery-awesome-cursor/-/jquery-awesome-cursor-0.3.1.tgz",
             "integrity": "sha1-1pcaMrRiRhC868rAkDsAFWHQXso=",
             "requires": {
-                "font-awesome": "4.7.0"
+                "font-awesome": "4.x"
             }
         },
         "js-base64": {
@@ -7402,8 +7421,8 @@
             "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
             "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
             "requires": {
-                "argparse": "1.0.10",
-                "esprima": "4.0.1"
+                "argparse": "^1.0.7",
+                "esprima": "^4.0.0"
             }
         },
         "jsbn": {
@@ -7417,32 +7436,32 @@
             "integrity": "sha512-cQZRBB33arrDAeCrAEWn1U3SvrvC8XysBua9Oqg1yWrsY/gYcusloJC3RZJXuY5eehSCmws8f2YeliCqGSkrtQ==",
             "dev": true,
             "requires": {
-                "abab": "2.0.0",
-                "acorn": "6.2.1",
-                "acorn-globals": "4.3.2",
-                "array-equal": "1.0.0",
-                "cssom": "0.3.8",
-                "cssstyle": "1.4.0",
-                "data-urls": "1.1.0",
-                "domexception": "1.0.1",
-                "escodegen": "1.11.1",
-                "html-encoding-sniffer": "1.0.2",
-                "nwsapi": "2.1.4",
+                "abab": "^2.0.0",
+                "acorn": "^6.1.1",
+                "acorn-globals": "^4.3.2",
+                "array-equal": "^1.0.0",
+                "cssom": "^0.3.6",
+                "cssstyle": "^1.2.2",
+                "data-urls": "^1.1.0",
+                "domexception": "^1.0.1",
+                "escodegen": "^1.11.1",
+                "html-encoding-sniffer": "^1.0.2",
+                "nwsapi": "^2.1.4",
                 "parse5": "5.1.0",
-                "pn": "1.1.0",
-                "request": "2.88.0",
-                "request-promise-native": "1.0.7",
-                "saxes": "3.1.11",
-                "symbol-tree": "3.2.4",
-                "tough-cookie": "3.0.1",
-                "w3c-hr-time": "1.0.1",
-                "w3c-xmlserializer": "1.1.2",
-                "webidl-conversions": "4.0.2",
-                "whatwg-encoding": "1.0.5",
-                "whatwg-mimetype": "2.3.0",
-                "whatwg-url": "7.0.0",
-                "ws": "7.1.1",
-                "xml-name-validator": "3.0.0"
+                "pn": "^1.1.0",
+                "request": "^2.88.0",
+                "request-promise-native": "^1.0.7",
+                "saxes": "^3.1.9",
+                "symbol-tree": "^3.2.2",
+                "tough-cookie": "^3.0.1",
+                "w3c-hr-time": "^1.0.1",
+                "w3c-xmlserializer": "^1.1.2",
+                "webidl-conversions": "^4.0.2",
+                "whatwg-encoding": "^1.0.5",
+                "whatwg-mimetype": "^2.3.0",
+                "whatwg-url": "^7.0.0",
+                "ws": "^7.0.0",
+                "xml-name-validator": "^3.0.0"
             },
             "dependencies": {
                 "acorn": {
@@ -7457,8 +7476,8 @@
                     "integrity": "sha512-BbzvZhVtZP+Bs1J1HcwrQe8ycfO0wStkSGxuul3He3GkHOIZ6eTqOkPuw9IP1X3+IkOo4wiJmwkobzXYz4wewQ==",
                     "dev": true,
                     "requires": {
-                        "acorn": "6.2.1",
-                        "acorn-walk": "6.2.0"
+                        "acorn": "^6.0.1",
+                        "acorn-walk": "^6.0.1"
                     }
                 },
                 "assert-plus": {
@@ -7485,9 +7504,9 @@
                     "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
                     "dev": true,
                     "requires": {
-                        "asynckit": "0.4.0",
-                        "combined-stream": "1.0.8",
-                        "mime-types": "2.1.24"
+                        "asynckit": "^0.4.0",
+                        "combined-stream": "^1.0.6",
+                        "mime-types": "^2.1.12"
                     }
                 },
                 "har-validator": {
@@ -7496,8 +7515,8 @@
                     "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
                     "dev": true,
                     "requires": {
-                        "ajv": "6.10.2",
-                        "har-schema": "2.0.0"
+                        "ajv": "^6.5.5",
+                        "har-schema": "^2.0.0"
                     }
                 },
                 "http-signature": {
@@ -7506,9 +7525,9 @@
                     "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
                     "dev": true,
                     "requires": {
-                        "assert-plus": "1.0.0",
-                        "jsprim": "1.4.1",
-                        "sshpk": "1.16.1"
+                        "assert-plus": "^1.0.0",
+                        "jsprim": "^1.2.2",
+                        "sshpk": "^1.7.0"
                     }
                 },
                 "oauth-sign": {
@@ -7529,26 +7548,26 @@
                     "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
                     "dev": true,
                     "requires": {
-                        "aws-sign2": "0.7.0",
-                        "aws4": "1.9.0",
-                        "caseless": "0.12.0",
-                        "combined-stream": "1.0.8",
-                        "extend": "3.0.2",
-                        "forever-agent": "0.6.1",
-                        "form-data": "2.3.3",
-                        "har-validator": "5.1.3",
-                        "http-signature": "1.2.0",
-                        "is-typedarray": "1.0.0",
-                        "isstream": "0.1.2",
-                        "json-stringify-safe": "5.0.1",
-                        "mime-types": "2.1.24",
-                        "oauth-sign": "0.9.0",
-                        "performance-now": "2.1.0",
-                        "qs": "6.5.2",
-                        "safe-buffer": "5.1.2",
-                        "tough-cookie": "2.4.3",
-                        "tunnel-agent": "0.6.0",
-                        "uuid": "3.3.2"
+                        "aws-sign2": "~0.7.0",
+                        "aws4": "^1.8.0",
+                        "caseless": "~0.12.0",
+                        "combined-stream": "~1.0.6",
+                        "extend": "~3.0.2",
+                        "forever-agent": "~0.6.1",
+                        "form-data": "~2.3.2",
+                        "har-validator": "~5.1.0",
+                        "http-signature": "~1.2.0",
+                        "is-typedarray": "~1.0.0",
+                        "isstream": "~0.1.2",
+                        "json-stringify-safe": "~5.0.1",
+                        "mime-types": "~2.1.19",
+                        "oauth-sign": "~0.9.0",
+                        "performance-now": "^2.1.0",
+                        "qs": "~6.5.2",
+                        "safe-buffer": "^5.1.2",
+                        "tough-cookie": "~2.4.3",
+                        "tunnel-agent": "^0.6.0",
+                        "uuid": "^3.3.2"
                     },
                     "dependencies": {
                         "punycode": {
@@ -7563,8 +7582,8 @@
                             "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
                             "dev": true,
                             "requires": {
-                                "psl": "1.2.0",
-                                "punycode": "1.4.1"
+                                "psl": "^1.1.24",
+                                "punycode": "^1.4.1"
                             }
                         }
                     }
@@ -7575,9 +7594,9 @@
                     "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==",
                     "dev": true,
                     "requires": {
-                        "ip-regex": "2.1.0",
-                        "psl": "1.2.0",
-                        "punycode": "2.1.1"
+                        "ip-regex": "^2.1.0",
+                        "psl": "^1.1.28",
+                        "punycode": "^2.1.1"
                     }
                 },
                 "ws": {
@@ -7586,7 +7605,7 @@
                     "integrity": "sha512-o41D/WmDeca0BqYhsr3nJzQyg9NF5X8l/UdnFNux9cS3lwB+swm8qGWX5rn+aD6xfBU3rGmtHij7g7x6LxFU3A==",
                     "dev": true,
                     "requires": {
-                        "async-limiter": "1.0.0"
+                        "async-limiter": "^1.0.0"
                     }
                 }
             }
@@ -7596,7 +7615,7 @@
             "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz",
             "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=",
             "requires": {
-                "bignumber.js": "7.2.1"
+                "bignumber.js": "^7.0.0"
             }
         },
         "json-parse-better-errors": {
@@ -7635,7 +7654,7 @@
             "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
             "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
             "requires": {
-                "minimist": "1.2.0"
+                "minimist": "^1.2.0"
             },
             "dependencies": {
                 "minimist": {
@@ -7650,7 +7669,7 @@
             "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
             "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
             "requires": {
-                "graceful-fs": "4.2.0"
+                "graceful-fs": "^4.1.6"
             }
         },
         "jsonwebtoken": {
@@ -7658,16 +7677,16 @@
             "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
             "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
             "requires": {
-                "jws": "3.2.2",
-                "lodash.includes": "4.3.0",
-                "lodash.isboolean": "3.0.3",
-                "lodash.isinteger": "4.0.4",
-                "lodash.isnumber": "3.0.3",
-                "lodash.isplainobject": "4.0.6",
-                "lodash.isstring": "4.0.1",
-                "lodash.once": "4.1.1",
-                "ms": "2.1.2",
-                "semver": "5.7.0"
+                "jws": "^3.2.2",
+                "lodash.includes": "^4.3.0",
+                "lodash.isboolean": "^3.0.3",
+                "lodash.isinteger": "^4.0.4",
+                "lodash.isnumber": "^3.0.3",
+                "lodash.isplainobject": "^4.0.6",
+                "lodash.isstring": "^4.0.1",
+                "lodash.once": "^4.0.0",
+                "ms": "^2.1.1",
+                "semver": "^5.6.0"
             },
             "dependencies": {
                 "ms": {
@@ -7700,11 +7719,11 @@
             "resolved": "https://registry.npmjs.org/jstransform/-/jstransform-11.0.3.tgz",
             "integrity": "sha1-CaeJk+CuTU70SH9hVakfYZDLQiM=",
             "requires": {
-                "base62": "1.2.8",
-                "commoner": "0.10.8",
-                "esprima-fb": "15001.1.0-dev-harmony-fb",
-                "object-assign": "2.1.1",
-                "source-map": "0.4.4"
+                "base62": "^1.1.0",
+                "commoner": "^0.10.1",
+                "esprima-fb": "^15001.1.0-dev-harmony-fb",
+                "object-assign": "^2.0.0",
+                "source-map": "^0.4.2"
             },
             "dependencies": {
                 "esprima-fb": {
@@ -7722,7 +7741,7 @@
                     "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
                     "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
                     "requires": {
-                        "amdefine": "1.0.1"
+                        "amdefine": ">=0.0.4"
                     }
                 }
             }
@@ -7732,8 +7751,8 @@
             "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz",
             "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=",
             "requires": {
-                "is-promise": "2.1.0",
-                "promise": "7.3.1"
+                "is-promise": "^2.0.0",
+                "promise": "^7.0.1"
             }
         },
         "jsx-to-string": {
@@ -7741,9 +7760,9 @@
             "resolved": "https://registry.npmjs.org/jsx-to-string/-/jsx-to-string-1.4.0.tgz",
             "integrity": "sha1-Ztw013PaufQP6ZPP+ZQOXaZVtwU=",
             "requires": {
-                "immutable": "4.0.0-rc.12",
-                "json-stringify-pretty-compact": "1.2.0",
-                "react": "0.14.9"
+                "immutable": "^4.0.0-rc.9",
+                "json-stringify-pretty-compact": "^1.0.1",
+                "react": "^0.14.0"
             },
             "dependencies": {
                 "fbjs": {
@@ -7751,11 +7770,11 @@
                     "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.6.1.tgz",
                     "integrity": "sha1-lja3cF9bqWhNRLcveDISVK/IYPc=",
                     "requires": {
-                        "core-js": "1.2.7",
-                        "loose-envify": "1.4.0",
-                        "promise": "7.3.1",
-                        "ua-parser-js": "0.7.20",
-                        "whatwg-fetch": "0.9.0"
+                        "core-js": "^1.0.0",
+                        "loose-envify": "^1.0.0",
+                        "promise": "^7.0.3",
+                        "ua-parser-js": "^0.7.9",
+                        "whatwg-fetch": "^0.9.0"
                     }
                 },
                 "react": {
@@ -7763,8 +7782,8 @@
                     "resolved": "https://registry.npmjs.org/react/-/react-0.14.9.tgz",
                     "integrity": "sha1-kRCmSXxJ1EuhwO3TF67CnC4NkdE=",
                     "requires": {
-                        "envify": "3.4.1",
-                        "fbjs": "0.6.1"
+                        "envify": "^3.0.0",
+                        "fbjs": "^0.6.1"
                     }
                 },
                 "whatwg-fetch": {
@@ -7781,7 +7800,7 @@
             "requires": {
                 "buffer-equal-constant-time": "1.0.1",
                 "ecdsa-sig-formatter": "1.0.11",
-                "safe-buffer": "5.1.2"
+                "safe-buffer": "^5.0.1"
             }
         },
         "jws": {
@@ -7789,8 +7808,8 @@
             "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
             "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
             "requires": {
-                "jwa": "1.4.1",
-                "safe-buffer": "5.1.2"
+                "jwa": "^1.4.1",
+                "safe-buffer": "^5.0.1"
             }
         },
         "kareem": {
@@ -7813,8 +7832,8 @@
             "resolved": "https://registry.npmjs.org/kill-port/-/kill-port-1.6.0.tgz",
             "integrity": "sha512-gwHRBZ3OLBcupsOJZlIt2Xvf6QqFH3lfdpGnmonXJnJrqq819UXtItGEU1rCMXHK6sXFlxdpkw8ka56rtWw/eQ==",
             "requires": {
-                "get-them-args": "1.3.2",
-                "shell-exec": "1.0.2"
+                "get-them-args": "^1.3.1",
+                "shell-exec": "^1.0.2"
             }
         },
         "killable": {
@@ -7833,7 +7852,7 @@
             "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
             "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=",
             "requires": {
-                "graceful-fs": "4.2.0"
+                "graceful-fs": "^4.1.9"
             }
         },
         "latest-version": {
@@ -7841,7 +7860,7 @@
             "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz",
             "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=",
             "requires": {
-                "package-json": "4.0.1"
+                "package-json": "^4.0.0"
             }
         },
         "lazy-cache": {
@@ -7854,7 +7873,7 @@
             "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
             "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=",
             "requires": {
-                "readable-stream": "2.3.6"
+                "readable-stream": "^2.0.5"
             }
         },
         "lcid": {
@@ -7862,7 +7881,7 @@
             "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
             "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
             "requires": {
-                "invert-kv": "1.0.0"
+                "invert-kv": "^1.0.0"
             }
         },
         "levn": {
@@ -7871,8 +7890,8 @@
             "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
             "dev": true,
             "requires": {
-                "prelude-ls": "1.1.2",
-                "type-check": "0.3.2"
+                "prelude-ls": "~1.1.2",
+                "type-check": "~0.3.2"
             }
         },
         "lines-and-columns": {
@@ -7885,11 +7904,11 @@
             "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
             "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
             "requires": {
-                "graceful-fs": "4.2.0",
-                "parse-json": "2.2.0",
-                "pify": "2.3.0",
-                "pinkie-promise": "2.0.1",
-                "strip-bom": "2.0.0"
+                "graceful-fs": "^4.1.2",
+                "parse-json": "^2.2.0",
+                "pify": "^2.0.0",
+                "pinkie-promise": "^2.0.0",
+                "strip-bom": "^2.0.0"
             },
             "dependencies": {
                 "parse-json": {
@@ -7897,7 +7916,7 @@
                     "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
                     "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
                     "requires": {
-                        "error-ex": "1.3.2"
+                        "error-ex": "^1.2.0"
                     }
                 }
             }
@@ -7913,9 +7932,9 @@
             "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
             "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
             "requires": {
-                "big.js": "5.2.2",
-                "emojis-list": "2.1.0",
-                "json5": "1.0.1"
+                "big.js": "^5.2.2",
+                "emojis-list": "^2.0.0",
+                "json5": "^1.0.1"
             }
         },
         "locate-path": {
@@ -7923,8 +7942,8 @@
             "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
             "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
             "requires": {
-                "p-locate": "2.0.0",
-                "path-exists": "3.0.0"
+                "p-locate": "^2.0.0",
+                "path-exists": "^3.0.0"
             }
         },
         "lodash": {
@@ -8024,7 +8043,7 @@
             "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
             "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
             "requires": {
-                "chalk": "2.4.2"
+                "chalk": "^2.0.1"
             },
             "dependencies": {
                 "ansi-styles": {
@@ -8032,7 +8051,7 @@
                     "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
                     "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
                     "requires": {
-                        "color-convert": "1.9.3"
+                        "color-convert": "^1.9.0"
                     }
                 },
                 "chalk": {
@@ -8040,9 +8059,9 @@
                     "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
                     "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
                     "requires": {
-                        "ansi-styles": "3.2.1",
-                        "escape-string-regexp": "1.0.5",
-                        "supports-color": "5.5.0"
+                        "ansi-styles": "^3.2.1",
+                        "escape-string-regexp": "^1.0.5",
+                        "supports-color": "^5.3.0"
                     }
                 },
                 "supports-color": {
@@ -8050,7 +8069,7 @@
                     "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
                     "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
                     "requires": {
-                        "has-flag": "3.0.0"
+                        "has-flag": "^3.0.0"
                     }
                 }
             }
@@ -8067,8 +8086,8 @@
             "integrity": "sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A==",
             "dev": true,
             "requires": {
-                "es6-symbol": "3.1.1",
-                "object.assign": "4.1.0"
+                "es6-symbol": "^3.1.1",
+                "object.assign": "^4.1.0"
             }
         },
         "longest": {
@@ -8081,7 +8100,7 @@
             "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
             "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
             "requires": {
-                "js-tokens": "4.0.0"
+                "js-tokens": "^3.0.0 || ^4.0.0"
             }
         },
         "loud-rejection": {
@@ -8089,8 +8108,8 @@
             "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
             "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
             "requires": {
-                "currently-unhandled": "0.4.1",
-                "signal-exit": "3.0.2"
+                "currently-unhandled": "^0.4.1",
+                "signal-exit": "^3.0.0"
             }
         },
         "lowercase-keys": {
@@ -8103,8 +8122,8 @@
             "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
             "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
             "requires": {
-                "pseudomap": "1.0.2",
-                "yallist": "2.1.2"
+                "pseudomap": "^1.0.2",
+                "yallist": "^2.1.2"
             },
             "dependencies": {
                 "yallist": {
@@ -8120,7 +8139,7 @@
             "integrity": "sha512-x/eBenweAHF+DsYy172sK4doRxZl0yrJnfxhLJiN7H6hPM3Ya0PfI6uBZshZ3ScFFSQD7HXgBqMdbnXKEZsO1g==",
             "requires": {
                 "cliss": "0.0.2",
-                "find-up": "2.1.0",
+                "find-up": "^2.1.0",
                 "for-each-property": "0.0.4",
                 "inspect-property": "0.0.6"
             }
@@ -8130,7 +8149,7 @@
             "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
             "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
             "requires": {
-                "pify": "3.0.0"
+                "pify": "^3.0.0"
             },
             "dependencies": {
                 "pify": {
@@ -8158,7 +8177,7 @@
             "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
             "dev": true,
             "requires": {
-                "p-defer": "1.0.0"
+                "p-defer": "^1.0.0"
             }
         },
         "map-cache": {
@@ -8176,7 +8195,7 @@
             "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
             "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
             "requires": {
-                "object-visit": "1.0.1"
+                "object-visit": "^1.0.0"
             }
         },
         "material-colors": {
@@ -8189,9 +8208,9 @@
             "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
             "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
             "requires": {
-                "hash-base": "3.0.4",
-                "inherits": "2.0.3",
-                "safe-buffer": "5.1.2"
+                "hash-base": "^3.0.0",
+                "inherits": "^2.0.1",
+                "safe-buffer": "^5.1.2"
             }
         },
         "media-typer": {
@@ -8205,9 +8224,9 @@
             "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
             "dev": true,
             "requires": {
-                "map-age-cleaner": "0.1.3",
-                "mimic-fn": "2.1.0",
-                "p-is-promise": "2.1.0"
+                "map-age-cleaner": "^0.1.1",
+                "mimic-fn": "^2.0.0",
+                "p-is-promise": "^2.0.0"
             }
         },
         "memory-fs": {
@@ -8216,8 +8235,8 @@
             "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
             "dev": true,
             "requires": {
-                "errno": "0.1.7",
-                "readable-stream": "2.3.6"
+                "errno": "^0.1.3",
+                "readable-stream": "^2.0.1"
             }
         },
         "memory-pager": {
@@ -8231,16 +8250,16 @@
             "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
             "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
             "requires": {
-                "camelcase-keys": "2.1.0",
-                "decamelize": "1.2.0",
-                "loud-rejection": "1.6.0",
-                "map-obj": "1.0.1",
-                "minimist": "1.2.0",
-                "normalize-package-data": "2.5.0",
-                "object-assign": "4.1.1",
-                "read-pkg-up": "1.0.1",
-                "redent": "1.0.0",
-                "trim-newlines": "1.0.0"
+                "camelcase-keys": "^2.0.0",
+                "decamelize": "^1.1.2",
+                "loud-rejection": "^1.0.0",
+                "map-obj": "^1.0.1",
+                "minimist": "^1.1.3",
+                "normalize-package-data": "^2.3.4",
+                "object-assign": "^4.0.1",
+                "read-pkg-up": "^1.0.1",
+                "redent": "^1.0.0",
+                "trim-newlines": "^1.0.0"
             },
             "dependencies": {
                 "minimist": {
@@ -8271,19 +8290,19 @@
             "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
             "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
             "requires": {
-                "arr-diff": "4.0.0",
-                "array-unique": "0.3.2",
-                "braces": "2.3.2",
-                "define-property": "2.0.2",
-                "extend-shallow": "3.0.2",
-                "extglob": "2.0.4",
-                "fragment-cache": "0.2.1",
-                "kind-of": "6.0.2",
-                "nanomatch": "1.2.13",
-                "object.pick": "1.3.0",
-                "regex-not": "1.0.2",
-                "snapdragon": "0.8.2",
-                "to-regex": "3.0.2"
+                "arr-diff": "^4.0.0",
+                "array-unique": "^0.3.2",
+                "braces": "^2.3.1",
+                "define-property": "^2.0.2",
+                "extend-shallow": "^3.0.2",
+                "extglob": "^2.0.4",
+                "fragment-cache": "^0.2.1",
+                "kind-of": "^6.0.2",
+                "nanomatch": "^1.2.9",
+                "object.pick": "^1.3.0",
+                "regex-not": "^1.0.0",
+                "snapdragon": "^0.8.1",
+                "to-regex": "^3.0.2"
             }
         },
         "miller-rabin": {
@@ -8291,8 +8310,8 @@
             "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
             "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
             "requires": {
-                "bn.js": "4.11.8",
-                "brorand": "1.1.0"
+                "bn.js": "^4.0.0",
+                "brorand": "^1.0.1"
             }
         },
         "mime": {
@@ -8339,7 +8358,7 @@
             "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
             "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
             "requires": {
-                "brace-expansion": "1.1.11"
+                "brace-expansion": "^1.1.7"
             }
         },
         "minimist": {
@@ -8352,8 +8371,8 @@
             "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
             "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
             "requires": {
-                "safe-buffer": "5.1.2",
-                "yallist": "3.0.3"
+                "safe-buffer": "^5.1.2",
+                "yallist": "^3.0.0"
             }
         },
         "minizlib": {
@@ -8361,7 +8380,7 @@
             "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz",
             "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
             "requires": {
-                "minipass": "2.3.5"
+                "minipass": "^2.2.1"
             }
         },
         "mississippi": {
@@ -8370,16 +8389,16 @@
             "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==",
             "dev": true,
             "requires": {
-                "concat-stream": "1.6.2",
-                "duplexify": "3.7.1",
-                "end-of-stream": "1.4.1",
-                "flush-write-stream": "1.1.1",
-                "from2": "2.3.0",
-                "parallel-transform": "1.1.0",
-                "pump": "2.0.1",
-                "pumpify": "1.5.1",
-                "stream-each": "1.2.3",
-                "through2": "2.0.5"
+                "concat-stream": "^1.5.0",
+                "duplexify": "^3.4.2",
+                "end-of-stream": "^1.1.0",
+                "flush-write-stream": "^1.0.0",
+                "from2": "^2.1.0",
+                "parallel-transform": "^1.1.0",
+                "pump": "^2.0.1",
+                "pumpify": "^1.3.3",
+                "stream-each": "^1.1.0",
+                "through2": "^2.0.0"
             }
         },
         "mixin-deep": {
@@ -8387,8 +8406,8 @@
             "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
             "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
             "requires": {
-                "for-in": "1.0.2",
-                "is-extendable": "1.0.1"
+                "for-in": "^1.0.2",
+                "is-extendable": "^1.0.1"
             },
             "dependencies": {
                 "is-extendable": {
@@ -8396,7 +8415,7 @@
                     "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
                     "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
                     "requires": {
-                        "is-plain-object": "2.0.4"
+                        "is-plain-object": "^2.0.4"
                     }
                 }
             }
@@ -8407,8 +8426,8 @@
             "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=",
             "dev": true,
             "requires": {
-                "for-in": "0.1.8",
-                "is-extendable": "0.1.1"
+                "for-in": "^0.1.3",
+                "is-extendable": "^0.1.1"
             },
             "dependencies": {
                 "for-in": {
@@ -8442,8 +8461,8 @@
             "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-5.4.4.tgz",
             "integrity": "sha512-2mTzpyEjVB/RGk2i6KbcmP4HWcAUFox5ZRCrGvSyz49w20I4C4qql63grPpYrS9E9GKwgydBHQlA4y665LuRCQ==",
             "requires": {
-                "hoist-non-react-statics": "3.3.0",
-                "react-lifecycles-compat": "3.0.4"
+                "hoist-non-react-statics": "^3.0.0",
+                "react-lifecycles-compat": "^3.0.2"
             }
         },
         "mobx-react-devtools": {
@@ -8502,12 +8521,12 @@
                     "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
                     "dev": true,
                     "requires": {
-                        "fs.realpath": "1.0.0",
-                        "inflight": "1.0.6",
-                        "inherits": "2.0.3",
-                        "minimatch": "3.0.4",
-                        "once": "1.4.0",
-                        "path-is-absolute": "1.0.1"
+                        "fs.realpath": "^1.0.0",
+                        "inflight": "^1.0.4",
+                        "inherits": "2",
+                        "minimatch": "^3.0.4",
+                        "once": "^1.3.0",
+                        "path-is-absolute": "^1.0.0"
                     }
                 },
                 "supports-color": {
@@ -8516,7 +8535,7 @@
                     "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
                     "dev": true,
                     "requires": {
-                        "has-flag": "3.0.0"
+                        "has-flag": "^3.0.0"
                     }
                 }
             }
@@ -8532,7 +8551,7 @@
             "integrity": "sha512-2YdWrdf1PJgxcCrT1tWoL6nHuk6hCxhddAAaEh8QJL231ci4+P9FLyqopbTm2Z2sAU6mhCri+wd9r1hOcHdoMw==",
             "requires": {
                 "mongodb-core": "3.2.7",
-                "safe-buffer": "5.1.2"
+                "safe-buffer": "^5.1.2"
             },
             "dependencies": {
                 "bson": {
@@ -8545,10 +8564,10 @@
                     "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.2.7.tgz",
                     "integrity": "sha512-WypKdLxFNPOH/Jy6i9z47IjG2wIldA54iDZBmHMINcgKOUcWJh8og+Wix76oGd7EyYkHJKssQ2FAOw5Su/n4XQ==",
                     "requires": {
-                        "bson": "1.1.1",
-                        "require_optional": "1.0.1",
-                        "safe-buffer": "5.1.2",
-                        "saslprep": "1.0.3"
+                        "bson": "^1.1.1",
+                        "require_optional": "^1.0.1",
+                        "safe-buffer": "^5.1.2",
+                        "saslprep": "^1.0.0"
                     }
                 }
             }
@@ -8558,8 +8577,8 @@
             "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.20.tgz",
             "integrity": "sha512-IN57CX5/Q1bhDq6ShAR6gIv4koFsZP7L8WOK1S0lR0pVDQaScffSMV5jxubLsmZ7J+UdqmykKw4r9hG3XQEGgQ==",
             "requires": {
-                "bson": "1.0.9",
-                "require_optional": "1.0.1"
+                "bson": "~1.0.4",
+                "require_optional": "~1.0.0"
             }
         },
         "mongoose": {
@@ -8568,7 +8587,7 @@
             "integrity": "sha512-5uecJSyl2TwbGM9vJteP4C54zsQL6qllq1qe/JPGO3oqIWcK/PnzCL91E0gfPH5VVpvWGX+6PafNYmU3NK8S7w==",
             "requires": {
                 "async": "2.6.2",
-                "bson": "1.1.1",
+                "bson": "~1.1.1",
                 "kareem": "2.3.0",
                 "mongodb": "3.2.7",
                 "mongodb-core": "3.2.7",
@@ -8587,7 +8606,7 @@
                     "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
                     "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
                     "requires": {
-                        "lodash": "4.17.15"
+                        "lodash": "^4.17.11"
                     }
                 },
                 "bson": {
@@ -8600,10 +8619,10 @@
                     "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.2.7.tgz",
                     "integrity": "sha512-WypKdLxFNPOH/Jy6i9z47IjG2wIldA54iDZBmHMINcgKOUcWJh8og+Wix76oGd7EyYkHJKssQ2FAOw5Su/n4XQ==",
                     "requires": {
-                        "bson": "1.1.1",
-                        "require_optional": "1.0.1",
-                        "safe-buffer": "5.1.2",
-                        "saslprep": "1.0.3"
+                        "bson": "^1.1.1",
+                        "require_optional": "^1.0.1",
+                        "safe-buffer": "^5.1.2",
+                        "saslprep": "^1.0.0"
                     }
                 },
                 "ms": {
@@ -8624,12 +8643,12 @@
             "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
             "dev": true,
             "requires": {
-                "aproba": "1.2.0",
-                "copy-concurrently": "1.0.5",
-                "fs-write-stream-atomic": "1.0.10",
-                "mkdirp": "0.5.1",
-                "rimraf": "2.7.1",
-                "run-queue": "1.0.3"
+                "aproba": "^1.1.1",
+                "copy-concurrently": "^1.0.0",
+                "fs-write-stream-atomic": "^1.0.8",
+                "mkdirp": "^0.5.1",
+                "rimraf": "^2.5.4",
+                "run-queue": "^1.0.3"
             },
             "dependencies": {
                 "rimraf": {
@@ -8638,7 +8657,7 @@
                     "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
                     "dev": true,
                     "requires": {
-                        "glob": "7.1.4"
+                        "glob": "^7.1.3"
                     }
                 }
             }
@@ -8655,7 +8674,7 @@
             "requires": {
                 "bluebird": "3.5.1",
                 "debug": "3.1.0",
-                "regexp-clone": "1.0.0",
+                "regexp-clone": "^1.0.0",
                 "safe-buffer": "5.1.2",
                 "sliced": "1.0.1"
             },
@@ -8686,8 +8705,8 @@
             "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==",
             "dev": true,
             "requires": {
-                "dns-packet": "1.3.1",
-                "thunky": "1.0.3"
+                "dns-packet": "^1.3.1",
+                "thunky": "^1.0.2"
             }
         },
         "multicast-dns-service-types": {
@@ -8706,17 +8725,17 @@
             "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
             "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
             "requires": {
-                "arr-diff": "4.0.0",
-                "array-unique": "0.3.2",
-                "define-property": "2.0.2",
-                "extend-shallow": "3.0.2",
-                "fragment-cache": "0.2.1",
-                "is-windows": "1.0.2",
-                "kind-of": "6.0.2",
-                "object.pick": "1.3.0",
-                "regex-not": "1.0.2",
-                "snapdragon": "0.8.2",
-                "to-regex": "3.0.2"
+                "arr-diff": "^4.0.0",
+                "array-unique": "^0.3.2",
+                "define-property": "^2.0.2",
+                "extend-shallow": "^3.0.2",
+                "fragment-cache": "^0.2.1",
+                "is-windows": "^1.0.2",
+                "kind-of": "^6.0.2",
+                "object.pick": "^1.3.0",
+                "regex-not": "^1.0.0",
+                "snapdragon": "^0.8.1",
+                "to-regex": "^3.0.1"
             }
         },
         "napi-build-utils": {
@@ -8734,9 +8753,9 @@
             "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz",
             "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==",
             "requires": {
-                "debug": "3.2.6",
-                "iconv-lite": "0.4.24",
-                "sax": "1.2.4"
+                "debug": "^3.2.6",
+                "iconv-lite": "^0.4.4",
+                "sax": "^1.2.4"
             },
             "dependencies": {
                 "debug": {
@@ -8744,7 +8763,7 @@
                     "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
                     "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
                     "requires": {
-                        "ms": "2.1.2"
+                        "ms": "^2.1.1"
                     }
                 },
                 "ms": {
@@ -8781,7 +8800,7 @@
             "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.9.0.tgz",
             "integrity": "sha512-jmEOvv0eanWjhX8dX1pmjb7oJl1U1oR4FOh0b2GnvALwSYoOdU7sj+kLDSAyjo4pfC9aj/IxkloxdLJQhSSQBA==",
             "requires": {
-                "semver": "5.7.0"
+                "semver": "^5.4.1"
             }
         },
         "node-ensure": {
@@ -8794,8 +8813,8 @@
             "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz",
             "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==",
             "requires": {
-                "object.getownpropertydescriptors": "2.0.3",
-                "semver": "5.7.0"
+                "object.getownpropertydescriptors": "^2.0.3",
+                "semver": "^5.7.0"
             }
         },
         "node-fetch": {
@@ -8803,8 +8822,8 @@
             "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
             "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
             "requires": {
-                "encoding": "0.1.12",
-                "is-stream": "1.1.0"
+                "encoding": "^0.1.11",
+                "is-stream": "^1.0.1"
             }
         },
         "node-forge": {
@@ -8818,18 +8837,18 @@
             "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",
             "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==",
             "requires": {
-                "fstream": "1.0.12",
-                "glob": "7.1.4",
-                "graceful-fs": "4.2.0",
-                "mkdirp": "0.5.1",
-                "nopt": "3.0.6",
-                "npmlog": "4.1.2",
-                "osenv": "0.1.5",
-                "request": "2.88.0",
-                "rimraf": "2.7.1",
-                "semver": "5.3.0",
-                "tar": "2.2.2",
-                "which": "1.3.1"
+                "fstream": "^1.0.0",
+                "glob": "^7.0.3",
+                "graceful-fs": "^4.1.2",
+                "mkdirp": "^0.5.0",
+                "nopt": "2 || 3",
+                "npmlog": "0 || 1 || 2 || 3 || 4",
+                "osenv": "0",
+                "request": "^2.87.0",
+                "rimraf": "2",
+                "semver": "~5.3.0",
+                "tar": "^2.0.0",
+                "which": "1"
             },
             "dependencies": {
                 "assert-plus": {
@@ -8852,9 +8871,9 @@
                     "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
                     "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
                     "requires": {
-                        "asynckit": "0.4.0",
-                        "combined-stream": "1.0.8",
-                        "mime-types": "2.1.24"
+                        "asynckit": "^0.4.0",
+                        "combined-stream": "^1.0.6",
+                        "mime-types": "^2.1.12"
                     }
                 },
                 "har-validator": {
@@ -8862,8 +8881,8 @@
                     "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
                     "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
                     "requires": {
-                        "ajv": "6.10.2",
-                        "har-schema": "2.0.0"
+                        "ajv": "^6.5.5",
+                        "har-schema": "^2.0.0"
                     }
                 },
                 "http-signature": {
@@ -8871,9 +8890,9 @@
                     "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
                     "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
                     "requires": {
-                        "assert-plus": "1.0.0",
-                        "jsprim": "1.4.1",
-                        "sshpk": "1.16.1"
+                        "assert-plus": "^1.0.0",
+                        "jsprim": "^1.2.2",
+                        "sshpk": "^1.7.0"
                     }
                 },
                 "nopt": {
@@ -8881,7 +8900,7 @@
                     "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
                     "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
                     "requires": {
-                        "abbrev": "1.1.1"
+                        "abbrev": "1"
                     }
                 },
                 "oauth-sign": {
@@ -8899,26 +8918,26 @@
                     "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
                     "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
                     "requires": {
-                        "aws-sign2": "0.7.0",
-                        "aws4": "1.9.0",
-                        "caseless": "0.12.0",
-                        "combined-stream": "1.0.8",
-                        "extend": "3.0.2",
-                        "forever-agent": "0.6.1",
-                        "form-data": "2.3.3",
-                        "har-validator": "5.1.3",
-                        "http-signature": "1.2.0",
-                        "is-typedarray": "1.0.0",
-                        "isstream": "0.1.2",
-                        "json-stringify-safe": "5.0.1",
-                        "mime-types": "2.1.24",
-                        "oauth-sign": "0.9.0",
-                        "performance-now": "2.1.0",
-                        "qs": "6.5.2",
-                        "safe-buffer": "5.1.2",
-                        "tough-cookie": "2.4.3",
-                        "tunnel-agent": "0.6.0",
-                        "uuid": "3.3.2"
+                        "aws-sign2": "~0.7.0",
+                        "aws4": "^1.8.0",
+                        "caseless": "~0.12.0",
+                        "combined-stream": "~1.0.6",
+                        "extend": "~3.0.2",
+                        "forever-agent": "~0.6.1",
+                        "form-data": "~2.3.2",
+                        "har-validator": "~5.1.0",
+                        "http-signature": "~1.2.0",
+                        "is-typedarray": "~1.0.0",
+                        "isstream": "~0.1.2",
+                        "json-stringify-safe": "~5.0.1",
+                        "mime-types": "~2.1.19",
+                        "oauth-sign": "~0.9.0",
+                        "performance-now": "^2.1.0",
+                        "qs": "~6.5.2",
+                        "safe-buffer": "^5.1.2",
+                        "tough-cookie": "~2.4.3",
+                        "tunnel-agent": "^0.6.0",
+                        "uuid": "^3.3.2"
                     }
                 },
                 "rimraf": {
@@ -8926,7 +8945,7 @@
                     "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
                     "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
                     "requires": {
-                        "glob": "7.1.4"
+                        "glob": "^7.1.3"
                     }
                 },
                 "semver": {
@@ -8939,9 +8958,9 @@
                     "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
                     "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
                     "requires": {
-                        "block-stream": "0.0.9",
-                        "fstream": "1.0.12",
-                        "inherits": "2.0.3"
+                        "block-stream": "*",
+                        "fstream": "^1.0.12",
+                        "inherits": "2"
                     }
                 }
             }
@@ -8952,29 +8971,29 @@
             "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==",
             "dev": true,
             "requires": {
-                "assert": "1.5.0",
-                "browserify-zlib": "0.2.0",
-                "buffer": "4.9.1",
-                "console-browserify": "1.1.0",
-                "constants-browserify": "1.0.0",
-                "crypto-browserify": "3.12.0",
-                "domain-browser": "1.2.0",
-                "events": "3.0.0",
-                "https-browserify": "1.0.0",
-                "os-browserify": "0.3.0",
+                "assert": "^1.1.1",
+                "browserify-zlib": "^0.2.0",
+                "buffer": "^4.3.0",
+                "console-browserify": "^1.1.0",
+                "constants-browserify": "^1.0.0",
+                "crypto-browserify": "^3.11.0",
+                "domain-browser": "^1.1.1",
+                "events": "^3.0.0",
+                "https-browserify": "^1.0.0",
+                "os-browserify": "^0.3.0",
                 "path-browserify": "0.0.1",
-                "process": "0.11.10",
-                "punycode": "1.4.1",
-                "querystring-es3": "0.2.1",
-                "readable-stream": "2.3.6",
-                "stream-browserify": "2.0.2",
-                "stream-http": "2.8.3",
-                "string_decoder": "1.1.1",
-                "timers-browserify": "2.0.10",
+                "process": "^0.11.10",
+                "punycode": "^1.2.4",
+                "querystring-es3": "^0.2.0",
+                "readable-stream": "^2.3.3",
+                "stream-browserify": "^2.0.1",
+                "stream-http": "^2.7.2",
+                "string_decoder": "^1.0.0",
+                "timers-browserify": "^2.0.4",
                 "tty-browserify": "0.0.0",
-                "url": "0.11.0",
-                "util": "0.11.1",
-                "vm-browserify": "1.1.0"
+                "url": "^0.11.0",
+                "util": "^0.11.0",
+                "vm-browserify": "^1.0.1"
             },
             "dependencies": {
                 "punycode": {
@@ -8991,11 +9010,11 @@
             "integrity": "sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ==",
             "dev": true,
             "requires": {
-                "growly": "1.3.0",
-                "is-wsl": "1.1.0",
-                "semver": "5.7.0",
-                "shellwords": "0.1.1",
-                "which": "1.3.1"
+                "growly": "^1.3.0",
+                "is-wsl": "^1.1.0",
+                "semver": "^5.5.0",
+                "shellwords": "^0.1.1",
+                "which": "^1.3.0"
             }
         },
         "node-pre-gyp": {
@@ -9003,16 +9022,16 @@
             "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz",
             "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==",
             "requires": {
-                "detect-libc": "1.0.3",
-                "mkdirp": "0.5.1",
-                "needle": "2.4.0",
-                "nopt": "4.0.1",
-                "npm-packlist": "1.4.4",
-                "npmlog": "4.1.2",
-                "rc": "1.2.8",
-                "rimraf": "2.7.1",
-                "semver": "5.7.0",
-                "tar": "4.4.10"
+                "detect-libc": "^1.0.2",
+                "mkdirp": "^0.5.1",
+                "needle": "^2.2.1",
+                "nopt": "^4.0.1",
+                "npm-packlist": "^1.1.6",
+                "npmlog": "^4.0.2",
+                "rc": "^1.2.7",
+                "rimraf": "^2.6.1",
+                "semver": "^5.3.0",
+                "tar": "^4"
             },
             "dependencies": {
                 "rimraf": {
@@ -9020,7 +9039,7 @@
                     "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
                     "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
                     "requires": {
-                        "glob": "7.1.4"
+                        "glob": "^7.1.3"
                     }
                 }
             }
@@ -9030,23 +9049,23 @@
             "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz",
             "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==",
             "requires": {
-                "async-foreach": "0.1.3",
-                "chalk": "1.1.3",
-                "cross-spawn": "3.0.1",
-                "gaze": "1.1.3",
-                "get-stdin": "4.0.1",
-                "glob": "7.1.4",
-                "in-publish": "2.0.0",
-                "lodash": "4.17.15",
-                "meow": "3.7.0",
-                "mkdirp": "0.5.1",
-                "nan": "2.14.0",
-                "node-gyp": "3.8.0",
-                "npmlog": "4.1.2",
-                "request": "2.88.0",
-                "sass-graph": "2.2.4",
-                "stdout-stream": "1.4.1",
-                "true-case-path": "1.0.3"
+                "async-foreach": "^0.1.3",
+                "chalk": "^1.1.1",
+                "cross-spawn": "^3.0.0",
+                "gaze": "^1.0.0",
+                "get-stdin": "^4.0.1",
+                "glob": "^7.0.3",
+                "in-publish": "^2.0.0",
+                "lodash": "^4.17.11",
+                "meow": "^3.7.0",
+                "mkdirp": "^0.5.1",
+                "nan": "^2.13.2",
+                "node-gyp": "^3.8.0",
+                "npmlog": "^4.0.0",
+                "request": "^2.88.0",
+                "sass-graph": "^2.2.4",
+                "stdout-stream": "^1.4.0",
+                "true-case-path": "^1.0.2"
             },
             "dependencies": {
                 "assert-plus": {
@@ -9069,9 +9088,9 @@
                     "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
                     "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
                     "requires": {
-                        "asynckit": "0.4.0",
-                        "combined-stream": "1.0.8",
-                        "mime-types": "2.1.24"
+                        "asynckit": "^0.4.0",
+                        "combined-stream": "^1.0.6",
+                        "mime-types": "^2.1.12"
                     }
                 },
                 "get-stdin": {
@@ -9084,8 +9103,8 @@
                     "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
                     "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
                     "requires": {
-                        "ajv": "6.10.2",
-                        "har-schema": "2.0.0"
+                        "ajv": "^6.5.5",
+                        "har-schema": "^2.0.0"
                     }
                 },
                 "http-signature": {
@@ -9093,9 +9112,9 @@
                     "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
                     "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
                     "requires": {
-                        "assert-plus": "1.0.0",
-                        "jsprim": "1.4.1",
-                        "sshpk": "1.16.1"
+                        "assert-plus": "^1.0.0",
+                        "jsprim": "^1.2.2",
+                        "sshpk": "^1.7.0"
                     }
                 },
                 "oauth-sign": {
@@ -9113,26 +9132,26 @@
                     "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
                     "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
                     "requires": {
-                        "aws-sign2": "0.7.0",
-                        "aws4": "1.9.0",
-                        "caseless": "0.12.0",
-                        "combined-stream": "1.0.8",
-                        "extend": "3.0.2",
-                        "forever-agent": "0.6.1",
-                        "form-data": "2.3.3",
-                        "har-validator": "5.1.3",
-                        "http-signature": "1.2.0",
-                        "is-typedarray": "1.0.0",
-                        "isstream": "0.1.2",
-                        "json-stringify-safe": "5.0.1",
-                        "mime-types": "2.1.24",
-                        "oauth-sign": "0.9.0",
-                        "performance-now": "2.1.0",
-                        "qs": "6.5.2",
-                        "safe-buffer": "5.1.2",
-                        "tough-cookie": "2.4.3",
-                        "tunnel-agent": "0.6.0",
-                        "uuid": "3.3.2"
+                        "aws-sign2": "~0.7.0",
+                        "aws4": "^1.8.0",
+                        "caseless": "~0.12.0",
+                        "combined-stream": "~1.0.6",
+                        "extend": "~3.0.2",
+                        "forever-agent": "~0.6.1",
+                        "form-data": "~2.3.2",
+                        "har-validator": "~5.1.0",
+                        "http-signature": "~1.2.0",
+                        "is-typedarray": "~1.0.0",
+                        "isstream": "~0.1.2",
+                        "json-stringify-safe": "~5.0.1",
+                        "mime-types": "~2.1.19",
+                        "oauth-sign": "~0.9.0",
+                        "performance-now": "^2.1.0",
+                        "qs": "~6.5.2",
+                        "safe-buffer": "^5.1.2",
+                        "tough-cookie": "~2.4.3",
+                        "tunnel-agent": "^0.6.0",
+                        "uuid": "^3.3.2"
                     }
                 }
             }
@@ -9147,16 +9166,16 @@
             "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.19.1.tgz",
             "integrity": "sha512-/DXLzd/GhiaDXXbGId5BzxP1GlsqtMGM9zTmkWrgXtSqjKmGSbLicM/oAy4FR0YWm14jCHRwnR31AHS2dYFHrg==",
             "requires": {
-                "chokidar": "2.1.6",
-                "debug": "3.2.6",
-                "ignore-by-default": "1.0.1",
-                "minimatch": "3.0.4",
-                "pstree.remy": "1.1.7",
-                "semver": "5.7.0",
-                "supports-color": "5.5.0",
-                "touch": "3.1.0",
-                "undefsafe": "2.0.2",
-                "update-notifier": "2.5.0"
+                "chokidar": "^2.1.5",
+                "debug": "^3.1.0",
+                "ignore-by-default": "^1.0.1",
+                "minimatch": "^3.0.4",
+                "pstree.remy": "^1.1.6",
+                "semver": "^5.5.0",
+                "supports-color": "^5.2.0",
+                "touch": "^3.1.0",
+                "undefsafe": "^2.0.2",
+                "update-notifier": "^2.5.0"
             },
             "dependencies": {
                 "debug": {
@@ -9164,7 +9183,7 @@
                     "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
                     "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
                     "requires": {
-                        "ms": "2.1.2"
+                        "ms": "^2.1.1"
                     }
                 },
                 "ms": {
@@ -9177,7 +9196,7 @@
                     "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
                     "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
                     "requires": {
-                        "has-flag": "3.0.0"
+                        "has-flag": "^3.0.0"
                     }
                 }
             }
@@ -9192,8 +9211,8 @@
             "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
             "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
             "requires": {
-                "abbrev": "1.1.1",
-                "osenv": "0.1.5"
+                "abbrev": "1",
+                "osenv": "^0.1.4"
             }
         },
         "normalize-package-data": {
@@ -9201,10 +9220,10 @@
             "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
             "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
             "requires": {
-                "hosted-git-info": "2.7.1",
-                "resolve": "1.11.1",
-                "semver": "5.7.0",
-                "validate-npm-package-license": "3.0.4"
+                "hosted-git-info": "^2.1.4",
+                "resolve": "^1.10.0",
+                "semver": "2 || 3 || 4 || 5",
+                "validate-npm-package-license": "^3.0.1"
             }
         },
         "normalize-path": {
@@ -9222,137 +9241,137 @@
             "resolved": "https://registry.npmjs.org/npm/-/npm-6.13.4.tgz",
             "integrity": "sha512-vTcUL4SCg3AzwInWTbqg1OIaOXlzKSS8Mb8kc5avwrJpcvevDA5J9BhYSuei+fNs3pwOp4lzA5x2FVDXACvoXA==",
             "requires": {
-                "JSONStream": "1.3.5",
-                "abbrev": "1.1.1",
-                "ansicolors": "0.3.2",
-                "ansistyles": "0.1.3",
-                "aproba": "2.0.0",
-                "archy": "1.0.0",
-                "bin-links": "1.1.6",
-                "bluebird": "3.5.5",
-                "byte-size": "5.0.1",
-                "cacache": "12.0.3",
-                "call-limit": "1.1.1",
-                "chownr": "1.1.3",
-                "ci-info": "2.0.0",
-                "cli-columns": "3.1.2",
-                "cli-table3": "0.5.1",
-                "cmd-shim": "3.0.3",
-                "columnify": "1.5.4",
-                "config-chain": "1.1.12",
-                "debuglog": "1.0.1",
-                "detect-indent": "5.0.0",
-                "detect-newline": "2.1.0",
-                "dezalgo": "1.0.3",
-                "editor": "1.0.0",
-                "figgy-pudding": "3.5.1",
-                "find-npm-prefix": "1.0.2",
-                "fs-vacuum": "1.2.10",
-                "fs-write-stream-atomic": "1.0.10",
-                "gentle-fs": "2.3.0",
-                "glob": "7.1.4",
-                "graceful-fs": "4.2.3",
-                "has-unicode": "2.0.1",
-                "hosted-git-info": "2.8.5",
-                "iferr": "1.0.2",
-                "imurmurhash": "0.1.4",
-                "infer-owner": "1.0.4",
-                "inflight": "1.0.6",
-                "inherits": "2.0.4",
-                "ini": "1.3.5",
-                "init-package-json": "1.10.3",
-                "is-cidr": "3.0.0",
-                "json-parse-better-errors": "1.0.2",
-                "lazy-property": "1.0.0",
-                "libcipm": "4.0.7",
-                "libnpm": "3.0.1",
-                "libnpmaccess": "3.0.2",
-                "libnpmhook": "5.0.3",
-                "libnpmorg": "1.0.1",
-                "libnpmsearch": "2.0.2",
-                "libnpmteam": "1.0.2",
-                "libnpx": "10.2.0",
-                "lock-verify": "2.1.0",
-                "lockfile": "1.0.4",
-                "lodash._baseindexof": "3.1.0",
-                "lodash._baseuniq": "4.6.0",
-                "lodash._bindcallback": "3.0.1",
-                "lodash._cacheindexof": "3.0.2",
-                "lodash._createcache": "3.1.2",
-                "lodash._getnative": "3.9.1",
-                "lodash.clonedeep": "4.5.0",
-                "lodash.restparam": "3.6.1",
-                "lodash.union": "4.6.0",
-                "lodash.uniq": "4.5.0",
-                "lodash.without": "4.4.0",
-                "lru-cache": "5.1.1",
-                "meant": "1.0.1",
-                "mississippi": "3.0.0",
-                "mkdirp": "0.5.1",
-                "move-concurrently": "1.0.1",
-                "node-gyp": "5.0.5",
-                "nopt": "4.0.1",
-                "normalize-package-data": "2.5.0",
-                "npm-audit-report": "1.3.2",
-                "npm-cache-filename": "1.0.2",
-                "npm-install-checks": "3.0.2",
-                "npm-lifecycle": "3.1.4",
-                "npm-package-arg": "6.1.1",
-                "npm-packlist": "1.4.7",
-                "npm-pick-manifest": "3.0.2",
-                "npm-profile": "4.0.2",
-                "npm-registry-fetch": "4.0.2",
-                "npm-user-validate": "1.0.0",
-                "npmlog": "4.1.2",
-                "once": "1.4.0",
-                "opener": "1.5.1",
-                "osenv": "0.1.5",
-                "pacote": "9.5.11",
-                "path-is-inside": "1.0.2",
-                "promise-inflight": "1.0.1",
-                "qrcode-terminal": "0.12.0",
-                "query-string": "6.8.2",
-                "qw": "1.0.1",
-                "read": "1.0.7",
-                "read-cmd-shim": "1.0.5",
-                "read-installed": "4.0.3",
-                "read-package-json": "2.1.1",
-                "read-package-tree": "5.3.1",
-                "readable-stream": "3.4.0",
-                "readdir-scoped-modules": "1.1.0",
-                "request": "2.88.0",
-                "retry": "0.12.0",
-                "rimraf": "2.6.3",
-                "safe-buffer": "5.1.2",
-                "semver": "5.7.1",
-                "sha": "3.0.0",
-                "slide": "1.1.6",
-                "sorted-object": "2.0.1",
-                "sorted-union-stream": "2.1.3",
-                "ssri": "6.0.1",
-                "stringify-package": "1.0.1",
-                "tar": "4.4.13",
-                "text-table": "0.2.0",
-                "tiny-relative-date": "1.3.0",
+                "JSONStream": "^1.3.5",
+                "abbrev": "~1.1.1",
+                "ansicolors": "~0.3.2",
+                "ansistyles": "~0.1.3",
+                "aproba": "^2.0.0",
+                "archy": "~1.0.0",
+                "bin-links": "^1.1.6",
+                "bluebird": "^3.5.5",
+                "byte-size": "^5.0.1",
+                "cacache": "^12.0.3",
+                "call-limit": "^1.1.1",
+                "chownr": "^1.1.3",
+                "ci-info": "^2.0.0",
+                "cli-columns": "^3.1.2",
+                "cli-table3": "^0.5.1",
+                "cmd-shim": "^3.0.3",
+                "columnify": "~1.5.4",
+                "config-chain": "^1.1.12",
+                "debuglog": "*",
+                "detect-indent": "~5.0.0",
+                "detect-newline": "^2.1.0",
+                "dezalgo": "~1.0.3",
+                "editor": "~1.0.0",
+                "figgy-pudding": "^3.5.1",
+                "find-npm-prefix": "^1.0.2",
+                "fs-vacuum": "~1.2.10",
+                "fs-write-stream-atomic": "~1.0.10",
+                "gentle-fs": "^2.3.0",
+                "glob": "^7.1.4",
+                "graceful-fs": "^4.2.3",
+                "has-unicode": "~2.0.1",
+                "hosted-git-info": "^2.8.5",
+                "iferr": "^1.0.2",
+                "imurmurhash": "*",
+                "infer-owner": "^1.0.4",
+                "inflight": "~1.0.6",
+                "inherits": "^2.0.4",
+                "ini": "^1.3.5",
+                "init-package-json": "^1.10.3",
+                "is-cidr": "^3.0.0",
+                "json-parse-better-errors": "^1.0.2",
+                "lazy-property": "~1.0.0",
+                "libcipm": "^4.0.7",
+                "libnpm": "^3.0.1",
+                "libnpmaccess": "^3.0.2",
+                "libnpmhook": "^5.0.3",
+                "libnpmorg": "^1.0.1",
+                "libnpmsearch": "^2.0.2",
+                "libnpmteam": "^1.0.2",
+                "libnpx": "^10.2.0",
+                "lock-verify": "^2.1.0",
+                "lockfile": "^1.0.4",
+                "lodash._baseindexof": "*",
+                "lodash._baseuniq": "~4.6.0",
+                "lodash._bindcallback": "*",
+                "lodash._cacheindexof": "*",
+                "lodash._createcache": "*",
+                "lodash._getnative": "*",
+                "lodash.clonedeep": "~4.5.0",
+                "lodash.restparam": "*",
+                "lodash.union": "~4.6.0",
+                "lodash.uniq": "~4.5.0",
+                "lodash.without": "~4.4.0",
+                "lru-cache": "^5.1.1",
+                "meant": "~1.0.1",
+                "mississippi": "^3.0.0",
+                "mkdirp": "~0.5.1",
+                "move-concurrently": "^1.0.1",
+                "node-gyp": "^5.0.5",
+                "nopt": "~4.0.1",
+                "normalize-package-data": "^2.5.0",
+                "npm-audit-report": "^1.3.2",
+                "npm-cache-filename": "~1.0.2",
+                "npm-install-checks": "^3.0.2",
+                "npm-lifecycle": "^3.1.4",
+                "npm-package-arg": "^6.1.1",
+                "npm-packlist": "^1.4.7",
+                "npm-pick-manifest": "^3.0.2",
+                "npm-profile": "^4.0.2",
+                "npm-registry-fetch": "^4.0.2",
+                "npm-user-validate": "~1.0.0",
+                "npmlog": "~4.1.2",
+                "once": "~1.4.0",
+                "opener": "^1.5.1",
+                "osenv": "^0.1.5",
+                "pacote": "^9.5.11",
+                "path-is-inside": "~1.0.2",
+                "promise-inflight": "~1.0.1",
+                "qrcode-terminal": "^0.12.0",
+                "query-string": "^6.8.2",
+                "qw": "~1.0.1",
+                "read": "~1.0.7",
+                "read-cmd-shim": "^1.0.5",
+                "read-installed": "~4.0.3",
+                "read-package-json": "^2.1.1",
+                "read-package-tree": "^5.3.1",
+                "readable-stream": "^3.4.0",
+                "readdir-scoped-modules": "^1.1.0",
+                "request": "^2.88.0",
+                "retry": "^0.12.0",
+                "rimraf": "^2.6.3",
+                "safe-buffer": "^5.1.2",
+                "semver": "^5.7.1",
+                "sha": "^3.0.0",
+                "slide": "~1.1.6",
+                "sorted-object": "~2.0.1",
+                "sorted-union-stream": "~2.1.3",
+                "ssri": "^6.0.1",
+                "stringify-package": "^1.0.1",
+                "tar": "^4.4.13",
+                "text-table": "~0.2.0",
+                "tiny-relative-date": "^1.3.0",
                 "uid-number": "0.0.6",
-                "umask": "1.1.0",
-                "unique-filename": "1.1.1",
-                "unpipe": "1.0.0",
-                "update-notifier": "2.5.0",
-                "uuid": "3.3.3",
-                "validate-npm-package-license": "3.0.4",
-                "validate-npm-package-name": "3.0.0",
-                "which": "1.3.1",
-                "worker-farm": "1.7.0",
-                "write-file-atomic": "2.4.3"
+                "umask": "~1.1.0",
+                "unique-filename": "^1.1.1",
+                "unpipe": "~1.0.0",
+                "update-notifier": "^2.5.0",
+                "uuid": "^3.3.3",
+                "validate-npm-package-license": "^3.0.4",
+                "validate-npm-package-name": "~3.0.0",
+                "which": "^1.3.1",
+                "worker-farm": "^1.7.0",
+                "write-file-atomic": "^2.4.3"
             },
             "dependencies": {
                 "JSONStream": {
                     "version": "1.3.5",
                     "bundled": true,
                     "requires": {
-                        "jsonparse": "1.3.1",
-                        "through": "2.3.8"
+                        "jsonparse": "^1.2.0",
+                        "through": ">=2.2.7 <3"
                     }
                 },
                 "abbrev": {
@@ -9363,31 +9382,31 @@
                     "version": "4.3.0",
                     "bundled": true,
                     "requires": {
-                        "es6-promisify": "5.0.0"
+                        "es6-promisify": "^5.0.0"
                     }
                 },
                 "agentkeepalive": {
                     "version": "3.5.2",
                     "bundled": true,
                     "requires": {
-                        "humanize-ms": "1.2.1"
+                        "humanize-ms": "^1.2.1"
                     }
                 },
                 "ajv": {
                     "version": "5.5.2",
                     "bundled": true,
                     "requires": {
-                        "co": "4.6.0",
-                        "fast-deep-equal": "1.1.0",
-                        "fast-json-stable-stringify": "2.0.0",
-                        "json-schema-traverse": "0.3.1"
+                        "co": "^4.6.0",
+                        "fast-deep-equal": "^1.0.0",
+                        "fast-json-stable-stringify": "^2.0.0",
+                        "json-schema-traverse": "^0.3.0"
                     }
                 },
                 "ansi-align": {
                     "version": "2.0.0",
                     "bundled": true,
                     "requires": {
-                        "string-width": "2.1.1"
+                        "string-width": "^2.0.0"
                     }
                 },
                 "ansi-regex": {
@@ -9398,7 +9417,7 @@
                     "version": "3.2.1",
                     "bundled": true,
                     "requires": {
-                        "color-convert": "1.9.1"
+                        "color-convert": "^1.9.0"
                     }
                 },
                 "ansicolors": {
@@ -9421,28 +9440,28 @@
                     "version": "1.1.4",
                     "bundled": true,
                     "requires": {
-                        "delegates": "1.0.0",
-                        "readable-stream": "2.3.6"
+                        "delegates": "^1.0.0",
+                        "readable-stream": "^2.0.6"
                     },
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
                             "bundled": true,
                             "requires": {
-                                "core-util-is": "1.0.2",
-                                "inherits": "2.0.4",
-                                "isarray": "1.0.0",
-                                "process-nextick-args": "2.0.0",
-                                "safe-buffer": "5.1.2",
-                                "string_decoder": "1.1.1",
-                                "util-deprecate": "1.0.2"
+                                "core-util-is": "~1.0.0",
+                                "inherits": "~2.0.3",
+                                "isarray": "~1.0.0",
+                                "process-nextick-args": "~2.0.0",
+                                "safe-buffer": "~5.1.1",
+                                "string_decoder": "~1.1.1",
+                                "util-deprecate": "~1.0.1"
                             }
                         },
                         "string_decoder": {
                             "version": "1.1.1",
                             "bundled": true,
                             "requires": {
-                                "safe-buffer": "5.1.2"
+                                "safe-buffer": "~5.1.0"
                             }
                         }
                     }
@@ -9455,7 +9474,7 @@
                     "version": "0.2.4",
                     "bundled": true,
                     "requires": {
-                        "safer-buffer": "2.1.2"
+                        "safer-buffer": "~2.1.0"
                     }
                 },
                 "assert-plus": {
@@ -9483,19 +9502,19 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "tweetnacl": "0.14.5"
+                        "tweetnacl": "^0.14.3"
                     }
                 },
                 "bin-links": {
                     "version": "1.1.6",
                     "bundled": true,
                     "requires": {
-                        "bluebird": "3.5.5",
-                        "cmd-shim": "3.0.3",
-                        "gentle-fs": "2.3.0",
-                        "graceful-fs": "4.2.3",
-                        "npm-normalize-package-bin": "1.0.1",
-                        "write-file-atomic": "2.4.3"
+                        "bluebird": "^3.5.3",
+                        "cmd-shim": "^3.0.0",
+                        "gentle-fs": "^2.3.0",
+                        "graceful-fs": "^4.1.15",
+                        "npm-normalize-package-bin": "^1.0.0",
+                        "write-file-atomic": "^2.3.0"
                     }
                 },
                 "bluebird": {
@@ -9506,20 +9525,20 @@
                     "version": "1.3.0",
                     "bundled": true,
                     "requires": {
-                        "ansi-align": "2.0.0",
-                        "camelcase": "4.1.0",
-                        "chalk": "2.4.1",
-                        "cli-boxes": "1.0.0",
-                        "string-width": "2.1.1",
-                        "term-size": "1.2.0",
-                        "widest-line": "2.0.0"
+                        "ansi-align": "^2.0.0",
+                        "camelcase": "^4.0.0",
+                        "chalk": "^2.0.1",
+                        "cli-boxes": "^1.0.0",
+                        "string-width": "^2.0.0",
+                        "term-size": "^1.2.0",
+                        "widest-line": "^2.0.0"
                     }
                 },
                 "brace-expansion": {
                     "version": "1.1.11",
                     "bundled": true,
                     "requires": {
-                        "balanced-match": "1.0.0",
+                        "balanced-match": "^1.0.0",
                         "concat-map": "0.0.1"
                     }
                 },
@@ -9543,21 +9562,21 @@
                     "version": "12.0.3",
                     "bundled": true,
                     "requires": {
-                        "bluebird": "3.5.5",
-                        "chownr": "1.1.3",
-                        "figgy-pudding": "3.5.1",
-                        "glob": "7.1.4",
-                        "graceful-fs": "4.2.3",
-                        "infer-owner": "1.0.4",
-                        "lru-cache": "5.1.1",
-                        "mississippi": "3.0.0",
-                        "mkdirp": "0.5.1",
-                        "move-concurrently": "1.0.1",
-                        "promise-inflight": "1.0.1",
-                        "rimraf": "2.6.3",
-                        "ssri": "6.0.1",
-                        "unique-filename": "1.1.1",
-                        "y18n": "4.0.0"
+                        "bluebird": "^3.5.5",
+                        "chownr": "^1.1.1",
+                        "figgy-pudding": "^3.5.1",
+                        "glob": "^7.1.4",
+                        "graceful-fs": "^4.1.15",
+                        "infer-owner": "^1.0.3",
+                        "lru-cache": "^5.1.1",
+                        "mississippi": "^3.0.0",
+                        "mkdirp": "^0.5.1",
+                        "move-concurrently": "^1.0.1",
+                        "promise-inflight": "^1.0.1",
+                        "rimraf": "^2.6.3",
+                        "ssri": "^6.0.1",
+                        "unique-filename": "^1.1.1",
+                        "y18n": "^4.0.0"
                     }
                 },
                 "call-limit": {
@@ -9580,9 +9599,9 @@
                     "version": "2.4.1",
                     "bundled": true,
                     "requires": {
-                        "ansi-styles": "3.2.1",
-                        "escape-string-regexp": "1.0.5",
-                        "supports-color": "5.4.0"
+                        "ansi-styles": "^3.2.1",
+                        "escape-string-regexp": "^1.0.5",
+                        "supports-color": "^5.3.0"
                     }
                 },
                 "chownr": {
@@ -9597,7 +9616,7 @@
                     "version": "2.0.10",
                     "bundled": true,
                     "requires": {
-                        "ip-regex": "2.1.0"
+                        "ip-regex": "^2.1.0"
                     }
                 },
                 "cli-boxes": {
@@ -9608,26 +9627,26 @@
                     "version": "3.1.2",
                     "bundled": true,
                     "requires": {
-                        "string-width": "2.1.1",
-                        "strip-ansi": "3.0.1"
+                        "string-width": "^2.0.0",
+                        "strip-ansi": "^3.0.1"
                     }
                 },
                 "cli-table3": {
                     "version": "0.5.1",
                     "bundled": true,
                     "requires": {
-                        "colors": "1.3.3",
-                        "object-assign": "4.1.1",
-                        "string-width": "2.1.1"
+                        "colors": "^1.1.2",
+                        "object-assign": "^4.1.0",
+                        "string-width": "^2.1.1"
                     }
                 },
                 "cliui": {
                     "version": "4.1.0",
                     "bundled": true,
                     "requires": {
-                        "string-width": "2.1.1",
-                        "strip-ansi": "4.0.0",
-                        "wrap-ansi": "2.1.0"
+                        "string-width": "^2.1.1",
+                        "strip-ansi": "^4.0.0",
+                        "wrap-ansi": "^2.0.0"
                     },
                     "dependencies": {
                         "ansi-regex": {
@@ -9638,7 +9657,7 @@
                             "version": "4.0.0",
                             "bundled": true,
                             "requires": {
-                                "ansi-regex": "3.0.0"
+                                "ansi-regex": "^3.0.0"
                             }
                         }
                     }
@@ -9651,8 +9670,8 @@
                     "version": "3.0.3",
                     "bundled": true,
                     "requires": {
-                        "graceful-fs": "4.2.3",
-                        "mkdirp": "0.5.1"
+                        "graceful-fs": "^4.1.2",
+                        "mkdirp": "~0.5.0"
                     }
                 },
                 "co": {
@@ -9667,7 +9686,7 @@
                     "version": "1.9.1",
                     "bundled": true,
                     "requires": {
-                        "color-name": "1.1.3"
+                        "color-name": "^1.1.1"
                     }
                 },
                 "color-name": {
@@ -9683,15 +9702,15 @@
                     "version": "1.5.4",
                     "bundled": true,
                     "requires": {
-                        "strip-ansi": "3.0.1",
-                        "wcwidth": "1.0.1"
+                        "strip-ansi": "^3.0.0",
+                        "wcwidth": "^1.0.0"
                     }
                 },
                 "combined-stream": {
                     "version": "1.0.6",
                     "bundled": true,
                     "requires": {
-                        "delayed-stream": "1.0.0"
+                        "delayed-stream": "~1.0.0"
                     }
                 },
                 "concat-map": {
@@ -9702,30 +9721,30 @@
                     "version": "1.6.2",
                     "bundled": true,
                     "requires": {
-                        "buffer-from": "1.0.0",
-                        "inherits": "2.0.4",
-                        "readable-stream": "2.3.6",
-                        "typedarray": "0.0.6"
+                        "buffer-from": "^1.0.0",
+                        "inherits": "^2.0.3",
+                        "readable-stream": "^2.2.2",
+                        "typedarray": "^0.0.6"
                     },
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
                             "bundled": true,
                             "requires": {
-                                "core-util-is": "1.0.2",
-                                "inherits": "2.0.4",
-                                "isarray": "1.0.0",
-                                "process-nextick-args": "2.0.0",
-                                "safe-buffer": "5.1.2",
-                                "string_decoder": "1.1.1",
-                                "util-deprecate": "1.0.2"
+                                "core-util-is": "~1.0.0",
+                                "inherits": "~2.0.3",
+                                "isarray": "~1.0.0",
+                                "process-nextick-args": "~2.0.0",
+                                "safe-buffer": "~5.1.1",
+                                "string_decoder": "~1.1.1",
+                                "util-deprecate": "~1.0.1"
                             }
                         },
                         "string_decoder": {
                             "version": "1.1.1",
                             "bundled": true,
                             "requires": {
-                                "safe-buffer": "5.1.2"
+                                "safe-buffer": "~5.1.0"
                             }
                         }
                     }
@@ -9734,20 +9753,20 @@
                     "version": "1.1.12",
                     "bundled": true,
                     "requires": {
-                        "ini": "1.3.5",
-                        "proto-list": "1.2.4"
+                        "ini": "^1.3.4",
+                        "proto-list": "~1.2.1"
                     }
                 },
                 "configstore": {
                     "version": "3.1.2",
                     "bundled": true,
                     "requires": {
-                        "dot-prop": "4.2.0",
-                        "graceful-fs": "4.2.3",
-                        "make-dir": "1.3.0",
-                        "unique-string": "1.0.0",
-                        "write-file-atomic": "2.4.3",
-                        "xdg-basedir": "3.0.0"
+                        "dot-prop": "^4.1.0",
+                        "graceful-fs": "^4.1.2",
+                        "make-dir": "^1.0.0",
+                        "unique-string": "^1.0.0",
+                        "write-file-atomic": "^2.0.0",
+                        "xdg-basedir": "^3.0.0"
                     }
                 },
                 "console-control-strings": {
@@ -9758,12 +9777,12 @@
                     "version": "1.0.5",
                     "bundled": true,
                     "requires": {
-                        "aproba": "1.2.0",
-                        "fs-write-stream-atomic": "1.0.10",
-                        "iferr": "0.1.5",
-                        "mkdirp": "0.5.1",
-                        "rimraf": "2.6.3",
-                        "run-queue": "1.0.3"
+                        "aproba": "^1.1.1",
+                        "fs-write-stream-atomic": "^1.0.8",
+                        "iferr": "^0.1.5",
+                        "mkdirp": "^0.5.1",
+                        "rimraf": "^2.5.4",
+                        "run-queue": "^1.0.0"
                     },
                     "dependencies": {
                         "aproba": {
@@ -9784,24 +9803,24 @@
                     "version": "3.0.2",
                     "bundled": true,
                     "requires": {
-                        "capture-stack-trace": "1.0.0"
+                        "capture-stack-trace": "^1.0.0"
                     }
                 },
                 "cross-spawn": {
                     "version": "5.1.0",
                     "bundled": true,
                     "requires": {
-                        "lru-cache": "4.1.5",
-                        "shebang-command": "1.2.0",
-                        "which": "1.3.1"
+                        "lru-cache": "^4.0.1",
+                        "shebang-command": "^1.2.0",
+                        "which": "^1.2.9"
                     },
                     "dependencies": {
                         "lru-cache": {
                             "version": "4.1.5",
                             "bundled": true,
                             "requires": {
-                                "pseudomap": "1.0.2",
-                                "yallist": "2.1.2"
+                                "pseudomap": "^1.0.2",
+                                "yallist": "^2.1.2"
                             }
                         },
                         "yallist": {
@@ -9822,7 +9841,7 @@
                     "version": "1.14.1",
                     "bundled": true,
                     "requires": {
-                        "assert-plus": "1.0.0"
+                        "assert-plus": "^1.0.0"
                     }
                 },
                 "debug": {
@@ -9858,14 +9877,14 @@
                     "version": "1.0.3",
                     "bundled": true,
                     "requires": {
-                        "clone": "1.0.4"
+                        "clone": "^1.0.2"
                     }
                 },
                 "define-properties": {
                     "version": "1.1.3",
                     "bundled": true,
                     "requires": {
-                        "object-keys": "1.0.12"
+                        "object-keys": "^1.0.12"
                     }
                 },
                 "delayed-stream": {
@@ -9888,15 +9907,15 @@
                     "version": "1.0.3",
                     "bundled": true,
                     "requires": {
-                        "asap": "2.0.6",
-                        "wrappy": "1.0.2"
+                        "asap": "^2.0.0",
+                        "wrappy": "1"
                     }
                 },
                 "dot-prop": {
                     "version": "4.2.0",
                     "bundled": true,
                     "requires": {
-                        "is-obj": "1.0.1"
+                        "is-obj": "^1.0.0"
                     }
                 },
                 "dotenv": {
@@ -9911,30 +9930,30 @@
                     "version": "3.6.0",
                     "bundled": true,
                     "requires": {
-                        "end-of-stream": "1.4.1",
-                        "inherits": "2.0.4",
-                        "readable-stream": "2.3.6",
-                        "stream-shift": "1.0.0"
+                        "end-of-stream": "^1.0.0",
+                        "inherits": "^2.0.1",
+                        "readable-stream": "^2.0.0",
+                        "stream-shift": "^1.0.0"
                     },
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
                             "bundled": true,
                             "requires": {
-                                "core-util-is": "1.0.2",
-                                "inherits": "2.0.4",
-                                "isarray": "1.0.0",
-                                "process-nextick-args": "2.0.0",
-                                "safe-buffer": "5.1.2",
-                                "string_decoder": "1.1.1",
-                                "util-deprecate": "1.0.2"
+                                "core-util-is": "~1.0.0",
+                                "inherits": "~2.0.3",
+                                "isarray": "~1.0.0",
+                                "process-nextick-args": "~2.0.0",
+                                "safe-buffer": "~5.1.1",
+                                "string_decoder": "~1.1.1",
+                                "util-deprecate": "~1.0.1"
                             }
                         },
                         "string_decoder": {
                             "version": "1.1.1",
                             "bundled": true,
                             "requires": {
-                                "safe-buffer": "5.1.2"
+                                "safe-buffer": "~5.1.0"
                             }
                         }
                     }
@@ -9944,8 +9963,8 @@
                     "bundled": true,
                     "optional": true,
                     "requires": {
-                        "jsbn": "0.1.1",
-                        "safer-buffer": "2.1.2"
+                        "jsbn": "~0.1.0",
+                        "safer-buffer": "^2.1.0"
                     }
                 },
                 "editor": {
@@ -9956,14 +9975,14 @@
                     "version": "0.1.12",
                     "bundled": true,
                     "requires": {
-                        "iconv-lite": "0.4.23"
+                        "iconv-lite": "~0.4.13"
                     }
                 },
                 "end-of-stream": {
                     "version": "1.4.1",
                     "bundled": true,
                     "requires": {
-                        "once": "1.4.0"
+                        "once": "^1.4.0"
                     }
                 },
                 "env-paths": {
@@ -9978,27 +9997,27 @@
                     "version": "0.1.7",
                     "bundled": true,
                     "requires": {
-                        "prr": "1.0.1"
+                        "prr": "~1.0.1"
                     }
                 },
                 "es-abstract": {
                     "version": "1.12.0",
                     "bundled": true,
                     "requires": {
-                        "es-to-primitive": "1.2.0",
-                        "function-bind": "1.1.1",
-                        "has": "1.0.3",
-                        "is-callable": "1.1.4",
-                        "is-regex": "1.0.4"
+                        "es-to-primitive": "^1.1.1",
+                        "function-bind": "^1.1.1",
+                        "has": "^1.0.1",
+                        "is-callable": "^1.1.3",
+                        "is-regex": "^1.0.4"
                     }
                 },
                 "es-to-primitive": {
                     "version": "1.2.0",
                     "bundled": true,
                     "requires": {
-                        "is-callable": "1.1.4",
-                        "is-date-object": "1.0.1",
-                        "is-symbol": "1.0.2"
+                        "is-callable": "^1.1.4",
+                        "is-date-object": "^1.0.1",
+                        "is-symbol": "^1.0.2"
                     }
                 },
                 "es6-promise": {
@@ -10009,7 +10028,7 @@
                     "version": "5.0.0",
                     "bundled": true,
                     "requires": {
-                        "es6-promise": "4.2.8"
+                        "es6-promise": "^4.0.3"
                     }
                 },
                 "escape-string-regexp": {
@@ -10020,13 +10039,13 @@
                     "version": "0.7.0",
                     "bundled": true,
                     "requires": {
-                        "cross-spawn": "5.1.0",
-                        "get-stream": "3.0.0",
-                        "is-stream": "1.1.0",
-                        "npm-run-path": "2.0.2",
-                        "p-finally": "1.0.0",
-                        "signal-exit": "3.0.2",
-                        "strip-eof": "1.0.0"
+                        "cross-spawn": "^5.0.1",
+                        "get-stream": "^3.0.0",
+                        "is-stream": "^1.1.0",
+                        "npm-run-path": "^2.0.0",
+                        "p-finally": "^1.0.0",
+                        "signal-exit": "^3.0.0",
+                        "strip-eof": "^1.0.0"
                     },
                     "dependencies": {
                         "get-stream": {
@@ -10063,35 +10082,35 @@
                     "version": "2.1.0",
                     "bundled": true,
                     "requires": {
-                        "locate-path": "2.0.0"
+                        "locate-path": "^2.0.0"
                     }
                 },
                 "flush-write-stream": {
                     "version": "1.0.3",
                     "bundled": true,
                     "requires": {
-                        "inherits": "2.0.4",
-                        "readable-stream": "2.3.6"
+                        "inherits": "^2.0.1",
+                        "readable-stream": "^2.0.4"
                     },
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
                             "bundled": true,
                             "requires": {
-                                "core-util-is": "1.0.2",
-                                "inherits": "2.0.4",
-                                "isarray": "1.0.0",
-                                "process-nextick-args": "2.0.0",
-                                "safe-buffer": "5.1.2",
-                                "string_decoder": "1.1.1",
-                                "util-deprecate": "1.0.2"
+                                "core-util-is": "~1.0.0",
+                                "inherits": "~2.0.3",
+                                "isarray": "~1.0.0",
+                                "process-nextick-args": "~2.0.0",
+                                "safe-buffer": "~5.1.1",
+                                "string_decoder": "~1.1.1",
+                                "util-deprecate": "~1.0.1"
                             }
                         },
                         "string_decoder": {
                             "version": "1.1.1",
                             "bundled": true,
                             "requires": {
-                                "safe-buffer": "5.1.2"
+                                "safe-buffer": "~5.1.0"
                             }
                         }
                     }
@@ -10104,37 +10123,37 @@
                     "version": "2.3.2",
                     "bundled": true,
                     "requires": {
-                        "asynckit": "0.4.0",
+                        "asynckit": "^0.4.0",
                         "combined-stream": "1.0.6",
-                        "mime-types": "2.1.19"
+                        "mime-types": "^2.1.12"
                     }
                 },
                 "from2": {
                     "version": "2.3.0",
                     "bundled": true,
                     "requires": {
-                        "inherits": "2.0.4",
-                        "readable-stream": "2.3.6"
+                        "inherits": "^2.0.1",
+                        "readable-stream": "^2.0.0"
                     },
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
                             "bundled": true,
                             "requires": {
-                                "core-util-is": "1.0.2",
-                                "inherits": "2.0.4",
-                                "isarray": "1.0.0",
-                                "process-nextick-args": "2.0.0",
-                                "safe-buffer": "5.1.2",
-                                "string_decoder": "1.1.1",
-                                "util-deprecate": "1.0.2"
+                                "core-util-is": "~1.0.0",
+                                "inherits": "~2.0.3",
+                                "isarray": "~1.0.0",
+                                "process-nextick-args": "~2.0.0",
+                                "safe-buffer": "~5.1.1",
+                                "string_decoder": "~1.1.1",
+                                "util-deprecate": "~1.0.1"
                             }
                         },
                         "string_decoder": {
                             "version": "1.1.1",
                             "bundled": true,
                             "requires": {
-                                "safe-buffer": "5.1.2"
+                                "safe-buffer": "~5.1.0"
                             }
                         }
                     }
@@ -10143,15 +10162,15 @@
                     "version": "1.2.7",
                     "bundled": true,
                     "requires": {
-                        "minipass": "2.9.0"
+                        "minipass": "^2.6.0"
                     },
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
                             "bundled": true,
                             "requires": {
-                                "safe-buffer": "5.1.2",
-                                "yallist": "3.0.3"
+                                "safe-buffer": "^5.1.2",
+                                "yallist": "^3.0.0"
                             }
                         }
                     }
@@ -10160,19 +10179,19 @@
                     "version": "1.2.10",
                     "bundled": true,
                     "requires": {
-                        "graceful-fs": "4.2.3",
-                        "path-is-inside": "1.0.2",
-                        "rimraf": "2.6.3"
+                        "graceful-fs": "^4.1.2",
+                        "path-is-inside": "^1.0.1",
+                        "rimraf": "^2.5.2"
                     }
                 },
                 "fs-write-stream-atomic": {
                     "version": "1.0.10",
                     "bundled": true,
                     "requires": {
-                        "graceful-fs": "4.2.3",
-                        "iferr": "0.1.5",
-                        "imurmurhash": "0.1.4",
-                        "readable-stream": "2.3.6"
+                        "graceful-fs": "^4.1.2",
+                        "iferr": "^0.1.5",
+                        "imurmurhash": "^0.1.4",
+                        "readable-stream": "1 || 2"
                     },
                     "dependencies": {
                         "iferr": {
@@ -10183,20 +10202,20 @@
                             "version": "2.3.6",
                             "bundled": true,
                             "requires": {
-                                "core-util-is": "1.0.2",
-                                "inherits": "2.0.4",
-                                "isarray": "1.0.0",
-                                "process-nextick-args": "2.0.0",
-                                "safe-buffer": "5.1.2",
-                                "string_decoder": "1.1.1",
-                                "util-deprecate": "1.0.2"
+                                "core-util-is": "~1.0.0",
+                                "inherits": "~2.0.3",
+                                "isarray": "~1.0.0",
+                                "process-nextick-args": "~2.0.0",
+                                "safe-buffer": "~5.1.1",
+                                "string_decoder": "~1.1.1",
+                                "util-deprecate": "~1.0.1"
                             }
                         },
                         "string_decoder": {
                             "version": "1.1.1",
                             "bundled": true,
                             "requires": {
-                                "safe-buffer": "5.1.2"
+                                "safe-buffer": "~5.1.0"
                             }
                         }
                     }
@@ -10213,14 +10232,14 @@
                     "version": "2.7.4",
                     "bundled": true,
                     "requires": {
-                        "aproba": "1.2.0",
-                        "console-control-strings": "1.1.0",
-                        "has-unicode": "2.0.1",
-                        "object-assign": "4.1.1",
-                        "signal-exit": "3.0.2",
-                        "string-width": "1.0.2",
-                        "strip-ansi": "3.0.1",
-                        "wide-align": "1.1.2"
+                        "aproba": "^1.0.3",
+                        "console-control-strings": "^1.0.0",
+                        "has-unicode": "^2.0.0",
+                        "object-assign": "^4.1.0",
+                        "signal-exit": "^3.0.0",
+                        "string-width": "^1.0.1",
+                        "strip-ansi": "^3.0.1",
+                        "wide-align": "^1.1.0"
                     },
                     "dependencies": {
                         "aproba": {
@@ -10231,9 +10250,9 @@
                             "version": "1.0.2",
                             "bundled": true,
                             "requires": {
-                                "code-point-at": "1.1.0",
-                                "is-fullwidth-code-point": "1.0.0",
-                                "strip-ansi": "3.0.1"
+                                "code-point-at": "^1.0.0",
+                                "is-fullwidth-code-point": "^1.0.0",
+                                "strip-ansi": "^3.0.0"
                             }
                         }
                     }
@@ -10246,17 +10265,17 @@
                     "version": "2.3.0",
                     "bundled": true,
                     "requires": {
-                        "aproba": "1.2.0",
-                        "chownr": "1.1.3",
-                        "cmd-shim": "3.0.3",
-                        "fs-vacuum": "1.2.10",
-                        "graceful-fs": "4.2.3",
-                        "iferr": "0.1.5",
-                        "infer-owner": "1.0.4",
-                        "mkdirp": "0.5.1",
-                        "path-is-inside": "1.0.2",
-                        "read-cmd-shim": "1.0.5",
-                        "slide": "1.1.6"
+                        "aproba": "^1.1.2",
+                        "chownr": "^1.1.2",
+                        "cmd-shim": "^3.0.3",
+                        "fs-vacuum": "^1.2.10",
+                        "graceful-fs": "^4.1.11",
+                        "iferr": "^0.1.5",
+                        "infer-owner": "^1.0.4",
+                        "mkdirp": "^0.5.1",
+                        "path-is-inside": "^1.0.2",
+                        "read-cmd-shim": "^1.0.1",
+                        "slide": "^1.1.6"
                     },
                     "dependencies": {
                         "aproba": {
@@ -10277,50 +10296,50 @@
                     "version": "4.1.0",
                     "bundled": true,
                     "requires": {
-                        "pump": "3.0.0"
+                        "pump": "^3.0.0"
                     }
                 },
                 "getpass": {
                     "version": "0.1.7",
                     "bundled": true,
                     "requires": {
-                        "assert-plus": "1.0.0"
+                        "assert-plus": "^1.0.0"
                     }
                 },
                 "glob": {
                     "version": "7.1.4",
                     "bundled": true,
                     "requires": {
-                        "fs.realpath": "1.0.0",
-                        "inflight": "1.0.6",
-                        "inherits": "2.0.4",
-                        "minimatch": "3.0.4",
-                        "once": "1.4.0",
-                        "path-is-absolute": "1.0.1"
+                        "fs.realpath": "^1.0.0",
+                        "inflight": "^1.0.4",
+                        "inherits": "2",
+                        "minimatch": "^3.0.4",
+                        "once": "^1.3.0",
+                        "path-is-absolute": "^1.0.0"
                     }
                 },
                 "global-dirs": {
                     "version": "0.1.1",
                     "bundled": true,
                     "requires": {
-                        "ini": "1.3.5"
+                        "ini": "^1.3.4"
                     }
                 },
                 "got": {
                     "version": "6.7.1",
                     "bundled": true,
                     "requires": {
-                        "create-error-class": "3.0.2",
-                        "duplexer3": "0.1.4",
-                        "get-stream": "3.0.0",
-                        "is-redirect": "1.0.0",
-                        "is-retry-allowed": "1.1.0",
-                        "is-stream": "1.1.0",
-                        "lowercase-keys": "1.0.1",
-                        "safe-buffer": "5.1.2",
-                        "timed-out": "4.0.1",
-                        "unzip-response": "2.0.1",
-                        "url-parse-lax": "1.0.0"
+                        "create-error-class": "^3.0.0",
+                        "duplexer3": "^0.1.4",
+                        "get-stream": "^3.0.0",
+                        "is-redirect": "^1.0.0",
+                        "is-retry-allowed": "^1.0.0",
+                        "is-stream": "^1.0.0",
+                        "lowercase-keys": "^1.0.0",
+                        "safe-buffer": "^5.0.1",
+                        "timed-out": "^4.0.0",
+                        "unzip-response": "^2.0.1",
+                        "url-parse-lax": "^1.0.0"
                     },
                     "dependencies": {
                         "get-stream": {
@@ -10341,15 +10360,15 @@
                     "version": "5.1.0",
                     "bundled": true,
                     "requires": {
-                        "ajv": "5.5.2",
-                        "har-schema": "2.0.0"
+                        "ajv": "^5.3.0",
+                        "har-schema": "^2.0.0"
                     }
                 },
                 "has": {
                     "version": "1.0.3",
                     "bundled": true,
                     "requires": {
-                        "function-bind": "1.1.1"
+                        "function-bind": "^1.1.1"
                     }
                 },
                 "has-flag": {
@@ -10376,7 +10395,7 @@
                     "version": "2.1.0",
                     "bundled": true,
                     "requires": {
-                        "agent-base": "4.3.0",
+                        "agent-base": "4",
                         "debug": "3.1.0"
                     }
                 },
@@ -10384,31 +10403,31 @@
                     "version": "1.2.0",
                     "bundled": true,
                     "requires": {
-                        "assert-plus": "1.0.0",
-                        "jsprim": "1.4.1",
-                        "sshpk": "1.14.2"
+                        "assert-plus": "^1.0.0",
+                        "jsprim": "^1.2.2",
+                        "sshpk": "^1.7.0"
                     }
                 },
                 "https-proxy-agent": {
                     "version": "2.2.4",
                     "bundled": true,
                     "requires": {
-                        "agent-base": "4.3.0",
-                        "debug": "3.1.0"
+                        "agent-base": "^4.3.0",
+                        "debug": "^3.1.0"
                     }
                 },
                 "humanize-ms": {
                     "version": "1.2.1",
                     "bundled": true,
                     "requires": {
-                        "ms": "2.1.1"
+                        "ms": "^2.0.0"
                     }
                 },
                 "iconv-lite": {
                     "version": "0.4.23",
                     "bundled": true,
                     "requires": {
-                        "safer-buffer": "2.1.2"
+                        "safer-buffer": ">= 2.1.2 < 3"
                     }
                 },
                 "iferr": {
@@ -10419,7 +10438,7 @@
                     "version": "3.0.3",
                     "bundled": true,
                     "requires": {
-                        "minimatch": "3.0.4"
+                        "minimatch": "^3.0.4"
                     }
                 },
                 "import-lazy": {
@@ -10438,8 +10457,8 @@
                     "version": "1.0.6",
                     "bundled": true,
                     "requires": {
-                        "once": "1.4.0",
-                        "wrappy": "1.0.2"
+                        "once": "^1.3.0",
+                        "wrappy": "1"
                     }
                 },
                 "inherits": {
@@ -10454,14 +10473,14 @@
                     "version": "1.10.3",
                     "bundled": true,
                     "requires": {
-                        "glob": "7.1.4",
-                        "npm-package-arg": "6.1.1",
-                        "promzard": "0.3.0",
-                        "read": "1.0.7",
-                        "read-package-json": "2.1.1",
-                        "semver": "5.7.1",
-                        "validate-npm-package-license": "3.0.4",
-                        "validate-npm-package-name": "3.0.0"
+                        "glob": "^7.1.1",
+                        "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0",
+                        "promzard": "^0.3.0",
+                        "read": "~1.0.1",
+                        "read-package-json": "1 || 2",
+                        "semver": "2.x || 3.x || 4 || 5",
+                        "validate-npm-package-license": "^3.0.1",
+                        "validate-npm-package-name": "^3.0.0"
                     }
                 },
                 "invert-kv": {
@@ -10484,7 +10503,7 @@
                     "version": "1.1.0",
                     "bundled": true,
                     "requires": {
-                        "ci-info": "1.6.0"
+                        "ci-info": "^1.0.0"
                     },
                     "dependencies": {
                         "ci-info": {
@@ -10497,7 +10516,7 @@
                     "version": "3.0.0",
                     "bundled": true,
                     "requires": {
-                        "cidr-regex": "2.0.10"
+                        "cidr-regex": "^2.0.10"
                     }
                 },
                 "is-date-object": {
@@ -10508,15 +10527,15 @@
                     "version": "1.0.0",
                     "bundled": true,
                     "requires": {
-                        "number-is-nan": "1.0.1"
+                        "number-is-nan": "^1.0.0"
                     }
                 },
                 "is-installed-globally": {
                     "version": "0.1.0",
                     "bundled": true,
                     "requires": {
-                        "global-dirs": "0.1.1",
-                        "is-path-inside": "1.0.1"
+                        "global-dirs": "^0.1.0",
+                        "is-path-inside": "^1.0.0"
                     }
                 },
                 "is-npm": {
@@ -10531,7 +10550,7 @@
                     "version": "1.0.1",
                     "bundled": true,
                     "requires": {
-                        "path-is-inside": "1.0.2"
+                        "path-is-inside": "^1.0.1"
                     }
                 },
                 "is-redirect": {
@@ -10542,7 +10561,7 @@
                     "version": "1.0.4",
                     "bundled": true,
                     "requires": {
-                        "has": "1.0.3"
+                        "has": "^1.0.1"
                     }
                 },
                 "is-retry-allowed": {
@@ -10557,7 +10576,7 @@
                     "version": "1.0.2",
                     "bundled": true,
                     "requires": {
-                        "has-symbols": "1.0.0"
+                        "has-symbols": "^1.0.0"
                     }
                 },
                 "is-typedarray": {
@@ -10615,7 +10634,7 @@
                     "version": "3.1.0",
                     "bundled": true,
                     "requires": {
-                        "package-json": "4.0.1"
+                        "package-json": "^4.0.0"
                     }
                 },
                 "lazy-property": {
@@ -10626,102 +10645,102 @@
                     "version": "1.0.0",
                     "bundled": true,
                     "requires": {
-                        "invert-kv": "1.0.0"
+                        "invert-kv": "^1.0.0"
                     }
                 },
                 "libcipm": {
                     "version": "4.0.7",
                     "bundled": true,
                     "requires": {
-                        "bin-links": "1.1.6",
-                        "bluebird": "3.5.5",
-                        "figgy-pudding": "3.5.1",
-                        "find-npm-prefix": "1.0.2",
-                        "graceful-fs": "4.2.3",
-                        "ini": "1.3.5",
-                        "lock-verify": "2.1.0",
-                        "mkdirp": "0.5.1",
-                        "npm-lifecycle": "3.1.4",
-                        "npm-logical-tree": "1.2.1",
-                        "npm-package-arg": "6.1.1",
-                        "pacote": "9.5.11",
-                        "read-package-json": "2.1.1",
-                        "rimraf": "2.6.3",
-                        "worker-farm": "1.7.0"
+                        "bin-links": "^1.1.2",
+                        "bluebird": "^3.5.1",
+                        "figgy-pudding": "^3.5.1",
+                        "find-npm-prefix": "^1.0.2",
+                        "graceful-fs": "^4.1.11",
+                        "ini": "^1.3.5",
+                        "lock-verify": "^2.0.2",
+                        "mkdirp": "^0.5.1",
+                        "npm-lifecycle": "^3.0.0",
+                        "npm-logical-tree": "^1.2.1",
+                        "npm-package-arg": "^6.1.0",
+                        "pacote": "^9.1.0",
+                        "read-package-json": "^2.0.13",
+                        "rimraf": "^2.6.2",
+                        "worker-farm": "^1.6.0"
                     }
                 },
                 "libnpm": {
                     "version": "3.0.1",
                     "bundled": true,
                     "requires": {
-                        "bin-links": "1.1.6",
-                        "bluebird": "3.5.5",
-                        "find-npm-prefix": "1.0.2",
-                        "libnpmaccess": "3.0.2",
-                        "libnpmconfig": "1.2.1",
-                        "libnpmhook": "5.0.3",
-                        "libnpmorg": "1.0.1",
-                        "libnpmpublish": "1.1.2",
-                        "libnpmsearch": "2.0.2",
-                        "libnpmteam": "1.0.2",
-                        "lock-verify": "2.1.0",
-                        "npm-lifecycle": "3.1.4",
-                        "npm-logical-tree": "1.2.1",
-                        "npm-package-arg": "6.1.1",
-                        "npm-profile": "4.0.2",
-                        "npm-registry-fetch": "4.0.2",
-                        "npmlog": "4.1.2",
-                        "pacote": "9.5.11",
-                        "read-package-json": "2.1.1",
-                        "stringify-package": "1.0.1"
+                        "bin-links": "^1.1.2",
+                        "bluebird": "^3.5.3",
+                        "find-npm-prefix": "^1.0.2",
+                        "libnpmaccess": "^3.0.2",
+                        "libnpmconfig": "^1.2.1",
+                        "libnpmhook": "^5.0.3",
+                        "libnpmorg": "^1.0.1",
+                        "libnpmpublish": "^1.1.2",
+                        "libnpmsearch": "^2.0.2",
+                        "libnpmteam": "^1.0.2",
+                        "lock-verify": "^2.0.2",
+                        "npm-lifecycle": "^3.0.0",
+                        "npm-logical-tree": "^1.2.1",
+                        "npm-package-arg": "^6.1.0",
+                        "npm-profile": "^4.0.2",
+                        "npm-registry-fetch": "^4.0.0",
+                        "npmlog": "^4.1.2",
+                        "pacote": "^9.5.3",
+                        "read-package-json": "^2.0.13",
+                        "stringify-package": "^1.0.0"
                     }
                 },
                 "libnpmaccess": {
                     "version": "3.0.2",
                     "bundled": true,
                     "requires": {
-                        "aproba": "2.0.0",
-                        "get-stream": "4.1.0",
-                        "npm-package-arg": "6.1.1",
-                        "npm-registry-fetch": "4.0.2"
+                        "aproba": "^2.0.0",
+                        "get-stream": "^4.0.0",
+                        "npm-package-arg": "^6.1.0",
+                        "npm-registry-fetch": "^4.0.0"
                     }
                 },
                 "libnpmconfig": {
                     "version": "1.2.1",
                     "bundled": true,
                     "requires": {
-                        "figgy-pudding": "3.5.1",
-                        "find-up": "3.0.0",
-                        "ini": "1.3.5"
+                        "figgy-pudding": "^3.5.1",
+                        "find-up": "^3.0.0",
+                        "ini": "^1.3.5"
                     },
                     "dependencies": {
                         "find-up": {
                             "version": "3.0.0",
                             "bundled": true,
                             "requires": {
-                                "locate-path": "3.0.0"
+                                "locate-path": "^3.0.0"
                             }
                         },
                         "locate-path": {
                             "version": "3.0.0",
                             "bundled": true,
                             "requires": {
-                                "p-locate": "3.0.0",
-                                "path-exists": "3.0.0"
+                                "p-locate": "^3.0.0",
+                                "path-exists": "^3.0.0"
                             }
                         },
                         "p-limit": {
                             "version": "2.2.0",
                             "bundled": true,
                             "requires": {
-                                "p-try": "2.2.0"
+                                "p-try": "^2.0.0"
                             }
                         },
                         "p-locate": {
                             "version": "3.0.0",
                             "bundled": true,
                             "requires": {
-                                "p-limit": "2.2.0"
+                                "p-limit": "^2.0.0"
                             }
                         },
                         "p-try": {
@@ -10734,91 +10753,91 @@
                     "version": "5.0.3",
                     "bundled": true,
                     "requires": {
-                        "aproba": "2.0.0",
-                        "figgy-pudding": "3.5.1",
-                        "get-stream": "4.1.0",
-                        "npm-registry-fetch": "4.0.2"
+                        "aproba": "^2.0.0",
+                        "figgy-pudding": "^3.4.1",
+                        "get-stream": "^4.0.0",
+                        "npm-registry-fetch": "^4.0.0"
                     }
                 },
                 "libnpmorg": {
                     "version": "1.0.1",
                     "bundled": true,
                     "requires": {
-                        "aproba": "2.0.0",
-                        "figgy-pudding": "3.5.1",
-                        "get-stream": "4.1.0",
-                        "npm-registry-fetch": "4.0.2"
+                        "aproba": "^2.0.0",
+                        "figgy-pudding": "^3.4.1",
+                        "get-stream": "^4.0.0",
+                        "npm-registry-fetch": "^4.0.0"
                     }
                 },
                 "libnpmpublish": {
                     "version": "1.1.2",
                     "bundled": true,
                     "requires": {
-                        "aproba": "2.0.0",
-                        "figgy-pudding": "3.5.1",
-                        "get-stream": "4.1.0",
-                        "lodash.clonedeep": "4.5.0",
-                        "normalize-package-data": "2.5.0",
-                        "npm-package-arg": "6.1.1",
-                        "npm-registry-fetch": "4.0.2",
-                        "semver": "5.7.1",
-                        "ssri": "6.0.1"
+                        "aproba": "^2.0.0",
+                        "figgy-pudding": "^3.5.1",
+                        "get-stream": "^4.0.0",
+                        "lodash.clonedeep": "^4.5.0",
+                        "normalize-package-data": "^2.4.0",
+                        "npm-package-arg": "^6.1.0",
+                        "npm-registry-fetch": "^4.0.0",
+                        "semver": "^5.5.1",
+                        "ssri": "^6.0.1"
                     }
                 },
                 "libnpmsearch": {
                     "version": "2.0.2",
                     "bundled": true,
                     "requires": {
-                        "figgy-pudding": "3.5.1",
-                        "get-stream": "4.1.0",
-                        "npm-registry-fetch": "4.0.2"
+                        "figgy-pudding": "^3.5.1",
+                        "get-stream": "^4.0.0",
+                        "npm-registry-fetch": "^4.0.0"
                     }
                 },
                 "libnpmteam": {
                     "version": "1.0.2",
                     "bundled": true,
                     "requires": {
-                        "aproba": "2.0.0",
-                        "figgy-pudding": "3.5.1",
-                        "get-stream": "4.1.0",
-                        "npm-registry-fetch": "4.0.2"
+                        "aproba": "^2.0.0",
+                        "figgy-pudding": "^3.4.1",
+                        "get-stream": "^4.0.0",
+                        "npm-registry-fetch": "^4.0.0"
                     }
                 },
                 "libnpx": {
                     "version": "10.2.0",
                     "bundled": true,
                     "requires": {
-                        "dotenv": "5.0.1",
-                        "npm-package-arg": "6.1.1",
-                        "rimraf": "2.6.3",
-                        "safe-buffer": "5.1.2",
-                        "update-notifier": "2.5.0",
-                        "which": "1.3.1",
-                        "y18n": "4.0.0",
-                        "yargs": "11.0.0"
+                        "dotenv": "^5.0.1",
+                        "npm-package-arg": "^6.0.0",
+                        "rimraf": "^2.6.2",
+                        "safe-buffer": "^5.1.0",
+                        "update-notifier": "^2.3.0",
+                        "which": "^1.3.0",
+                        "y18n": "^4.0.0",
+                        "yargs": "^11.0.0"
                     }
                 },
                 "locate-path": {
                     "version": "2.0.0",
                     "bundled": true,
                     "requires": {
-                        "p-locate": "2.0.0",
-                        "path-exists": "3.0.0"
+                        "p-locate": "^2.0.0",
+                        "path-exists": "^3.0.0"
                     }
                 },
                 "lock-verify": {
                     "version": "2.1.0",
                     "bundled": true,
                     "requires": {
-                        "npm-package-arg": "6.1.1",
-                        "semver": "5.7.1"
+                        "npm-package-arg": "^6.1.0",
+                        "semver": "^5.4.1"
                     }
                 },
                 "lockfile": {
                     "version": "1.0.4",
                     "bundled": true,
                     "requires": {
-                        "signal-exit": "3.0.2"
+                        "signal-exit": "^3.0.2"
                     }
                 },
                 "lodash._baseindexof": {
@@ -10829,8 +10848,8 @@
                     "version": "4.6.0",
                     "bundled": true,
                     "requires": {
-                        "lodash._createset": "4.0.3",
-                        "lodash._root": "3.0.1"
+                        "lodash._createset": "~4.0.0",
+                        "lodash._root": "~3.0.0"
                     }
                 },
                 "lodash._bindcallback": {
@@ -10845,7 +10864,7 @@
                     "version": "3.1.2",
                     "bundled": true,
                     "requires": {
-                        "lodash._getnative": "3.9.1"
+                        "lodash._getnative": "^3.0.0"
                     }
                 },
                 "lodash._createset": {
@@ -10888,31 +10907,31 @@
                     "version": "5.1.1",
                     "bundled": true,
                     "requires": {
-                        "yallist": "3.0.3"
+                        "yallist": "^3.0.2"
                     }
                 },
                 "make-dir": {
                     "version": "1.3.0",
                     "bundled": true,
                     "requires": {
-                        "pify": "3.0.0"
+                        "pify": "^3.0.0"
                     }
                 },
                 "make-fetch-happen": {
                     "version": "5.0.2",
                     "bundled": true,
                     "requires": {
-                        "agentkeepalive": "3.5.2",
-                        "cacache": "12.0.3",
-                        "http-cache-semantics": "3.8.1",
-                        "http-proxy-agent": "2.1.0",
-                        "https-proxy-agent": "2.2.4",
-                        "lru-cache": "5.1.1",
-                        "mississippi": "3.0.0",
-                        "node-fetch-npm": "2.0.2",
-                        "promise-retry": "1.1.1",
-                        "socks-proxy-agent": "4.0.2",
-                        "ssri": "6.0.1"
+                        "agentkeepalive": "^3.4.1",
+                        "cacache": "^12.0.0",
+                        "http-cache-semantics": "^3.8.1",
+                        "http-proxy-agent": "^2.1.0",
+                        "https-proxy-agent": "^2.2.3",
+                        "lru-cache": "^5.1.1",
+                        "mississippi": "^3.0.0",
+                        "node-fetch-npm": "^2.0.2",
+                        "promise-retry": "^1.1.1",
+                        "socks-proxy-agent": "^4.0.0",
+                        "ssri": "^6.0.0"
                     }
                 },
                 "meant": {
@@ -10923,7 +10942,7 @@
                     "version": "1.1.0",
                     "bundled": true,
                     "requires": {
-                        "mimic-fn": "1.2.0"
+                        "mimic-fn": "^1.0.0"
                     }
                 },
                 "mime-db": {
@@ -10934,7 +10953,7 @@
                     "version": "2.1.19",
                     "bundled": true,
                     "requires": {
-                        "mime-db": "1.35.0"
+                        "mime-db": "~1.35.0"
                     }
                 },
                 "mimic-fn": {
@@ -10945,7 +10964,7 @@
                     "version": "3.0.4",
                     "bundled": true,
                     "requires": {
-                        "brace-expansion": "1.1.11"
+                        "brace-expansion": "^1.1.7"
                     }
                 },
                 "minimist": {
@@ -10956,15 +10975,15 @@
                     "version": "1.3.3",
                     "bundled": true,
                     "requires": {
-                        "minipass": "2.9.0"
+                        "minipass": "^2.9.0"
                     },
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
                             "bundled": true,
                             "requires": {
-                                "safe-buffer": "5.1.2",
-                                "yallist": "3.0.3"
+                                "safe-buffer": "^5.1.2",
+                                "yallist": "^3.0.0"
                             }
                         }
                     }
@@ -10973,16 +10992,16 @@
                     "version": "3.0.0",
                     "bundled": true,
                     "requires": {
-                        "concat-stream": "1.6.2",
-                        "duplexify": "3.6.0",
-                        "end-of-stream": "1.4.1",
-                        "flush-write-stream": "1.0.3",
-                        "from2": "2.3.0",
-                        "parallel-transform": "1.1.0",
-                        "pump": "3.0.0",
-                        "pumpify": "1.5.1",
-                        "stream-each": "1.2.2",
-                        "through2": "2.0.3"
+                        "concat-stream": "^1.5.0",
+                        "duplexify": "^3.4.2",
+                        "end-of-stream": "^1.1.0",
+                        "flush-write-stream": "^1.0.0",
+                        "from2": "^2.1.0",
+                        "parallel-transform": "^1.1.0",
+                        "pump": "^3.0.0",
+                        "pumpify": "^1.3.3",
+                        "stream-each": "^1.1.0",
+                        "through2": "^2.0.0"
                     }
                 },
                 "mkdirp": {
@@ -10996,12 +11015,12 @@
                     "version": "1.0.1",
                     "bundled": true,
                     "requires": {
-                        "aproba": "1.2.0",
-                        "copy-concurrently": "1.0.5",
-                        "fs-write-stream-atomic": "1.0.10",
-                        "mkdirp": "0.5.1",
-                        "rimraf": "2.6.3",
-                        "run-queue": "1.0.3"
+                        "aproba": "^1.1.1",
+                        "copy-concurrently": "^1.0.0",
+                        "fs-write-stream-atomic": "^1.0.8",
+                        "mkdirp": "^0.5.1",
+                        "rimraf": "^2.5.4",
+                        "run-queue": "^1.0.3"
                     },
                     "dependencies": {
                         "aproba": {
@@ -11022,33 +11041,33 @@
                     "version": "2.0.2",
                     "bundled": true,
                     "requires": {
-                        "encoding": "0.1.12",
-                        "json-parse-better-errors": "1.0.2",
-                        "safe-buffer": "5.1.2"
+                        "encoding": "^0.1.11",
+                        "json-parse-better-errors": "^1.0.0",
+                        "safe-buffer": "^5.1.1"
                     }
                 },
                 "node-gyp": {
                     "version": "5.0.5",
                     "bundled": true,
                     "requires": {
-                        "env-paths": "1.0.0",
-                        "glob": "7.1.4",
-                        "graceful-fs": "4.2.3",
-                        "mkdirp": "0.5.1",
-                        "nopt": "3.0.6",
-                        "npmlog": "4.1.2",
-                        "request": "2.88.0",
-                        "rimraf": "2.6.3",
-                        "semver": "5.3.0",
-                        "tar": "4.4.13",
-                        "which": "1.3.1"
+                        "env-paths": "^1.0.0",
+                        "glob": "^7.0.3",
+                        "graceful-fs": "^4.1.2",
+                        "mkdirp": "^0.5.0",
+                        "nopt": "2 || 3",
+                        "npmlog": "0 || 1 || 2 || 3 || 4",
+                        "request": "^2.87.0",
+                        "rimraf": "2",
+                        "semver": "~5.3.0",
+                        "tar": "^4.4.12",
+                        "which": "1"
                     },
                     "dependencies": {
                         "nopt": {
                             "version": "3.0.6",
                             "bundled": true,
                             "requires": {
-                                "abbrev": "1.1.1"
+                                "abbrev": "1"
                             }
                         },
                         "semver": {
@@ -11061,25 +11080,25 @@
                     "version": "4.0.1",
                     "bundled": true,
                     "requires": {
-                        "abbrev": "1.1.1",
-                        "osenv": "0.1.5"
+                        "abbrev": "1",
+                        "osenv": "^0.1.4"
                     }
                 },
                 "normalize-package-data": {
                     "version": "2.5.0",
                     "bundled": true,
                     "requires": {
-                        "hosted-git-info": "2.8.5",
-                        "resolve": "1.10.0",
-                        "semver": "5.7.1",
-                        "validate-npm-package-license": "3.0.4"
+                        "hosted-git-info": "^2.1.4",
+                        "resolve": "^1.10.0",
+                        "semver": "2 || 3 || 4 || 5",
+                        "validate-npm-package-license": "^3.0.1"
                     },
                     "dependencies": {
                         "resolve": {
                             "version": "1.10.0",
                             "bundled": true,
                             "requires": {
-                                "path-parse": "1.0.6"
+                                "path-parse": "^1.0.6"
                             }
                         }
                     }
@@ -11088,15 +11107,15 @@
                     "version": "1.3.2",
                     "bundled": true,
                     "requires": {
-                        "cli-table3": "0.5.1",
-                        "console-control-strings": "1.1.0"
+                        "cli-table3": "^0.5.0",
+                        "console-control-strings": "^1.1.0"
                     }
                 },
                 "npm-bundled": {
                     "version": "1.1.1",
                     "bundled": true,
                     "requires": {
-                        "npm-normalize-package-bin": "1.0.1"
+                        "npm-normalize-package-bin": "^1.0.1"
                     }
                 },
                 "npm-cache-filename": {
@@ -11107,21 +11126,21 @@
                     "version": "3.0.2",
                     "bundled": true,
                     "requires": {
-                        "semver": "5.7.1"
+                        "semver": "^2.3.0 || 3.x || 4 || 5"
                     }
                 },
                 "npm-lifecycle": {
                     "version": "3.1.4",
                     "bundled": true,
                     "requires": {
-                        "byline": "5.0.0",
-                        "graceful-fs": "4.2.3",
-                        "node-gyp": "5.0.5",
-                        "resolve-from": "4.0.0",
-                        "slide": "1.1.6",
+                        "byline": "^5.0.0",
+                        "graceful-fs": "^4.1.15",
+                        "node-gyp": "^5.0.2",
+                        "resolve-from": "^4.0.0",
+                        "slide": "^1.1.6",
                         "uid-number": "0.0.6",
-                        "umask": "1.1.0",
-                        "which": "1.3.1"
+                        "umask": "^1.1.0",
+                        "which": "^1.3.1"
                     }
                 },
                 "npm-logical-tree": {
@@ -11136,49 +11155,49 @@
                     "version": "6.1.1",
                     "bundled": true,
                     "requires": {
-                        "hosted-git-info": "2.8.5",
-                        "osenv": "0.1.5",
-                        "semver": "5.7.1",
-                        "validate-npm-package-name": "3.0.0"
+                        "hosted-git-info": "^2.7.1",
+                        "osenv": "^0.1.5",
+                        "semver": "^5.6.0",
+                        "validate-npm-package-name": "^3.0.0"
                     }
                 },
                 "npm-packlist": {
                     "version": "1.4.7",
                     "bundled": true,
                     "requires": {
-                        "ignore-walk": "3.0.3",
-                        "npm-bundled": "1.1.1"
+                        "ignore-walk": "^3.0.1",
+                        "npm-bundled": "^1.0.1"
                     }
                 },
                 "npm-pick-manifest": {
                     "version": "3.0.2",
                     "bundled": true,
                     "requires": {
-                        "figgy-pudding": "3.5.1",
-                        "npm-package-arg": "6.1.1",
-                        "semver": "5.7.1"
+                        "figgy-pudding": "^3.5.1",
+                        "npm-package-arg": "^6.0.0",
+                        "semver": "^5.4.1"
                     }
                 },
                 "npm-profile": {
                     "version": "4.0.2",
                     "bundled": true,
                     "requires": {
-                        "aproba": "2.0.0",
-                        "figgy-pudding": "3.5.1",
-                        "npm-registry-fetch": "4.0.2"
+                        "aproba": "^1.1.2 || 2",
+                        "figgy-pudding": "^3.4.1",
+                        "npm-registry-fetch": "^4.0.0"
                     }
                 },
                 "npm-registry-fetch": {
                     "version": "4.0.2",
                     "bundled": true,
                     "requires": {
-                        "JSONStream": "1.3.5",
-                        "bluebird": "3.5.5",
-                        "figgy-pudding": "3.5.1",
-                        "lru-cache": "5.1.1",
-                        "make-fetch-happen": "5.0.2",
-                        "npm-package-arg": "6.1.1",
-                        "safe-buffer": "5.2.0"
+                        "JSONStream": "^1.3.4",
+                        "bluebird": "^3.5.1",
+                        "figgy-pudding": "^3.4.1",
+                        "lru-cache": "^5.1.1",
+                        "make-fetch-happen": "^5.0.0",
+                        "npm-package-arg": "^6.1.0",
+                        "safe-buffer": "^5.2.0"
                     },
                     "dependencies": {
                         "safe-buffer": {
@@ -11191,7 +11210,7 @@
                     "version": "2.0.2",
                     "bundled": true,
                     "requires": {
-                        "path-key": "2.0.1"
+                        "path-key": "^2.0.0"
                     }
                 },
                 "npm-user-validate": {
@@ -11202,10 +11221,10 @@
                     "version": "4.1.2",
                     "bundled": true,
                     "requires": {
-                        "are-we-there-yet": "1.1.4",
-                        "console-control-strings": "1.1.0",
-                        "gauge": "2.7.4",
-                        "set-blocking": "2.0.0"
+                        "are-we-there-yet": "~1.1.2",
+                        "console-control-strings": "~1.1.0",
+                        "gauge": "~2.7.3",
+                        "set-blocking": "~2.0.0"
                     }
                 },
                 "number-is-nan": {
@@ -11228,15 +11247,15 @@
                     "version": "2.0.3",
                     "bundled": true,
                     "requires": {
-                        "define-properties": "1.1.3",
-                        "es-abstract": "1.12.0"
+                        "define-properties": "^1.1.2",
+                        "es-abstract": "^1.5.1"
                     }
                 },
                 "once": {
                     "version": "1.4.0",
                     "bundled": true,
                     "requires": {
-                        "wrappy": "1.0.2"
+                        "wrappy": "1"
                     }
                 },
                 "opener": {
@@ -11251,9 +11270,9 @@
                     "version": "2.1.0",
                     "bundled": true,
                     "requires": {
-                        "execa": "0.7.0",
-                        "lcid": "1.0.0",
-                        "mem": "1.1.0"
+                        "execa": "^0.7.0",
+                        "lcid": "^1.0.0",
+                        "mem": "^1.1.0"
                     }
                 },
                 "os-tmpdir": {
@@ -11264,8 +11283,8 @@
                     "version": "0.1.5",
                     "bundled": true,
                     "requires": {
-                        "os-homedir": "1.0.2",
-                        "os-tmpdir": "1.0.2"
+                        "os-homedir": "^1.0.0",
+                        "os-tmpdir": "^1.0.0"
                     }
                 },
                 "p-finally": {
@@ -11276,14 +11295,14 @@
                     "version": "1.2.0",
                     "bundled": true,
                     "requires": {
-                        "p-try": "1.0.0"
+                        "p-try": "^1.0.0"
                     }
                 },
                 "p-locate": {
                     "version": "2.0.0",
                     "bundled": true,
                     "requires": {
-                        "p-limit": "1.2.0"
+                        "p-limit": "^1.1.0"
                     }
                 },
                 "p-try": {
@@ -11294,54 +11313,54 @@
                     "version": "4.0.1",
                     "bundled": true,
                     "requires": {
-                        "got": "6.7.1",
-                        "registry-auth-token": "3.3.2",
-                        "registry-url": "3.1.0",
-                        "semver": "5.7.1"
+                        "got": "^6.7.1",
+                        "registry-auth-token": "^3.0.1",
+                        "registry-url": "^3.0.3",
+                        "semver": "^5.1.0"
                     }
                 },
                 "pacote": {
                     "version": "9.5.11",
                     "bundled": true,
                     "requires": {
-                        "bluebird": "3.5.5",
-                        "cacache": "12.0.3",
-                        "chownr": "1.1.3",
-                        "figgy-pudding": "3.5.1",
-                        "get-stream": "4.1.0",
-                        "glob": "7.1.4",
-                        "infer-owner": "1.0.4",
-                        "lru-cache": "5.1.1",
-                        "make-fetch-happen": "5.0.2",
-                        "minimatch": "3.0.4",
-                        "minipass": "2.9.0",
-                        "mississippi": "3.0.0",
-                        "mkdirp": "0.5.1",
-                        "normalize-package-data": "2.5.0",
-                        "npm-normalize-package-bin": "1.0.1",
-                        "npm-package-arg": "6.1.1",
-                        "npm-packlist": "1.4.7",
-                        "npm-pick-manifest": "3.0.2",
-                        "npm-registry-fetch": "4.0.2",
-                        "osenv": "0.1.5",
-                        "promise-inflight": "1.0.1",
-                        "promise-retry": "1.1.1",
-                        "protoduck": "5.0.1",
-                        "rimraf": "2.6.3",
-                        "safe-buffer": "5.1.2",
-                        "semver": "5.7.1",
-                        "ssri": "6.0.1",
-                        "tar": "4.4.13",
-                        "unique-filename": "1.1.1",
-                        "which": "1.3.1"
+                        "bluebird": "^3.5.3",
+                        "cacache": "^12.0.2",
+                        "chownr": "^1.1.2",
+                        "figgy-pudding": "^3.5.1",
+                        "get-stream": "^4.1.0",
+                        "glob": "^7.1.3",
+                        "infer-owner": "^1.0.4",
+                        "lru-cache": "^5.1.1",
+                        "make-fetch-happen": "^5.0.0",
+                        "minimatch": "^3.0.4",
+                        "minipass": "^2.3.5",
+                        "mississippi": "^3.0.0",
+                        "mkdirp": "^0.5.1",
+                        "normalize-package-data": "^2.4.0",
+                        "npm-normalize-package-bin": "^1.0.0",
+                        "npm-package-arg": "^6.1.0",
+                        "npm-packlist": "^1.1.12",
+                        "npm-pick-manifest": "^3.0.0",
+                        "npm-registry-fetch": "^4.0.0",
+                        "osenv": "^0.1.5",
+                        "promise-inflight": "^1.0.1",
+                        "promise-retry": "^1.1.1",
+                        "protoduck": "^5.0.1",
+                        "rimraf": "^2.6.2",
+                        "safe-buffer": "^5.1.2",
+                        "semver": "^5.6.0",
+                        "ssri": "^6.0.1",
+                        "tar": "^4.4.10",
+                        "unique-filename": "^1.1.1",
+                        "which": "^1.3.1"
                     },
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
                             "bundled": true,
                             "requires": {
-                                "safe-buffer": "5.1.2",
-                                "yallist": "3.0.3"
+                                "safe-buffer": "^5.1.2",
+                                "yallist": "^3.0.0"
                             }
                         }
                     }
@@ -11350,29 +11369,29 @@
                     "version": "1.1.0",
                     "bundled": true,
                     "requires": {
-                        "cyclist": "0.2.2",
-                        "inherits": "2.0.4",
-                        "readable-stream": "2.3.6"
+                        "cyclist": "~0.2.2",
+                        "inherits": "^2.0.3",
+                        "readable-stream": "^2.1.5"
                     },
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
                             "bundled": true,
                             "requires": {
-                                "core-util-is": "1.0.2",
-                                "inherits": "2.0.4",
-                                "isarray": "1.0.0",
-                                "process-nextick-args": "2.0.0",
-                                "safe-buffer": "5.1.2",
-                                "string_decoder": "1.1.1",
-                                "util-deprecate": "1.0.2"
+                                "core-util-is": "~1.0.0",
+                                "inherits": "~2.0.3",
+                                "isarray": "~1.0.0",
+                                "process-nextick-args": "~2.0.0",
+                                "safe-buffer": "~5.1.1",
+                                "string_decoder": "~1.1.1",
+                                "util-deprecate": "~1.0.1"
                             }
                         },
                         "string_decoder": {
                             "version": "1.1.1",
                             "bundled": true,
                             "requires": {
-                                "safe-buffer": "5.1.2"
+                                "safe-buffer": "~5.1.0"
                             }
                         }
                     }
@@ -11421,8 +11440,8 @@
                     "version": "1.1.1",
                     "bundled": true,
                     "requires": {
-                        "err-code": "1.1.2",
-                        "retry": "0.10.1"
+                        "err-code": "^1.0.0",
+                        "retry": "^0.10.0"
                     },
                     "dependencies": {
                         "retry": {
@@ -11435,7 +11454,7 @@
                     "version": "0.3.0",
                     "bundled": true,
                     "requires": {
-                        "read": "1.0.7"
+                        "read": "1"
                     }
                 },
                 "proto-list": {
@@ -11446,7 +11465,7 @@
                     "version": "5.0.1",
                     "bundled": true,
                     "requires": {
-                        "genfun": "5.0.0"
+                        "genfun": "^5.0.0"
                     }
                 },
                 "prr": {
@@ -11465,25 +11484,25 @@
                     "version": "3.0.0",
                     "bundled": true,
                     "requires": {
-                        "end-of-stream": "1.4.1",
-                        "once": "1.4.0"
+                        "end-of-stream": "^1.1.0",
+                        "once": "^1.3.1"
                     }
                 },
                 "pumpify": {
                     "version": "1.5.1",
                     "bundled": true,
                     "requires": {
-                        "duplexify": "3.6.0",
-                        "inherits": "2.0.4",
-                        "pump": "2.0.1"
+                        "duplexify": "^3.6.0",
+                        "inherits": "^2.0.3",
+                        "pump": "^2.0.0"
                     },
                     "dependencies": {
                         "pump": {
                             "version": "2.0.1",
                             "bundled": true,
                             "requires": {
-                                "end-of-stream": "1.4.1",
-                                "once": "1.4.0"
+                                "end-of-stream": "^1.1.0",
+                                "once": "^1.3.1"
                             }
                         }
                     }
@@ -11504,9 +11523,9 @@
                     "version": "6.8.2",
                     "bundled": true,
                     "requires": {
-                        "decode-uri-component": "0.2.0",
-                        "split-on-first": "1.1.0",
-                        "strict-uri-encode": "2.0.0"
+                        "decode-uri-component": "^0.2.0",
+                        "split-on-first": "^1.0.0",
+                        "strict-uri-encode": "^2.0.0"
                     }
                 },
                 "qw": {
@@ -11517,10 +11536,10 @@
                     "version": "1.2.7",
                     "bundled": true,
                     "requires": {
-                        "deep-extend": "0.5.1",
-                        "ini": "1.3.5",
-                        "minimist": "1.2.0",
-                        "strip-json-comments": "2.0.1"
+                        "deep-extend": "^0.5.1",
+                        "ini": "~1.3.0",
+                        "minimist": "^1.2.0",
+                        "strip-json-comments": "~2.0.1"
                     },
                     "dependencies": {
                         "minimist": {
@@ -11533,107 +11552,107 @@
                     "version": "1.0.7",
                     "bundled": true,
                     "requires": {
-                        "mute-stream": "0.0.7"
+                        "mute-stream": "~0.0.4"
                     }
                 },
                 "read-cmd-shim": {
                     "version": "1.0.5",
                     "bundled": true,
                     "requires": {
-                        "graceful-fs": "4.2.3"
+                        "graceful-fs": "^4.1.2"
                     }
                 },
                 "read-installed": {
                     "version": "4.0.3",
                     "bundled": true,
                     "requires": {
-                        "debuglog": "1.0.1",
-                        "graceful-fs": "4.2.3",
-                        "read-package-json": "2.1.1",
-                        "readdir-scoped-modules": "1.1.0",
-                        "semver": "5.7.1",
-                        "slide": "1.1.6",
-                        "util-extend": "1.0.3"
+                        "debuglog": "^1.0.1",
+                        "graceful-fs": "^4.1.2",
+                        "read-package-json": "^2.0.0",
+                        "readdir-scoped-modules": "^1.0.0",
+                        "semver": "2 || 3 || 4 || 5",
+                        "slide": "~1.1.3",
+                        "util-extend": "^1.0.1"
                     }
                 },
                 "read-package-json": {
                     "version": "2.1.1",
                     "bundled": true,
                     "requires": {
-                        "glob": "7.1.4",
-                        "graceful-fs": "4.2.3",
-                        "json-parse-better-errors": "1.0.2",
-                        "normalize-package-data": "2.5.0",
-                        "npm-normalize-package-bin": "1.0.1"
+                        "glob": "^7.1.1",
+                        "graceful-fs": "^4.1.2",
+                        "json-parse-better-errors": "^1.0.1",
+                        "normalize-package-data": "^2.0.0",
+                        "npm-normalize-package-bin": "^1.0.0"
                     }
                 },
                 "read-package-tree": {
                     "version": "5.3.1",
                     "bundled": true,
                     "requires": {
-                        "read-package-json": "2.1.1",
-                        "readdir-scoped-modules": "1.1.0",
-                        "util-promisify": "2.1.0"
+                        "read-package-json": "^2.0.0",
+                        "readdir-scoped-modules": "^1.0.0",
+                        "util-promisify": "^2.1.0"
                     }
                 },
                 "readable-stream": {
                     "version": "3.4.0",
                     "bundled": true,
                     "requires": {
-                        "inherits": "2.0.4",
-                        "string_decoder": "1.2.0",
-                        "util-deprecate": "1.0.2"
+                        "inherits": "^2.0.3",
+                        "string_decoder": "^1.1.1",
+                        "util-deprecate": "^1.0.1"
                     }
                 },
                 "readdir-scoped-modules": {
                     "version": "1.1.0",
                     "bundled": true,
                     "requires": {
-                        "debuglog": "1.0.1",
-                        "dezalgo": "1.0.3",
-                        "graceful-fs": "4.2.3",
-                        "once": "1.4.0"
+                        "debuglog": "^1.0.1",
+                        "dezalgo": "^1.0.0",
+                        "graceful-fs": "^4.1.2",
+                        "once": "^1.3.0"
                     }
                 },
                 "registry-auth-token": {
                     "version": "3.3.2",
                     "bundled": true,
                     "requires": {
-                        "rc": "1.2.7",
-                        "safe-buffer": "5.1.2"
+                        "rc": "^1.1.6",
+                        "safe-buffer": "^5.0.1"
                     }
                 },
                 "registry-url": {
                     "version": "3.1.0",
                     "bundled": true,
                     "requires": {
-                        "rc": "1.2.7"
+                        "rc": "^1.0.1"
                     }
                 },
                 "request": {
                     "version": "2.88.0",
                     "bundled": true,
                     "requires": {
-                        "aws-sign2": "0.7.0",
-                        "aws4": "1.8.0",
-                        "caseless": "0.12.0",
-                        "combined-stream": "1.0.6",
-                        "extend": "3.0.2",
-                        "forever-agent": "0.6.1",
-                        "form-data": "2.3.2",
-                        "har-validator": "5.1.0",
-                        "http-signature": "1.2.0",
-                        "is-typedarray": "1.0.0",
-                        "isstream": "0.1.2",
-                        "json-stringify-safe": "5.0.1",
-                        "mime-types": "2.1.19",
-                        "oauth-sign": "0.9.0",
-                        "performance-now": "2.1.0",
-                        "qs": "6.5.2",
-                        "safe-buffer": "5.1.2",
-                        "tough-cookie": "2.4.3",
-                        "tunnel-agent": "0.6.0",
-                        "uuid": "3.3.3"
+                        "aws-sign2": "~0.7.0",
+                        "aws4": "^1.8.0",
+                        "caseless": "~0.12.0",
+                        "combined-stream": "~1.0.6",
+                        "extend": "~3.0.2",
+                        "forever-agent": "~0.6.1",
+                        "form-data": "~2.3.2",
+                        "har-validator": "~5.1.0",
+                        "http-signature": "~1.2.0",
+                        "is-typedarray": "~1.0.0",
+                        "isstream": "~0.1.2",
+                        "json-stringify-safe": "~5.0.1",
+                        "mime-types": "~2.1.19",
+                        "oauth-sign": "~0.9.0",
+                        "performance-now": "^2.1.0",
+                        "qs": "~6.5.2",
+                        "safe-buffer": "^5.1.2",
+                        "tough-cookie": "~2.4.3",
+                        "tunnel-agent": "^0.6.0",
+                        "uuid": "^3.3.2"
                     }
                 },
                 "require-directory": {
@@ -11656,14 +11675,14 @@
                     "version": "2.6.3",
                     "bundled": true,
                     "requires": {
-                        "glob": "7.1.4"
+                        "glob": "^7.1.3"
                     }
                 },
                 "run-queue": {
                     "version": "1.0.3",
                     "bundled": true,
                     "requires": {
-                        "aproba": "1.2.0"
+                        "aproba": "^1.1.1"
                     },
                     "dependencies": {
                         "aproba": {
@@ -11688,7 +11707,7 @@
                     "version": "2.1.0",
                     "bundled": true,
                     "requires": {
-                        "semver": "5.7.1"
+                        "semver": "^5.0.3"
                     }
                 },
                 "set-blocking": {
@@ -11699,14 +11718,14 @@
                     "version": "3.0.0",
                     "bundled": true,
                     "requires": {
-                        "graceful-fs": "4.2.3"
+                        "graceful-fs": "^4.1.2"
                     }
                 },
                 "shebang-command": {
                     "version": "1.2.0",
                     "bundled": true,
                     "requires": {
-                        "shebang-regex": "1.0.0"
+                        "shebang-regex": "^1.0.0"
                     }
                 },
                 "shebang-regex": {
@@ -11730,22 +11749,22 @@
                     "bundled": true,
                     "requires": {
                         "ip": "1.1.5",
-                        "smart-buffer": "4.1.0"
+                        "smart-buffer": "^4.1.0"
                     }
                 },
                 "socks-proxy-agent": {
                     "version": "4.0.2",
                     "bundled": true,
                     "requires": {
-                        "agent-base": "4.2.1",
-                        "socks": "2.3.3"
+                        "agent-base": "~4.2.1",
+                        "socks": "~2.3.2"
                     },
                     "dependencies": {
                         "agent-base": {
                             "version": "4.2.1",
                             "bundled": true,
                             "requires": {
-                                "es6-promisify": "5.0.0"
+                                "es6-promisify": "^5.0.0"
                             }
                         }
                     }
@@ -11758,16 +11777,16 @@
                     "version": "2.1.3",
                     "bundled": true,
                     "requires": {
-                        "from2": "1.3.0",
-                        "stream-iterate": "1.2.0"
+                        "from2": "^1.3.0",
+                        "stream-iterate": "^1.1.0"
                     },
                     "dependencies": {
                         "from2": {
                             "version": "1.3.0",
                             "bundled": true,
                             "requires": {
-                                "inherits": "2.0.4",
-                                "readable-stream": "1.1.14"
+                                "inherits": "~2.0.1",
+                                "readable-stream": "~1.1.10"
                             }
                         },
                         "isarray": {
@@ -11778,10 +11797,10 @@
                             "version": "1.1.14",
                             "bundled": true,
                             "requires": {
-                                "core-util-is": "1.0.2",
-                                "inherits": "2.0.4",
+                                "core-util-is": "~1.0.0",
+                                "inherits": "~2.0.1",
                                 "isarray": "0.0.1",
-                                "string_decoder": "0.10.31"
+                                "string_decoder": "~0.10.x"
                             }
                         },
                         "string_decoder": {
@@ -11794,8 +11813,8 @@
                     "version": "3.0.0",
                     "bundled": true,
                     "requires": {
-                        "spdx-expression-parse": "3.0.0",
-                        "spdx-license-ids": "3.0.3"
+                        "spdx-expression-parse": "^3.0.0",
+                        "spdx-license-ids": "^3.0.0"
                     }
                 },
                 "spdx-exceptions": {
@@ -11806,8 +11825,8 @@
                     "version": "3.0.0",
                     "bundled": true,
                     "requires": {
-                        "spdx-exceptions": "2.1.0",
-                        "spdx-license-ids": "3.0.3"
+                        "spdx-exceptions": "^2.1.0",
+                        "spdx-license-ids": "^3.0.0"
                     }
                 },
                 "spdx-license-ids": {
@@ -11822,58 +11841,58 @@
                     "version": "1.14.2",
                     "bundled": true,
                     "requires": {
-                        "asn1": "0.2.4",
-                        "assert-plus": "1.0.0",
-                        "bcrypt-pbkdf": "1.0.2",
-                        "dashdash": "1.14.1",
-                        "ecc-jsbn": "0.1.2",
-                        "getpass": "0.1.7",
-                        "jsbn": "0.1.1",
-                        "safer-buffer": "2.1.2",
-                        "tweetnacl": "0.14.5"
+                        "asn1": "~0.2.3",
+                        "assert-plus": "^1.0.0",
+                        "bcrypt-pbkdf": "^1.0.0",
+                        "dashdash": "^1.12.0",
+                        "ecc-jsbn": "~0.1.1",
+                        "getpass": "^0.1.1",
+                        "jsbn": "~0.1.0",
+                        "safer-buffer": "^2.0.2",
+                        "tweetnacl": "~0.14.0"
                     }
                 },
                 "ssri": {
                     "version": "6.0.1",
                     "bundled": true,
                     "requires": {
-                        "figgy-pudding": "3.5.1"
+                        "figgy-pudding": "^3.5.1"
                     }
                 },
                 "stream-each": {
                     "version": "1.2.2",
                     "bundled": true,
                     "requires": {
-                        "end-of-stream": "1.4.1",
-                        "stream-shift": "1.0.0"
+                        "end-of-stream": "^1.1.0",
+                        "stream-shift": "^1.0.0"
                     }
                 },
                 "stream-iterate": {
                     "version": "1.2.0",
                     "bundled": true,
                     "requires": {
-                        "readable-stream": "2.3.6",
-                        "stream-shift": "1.0.0"
+                        "readable-stream": "^2.1.5",
+                        "stream-shift": "^1.0.0"
                     },
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
                             "bundled": true,
                             "requires": {
-                                "core-util-is": "1.0.2",
-                                "inherits": "2.0.4",
-                                "isarray": "1.0.0",
-                                "process-nextick-args": "2.0.0",
-                                "safe-buffer": "5.1.2",
-                                "string_decoder": "1.1.1",
-                                "util-deprecate": "1.0.2"
+                                "core-util-is": "~1.0.0",
+                                "inherits": "~2.0.3",
+                                "isarray": "~1.0.0",
+                                "process-nextick-args": "~2.0.0",
+                                "safe-buffer": "~5.1.1",
+                                "string_decoder": "~1.1.1",
+                                "util-deprecate": "~1.0.1"
                             }
                         },
                         "string_decoder": {
                             "version": "1.1.1",
                             "bundled": true,
                             "requires": {
-                                "safe-buffer": "5.1.2"
+                                "safe-buffer": "~5.1.0"
                             }
                         }
                     }
@@ -11890,8 +11909,8 @@
                     "version": "2.1.1",
                     "bundled": true,
                     "requires": {
-                        "is-fullwidth-code-point": "2.0.0",
-                        "strip-ansi": "4.0.0"
+                        "is-fullwidth-code-point": "^2.0.0",
+                        "strip-ansi": "^4.0.0"
                     },
                     "dependencies": {
                         "ansi-regex": {
@@ -11906,7 +11925,7 @@
                             "version": "4.0.0",
                             "bundled": true,
                             "requires": {
-                                "ansi-regex": "3.0.0"
+                                "ansi-regex": "^3.0.0"
                             }
                         }
                     }
@@ -11915,7 +11934,7 @@
                     "version": "1.2.0",
                     "bundled": true,
                     "requires": {
-                        "safe-buffer": "5.1.2"
+                        "safe-buffer": "~5.1.0"
                     }
                 },
                 "stringify-package": {
@@ -11926,7 +11945,7 @@
                     "version": "3.0.1",
                     "bundled": true,
                     "requires": {
-                        "ansi-regex": "2.1.1"
+                        "ansi-regex": "^2.0.0"
                     }
                 },
                 "strip-eof": {
@@ -11941,28 +11960,28 @@
                     "version": "5.4.0",
                     "bundled": true,
                     "requires": {
-                        "has-flag": "3.0.0"
+                        "has-flag": "^3.0.0"
                     }
                 },
                 "tar": {
                     "version": "4.4.13",
                     "bundled": true,
                     "requires": {
-                        "chownr": "1.1.3",
-                        "fs-minipass": "1.2.7",
-                        "minipass": "2.9.0",
-                        "minizlib": "1.3.3",
-                        "mkdirp": "0.5.1",
-                        "safe-buffer": "5.1.2",
-                        "yallist": "3.0.3"
+                        "chownr": "^1.1.1",
+                        "fs-minipass": "^1.2.5",
+                        "minipass": "^2.8.6",
+                        "minizlib": "^1.2.1",
+                        "mkdirp": "^0.5.0",
+                        "safe-buffer": "^5.1.2",
+                        "yallist": "^3.0.3"
                     },
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
                             "bundled": true,
                             "requires": {
-                                "safe-buffer": "5.1.2",
-                                "yallist": "3.0.3"
+                                "safe-buffer": "^5.1.2",
+                                "yallist": "^3.0.0"
                             }
                         }
                     }
@@ -11971,7 +11990,7 @@
                     "version": "1.2.0",
                     "bundled": true,
                     "requires": {
-                        "execa": "0.7.0"
+                        "execa": "^0.7.0"
                     }
                 },
                 "text-table": {
@@ -11986,28 +12005,28 @@
                     "version": "2.0.3",
                     "bundled": true,
                     "requires": {
-                        "readable-stream": "2.3.6",
-                        "xtend": "4.0.1"
+                        "readable-stream": "^2.1.5",
+                        "xtend": "~4.0.1"
                     },
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
                             "bundled": true,
                             "requires": {
-                                "core-util-is": "1.0.2",
-                                "inherits": "2.0.4",
-                                "isarray": "1.0.0",
-                                "process-nextick-args": "2.0.0",
-                                "safe-buffer": "5.1.2",
-                                "string_decoder": "1.1.1",
-                                "util-deprecate": "1.0.2"
+                                "core-util-is": "~1.0.0",
+                                "inherits": "~2.0.3",
+                                "isarray": "~1.0.0",
+                                "process-nextick-args": "~2.0.0",
+                                "safe-buffer": "~5.1.1",
+                                "string_decoder": "~1.1.1",
+                                "util-deprecate": "~1.0.1"
                             }
                         },
                         "string_decoder": {
                             "version": "1.1.1",
                             "bundled": true,
                             "requires": {
-                                "safe-buffer": "5.1.2"
+                                "safe-buffer": "~5.1.0"
                             }
                         }
                     }
@@ -12024,15 +12043,15 @@
                     "version": "2.4.3",
                     "bundled": true,
                     "requires": {
-                        "psl": "1.1.29",
-                        "punycode": "1.4.1"
+                        "psl": "^1.1.24",
+                        "punycode": "^1.4.1"
                     }
                 },
                 "tunnel-agent": {
                     "version": "0.6.0",
                     "bundled": true,
                     "requires": {
-                        "safe-buffer": "5.1.2"
+                        "safe-buffer": "^5.0.1"
                     }
                 },
                 "tweetnacl": {
@@ -12056,21 +12075,21 @@
                     "version": "1.1.1",
                     "bundled": true,
                     "requires": {
-                        "unique-slug": "2.0.0"
+                        "unique-slug": "^2.0.0"
                     }
                 },
                 "unique-slug": {
                     "version": "2.0.0",
                     "bundled": true,
                     "requires": {
-                        "imurmurhash": "0.1.4"
+                        "imurmurhash": "^0.1.4"
                     }
                 },
                 "unique-string": {
                     "version": "1.0.0",
                     "bundled": true,
                     "requires": {
-                        "crypto-random-string": "1.0.0"
+                        "crypto-random-string": "^1.0.0"
                     }
                 },
                 "unpipe": {
@@ -12085,23 +12104,23 @@
                     "version": "2.5.0",
                     "bundled": true,
                     "requires": {
-                        "boxen": "1.3.0",
-                        "chalk": "2.4.1",
-                        "configstore": "3.1.2",
-                        "import-lazy": "2.1.0",
-                        "is-ci": "1.1.0",
-                        "is-installed-globally": "0.1.0",
-                        "is-npm": "1.0.0",
-                        "latest-version": "3.1.0",
-                        "semver-diff": "2.1.0",
-                        "xdg-basedir": "3.0.0"
+                        "boxen": "^1.2.1",
+                        "chalk": "^2.0.1",
+                        "configstore": "^3.0.0",
+                        "import-lazy": "^2.1.0",
+                        "is-ci": "^1.0.10",
+                        "is-installed-globally": "^0.1.0",
+                        "is-npm": "^1.0.0",
+                        "latest-version": "^3.0.0",
+                        "semver-diff": "^2.0.0",
+                        "xdg-basedir": "^3.0.0"
                     }
                 },
                 "url-parse-lax": {
                     "version": "1.0.0",
                     "bundled": true,
                     "requires": {
-                        "prepend-http": "1.0.4"
+                        "prepend-http": "^1.0.1"
                     }
                 },
                 "util-deprecate": {
@@ -12116,7 +12135,7 @@
                     "version": "2.1.0",
                     "bundled": true,
                     "requires": {
-                        "object.getownpropertydescriptors": "2.0.3"
+                        "object.getownpropertydescriptors": "^2.0.3"
                     }
                 },
                 "uuid": {
@@ -12127,38 +12146,38 @@
                     "version": "3.0.4",
                     "bundled": true,
                     "requires": {
-                        "spdx-correct": "3.0.0",
-                        "spdx-expression-parse": "3.0.0"
+                        "spdx-correct": "^3.0.0",
+                        "spdx-expression-parse": "^3.0.0"
                     }
                 },
                 "validate-npm-package-name": {
                     "version": "3.0.0",
                     "bundled": true,
                     "requires": {
-                        "builtins": "1.0.3"
+                        "builtins": "^1.0.3"
                     }
                 },
                 "verror": {
                     "version": "1.10.0",
                     "bundled": true,
                     "requires": {
-                        "assert-plus": "1.0.0",
+                        "assert-plus": "^1.0.0",
                         "core-util-is": "1.0.2",
-                        "extsprintf": "1.3.0"
+                        "extsprintf": "^1.2.0"
                     }
                 },
                 "wcwidth": {
                     "version": "1.0.1",
                     "bundled": true,
                     "requires": {
-                        "defaults": "1.0.3"
+                        "defaults": "^1.0.3"
                     }
                 },
                 "which": {
                     "version": "1.3.1",
                     "bundled": true,
                     "requires": {
-                        "isexe": "2.0.0"
+                        "isexe": "^2.0.0"
                     }
                 },
                 "which-module": {
@@ -12169,16 +12188,16 @@
                     "version": "1.1.2",
                     "bundled": true,
                     "requires": {
-                        "string-width": "1.0.2"
+                        "string-width": "^1.0.2"
                     },
                     "dependencies": {
                         "string-width": {
                             "version": "1.0.2",
                             "bundled": true,
                             "requires": {
-                                "code-point-at": "1.1.0",
-                                "is-fullwidth-code-point": "1.0.0",
-                                "strip-ansi": "3.0.1"
+                                "code-point-at": "^1.0.0",
+                                "is-fullwidth-code-point": "^1.0.0",
+                                "strip-ansi": "^3.0.0"
                             }
                         }
                     }
@@ -12187,31 +12206,31 @@
                     "version": "2.0.0",
                     "bundled": true,
                     "requires": {
-                        "string-width": "2.1.1"
+                        "string-width": "^2.1.1"
                     }
                 },
                 "worker-farm": {
                     "version": "1.7.0",
                     "bundled": true,
                     "requires": {
-                        "errno": "0.1.7"
+                        "errno": "~0.1.7"
                     }
                 },
                 "wrap-ansi": {
                     "version": "2.1.0",
                     "bundled": true,
                     "requires": {
-                        "string-width": "1.0.2",
-                        "strip-ansi": "3.0.1"
+                        "string-width": "^1.0.1",
+                        "strip-ansi": "^3.0.1"
                     },
                     "dependencies": {
                         "string-width": {
                             "version": "1.0.2",
                             "bundled": true,
                             "requires": {
-                                "code-point-at": "1.1.0",
-                                "is-fullwidth-code-point": "1.0.0",
-                                "strip-ansi": "3.0.1"
+                                "code-point-at": "^1.0.0",
+                                "is-fullwidth-code-point": "^1.0.0",
+                                "strip-ansi": "^3.0.0"
                             }
                         }
                     }
@@ -12224,9 +12243,9 @@
                     "version": "2.4.3",
                     "bundled": true,
                     "requires": {
-                        "graceful-fs": "4.2.3",
-                        "imurmurhash": "0.1.4",
-                        "signal-exit": "3.0.2"
+                        "graceful-fs": "^4.1.11",
+                        "imurmurhash": "^0.1.4",
+                        "signal-exit": "^3.0.2"
                     }
                 },
                 "xdg-basedir": {
@@ -12249,18 +12268,18 @@
                     "version": "11.0.0",
                     "bundled": true,
                     "requires": {
-                        "cliui": "4.1.0",
-                        "decamelize": "1.2.0",
-                        "find-up": "2.1.0",
-                        "get-caller-file": "1.0.2",
-                        "os-locale": "2.1.0",
-                        "require-directory": "2.1.1",
-                        "require-main-filename": "1.0.1",
-                        "set-blocking": "2.0.0",
-                        "string-width": "2.1.1",
-                        "which-module": "2.0.0",
-                        "y18n": "3.2.1",
-                        "yargs-parser": "9.0.2"
+                        "cliui": "^4.0.0",
+                        "decamelize": "^1.1.1",
+                        "find-up": "^2.1.0",
+                        "get-caller-file": "^1.0.1",
+                        "os-locale": "^2.0.0",
+                        "require-directory": "^2.1.1",
+                        "require-main-filename": "^1.0.1",
+                        "set-blocking": "^2.0.0",
+                        "string-width": "^2.0.0",
+                        "which-module": "^2.0.0",
+                        "y18n": "^3.2.1",
+                        "yargs-parser": "^9.0.2"
                     },
                     "dependencies": {
                         "y18n": {
@@ -12273,7 +12292,7 @@
                     "version": "9.0.2",
                     "bundled": true,
                     "requires": {
-                        "camelcase": "4.1.0"
+                        "camelcase": "^4.1.0"
                     }
                 }
             }
@@ -12288,8 +12307,8 @@
             "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.4.tgz",
             "integrity": "sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw==",
             "requires": {
-                "ignore-walk": "3.0.1",
-                "npm-bundled": "1.0.6"
+                "ignore-walk": "^3.0.1",
+                "npm-bundled": "^1.0.1"
             }
         },
         "npm-run-path": {
@@ -12297,7 +12316,7 @@
             "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
             "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
             "requires": {
-                "path-key": "2.0.1"
+                "path-key": "^2.0.0"
             }
         },
         "npmlog": {
@@ -12305,10 +12324,10 @@
             "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
             "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
             "requires": {
-                "are-we-there-yet": "1.1.5",
-                "console-control-strings": "1.1.0",
-                "gauge": "2.7.4",
-                "set-blocking": "2.0.0"
+                "are-we-there-yet": "~1.1.2",
+                "console-control-strings": "~1.1.0",
+                "gauge": "~2.7.3",
+                "set-blocking": "~2.0.0"
             }
         },
         "nth-check": {
@@ -12316,7 +12335,7 @@
             "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
             "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
             "requires": {
-                "boolbase": "1.0.0"
+                "boolbase": "~1.0.0"
             }
         },
         "number-is-nan": {
@@ -12355,9 +12374,9 @@
             "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
             "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
             "requires": {
-                "copy-descriptor": "0.1.1",
-                "define-property": "0.2.5",
-                "kind-of": "3.2.2"
+                "copy-descriptor": "^0.1.0",
+                "define-property": "^0.2.5",
+                "kind-of": "^3.0.3"
             },
             "dependencies": {
                 "define-property": {
@@ -12365,7 +12384,7 @@
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
                     "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
                     "requires": {
-                        "is-descriptor": "0.1.6"
+                        "is-descriptor": "^0.1.0"
                     }
                 },
                 "kind-of": {
@@ -12373,7 +12392,7 @@
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
                     "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
-                        "is-buffer": "1.1.6"
+                        "is-buffer": "^1.1.5"
                     }
                 }
             }
@@ -12416,10 +12435,10 @@
                     "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz",
                     "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=",
                     "requires": {
-                        "commander": "2.20.0",
-                        "get-stdin": "5.0.1",
-                        "inspect-function": "0.2.2",
-                        "pipe-functions": "1.3.0"
+                        "commander": "^2.9.0",
+                        "get-stdin": "^5.0.1",
+                        "inspect-function": "^0.2.1",
+                        "pipe-functions": "^1.2.0"
                     }
                 }
             }
@@ -12429,7 +12448,7 @@
             "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
             "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
             "requires": {
-                "isobject": "3.0.1"
+                "isobject": "^3.0.0"
             }
         },
         "object.assign": {
@@ -12437,10 +12456,10 @@
             "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
             "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
             "requires": {
-                "define-properties": "1.1.3",
-                "function-bind": "1.1.1",
-                "has-symbols": "1.0.0",
-                "object-keys": "1.1.1"
+                "define-properties": "^1.1.2",
+                "function-bind": "^1.1.1",
+                "has-symbols": "^1.0.0",
+                "object-keys": "^1.0.11"
             }
         },
         "object.getownpropertydescriptors": {
@@ -12448,8 +12467,8 @@
             "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
             "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=",
             "requires": {
-                "define-properties": "1.1.3",
-                "es-abstract": "1.16.0"
+                "define-properties": "^1.1.2",
+                "es-abstract": "^1.5.1"
             }
         },
         "object.pick": {
@@ -12457,7 +12476,7 @@
             "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
             "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
             "requires": {
-                "isobject": "3.0.1"
+                "isobject": "^3.0.1"
             }
         },
         "obuf": {
@@ -12484,7 +12503,7 @@
             "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
             "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
             "requires": {
-                "wrappy": "1.0.2"
+                "wrappy": "1"
             }
         },
         "opn": {
@@ -12493,7 +12512,7 @@
             "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==",
             "dev": true,
             "requires": {
-                "is-wsl": "1.1.0"
+                "is-wsl": "^1.1.0"
             }
         },
         "optionator": {
@@ -12502,12 +12521,12 @@
             "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
             "dev": true,
             "requires": {
-                "deep-is": "0.1.3",
-                "fast-levenshtein": "2.0.6",
-                "levn": "0.3.0",
-                "prelude-ls": "1.1.2",
-                "type-check": "0.3.2",
-                "wordwrap": "1.0.0"
+                "deep-is": "~0.1.3",
+                "fast-levenshtein": "~2.0.4",
+                "levn": "~0.3.0",
+                "prelude-ls": "~1.1.2",
+                "type-check": "~0.3.2",
+                "wordwrap": "~1.0.0"
             },
             "dependencies": {
                 "wordwrap": {
@@ -12529,7 +12548,7 @@
             "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
             "dev": true,
             "requires": {
-                "url-parse": "1.4.7"
+                "url-parse": "^1.4.3"
             }
         },
         "os-browserify": {
@@ -12548,7 +12567,7 @@
             "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
             "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
             "requires": {
-                "lcid": "1.0.0"
+                "lcid": "^1.0.0"
             }
         },
         "os-tmpdir": {
@@ -12561,8 +12580,8 @@
             "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
             "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
             "requires": {
-                "os-homedir": "1.0.2",
-                "os-tmpdir": "1.0.2"
+                "os-homedir": "^1.0.0",
+                "os-tmpdir": "^1.0.0"
             }
         },
         "p-defer": {
@@ -12587,7 +12606,7 @@
             "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
             "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
             "requires": {
-                "p-try": "2.2.0"
+                "p-try": "^2.0.0"
             },
             "dependencies": {
                 "p-try": {
@@ -12602,7 +12621,7 @@
             "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
             "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
             "requires": {
-                "p-limit": "1.3.0"
+                "p-limit": "^1.1.0"
             },
             "dependencies": {
                 "p-limit": {
@@ -12610,7 +12629,7 @@
                     "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
                     "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
                     "requires": {
-                        "p-try": "1.0.0"
+                        "p-try": "^1.0.0"
                     }
                 }
             }
@@ -12627,7 +12646,7 @@
             "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==",
             "dev": true,
             "requires": {
-                "retry": "0.12.0"
+                "retry": "^0.12.0"
             }
         },
         "p-try": {
@@ -12640,10 +12659,10 @@
             "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz",
             "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=",
             "requires": {
-                "got": "6.7.1",
-                "registry-auth-token": "3.4.0",
-                "registry-url": "3.1.0",
-                "semver": "5.7.0"
+                "got": "^6.7.1",
+                "registry-auth-token": "^3.0.1",
+                "registry-url": "^3.0.3",
+                "semver": "^5.1.0"
             }
         },
         "pako": {
@@ -12658,9 +12677,9 @@
             "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=",
             "dev": true,
             "requires": {
-                "cyclist": "0.2.2",
-                "inherits": "2.0.3",
-                "readable-stream": "2.3.6"
+                "cyclist": "~0.2.2",
+                "inherits": "^2.0.3",
+                "readable-stream": "^2.1.5"
             }
         },
         "parent-module": {
@@ -12668,7 +12687,7 @@
             "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
             "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
             "requires": {
-                "callsites": "3.1.0"
+                "callsites": "^3.0.0"
             }
         },
         "parse-asn1": {
@@ -12676,12 +12695,12 @@
             "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz",
             "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==",
             "requires": {
-                "asn1.js": "4.10.1",
-                "browserify-aes": "1.2.0",
-                "create-hash": "1.2.0",
-                "evp_bytestokey": "1.0.3",
-                "pbkdf2": "3.0.17",
-                "safe-buffer": "5.1.2"
+                "asn1.js": "^4.0.0",
+                "browserify-aes": "^1.0.0",
+                "create-hash": "^1.1.0",
+                "evp_bytestokey": "^1.0.0",
+                "pbkdf2": "^3.0.3",
+                "safe-buffer": "^5.1.1"
             }
         },
         "parse-json": {
@@ -12689,10 +12708,10 @@
             "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz",
             "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==",
             "requires": {
-                "@babel/code-frame": "7.5.5",
-                "error-ex": "1.3.2",
-                "json-parse-better-errors": "1.0.2",
-                "lines-and-columns": "1.1.6"
+                "@babel/code-frame": "^7.0.0",
+                "error-ex": "^1.3.1",
+                "json-parse-better-errors": "^1.0.1",
+                "lines-and-columns": "^1.1.6"
             }
         },
         "parse-passwd": {
@@ -12712,7 +12731,7 @@
             "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
             "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
             "requires": {
-                "better-assert": "1.0.2"
+                "better-assert": "~1.0.0"
             }
         },
         "parseuri": {
@@ -12720,7 +12739,7 @@
             "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
             "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
             "requires": {
-                "better-assert": "1.0.2"
+                "better-assert": "~1.0.0"
             }
         },
         "parseurl": {
@@ -12738,7 +12757,7 @@
             "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz",
             "integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=",
             "requires": {
-                "passport-strategy": "1.0.0",
+                "passport-strategy": "1.x.x",
                 "pause": "0.0.1"
             }
         },
@@ -12747,7 +12766,7 @@
             "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz",
             "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==",
             "requires": {
-                "passport-oauth2": "1.5.0"
+                "passport-oauth2": "1.x.x"
             }
         },
         "passport-local": {
@@ -12755,7 +12774,7 @@
             "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
             "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=",
             "requires": {
-                "passport-strategy": "1.0.0"
+                "passport-strategy": "1.x.x"
             }
         },
         "passport-oauth2": {
@@ -12763,11 +12782,11 @@
             "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.5.0.tgz",
             "integrity": "sha512-kqBt6vR/5VlCK8iCx1/KpY42kQ+NEHZwsSyt4Y6STiNjU+wWICG1i8ucc1FapXDGO15C5O5VZz7+7vRzrDPXXQ==",
             "requires": {
-                "base64url": "3.0.1",
-                "oauth": "0.9.15",
-                "passport-strategy": "1.0.0",
-                "uid2": "0.0.3",
-                "utils-merge": "1.0.1"
+                "base64url": "3.x.x",
+                "oauth": "0.9.x",
+                "passport-strategy": "1.x.x",
+                "uid2": "0.0.x",
+                "utils-merge": "1.x.x"
             }
         },
         "passport-strategy": {
@@ -12821,9 +12840,9 @@
             "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
             "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
             "requires": {
-                "graceful-fs": "4.2.0",
-                "pify": "2.3.0",
-                "pinkie-promise": "2.0.1"
+                "graceful-fs": "^4.1.2",
+                "pify": "^2.0.0",
+                "pinkie-promise": "^2.0.0"
             }
         },
         "pathval": {
@@ -12841,11 +12860,11 @@
             "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
             "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
             "requires": {
-                "create-hash": "1.2.0",
-                "create-hmac": "1.1.7",
-                "ripemd160": "2.0.2",
-                "safe-buffer": "5.1.2",
-                "sha.js": "2.4.11"
+                "create-hash": "^1.1.2",
+                "create-hmac": "^1.1.4",
+                "ripemd160": "^2.0.1",
+                "safe-buffer": "^5.0.1",
+                "sha.js": "^2.4.8"
             }
         },
         "pdf-parse": {
@@ -12853,8 +12872,8 @@
             "resolved": "https://registry.npmjs.org/pdf-parse/-/pdf-parse-1.1.1.tgz",
             "integrity": "sha512-v6ZJ/efsBpGrGGknjtq9J/oC8tZWq0KWL5vQrk2GlzLEQPUDB1ex+13Rmidl1neNN358Jn9EHZw5y07FFtaC7A==",
             "requires": {
-                "debug": "3.2.6",
-                "node-ensure": "0.0.0"
+                "debug": "^3.1.0",
+                "node-ensure": "^0.0.0"
             },
             "dependencies": {
                 "debug": {
@@ -12862,7 +12881,7 @@
                     "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
                     "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
                     "requires": {
-                        "ms": "2.1.2"
+                        "ms": "^2.1.1"
                     }
                 },
                 "ms": {
@@ -12877,8 +12896,8 @@
             "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.1.266.tgz",
             "integrity": "sha512-Jy7o1wE3NezPxozexSbq4ltuLT0Z21ew/qrEiAEeUZzHxMHGk4DUV1D7RuCXg5vJDvHmjX1YssN+we9QfRRgXQ==",
             "requires": {
-                "node-ensure": "0.0.0",
-                "worker-loader": "2.0.0"
+                "node-ensure": "^0.0.0",
+                "worker-loader": "^2.0.0"
             }
         },
         "performance-now": {
@@ -12901,7 +12920,7 @@
             "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
             "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
             "requires": {
-                "pinkie": "2.0.4"
+                "pinkie": "^2.0.0"
             }
         },
         "pipe-functions": {
@@ -12915,7 +12934,7 @@
             "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
             "dev": true,
             "requires": {
-                "find-up": "2.1.0"
+                "find-up": "^2.1.0"
             }
         },
         "pn": {
@@ -12935,9 +12954,9 @@
             "integrity": "sha512-ESabpDCzmBS3ekHbmpAIiESq3udRsCBGiBZLsC+HgBKv2ezb0R4oG+7RnYEVZ/ZCfhel5Tx3UzdNWA0Lox2QCA==",
             "dev": true,
             "requires": {
-                "async": "1.5.2",
-                "debug": "2.6.9",
-                "mkdirp": "0.5.1"
+                "async": "^1.5.2",
+                "debug": "^2.2.0",
+                "mkdirp": "0.5.x"
             },
             "dependencies": {
                 "async": {
@@ -12959,9 +12978,9 @@
             "integrity": "sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ==",
             "dev": true,
             "requires": {
-                "chalk": "2.4.2",
-                "source-map": "0.6.1",
-                "supports-color": "6.1.0"
+                "chalk": "^2.4.2",
+                "source-map": "^0.6.1",
+                "supports-color": "^6.1.0"
             },
             "dependencies": {
                 "ansi-styles": {
@@ -12970,7 +12989,7 @@
                     "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
                     "dev": true,
                     "requires": {
-                        "color-convert": "1.9.3"
+                        "color-convert": "^1.9.0"
                     }
                 },
                 "chalk": {
@@ -12979,9 +12998,9 @@
                     "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
                     "dev": true,
                     "requires": {
-                        "ansi-styles": "3.2.1",
-                        "escape-string-regexp": "1.0.5",
-                        "supports-color": "5.5.0"
+                        "ansi-styles": "^3.2.1",
+                        "escape-string-regexp": "^1.0.5",
+                        "supports-color": "^5.3.0"
                     },
                     "dependencies": {
                         "supports-color": {
@@ -12990,7 +13009,7 @@
                             "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
                             "dev": true,
                             "requires": {
-                                "has-flag": "3.0.0"
+                                "has-flag": "^3.0.0"
                             }
                         }
                     }
@@ -13007,7 +13026,7 @@
                     "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
                     "dev": true,
                     "requires": {
-                        "has-flag": "3.0.0"
+                        "has-flag": "^3.0.0"
                     }
                 }
             }
@@ -13018,7 +13037,7 @@
             "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==",
             "dev": true,
             "requires": {
-                "postcss": "7.0.17"
+                "postcss": "^7.0.5"
             }
         },
         "postcss-modules-local-by-default": {
@@ -13027,9 +13046,9 @@
             "integrity": "sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA==",
             "dev": true,
             "requires": {
-                "postcss": "7.0.17",
-                "postcss-selector-parser": "6.0.2",
-                "postcss-value-parser": "3.3.1"
+                "postcss": "^7.0.6",
+                "postcss-selector-parser": "^6.0.0",
+                "postcss-value-parser": "^3.3.1"
             }
         },
         "postcss-modules-scope": {
@@ -13038,8 +13057,8 @@
             "integrity": "sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A==",
             "dev": true,
             "requires": {
-                "postcss": "7.0.17",
-                "postcss-selector-parser": "6.0.2"
+                "postcss": "^7.0.6",
+                "postcss-selector-parser": "^6.0.0"
             }
         },
         "postcss-modules-values": {
@@ -13048,8 +13067,8 @@
             "integrity": "sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w==",
             "dev": true,
             "requires": {
-                "icss-replace-symbols": "1.1.0",
-                "postcss": "7.0.17"
+                "icss-replace-symbols": "^1.1.0",
+                "postcss": "^7.0.6"
             }
         },
         "postcss-selector-parser": {
@@ -13058,9 +13077,9 @@
             "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
             "dev": true,
             "requires": {
-                "cssesc": "3.0.0",
-                "indexes-of": "1.0.1",
-                "uniq": "1.0.1"
+                "cssesc": "^3.0.0",
+                "indexes-of": "^1.0.1",
+                "uniq": "^1.0.1"
             }
         },
         "postcss-value-parser": {
@@ -13074,22 +13093,22 @@
             "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.0.tgz",
             "integrity": "sha512-aaLVANlj4HgZweKttFNUVNRxDukytuIuxeK2boIMHjagNJCiVKWFsKF4tCE3ql3GbrD2tExPQ7/pwtEJcHNZeg==",
             "requires": {
-                "detect-libc": "1.0.3",
-                "expand-template": "2.0.3",
+                "detect-libc": "^1.0.3",
+                "expand-template": "^2.0.3",
                 "github-from-package": "0.0.0",
-                "minimist": "1.2.0",
-                "mkdirp": "0.5.1",
-                "napi-build-utils": "1.0.1",
-                "node-abi": "2.9.0",
-                "noop-logger": "0.1.1",
-                "npmlog": "4.1.2",
-                "os-homedir": "1.0.2",
-                "pump": "2.0.1",
-                "rc": "1.2.8",
-                "simple-get": "2.8.1",
-                "tar-fs": "1.16.3",
-                "tunnel-agent": "0.6.0",
-                "which-pm-runs": "1.0.0"
+                "minimist": "^1.2.0",
+                "mkdirp": "^0.5.1",
+                "napi-build-utils": "^1.0.1",
+                "node-abi": "^2.7.0",
+                "noop-logger": "^0.1.1",
+                "npmlog": "^4.0.1",
+                "os-homedir": "^1.0.1",
+                "pump": "^2.0.1",
+                "rc": "^1.2.7",
+                "simple-get": "^2.7.0",
+                "tar-fs": "^1.13.0",
+                "tunnel-agent": "^0.6.0",
+                "which-pm-runs": "^1.0.0"
             },
             "dependencies": {
                 "minimist": {
@@ -13102,9 +13121,9 @@
                     "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz",
                     "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==",
                     "requires": {
-                        "decompress-response": "3.3.0",
-                        "once": "1.4.0",
-                        "simple-concat": "1.0.0"
+                        "decompress-response": "^3.3.0",
+                        "once": "^1.3.1",
+                        "simple-concat": "^1.0.0"
                     }
                 }
             }
@@ -13130,12 +13149,12 @@
             "resolved": "https://registry.npmjs.org/probe-image-size/-/probe-image-size-4.1.1.tgz",
             "integrity": "sha512-42LqKZqTLxH/UvAZ2/cKhAsR4G/Y6B7i7fI2qtQu9hRBK4YjS6gqO+QRtwTjvojUx4+/+JuOMzLoFyRecT9qRw==",
             "requires": {
-                "any-promise": "1.3.0",
-                "deepmerge": "4.0.0",
-                "inherits": "2.0.3",
-                "next-tick": "1.0.0",
-                "request": "2.88.0",
-                "stream-parser": "0.3.1"
+                "any-promise": "^1.3.0",
+                "deepmerge": "^4.0.0",
+                "inherits": "^2.0.3",
+                "next-tick": "^1.0.0",
+                "request": "^2.83.0",
+                "stream-parser": "~0.3.1"
             },
             "dependencies": {
                 "assert-plus": {
@@ -13163,9 +13182,9 @@
                     "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
                     "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
                     "requires": {
-                        "asynckit": "0.4.0",
-                        "combined-stream": "1.0.8",
-                        "mime-types": "2.1.24"
+                        "asynckit": "^0.4.0",
+                        "combined-stream": "^1.0.6",
+                        "mime-types": "^2.1.12"
                     }
                 },
                 "har-validator": {
@@ -13173,8 +13192,8 @@
                     "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
                     "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
                     "requires": {
-                        "ajv": "6.10.2",
-                        "har-schema": "2.0.0"
+                        "ajv": "^6.5.5",
+                        "har-schema": "^2.0.0"
                     }
                 },
                 "http-signature": {
@@ -13182,9 +13201,9 @@
                     "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
                     "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
                     "requires": {
-                        "assert-plus": "1.0.0",
-                        "jsprim": "1.4.1",
-                        "sshpk": "1.16.1"
+                        "assert-plus": "^1.0.0",
+                        "jsprim": "^1.2.2",
+                        "sshpk": "^1.7.0"
                     }
                 },
                 "oauth-sign": {
@@ -13202,26 +13221,26 @@
                     "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
                     "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
                     "requires": {
-                        "aws-sign2": "0.7.0",
-                        "aws4": "1.9.0",
-                        "caseless": "0.12.0",
-                        "combined-stream": "1.0.8",
-                        "extend": "3.0.2",
-                        "forever-agent": "0.6.1",
-                        "form-data": "2.3.3",
-                        "har-validator": "5.1.3",
-                        "http-signature": "1.2.0",
-                        "is-typedarray": "1.0.0",
-                        "isstream": "0.1.2",
-                        "json-stringify-safe": "5.0.1",
-                        "mime-types": "2.1.24",
-                        "oauth-sign": "0.9.0",
-                        "performance-now": "2.1.0",
-                        "qs": "6.5.2",
-                        "safe-buffer": "5.1.2",
-                        "tough-cookie": "2.4.3",
-                        "tunnel-agent": "0.6.0",
-                        "uuid": "3.3.2"
+                        "aws-sign2": "~0.7.0",
+                        "aws4": "^1.8.0",
+                        "caseless": "~0.12.0",
+                        "combined-stream": "~1.0.6",
+                        "extend": "~3.0.2",
+                        "forever-agent": "~0.6.1",
+                        "form-data": "~2.3.2",
+                        "har-validator": "~5.1.0",
+                        "http-signature": "~1.2.0",
+                        "is-typedarray": "~1.0.0",
+                        "isstream": "~0.1.2",
+                        "json-stringify-safe": "~5.0.1",
+                        "mime-types": "~2.1.19",
+                        "oauth-sign": "~0.9.0",
+                        "performance-now": "^2.1.0",
+                        "qs": "~6.5.2",
+                        "safe-buffer": "^5.1.2",
+                        "tough-cookie": "~2.4.3",
+                        "tunnel-agent": "^0.6.0",
+                        "uuid": "^3.3.2"
                     }
                 }
             }
@@ -13242,7 +13261,7 @@
             "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
             "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
             "requires": {
-                "asap": "2.0.6"
+                "asap": "~2.0.3"
             }
         },
         "promise-inflight": {
@@ -13256,9 +13275,9 @@
             "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
             "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
             "requires": {
-                "loose-envify": "1.4.0",
-                "object-assign": "4.1.1",
-                "react-is": "16.8.6"
+                "loose-envify": "^1.4.0",
+                "object-assign": "^4.1.1",
+                "react-is": "^16.8.1"
             }
         },
         "prop-types-extra": {
@@ -13266,8 +13285,8 @@
             "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.0.tgz",
             "integrity": "sha512-QFyuDxvMipmIVKD2TwxLVPzMnO4e5oOf1vr3tJIomL8E7d0lr6phTHd5nkPhFIzTD1idBLLEPeylL9g+rrTzRg==",
             "requires": {
-                "react-is": "16.8.6",
-                "warning": "3.0.0"
+                "react-is": "^16.3.2",
+                "warning": "^3.0.0"
             }
         },
         "prosemirror-commands": {
@@ -13275,9 +13294,9 @@
             "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.0.8.tgz",
             "integrity": "sha512-P9QdkYYBHWsrJ1JztQuHgeZS7DPCcijQduOj9oxFiqK8Fm6eTsVHzU1IwiRBe+FlK7tyQaerhu/F5K8sqnZ1Cw==",
             "requires": {
-                "prosemirror-model": "1.7.2",
-                "prosemirror-state": "1.2.4",
-                "prosemirror-transform": "1.1.4"
+                "prosemirror-model": "^1.0.0",
+                "prosemirror-state": "^1.0.0",
+                "prosemirror-transform": "^1.0.0"
             }
         },
         "prosemirror-dropcursor": {
@@ -13285,9 +13304,9 @@
             "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.1.1.tgz",
             "integrity": "sha512-GeUyMO/tOEf8MXrP7Xb7UIMrfK86OGh0fnyBrHfhav4VjY9cw65mNoqHy87CklE5711AhCP5Qzfp8RL/hVKusg==",
             "requires": {
-                "prosemirror-state": "1.2.4",
-                "prosemirror-transform": "1.1.4",
-                "prosemirror-view": "1.10.3"
+                "prosemirror-state": "^1.0.0",
+                "prosemirror-transform": "^1.1.0",
+                "prosemirror-view": "^1.1.0"
             }
         },
         "prosemirror-example-setup": {
@@ -13295,15 +13314,15 @@
             "resolved": "https://registry.npmjs.org/prosemirror-example-setup/-/prosemirror-example-setup-1.0.1.tgz",
             "integrity": "sha512-4NKWpdmm75Zzgq/dIrypRnkBNPx+ONKyoGF42a9g3VIVv0TWglf1CBNxt5kzCgli9xdfut/xE5B42F9DR6BLHw==",
             "requires": {
-                "prosemirror-commands": "1.0.8",
-                "prosemirror-dropcursor": "1.1.1",
-                "prosemirror-gapcursor": "1.0.4",
-                "prosemirror-history": "1.0.4",
-                "prosemirror-inputrules": "1.0.4",
-                "prosemirror-keymap": "1.0.1",
-                "prosemirror-menu": "1.0.5",
-                "prosemirror-schema-list": "1.0.3",
-                "prosemirror-state": "1.2.4"
+                "prosemirror-commands": "^1.0.0",
+                "prosemirror-dropcursor": "^1.0.0",
+                "prosemirror-gapcursor": "^1.0.0",
+                "prosemirror-history": "^1.0.0",
+                "prosemirror-inputrules": "^1.0.0",
+                "prosemirror-keymap": "^1.0.0",
+                "prosemirror-menu": "^1.0.0",
+                "prosemirror-schema-list": "^1.0.0",
+                "prosemirror-state": "^1.0.0"
             }
         },
         "prosemirror-find-replace": {
@@ -13316,10 +13335,10 @@
             "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.0.4.tgz",
             "integrity": "sha512-k021MtJibWs3NaJI6S9tCXfTZ/kaugFZBndHkkWx3Zfk0QDUO6JfVATpflxADN6DUkRwJ7qWyHlLDWu71hxHFQ==",
             "requires": {
-                "prosemirror-keymap": "1.0.1",
-                "prosemirror-model": "1.7.2",
-                "prosemirror-state": "1.2.4",
-                "prosemirror-view": "1.10.3"
+                "prosemirror-keymap": "^1.0.0",
+                "prosemirror-model": "^1.0.0",
+                "prosemirror-state": "^1.0.0",
+                "prosemirror-view": "^1.0.0"
             }
         },
         "prosemirror-history": {
@@ -13327,9 +13346,9 @@
             "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.0.4.tgz",
             "integrity": "sha512-Kk2UisC9EzYcsNv+ILiQJWpsu0rbT6+oAAkvseFUHnudtfkmYAJu1+Xp3F0xTTCVmQdSqSLVk8qydllXUUOU4Q==",
             "requires": {
-                "prosemirror-state": "1.2.4",
-                "prosemirror-transform": "1.1.4",
-                "rope-sequence": "1.2.2"
+                "prosemirror-state": "^1.2.2",
+                "prosemirror-transform": "^1.0.0",
+                "rope-sequence": "^1.2.0"
             }
         },
         "prosemirror-inputrules": {
@@ -13337,8 +13356,8 @@
             "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.0.4.tgz",
             "integrity": "sha512-RhuBghqUgYWm8ai/P+k1lMl1ZGvt6Cs3Xeur8oN0L1Yy+Z5GmsTp3fT8RVl+vJeGkItEAxAit9Qh7yZxixX7rA==",
             "requires": {
-                "prosemirror-state": "1.2.4",
-                "prosemirror-transform": "1.1.4"
+                "prosemirror-state": "^1.0.0",
+                "prosemirror-transform": "^1.0.0"
             }
         },
         "prosemirror-keymap": {
@@ -13346,8 +13365,8 @@
             "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.0.1.tgz",
             "integrity": "sha512-e79ApE7PXXZMFtPz7WbjycjAFd1NPjgY1MkecVz98tqwlBSggXWXYQnWFk6x7UkmnBYRHHbXHkR/RXmu2wyBJg==",
             "requires": {
-                "prosemirror-state": "1.2.4",
-                "w3c-keyname": "1.1.8"
+                "prosemirror-state": "^1.0.0",
+                "w3c-keyname": "^1.1.8"
             }
         },
         "prosemirror-menu": {
@@ -13355,10 +13374,10 @@
             "resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.0.5.tgz",
             "integrity": "sha512-9Vrn7CC191v7FA4QrAkL8W1SrR73V3CRIYCDuk94R8oFVk4VxSFdoKVLHuvGzxZ8b5LCu3DMJfh86YW9uL4RkQ==",
             "requires": {
-                "crel": "3.1.0",
-                "prosemirror-commands": "1.0.8",
-                "prosemirror-history": "1.0.4",
-                "prosemirror-state": "1.2.4"
+                "crel": "^3.0.0",
+                "prosemirror-commands": "^1.0.0",
+                "prosemirror-history": "^1.0.0",
+                "prosemirror-state": "^1.0.0"
             }
         },
         "prosemirror-model": {
@@ -13366,7 +13385,7 @@
             "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.7.2.tgz",
             "integrity": "sha512-mopozod/qNTB6utEyY8q4w1nCLDakpr39d8smzHno/wuAivCzBU8HkC9YOx1MBdTcTU6sXiIEh08hQfkC3damw==",
             "requires": {
-                "orderedmap": "1.0.0"
+                "orderedmap": "^1.0.0"
             }
         },
         "prosemirror-schema-basic": {
@@ -13374,7 +13393,7 @@
             "resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.0.1.tgz",
             "integrity": "sha512-LFO/+zr7RSRJ95k6QGHdAwxsTsB3xxSCphU2Xkg6hNroblUV0rYelKe6s5uM5rdyPUdTTRTPjnZWQE28YsGVcA==",
             "requires": {
-                "prosemirror-model": "1.7.2"
+                "prosemirror-model": "^1.2.0"
             }
         },
         "prosemirror-schema-list": {
@@ -13382,8 +13401,8 @@
             "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.0.3.tgz",
             "integrity": "sha512-+zzSawVds8LsZpl/bLTCYk2lYactF93W219Czh81zBILikCRDOHjp1CQ1os4ZXBp6LlD+JnBqF1h59Q+hilOoQ==",
             "requires": {
-                "prosemirror-model": "1.7.2",
-                "prosemirror-transform": "1.1.4"
+                "prosemirror-model": "^1.0.0",
+                "prosemirror-transform": "^1.0.0"
             }
         },
         "prosemirror-state": {
@@ -13391,8 +13410,8 @@
             "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.2.4.tgz",
             "integrity": "sha512-ViXpXond3BbSL12ENARQGq3Y8igwFMbTcy96xUNK8kfIcfQRlYlgYrBPXIkHC5+QZtbPrYlpuJ2+QyeSlSX9Cw==",
             "requires": {
-                "prosemirror-model": "1.7.2",
-                "prosemirror-transform": "1.1.4"
+                "prosemirror-model": "^1.0.0",
+                "prosemirror-transform": "^1.0.0"
             }
         },
         "prosemirror-transform": {
@@ -13400,7 +13419,7 @@
             "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.1.4.tgz",
             "integrity": "sha512-1Y3XuaFJtwusYDvojcCxi3VZvNIntPVoh/dpeVaIM5Vf1V+M6xiIWcDgktUWWRovMxEhdibnpt5eyFmYJJhHtQ==",
             "requires": {
-                "prosemirror-model": "1.7.2"
+                "prosemirror-model": "^1.0.0"
             }
         },
         "prosemirror-view": {
@@ -13408,9 +13427,9 @@
             "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.10.3.tgz",
             "integrity": "sha512-3R5iTItRmE1wWZ3X5pbl4j2H6gElTr7Hcr6wTS0QuRlqE9xROcP6BPQuBxaOANgzUOiU8Skw42GCI8Xc/d9Y/A==",
             "requires": {
-                "prosemirror-model": "1.7.2",
-                "prosemirror-state": "1.2.4",
-                "prosemirror-transform": "1.1.4"
+                "prosemirror-model": "^1.1.0",
+                "prosemirror-state": "^1.0.0",
+                "prosemirror-transform": "^1.1.0"
             }
         },
         "proxy-addr": {
@@ -13418,7 +13437,7 @@
             "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
             "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
             "requires": {
-                "forwarded": "0.1.2",
+                "forwarded": "~0.1.2",
                 "ipaddr.js": "1.9.0"
             }
         },
@@ -13448,12 +13467,12 @@
             "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
             "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
             "requires": {
-                "bn.js": "4.11.8",
-                "browserify-rsa": "4.0.1",
-                "create-hash": "1.2.0",
-                "parse-asn1": "5.1.4",
-                "randombytes": "2.1.0",
-                "safe-buffer": "5.1.2"
+                "bn.js": "^4.1.0",
+                "browserify-rsa": "^4.0.0",
+                "create-hash": "^1.1.0",
+                "parse-asn1": "^5.0.0",
+                "randombytes": "^2.0.1",
+                "safe-buffer": "^5.1.2"
             }
         },
         "pug": {
@@ -13461,14 +13480,14 @@
             "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.4.tgz",
             "integrity": "sha512-XhoaDlvi6NIzL49nu094R2NA6P37ijtgMDuWE+ofekDChvfKnzFal60bhSdiy8y2PBO6fmz3oMEIcfpBVRUdvw==",
             "requires": {
-                "pug-code-gen": "2.0.2",
-                "pug-filters": "3.1.1",
-                "pug-lexer": "4.1.0",
-                "pug-linker": "3.0.6",
-                "pug-load": "2.0.12",
-                "pug-parser": "5.0.1",
-                "pug-runtime": "2.0.5",
-                "pug-strip-comments": "1.0.4"
+                "pug-code-gen": "^2.0.2",
+                "pug-filters": "^3.1.1",
+                "pug-lexer": "^4.1.0",
+                "pug-linker": "^3.0.6",
+                "pug-load": "^2.0.12",
+                "pug-parser": "^5.0.1",
+                "pug-runtime": "^2.0.5",
+                "pug-strip-comments": "^1.0.4"
             }
         },
         "pug-attrs": {
@@ -13476,9 +13495,9 @@
             "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.4.tgz",
             "integrity": "sha512-TaZ4Z2TWUPDJcV3wjU3RtUXMrd3kM4Wzjbe3EWnSsZPsJ3LDI0F3yCnf2/W7PPFF+edUFQ0HgDL1IoxSz5K8EQ==",
             "requires": {
-                "constantinople": "3.1.2",
-                "js-stringify": "1.0.2",
-                "pug-runtime": "2.0.5"
+                "constantinople": "^3.0.1",
+                "js-stringify": "^1.0.1",
+                "pug-runtime": "^2.0.5"
             }
         },
         "pug-code-gen": {
@@ -13486,14 +13505,14 @@
             "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-2.0.2.tgz",
             "integrity": "sha512-kROFWv/AHx/9CRgoGJeRSm+4mLWchbgpRzTEn8XCiwwOy6Vh0gAClS8Vh5TEJ9DBjaP8wCjS3J6HKsEsYdvaCw==",
             "requires": {
-                "constantinople": "3.1.2",
-                "doctypes": "1.1.0",
-                "js-stringify": "1.0.2",
-                "pug-attrs": "2.0.4",
-                "pug-error": "1.3.3",
-                "pug-runtime": "2.0.5",
-                "void-elements": "2.0.1",
-                "with": "5.1.1"
+                "constantinople": "^3.1.2",
+                "doctypes": "^1.1.0",
+                "js-stringify": "^1.0.1",
+                "pug-attrs": "^2.0.4",
+                "pug-error": "^1.3.3",
+                "pug-runtime": "^2.0.5",
+                "void-elements": "^2.0.1",
+                "with": "^5.0.0"
             }
         },
         "pug-error": {
@@ -13506,13 +13525,13 @@
             "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-3.1.1.tgz",
             "integrity": "sha512-lFfjNyGEyVWC4BwX0WyvkoWLapI5xHSM3xZJFUhx4JM4XyyRdO8Aucc6pCygnqV2uSgJFaJWW3Ft1wCWSoQkQg==",
             "requires": {
-                "clean-css": "4.2.1",
-                "constantinople": "3.1.2",
+                "clean-css": "^4.1.11",
+                "constantinople": "^3.0.1",
                 "jstransformer": "1.0.0",
-                "pug-error": "1.3.3",
-                "pug-walk": "1.1.8",
-                "resolve": "1.11.1",
-                "uglify-js": "2.8.29"
+                "pug-error": "^1.3.3",
+                "pug-walk": "^1.1.8",
+                "resolve": "^1.1.6",
+                "uglify-js": "^2.6.1"
             }
         },
         "pug-lexer": {
@@ -13520,9 +13539,9 @@
             "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-4.1.0.tgz",
             "integrity": "sha512-i55yzEBtjm0mlplW4LoANq7k3S8gDdfC6+LThGEvsK4FuobcKfDAwt6V4jKPH9RtiE3a2Akfg5UpafZ1OksaPA==",
             "requires": {
-                "character-parser": "2.2.0",
-                "is-expression": "3.0.0",
-                "pug-error": "1.3.3"
+                "character-parser": "^2.1.1",
+                "is-expression": "^3.0.0",
+                "pug-error": "^1.3.3"
             }
         },
         "pug-linker": {
@@ -13530,8 +13549,8 @@
             "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-3.0.6.tgz",
             "integrity": "sha512-bagfuHttfQOpANGy1Y6NJ+0mNb7dD2MswFG2ZKj22s8g0wVsojpRlqveEQHmgXXcfROB2RT6oqbPYr9EN2ZWzg==",
             "requires": {
-                "pug-error": "1.3.3",
-                "pug-walk": "1.1.8"
+                "pug-error": "^1.3.3",
+                "pug-walk": "^1.1.8"
             }
         },
         "pug-load": {
@@ -13539,8 +13558,8 @@
             "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.12.tgz",
             "integrity": "sha512-UqpgGpyyXRYgJs/X60sE6SIf8UBsmcHYKNaOccyVLEuT6OPBIMo6xMPhoJnqtB3Q3BbO4Z3Bjz5qDsUWh4rXsg==",
             "requires": {
-                "object-assign": "4.1.1",
-                "pug-walk": "1.1.8"
+                "object-assign": "^4.1.0",
+                "pug-walk": "^1.1.8"
             }
         },
         "pug-parser": {
@@ -13548,7 +13567,7 @@
             "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-5.0.1.tgz",
             "integrity": "sha512-nGHqK+w07p5/PsPIyzkTQfzlYfuqoiGjaoqHv1LjOv2ZLXmGX1O+4Vcvps+P4LhxZ3drYSljjq4b+Naid126wA==",
             "requires": {
-                "pug-error": "1.3.3",
+                "pug-error": "^1.3.3",
                 "token-stream": "0.0.1"
             }
         },
@@ -13562,7 +13581,7 @@
             "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.4.tgz",
             "integrity": "sha512-i5j/9CS4yFhSxHp5iKPHwigaig/VV9g+FgReLJWWHEHbvKsbqL0oP/K5ubuLco6Wu3Kan5p7u7qk8A4oLLh6vw==",
             "requires": {
-                "pug-error": "1.3.3"
+                "pug-error": "^1.3.3"
             }
         },
         "pug-walk": {
@@ -13575,8 +13594,8 @@
             "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
             "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
             "requires": {
-                "end-of-stream": "1.4.1",
-                "once": "1.4.0"
+                "end-of-stream": "^1.1.0",
+                "once": "^1.3.1"
             }
         },
         "pumpify": {
@@ -13585,9 +13604,9 @@
             "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
             "dev": true,
             "requires": {
-                "duplexify": "3.7.1",
-                "inherits": "2.0.3",
-                "pump": "2.0.1"
+                "duplexify": "^3.6.0",
+                "inherits": "^2.0.3",
+                "pump": "^2.0.0"
             }
         },
         "punycode": {
@@ -13610,9 +13629,9 @@
             "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.8.1.tgz",
             "integrity": "sha512-g6y0Lbq10a5pPQpjlFuojfMfV1Pd2Jw9h75ypiYPPia3Gcq2rgkKiIwbkS6JxH7c5f5u/B/sB+d13PU+g1eu4Q==",
             "requires": {
-                "decode-uri-component": "0.2.0",
-                "split-on-first": "1.1.0",
-                "strict-uri-encode": "2.0.0"
+                "decode-uri-component": "^0.2.0",
+                "split-on-first": "^1.0.0",
+                "strict-uri-encode": "^2.0.0"
             }
         },
         "querystring": {
@@ -13643,7 +13662,7 @@
             "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
             "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
             "requires": {
-                "safe-buffer": "5.1.2"
+                "safe-buffer": "^5.1.0"
             }
         },
         "randomfill": {
@@ -13651,8 +13670,8 @@
             "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
             "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
             "requires": {
-                "randombytes": "2.1.0",
-                "safe-buffer": "5.1.2"
+                "randombytes": "^2.0.5",
+                "safe-buffer": "^5.1.0"
             }
         },
         "range-parser": {
@@ -13676,8 +13695,8 @@
             "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-1.0.0.tgz",
             "integrity": "sha512-Uqy5AqELpytJTRxYT4fhltcKPj0TyaEpzJDcGz7DFJi+pQOOi3GjR/DOdxTkTsF+NzhnldIoG6TORaBlInUuqA==",
             "requires": {
-                "loader-utils": "1.2.3",
-                "schema-utils": "1.0.0"
+                "loader-utils": "^1.1.0",
+                "schema-utils": "^1.0.0"
             },
             "dependencies": {
                 "schema-utils": {
@@ -13685,9 +13704,9 @@
                     "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
                     "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
                     "requires": {
-                        "ajv": "6.10.2",
-                        "ajv-errors": "1.0.1",
-                        "ajv-keywords": "3.4.1"
+                        "ajv": "^6.1.0",
+                        "ajv-errors": "^1.0.0",
+                        "ajv-keywords": "^3.1.0"
                     }
                 }
             }
@@ -13697,10 +13716,10 @@
             "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
             "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
             "requires": {
-                "deep-extend": "0.6.0",
-                "ini": "1.3.5",
-                "minimist": "1.2.0",
-                "strip-json-comments": "2.0.1"
+                "deep-extend": "^0.6.0",
+                "ini": "~1.3.0",
+                "minimist": "^1.2.0",
+                "strip-json-comments": "~2.0.1"
             },
             "dependencies": {
                 "minimist": {
@@ -13715,9 +13734,9 @@
             "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-1.9.0.tgz",
             "integrity": "sha512-Isas+egaK6qSk64jaEw4GgPStY4umYDbT7ZY93bZF1Af+b/JEsKsJdNOU2qG3WI0Z6tXo2DDq0kJCv8Yhu0zww==",
             "requires": {
-                "classnames": "2.2.6",
-                "prop-types": "15.7.2",
-                "react-lifecycles-compat": "3.0.4"
+                "classnames": "^2.2.1",
+                "prop-types": "^15.5.6",
+                "react-lifecycles-compat": "^3.0.4"
             }
         },
         "react": {
@@ -13725,10 +13744,10 @@
             "resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz",
             "integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==",
             "requires": {
-                "loose-envify": "1.4.0",
-                "object-assign": "4.1.1",
-                "prop-types": "15.7.2",
-                "scheduler": "0.13.6"
+                "loose-envify": "^1.1.0",
+                "object-assign": "^4.1.1",
+                "prop-types": "^15.6.2",
+                "scheduler": "^0.13.6"
             }
         },
         "react-anime": {
@@ -13736,8 +13755,8 @@
             "resolved": "https://registry.npmjs.org/react-anime/-/react-anime-2.2.0.tgz",
             "integrity": "sha512-terZpZjLSanmxaPkb5mkIL1KSmMcF/4Unk5F1IfgoCTUkz6dRWcblA702X1NSsac/6oy60q2SyS857UcJCQPtQ==",
             "requires": {
-                "animejs": "2.2.0",
-                "lodash.isequal": "4.5.0"
+                "animejs": "^2.2.0",
+                "lodash.isequal": "^4.5.0"
             }
         },
         "react-autosuggest": {
@@ -13745,9 +13764,9 @@
             "resolved": "https://registry.npmjs.org/react-autosuggest/-/react-autosuggest-9.4.3.tgz",
             "integrity": "sha512-wFbp5QpgFQRfw9cwKvcgLR8theikOUkv8PFsuLYqI2PUgVlx186Cz8MYt5bLxculi+jxGGUUVt+h0esaBZZouw==",
             "requires": {
-                "prop-types": "15.7.2",
-                "react-autowhatever": "10.2.0",
-                "shallow-equal": "1.2.0"
+                "prop-types": "^15.5.10",
+                "react-autowhatever": "^10.1.2",
+                "shallow-equal": "^1.0.0"
             }
         },
         "react-autowhatever": {
@@ -13755,9 +13774,9 @@
             "resolved": "https://registry.npmjs.org/react-autowhatever/-/react-autowhatever-10.2.0.tgz",
             "integrity": "sha512-dqHH4uqiJldPMbL8hl/i2HV4E8FMTDEdVlOIbRqYnJi0kTpWseF9fJslk/KS9pGDnm80JkYzVI+nzFjnOG/u+g==",
             "requires": {
-                "prop-types": "15.7.2",
-                "react-themeable": "1.1.0",
-                "section-iterator": "2.0.0"
+                "prop-types": "^15.5.8",
+                "react-themeable": "^1.1.0",
+                "section-iterator": "^2.0.0"
             }
         },
         "react-bootstrap": {
@@ -13765,21 +13784,21 @@
             "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.0.0-beta.9.tgz",
             "integrity": "sha512-M0BYLuuUdMITJ16+DDDb1p4vWV87csEBi/uOxZYODuDZh7hvbrJVahfvPXcqeqq4eEpNL+PKSlqb9fNaY0HZyA==",
             "requires": {
-                "@babel/runtime": "7.5.5",
+                "@babel/runtime": "^7.4.2",
                 "@react-bootstrap/react-popper": "1.2.1",
-                "@restart/context": "2.1.4",
-                "@restart/hooks": "0.3.8",
-                "classnames": "2.2.6",
-                "dom-helpers": "3.4.0",
-                "invariant": "2.2.4",
-                "keycode": "2.2.0",
-                "popper.js": "1.15.0",
-                "prop-types": "15.7.2",
-                "prop-types-extra": "1.1.0",
-                "react-overlays": "1.2.0",
-                "react-transition-group": "4.2.1",
-                "uncontrollable": "6.2.3",
-                "warning": "4.0.3"
+                "@restart/context": "^2.1.4",
+                "@restart/hooks": "^0.3.0",
+                "classnames": "^2.2.6",
+                "dom-helpers": "^3.4.0",
+                "invariant": "^2.2.4",
+                "keycode": "^2.2.0",
+                "popper.js": "^1.14.7",
+                "prop-types": "^15.7.2",
+                "prop-types-extra": "^1.1.0",
+                "react-overlays": "^1.2.0",
+                "react-transition-group": "^4.0.0",
+                "uncontrollable": "^6.1.0",
+                "warning": "^4.0.3"
             },
             "dependencies": {
                 "react-transition-group": {
@@ -13787,10 +13806,10 @@
                     "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.2.1.tgz",
                     "integrity": "sha512-IXrPr93VzCPupwm2O6n6C2kJIofJ/Rp5Ltihhm9UfE8lkuVX2ng/SUUl/oWjblybK9Fq2Io7LGa6maVqPB762Q==",
                     "requires": {
-                        "@babel/runtime": "7.5.5",
-                        "dom-helpers": "3.4.0",
-                        "loose-envify": "1.4.0",
-                        "prop-types": "15.7.2"
+                        "@babel/runtime": "^7.4.5",
+                        "dom-helpers": "^3.4.0",
+                        "loose-envify": "^1.4.0",
+                        "prop-types": "^15.6.2"
                     }
                 },
                 "uncontrollable": {
@@ -13798,8 +13817,8 @@
                     "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-6.2.3.tgz",
                     "integrity": "sha512-VgOAoBU2ptCL2bfTG2Mra0I8i1u6Aq84AFonD5tmCAYSfs3hWvr2Rlw0q2ntoxXTHjcQOmZOh3FKaN+UZVyREQ==",
                     "requires": {
-                        "@babel/runtime": "7.5.5",
-                        "invariant": "2.2.4"
+                        "@babel/runtime": "^7.4.5",
+                        "invariant": "^2.2.4"
                     }
                 },
                 "warning": {
@@ -13807,7 +13826,7 @@
                     "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
                     "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
                     "requires": {
-                        "loose-envify": "1.4.0"
+                        "loose-envify": "^1.0.0"
                     }
                 }
             }
@@ -13822,12 +13841,12 @@
             "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.17.3.tgz",
             "integrity": "sha512-1dtO8LqAVotPIChlmo6kLtFS1FP89ll8/OiA8EcFRDR+ntcK+0ukJgByuIQHRtzvigf26dV5HklnxDIvhON9VQ==",
             "requires": {
-                "@icons/material": "0.2.4",
-                "lodash": "4.17.15",
-                "material-colors": "1.2.6",
-                "prop-types": "15.7.2",
-                "reactcss": "1.2.3",
-                "tinycolor2": "1.4.1"
+                "@icons/material": "^0.2.4",
+                "lodash": "^4.17.11",
+                "material-colors": "^1.2.1",
+                "prop-types": "^15.5.10",
+                "reactcss": "^1.2.0",
+                "tinycolor2": "^1.4.1"
             }
         },
         "react-context-toolbox": {
@@ -13840,7 +13859,7 @@
             "resolved": "https://registry.npmjs.org/react-dimensions/-/react-dimensions-1.3.1.tgz",
             "integrity": "sha512-go5vMuGUxaB5PiTSIk+ZfAxLbHwcIgIfLhkBZ2SIMQjaCgnpttxa30z5ijEzfDjeOCTGRpxvkzcmE4Vt4Ppvyw==",
             "requires": {
-                "element-resize-event": "2.0.9"
+                "element-resize-event": "^2.0.4"
             }
         },
         "react-dom": {
@@ -13848,10 +13867,10 @@
             "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz",
             "integrity": "sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==",
             "requires": {
-                "loose-envify": "1.4.0",
-                "object-assign": "4.1.1",
-                "prop-types": "15.7.2",
-                "scheduler": "0.13.6"
+                "loose-envify": "^1.1.0",
+                "object-assign": "^4.1.1",
+                "prop-types": "^15.6.2",
+                "scheduler": "^0.13.6"
             }
         },
         "react-golden-layout": {
@@ -13859,9 +13878,9 @@
             "resolved": "https://registry.npmjs.org/react-golden-layout/-/react-golden-layout-1.0.6.tgz",
             "integrity": "sha512-KZQ17Bnd+LfyCqe2scVMznrGKTciX3VwoT3y4xn3Qok9hknCvVXZfXe2RSX5zNG7FlLJzWt0VWqy8qZBHpQVuQ==",
             "requires": {
-                "golden-layout": "1.5.9",
-                "react": "16.8.6",
-                "react-dom": "16.8.6"
+                "golden-layout": "^1.5.9",
+                "react": "^16.3.0",
+                "react-dom": "^16.3.0"
             }
         },
         "react-image-lightbox-with-rotate": {
@@ -13869,9 +13888,9 @@
             "resolved": "https://registry.npmjs.org/react-image-lightbox-with-rotate/-/react-image-lightbox-with-rotate-5.1.1.tgz",
             "integrity": "sha512-5ZubUQefKSDGIiAwK4lkfmGr/bgIfNDHXqC+Fm6nbNwTVYuYOZ1RJjULOniEB4fxb3Vm0z/x0oNhi1lbP1aMtg==",
             "requires": {
-                "blueimp-load-image": "2.24.0",
-                "prop-types": "15.7.2",
-                "react-modal": "3.9.1"
+                "blueimp-load-image": "^2.19.0",
+                "prop-types": "^15.6.1",
+                "react-modal": "^3.4.4"
             }
         },
         "react-is": {
@@ -13884,7 +13903,7 @@
             "resolved": "https://registry.npmjs.org/react-jsx-parser/-/react-jsx-parser-1.19.1.tgz",
             "integrity": "sha512-ktc7P8v8dRSYtX5A06inci3dl4D6efGyJDqVSLQCEW0nCq5A+1gtKTcD7Wzmn84uY0eacM+zY15vN3ZQKUuQ1A==",
             "requires": {
-                "acorn-jsx": "4.1.1"
+                "acorn-jsx": "^4.1.1"
             }
         },
         "react-lifecycles-compat": {
@@ -13897,10 +13916,10 @@
             "resolved": "https://registry.npmjs.org/react-measure/-/react-measure-2.3.0.tgz",
             "integrity": "sha512-dwAvmiOeblj5Dvpnk8Jm7Q8B4THF/f1l1HtKVi0XDecsG6LXwGvzV5R1H32kq3TW6RW64OAf5aoQxpIgLa4z8A==",
             "requires": {
-                "@babel/runtime": "7.5.5",
-                "get-node-dimensions": "1.2.1",
-                "prop-types": "15.7.2",
-                "resize-observer-polyfill": "1.5.1"
+                "@babel/runtime": "^7.2.0",
+                "get-node-dimensions": "^1.2.1",
+                "prop-types": "^15.6.2",
+                "resize-observer-polyfill": "^1.5.0"
             }
         },
         "react-modal": {
@@ -13908,10 +13927,10 @@
             "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.9.1.tgz",
             "integrity": "sha512-k+TUkhGWpIVHLsEyjNmlyOYL0Uz03fNZvlkhCImd1h+6fhNgTi6H6jexVXPVhD2LMMDzJyfugxMN+APN/em+eQ==",
             "requires": {
-                "exenv": "1.2.2",
-                "prop-types": "15.7.2",
-                "react-lifecycles-compat": "3.0.4",
-                "warning": "4.0.3"
+                "exenv": "^1.2.0",
+                "prop-types": "^15.5.10",
+                "react-lifecycles-compat": "^3.0.0",
+                "warning": "^4.0.3"
             },
             "dependencies": {
                 "warning": {
@@ -13919,7 +13938,7 @@
                     "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
                     "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
                     "requires": {
-                        "loose-envify": "1.4.0"
+                        "loose-envify": "^1.0.0"
                     }
                 }
             }
@@ -13929,8 +13948,8 @@
             "resolved": "https://registry.npmjs.org/react-mosaic/-/react-mosaic-0.0.20.tgz",
             "integrity": "sha1-pSSr8uzyi5r2sh1NNQ/veCLvMJ4=",
             "requires": {
-                "prop-types": "15.7.2",
-                "threads": "0.8.1"
+                "prop-types": "^15.6.0",
+                "threads": "^0.8.0"
             }
         },
         "react-overlays": {
@@ -13938,14 +13957,14 @@
             "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.7.2",
-                "prop-types-extra": "1.1.0",
-                "react-context-toolbox": "2.0.2",
-                "react-popper": "1.3.3",
-                "uncontrollable": "6.2.3",
-                "warning": "4.0.3"
+                "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": {
                 "uncontrollable": {
@@ -13953,8 +13972,8 @@
                     "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-6.2.3.tgz",
                     "integrity": "sha512-VgOAoBU2ptCL2bfTG2Mra0I8i1u6Aq84AFonD5tmCAYSfs3hWvr2Rlw0q2ntoxXTHjcQOmZOh3FKaN+UZVyREQ==",
                     "requires": {
-                        "@babel/runtime": "7.5.5",
-                        "invariant": "2.2.4"
+                        "@babel/runtime": "^7.4.5",
+                        "invariant": "^2.2.4"
                     }
                 },
                 "warning": {
@@ -13962,7 +13981,7 @@
                     "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
                     "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
                     "requires": {
-                        "loose-envify": "1.4.0"
+                        "loose-envify": "^1.0.0"
                     }
                 }
             }
@@ -13972,12 +13991,12 @@
             "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.3.tgz",
             "integrity": "sha512-ynMZBPkXONPc5K4P5yFWgZx5JGAUIP3pGGLNs58cfAPgK67olx7fmLp+AdpZ0+GoQ+ieFDa/z4cdV6u7sioH6w==",
             "requires": {
-                "@babel/runtime": "7.5.5",
-                "create-react-context": "0.2.2",
-                "popper.js": "1.15.0",
-                "prop-types": "15.7.2",
-                "typed-styles": "0.0.7",
-                "warning": "4.0.3"
+                "@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": {
@@ -13985,8 +14004,8 @@
                     "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.17",
-                        "gud": "1.0.0"
+                        "fbjs": "^0.8.0",
+                        "gud": "^1.0.0"
                     }
                 },
                 "typed-styles": {
@@ -13999,7 +14018,7 @@
                     "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
                     "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
                     "requires": {
-                        "loose-envify": "1.4.0"
+                        "loose-envify": "^1.0.0"
                     }
                 }
             }
@@ -14009,8 +14028,8 @@
             "resolved": "https://registry.npmjs.org/react-simple-dropdown/-/react-simple-dropdown-3.2.3.tgz",
             "integrity": "sha512-NmyyvA0D4wph5ctzkn8U4wmblOacavJMl9gTOhQR3v8I997mc1FL1NFKkj3Mx+HNysBKRD/HI+kpxXCAgXumPw==",
             "requires": {
-                "classnames": "2.2.6",
-                "prop-types": "15.7.2"
+                "classnames": "^2.1.2",
+                "prop-types": "^15.5.8"
             }
         },
         "react-split-pane": {
@@ -14018,9 +14037,9 @@
             "resolved": "https://registry.npmjs.org/react-split-pane/-/react-split-pane-0.1.87.tgz",
             "integrity": "sha512-F22jqWyKB1WximT0U5HKdSuB9tmJGjjP+WUyveHxJJys3ANsljj163kCdsI6M3gdfyCVC+B2rq8sc5m2Ko02RA==",
             "requires": {
-                "prop-types": "15.7.2",
-                "react-lifecycles-compat": "3.0.4",
-                "react-style-proptype": "3.2.2"
+                "prop-types": "^15.5.10",
+                "react-lifecycles-compat": "^3.0.4",
+                "react-style-proptype": "^3.0.0"
             }
         },
         "react-style-proptype": {
@@ -14028,7 +14047,7 @@
             "resolved": "https://registry.npmjs.org/react-style-proptype/-/react-style-proptype-3.2.2.tgz",
             "integrity": "sha512-ywYLSjNkxKHiZOqNlso9PZByNEY+FTyh3C+7uuziK0xFXu9xzdyfHwg4S9iyiRRoPCR4k2LqaBBsWVmSBwCWYQ==",
             "requires": {
-                "prop-types": "15.7.2"
+                "prop-types": "^15.5.4"
             }
         },
         "react-table": {
@@ -14036,7 +14055,7 @@
             "resolved": "https://registry.npmjs.org/react-table/-/react-table-6.10.3.tgz",
             "integrity": "sha512-sVlq2/rxVaQJywGD95+qGiMr/SMHFIFnXdx619BLOWE/Os5FOGtV6pQJNAjZixbQZiOu7dmBO1kME28uxh6wmA==",
             "requires": {
-                "classnames": "2.2.6"
+                "classnames": "^2.2.5"
             }
         },
         "react-themeable": {
@@ -14044,7 +14063,7 @@
             "resolved": "https://registry.npmjs.org/react-themeable/-/react-themeable-1.1.0.tgz",
             "integrity": "sha1-fURm3ZsrX6dQWHJ4JenxUro3mg4=",
             "requires": {
-                "object-assign": "3.0.0"
+                "object-assign": "^3.0.0"
             },
             "dependencies": {
                 "object-assign": {
@@ -14059,10 +14078,10 @@
             "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
             "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
             "requires": {
-                "dom-helpers": "3.4.0",
-                "loose-envify": "1.4.0",
-                "prop-types": "15.7.2",
-                "react-lifecycles-compat": "3.0.4"
+                "dom-helpers": "^3.4.0",
+                "loose-envify": "^1.4.0",
+                "prop-types": "^15.6.2",
+                "react-lifecycles-compat": "^3.0.4"
             }
         },
         "reactcss": {
@@ -14070,7 +14089,7 @@
             "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
             "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==",
             "requires": {
-                "lodash": "4.17.15"
+                "lodash": "^4.0.1"
             }
         },
         "read-pkg": {
@@ -14078,9 +14097,9 @@
             "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
             "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
             "requires": {
-                "load-json-file": "1.1.0",
-                "normalize-package-data": "2.5.0",
-                "path-type": "1.1.0"
+                "load-json-file": "^1.0.0",
+                "normalize-package-data": "^2.3.2",
+                "path-type": "^1.0.0"
             }
         },
         "read-pkg-up": {
@@ -14088,8 +14107,8 @@
             "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
             "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
             "requires": {
-                "find-up": "1.1.2",
-                "read-pkg": "1.1.0"
+                "find-up": "^1.0.0",
+                "read-pkg": "^1.0.0"
             },
             "dependencies": {
                 "find-up": {
@@ -14097,8 +14116,8 @@
                     "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
                     "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
                     "requires": {
-                        "path-exists": "2.1.0",
-                        "pinkie-promise": "2.0.1"
+                        "path-exists": "^2.0.0",
+                        "pinkie-promise": "^2.0.0"
                     }
                 },
                 "path-exists": {
@@ -14106,7 +14125,7 @@
                     "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
                     "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
                     "requires": {
-                        "pinkie-promise": "2.0.1"
+                        "pinkie-promise": "^2.0.0"
                     }
                 }
             }
@@ -14116,13 +14135,13 @@
             "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
             "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
             "requires": {
-                "core-util-is": "1.0.2",
-                "inherits": "2.0.3",
-                "isarray": "1.0.0",
-                "process-nextick-args": "2.0.1",
-                "safe-buffer": "5.1.2",
-                "string_decoder": "1.1.1",
-                "util-deprecate": "1.0.2"
+                "core-util-is": "~1.0.0",
+                "inherits": "~2.0.3",
+                "isarray": "~1.0.0",
+                "process-nextick-args": "~2.0.0",
+                "safe-buffer": "~5.1.1",
+                "string_decoder": "~1.1.1",
+                "util-deprecate": "~1.0.1"
             }
         },
         "readdirp": {
@@ -14130,9 +14149,9 @@
             "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
             "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
             "requires": {
-                "graceful-fs": "4.2.0",
-                "micromatch": "3.1.10",
-                "readable-stream": "2.3.6"
+                "graceful-fs": "^4.1.11",
+                "micromatch": "^3.1.10",
+                "readable-stream": "^2.0.2"
             }
         },
         "readline": {
@@ -14146,9 +14165,9 @@
             "integrity": "sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=",
             "requires": {
                 "ast-types": "0.9.6",
-                "esprima": "3.1.3",
-                "private": "0.1.8",
-                "source-map": "0.5.7"
+                "esprima": "~3.1.0",
+                "private": "~0.1.5",
+                "source-map": "~0.5.0"
             },
             "dependencies": {
                 "esprima": {
@@ -14163,7 +14182,7 @@
             "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
             "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
             "requires": {
-                "resolve": "1.11.1"
+                "resolve": "^1.1.6"
             }
         },
         "redent": {
@@ -14171,8 +14190,8 @@
             "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
             "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
             "requires": {
-                "indent-string": "2.1.0",
-                "strip-indent": "1.0.1"
+                "indent-string": "^2.1.0",
+                "strip-indent": "^1.0.1"
             }
         },
         "reduce-flatten": {
@@ -14190,8 +14209,8 @@
             "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
             "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
             "requires": {
-                "extend-shallow": "3.0.2",
-                "safe-regex": "1.1.0"
+                "extend-shallow": "^3.0.2",
+                "safe-regex": "^1.1.0"
             }
         },
         "regexp-clone": {
@@ -14204,8 +14223,8 @@
             "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz",
             "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==",
             "requires": {
-                "rc": "1.2.8",
-                "safe-buffer": "5.1.2"
+                "rc": "^1.1.6",
+                "safe-buffer": "^5.0.1"
             }
         },
         "registry-url": {
@@ -14213,7 +14232,7 @@
             "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
             "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
             "requires": {
-                "rc": "1.2.8"
+                "rc": "^1.0.1"
             }
         },
         "remove-trailing-separator": {
@@ -14236,7 +14255,7 @@
             "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
             "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
             "requires": {
-                "is-finite": "1.0.2"
+                "is-finite": "^1.0.0"
             }
         },
         "request": {
@@ -14244,26 +14263,26 @@
             "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
             "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
             "requires": {
-                "aws-sign2": "0.7.0",
-                "aws4": "1.9.0",
-                "caseless": "0.12.0",
-                "combined-stream": "1.0.8",
-                "extend": "3.0.2",
-                "forever-agent": "0.6.1",
-                "form-data": "2.3.3",
-                "har-validator": "5.1.3",
-                "http-signature": "1.2.0",
-                "is-typedarray": "1.0.0",
-                "isstream": "0.1.2",
-                "json-stringify-safe": "5.0.1",
-                "mime-types": "2.1.24",
-                "oauth-sign": "0.9.0",
-                "performance-now": "2.1.0",
-                "qs": "6.5.2",
-                "safe-buffer": "5.1.2",
-                "tough-cookie": "2.4.3",
-                "tunnel-agent": "0.6.0",
-                "uuid": "3.3.2"
+                "aws-sign2": "~0.7.0",
+                "aws4": "^1.8.0",
+                "caseless": "~0.12.0",
+                "combined-stream": "~1.0.6",
+                "extend": "~3.0.2",
+                "forever-agent": "~0.6.1",
+                "form-data": "~2.3.2",
+                "har-validator": "~5.1.0",
+                "http-signature": "~1.2.0",
+                "is-typedarray": "~1.0.0",
+                "isstream": "~0.1.2",
+                "json-stringify-safe": "~5.0.1",
+                "mime-types": "~2.1.19",
+                "oauth-sign": "~0.9.0",
+                "performance-now": "^2.1.0",
+                "qs": "~6.5.2",
+                "safe-buffer": "^5.1.2",
+                "tough-cookie": "~2.4.3",
+                "tunnel-agent": "^0.6.0",
+                "uuid": "^3.3.2"
             },
             "dependencies": {
                 "form-data": {
@@ -14271,9 +14290,9 @@
                     "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
                     "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
                     "requires": {
-                        "asynckit": "0.4.0",
-                        "combined-stream": "1.0.8",
-                        "mime-types": "2.1.24"
+                        "asynckit": "^0.4.0",
+                        "combined-stream": "^1.0.6",
+                        "mime-types": "^2.1.12"
                     }
                 },
                 "qs": {
@@ -14288,10 +14307,10 @@
             "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.4.tgz",
             "integrity": "sha512-8wgMrvE546PzbR5WbYxUQogUnUDfM0S7QIFZMID+J73vdFARkFy+HElj4T+MWYhpXwlLp0EQ8Zoj8xUA0he4Vg==",
             "requires": {
-                "bluebird": "3.5.5",
+                "bluebird": "^3.5.0",
                 "request-promise-core": "1.1.2",
-                "stealthy-require": "1.1.1",
-                "tough-cookie": "2.4.3"
+                "stealthy-require": "^1.1.1",
+                "tough-cookie": "^2.3.3"
             }
         },
         "request-promise-core": {
@@ -14299,7 +14318,7 @@
             "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz",
             "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==",
             "requires": {
-                "lodash": "4.17.15"
+                "lodash": "^4.17.11"
             }
         },
         "request-promise-native": {
@@ -14309,8 +14328,8 @@
             "dev": true,
             "requires": {
                 "request-promise-core": "1.1.2",
-                "stealthy-require": "1.1.1",
-                "tough-cookie": "2.4.3"
+                "stealthy-require": "^1.1.1",
+                "tough-cookie": "^2.3.3"
             }
         },
         "require-directory": {
@@ -14328,8 +14347,8 @@
             "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
             "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==",
             "requires": {
-                "resolve-from": "2.0.0",
-                "semver": "5.7.0"
+                "resolve-from": "^2.0.0",
+                "semver": "^5.1.0"
             },
             "dependencies": {
                 "resolve-from": {
@@ -14355,7 +14374,7 @@
             "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz",
             "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==",
             "requires": {
-                "path-parse": "1.0.6"
+                "path-parse": "^1.0.6"
             }
         },
         "resolve-cwd": {
@@ -14364,7 +14383,7 @@
             "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
             "dev": true,
             "requires": {
-                "resolve-from": "3.0.0"
+                "resolve-from": "^3.0.0"
             }
         },
         "resolve-dir": {
@@ -14373,8 +14392,8 @@
             "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
             "dev": true,
             "requires": {
-                "expand-tilde": "2.0.2",
-                "global-modules": "1.0.0"
+                "expand-tilde": "^2.0.0",
+                "global-modules": "^1.0.0"
             },
             "dependencies": {
                 "global-modules": {
@@ -14383,9 +14402,9 @@
                     "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
                     "dev": true,
                     "requires": {
-                        "global-prefix": "1.0.2",
-                        "is-windows": "1.0.2",
-                        "resolve-dir": "1.0.1"
+                        "global-prefix": "^1.0.1",
+                        "is-windows": "^1.0.1",
+                        "resolve-dir": "^1.0.0"
                     }
                 }
             }
@@ -14417,7 +14436,7 @@
             "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
             "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
             "requires": {
-                "align-text": "0.1.4"
+                "align-text": "^0.1.1"
             }
         },
         "rimraf": {
@@ -14425,7 +14444,7 @@
             "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz",
             "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==",
             "requires": {
-                "glob": "7.1.4"
+                "glob": "^7.1.3"
             }
         },
         "ripemd160": {
@@ -14433,8 +14452,8 @@
             "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
             "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
             "requires": {
-                "hash-base": "3.0.4",
-                "inherits": "2.0.3"
+                "hash-base": "^3.0.0",
+                "inherits": "^2.0.1"
             }
         },
         "rope-sequence": {
@@ -14448,7 +14467,7 @@
             "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
             "dev": true,
             "requires": {
-                "aproba": "1.2.0"
+                "aproba": "^1.1.1"
             }
         },
         "safe-buffer": {
@@ -14461,7 +14480,7 @@
             "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
             "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
             "requires": {
-                "ret": "0.1.15"
+                "ret": "~0.1.10"
             }
         },
         "safer-buffer": {
@@ -14475,7 +14494,7 @@
             "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
             "optional": true,
             "requires": {
-                "sparse-bitfield": "3.0.3"
+                "sparse-bitfield": "^3.0.3"
             }
         },
         "sass-graph": {
@@ -14483,10 +14502,10 @@
             "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz",
             "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=",
             "requires": {
-                "glob": "7.1.4",
-                "lodash": "4.17.15",
-                "scss-tokenizer": "0.2.3",
-                "yargs": "7.1.0"
+                "glob": "^7.0.0",
+                "lodash": "^4.0.0",
+                "scss-tokenizer": "^0.2.3",
+                "yargs": "^7.0.0"
             }
         },
         "sass-loader": {
@@ -14495,12 +14514,12 @@
             "integrity": "sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==",
             "dev": true,
             "requires": {
-                "clone-deep": "2.0.2",
-                "loader-utils": "1.2.3",
-                "lodash.tail": "4.1.1",
-                "neo-async": "2.6.1",
-                "pify": "3.0.0",
-                "semver": "5.7.0"
+                "clone-deep": "^2.0.1",
+                "loader-utils": "^1.0.1",
+                "lodash.tail": "^4.1.1",
+                "neo-async": "^2.5.0",
+                "pify": "^3.0.0",
+                "semver": "^5.5.0"
             },
             "dependencies": {
                 "pify": {
@@ -14522,7 +14541,7 @@
             "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==",
             "dev": true,
             "requires": {
-                "xmlchars": "2.1.1"
+                "xmlchars": "^2.1.1"
             }
         },
         "scheduler": {
@@ -14530,8 +14549,8 @@
             "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz",
             "integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==",
             "requires": {
-                "loose-envify": "1.4.0",
-                "object-assign": "4.1.1"
+                "loose-envify": "^1.1.0",
+                "object-assign": "^4.1.1"
             }
         },
         "schema-utils": {
@@ -14539,8 +14558,8 @@
             "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz",
             "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==",
             "requires": {
-                "ajv": "6.10.2",
-                "ajv-keywords": "3.4.1"
+                "ajv": "^6.1.0",
+                "ajv-keywords": "^3.1.0"
             }
         },
         "scss-loader": {
@@ -14554,8 +14573,8 @@
             "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
             "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=",
             "requires": {
-                "js-base64": "2.5.1",
-                "source-map": "0.4.4"
+                "js-base64": "^2.1.8",
+                "source-map": "^0.4.2"
             },
             "dependencies": {
                 "source-map": {
@@ -14563,7 +14582,7 @@
                     "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
                     "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
                     "requires": {
-                        "amdefine": "1.0.1"
+                        "amdefine": ">=0.0.4"
                     }
                 }
             }
@@ -14598,7 +14617,7 @@
             "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz",
             "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
             "requires": {
-                "semver": "5.7.0"
+                "semver": "^5.0.3"
             }
         },
         "send": {
@@ -14607,18 +14626,18 @@
             "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
             "requires": {
                 "debug": "2.6.9",
-                "depd": "1.1.2",
-                "destroy": "1.0.4",
-                "encodeurl": "1.0.2",
-                "escape-html": "1.0.3",
-                "etag": "1.8.1",
+                "depd": "~1.1.2",
+                "destroy": "~1.0.4",
+                "encodeurl": "~1.0.2",
+                "escape-html": "~1.0.3",
+                "etag": "~1.8.1",
                 "fresh": "0.5.2",
-                "http-errors": "1.7.2",
+                "http-errors": "~1.7.2",
                 "mime": "1.6.0",
                 "ms": "2.1.1",
-                "on-finished": "2.3.0",
-                "range-parser": "1.2.1",
-                "statuses": "1.5.0"
+                "on-finished": "~2.3.0",
+                "range-parser": "~1.2.1",
+                "statuses": "~1.5.0"
             },
             "dependencies": {
                 "ms": {
@@ -14645,13 +14664,13 @@
             "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
             "dev": true,
             "requires": {
-                "accepts": "1.3.7",
+                "accepts": "~1.3.4",
                 "batch": "0.6.1",
                 "debug": "2.6.9",
-                "escape-html": "1.0.3",
-                "http-errors": "1.6.3",
-                "mime-types": "2.1.24",
-                "parseurl": "1.3.3"
+                "escape-html": "~1.0.3",
+                "http-errors": "~1.6.2",
+                "mime-types": "~2.1.17",
+                "parseurl": "~1.3.2"
             },
             "dependencies": {
                 "http-errors": {
@@ -14660,10 +14679,10 @@
                     "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
                     "dev": true,
                     "requires": {
-                        "depd": "1.1.2",
+                        "depd": "~1.1.2",
                         "inherits": "2.0.3",
                         "setprototypeof": "1.1.0",
-                        "statuses": "1.5.0"
+                        "statuses": ">= 1.4.0 < 2"
                     }
                 },
                 "setprototypeof": {
@@ -14679,9 +14698,9 @@
             "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
             "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
             "requires": {
-                "encodeurl": "1.0.2",
-                "escape-html": "1.0.3",
-                "parseurl": "1.3.3",
+                "encodeurl": "~1.0.2",
+                "escape-html": "~1.0.3",
+                "parseurl": "~1.3.3",
                 "send": "0.17.1"
             }
         },
@@ -14695,10 +14714,10 @@
             "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
             "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
             "requires": {
-                "extend-shallow": "2.0.1",
-                "is-extendable": "0.1.1",
-                "is-plain-object": "2.0.4",
-                "split-string": "3.1.0"
+                "extend-shallow": "^2.0.1",
+                "is-extendable": "^0.1.1",
+                "is-plain-object": "^2.0.3",
+                "split-string": "^3.0.1"
             },
             "dependencies": {
                 "extend-shallow": {
@@ -14706,7 +14725,7 @@
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
                     "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
                     "requires": {
-                        "is-extendable": "0.1.1"
+                        "is-extendable": "^0.1.0"
                     }
                 }
             }
@@ -14726,8 +14745,8 @@
             "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
             "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
             "requires": {
-                "inherits": "2.0.3",
-                "safe-buffer": "5.1.2"
+                "inherits": "^2.0.1",
+                "safe-buffer": "^5.0.1"
             }
         },
         "shallow-clone": {
@@ -14736,9 +14755,9 @@
             "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==",
             "dev": true,
             "requires": {
-                "is-extendable": "0.1.1",
-                "kind-of": "5.1.0",
-                "mixin-object": "2.0.1"
+                "is-extendable": "^0.1.1",
+                "kind-of": "^5.0.0",
+                "mixin-object": "^2.0.1"
             },
             "dependencies": {
                 "kind-of": {
@@ -14759,16 +14778,16 @@
             "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.22.1.tgz",
             "integrity": "sha512-lXzSk/FL5b/MpWrT1pQZneKe25stVjEbl6uhhJcTULm7PhmJgKKRbTDM/vtjyUuC/RLqL2PRyC4rpKwbv3soEw==",
             "requires": {
-                "color": "3.1.2",
-                "detect-libc": "1.0.3",
-                "fs-copy-file-sync": "1.1.1",
-                "nan": "2.14.0",
-                "npmlog": "4.1.2",
-                "prebuild-install": "5.3.0",
-                "semver": "6.2.0",
-                "simple-get": "3.0.3",
-                "tar": "4.4.10",
-                "tunnel-agent": "0.6.0"
+                "color": "^3.1.1",
+                "detect-libc": "^1.0.3",
+                "fs-copy-file-sync": "^1.1.1",
+                "nan": "^2.13.2",
+                "npmlog": "^4.1.2",
+                "prebuild-install": "^5.3.0",
+                "semver": "^6.0.0",
+                "simple-get": "^3.0.3",
+                "tar": "^4.4.8",
+                "tunnel-agent": "^0.6.0"
             },
             "dependencies": {
                 "semver": {
@@ -14783,7 +14802,7 @@
             "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
             "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
             "requires": {
-                "shebang-regex": "1.0.0"
+                "shebang-regex": "^1.0.0"
             }
         },
         "shebang-regex": {
@@ -14801,9 +14820,9 @@
             "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz",
             "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==",
             "requires": {
-                "glob": "7.1.4",
-                "interpret": "1.2.0",
-                "rechoir": "0.6.2"
+                "glob": "^7.0.0",
+                "interpret": "^1.0.0",
+                "rechoir": "^0.6.2"
             }
         },
         "shellwords": {
@@ -14832,9 +14851,9 @@
             "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.0.3.tgz",
             "integrity": "sha512-Wvre/Jq5vgoz31Z9stYWPLn0PqRqmBDpFSdypAnHu5AvRVCYPRYGnvryNLiXu8GOBNDH82J2FRHUGMjjHUpXFw==",
             "requires": {
-                "decompress-response": "3.3.0",
-                "once": "1.4.0",
-                "simple-concat": "1.0.0"
+                "decompress-response": "^3.3.0",
+                "once": "^1.3.1",
+                "simple-concat": "^1.0.0"
             }
         },
         "simple-swizzle": {
@@ -14842,7 +14861,7 @@
             "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
             "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
             "requires": {
-                "is-arrayish": "0.3.2"
+                "is-arrayish": "^0.3.1"
             },
             "dependencies": {
                 "is-arrayish": {
@@ -14868,14 +14887,14 @@
             "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
             "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
             "requires": {
-                "base": "0.11.2",
-                "debug": "2.6.9",
-                "define-property": "0.2.5",
-                "extend-shallow": "2.0.1",
-                "map-cache": "0.2.2",
-                "source-map": "0.5.7",
-                "source-map-resolve": "0.5.2",
-                "use": "3.1.1"
+                "base": "^0.11.1",
+                "debug": "^2.2.0",
+                "define-property": "^0.2.5",
+                "extend-shallow": "^2.0.1",
+                "map-cache": "^0.2.2",
+                "source-map": "^0.5.6",
+                "source-map-resolve": "^0.5.0",
+                "use": "^3.1.0"
             },
             "dependencies": {
                 "define-property": {
@@ -14883,7 +14902,7 @@
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
                     "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
                     "requires": {
-                        "is-descriptor": "0.1.6"
+                        "is-descriptor": "^0.1.0"
                     }
                 },
                 "extend-shallow": {
@@ -14891,7 +14910,7 @@
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
                     "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
                     "requires": {
-                        "is-extendable": "0.1.1"
+                        "is-extendable": "^0.1.0"
                     }
                 }
             }
@@ -14901,9 +14920,9 @@
             "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
             "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
             "requires": {
-                "define-property": "1.0.0",
-                "isobject": "3.0.1",
-                "snapdragon-util": "3.0.1"
+                "define-property": "^1.0.0",
+                "isobject": "^3.0.0",
+                "snapdragon-util": "^3.0.1"
             },
             "dependencies": {
                 "define-property": {
@@ -14911,7 +14930,7 @@
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
                     "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
                     "requires": {
-                        "is-descriptor": "1.0.2"
+                        "is-descriptor": "^1.0.0"
                     }
                 },
                 "is-accessor-descriptor": {
@@ -14919,7 +14938,7 @@
                     "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
                     "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
                     "requires": {
-                        "kind-of": "6.0.2"
+                        "kind-of": "^6.0.0"
                     }
                 },
                 "is-data-descriptor": {
@@ -14927,7 +14946,7 @@
                     "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
                     "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
                     "requires": {
-                        "kind-of": "6.0.2"
+                        "kind-of": "^6.0.0"
                     }
                 },
                 "is-descriptor": {
@@ -14935,9 +14954,9 @@
                     "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
                     "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
                     "requires": {
-                        "is-accessor-descriptor": "1.0.0",
-                        "is-data-descriptor": "1.0.0",
-                        "kind-of": "6.0.2"
+                        "is-accessor-descriptor": "^1.0.0",
+                        "is-data-descriptor": "^1.0.0",
+                        "kind-of": "^6.0.2"
                     }
                 }
             }
@@ -14947,7 +14966,7 @@
             "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
             "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
             "requires": {
-                "kind-of": "3.2.2"
+                "kind-of": "^3.2.0"
             },
             "dependencies": {
                 "kind-of": {
@@ -14955,7 +14974,7 @@
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
                     "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
-                        "is-buffer": "1.1.6"
+                        "is-buffer": "^1.1.5"
                     }
                 }
             }
@@ -14965,12 +14984,12 @@
             "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.2.0.tgz",
             "integrity": "sha512-wxXrIuZ8AILcn+f1B4ez4hJTPG24iNgxBBDaJfT6MsyOhVYiTXWexGoPkd87ktJG8kQEcL/NBvRi64+9k4Kc0w==",
             "requires": {
-                "debug": "4.1.1",
-                "engine.io": "3.3.2",
-                "has-binary2": "1.0.3",
-                "socket.io-adapter": "1.1.1",
+                "debug": "~4.1.0",
+                "engine.io": "~3.3.1",
+                "has-binary2": "~1.0.2",
+                "socket.io-adapter": "~1.1.0",
                 "socket.io-client": "2.2.0",
-                "socket.io-parser": "3.3.0"
+                "socket.io-parser": "~3.3.0"
             },
             "dependencies": {
                 "debug": {
@@ -14978,7 +14997,7 @@
                     "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
                     "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
                     "requires": {
-                        "ms": "2.1.2"
+                        "ms": "^2.1.1"
                     }
                 },
                 "ms": {
@@ -15002,15 +15021,15 @@
                 "base64-arraybuffer": "0.1.5",
                 "component-bind": "1.0.0",
                 "component-emitter": "1.2.1",
-                "debug": "3.1.0",
-                "engine.io-client": "3.3.2",
-                "has-binary2": "1.0.3",
+                "debug": "~3.1.0",
+                "engine.io-client": "~3.3.1",
+                "has-binary2": "~1.0.2",
                 "has-cors": "1.1.0",
                 "indexof": "0.0.1",
                 "object-component": "0.0.3",
                 "parseqs": "0.0.5",
                 "parseuri": "0.0.5",
-                "socket.io-parser": "3.3.0",
+                "socket.io-parser": "~3.3.0",
                 "to-array": "0.1.4"
             },
             "dependencies": {
@@ -15035,7 +15054,7 @@
             "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==",
             "requires": {
                 "component-emitter": "1.2.1",
-                "debug": "3.1.0",
+                "debug": "~3.1.0",
                 "isarray": "2.0.1"
             },
             "dependencies": {
@@ -15065,8 +15084,8 @@
             "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==",
             "dev": true,
             "requires": {
-                "faye-websocket": "0.10.0",
-                "uuid": "3.3.2"
+                "faye-websocket": "^0.10.0",
+                "uuid": "^3.0.1"
             }
         },
         "sockjs-client": {
@@ -15075,12 +15094,12 @@
             "integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==",
             "dev": true,
             "requires": {
-                "debug": "3.2.6",
-                "eventsource": "1.0.7",
-                "faye-websocket": "0.11.3",
-                "inherits": "2.0.3",
-                "json3": "3.3.3",
-                "url-parse": "1.4.7"
+                "debug": "^3.2.5",
+                "eventsource": "^1.0.7",
+                "faye-websocket": "~0.11.1",
+                "inherits": "^2.0.3",
+                "json3": "^3.3.2",
+                "url-parse": "^1.4.3"
             },
             "dependencies": {
                 "debug": {
@@ -15089,7 +15108,7 @@
                     "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
                     "dev": true,
                     "requires": {
-                        "ms": "2.1.2"
+                        "ms": "^2.1.1"
                     }
                 },
                 "faye-websocket": {
@@ -15098,7 +15117,7 @@
                     "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==",
                     "dev": true,
                     "requires": {
-                        "websocket-driver": "0.7.3"
+                        "websocket-driver": ">=0.5.1"
                     }
                 },
                 "ms": {
@@ -15114,9 +15133,9 @@
             "resolved": "https://registry.npmjs.org/solr-node/-/solr-node-1.2.1.tgz",
             "integrity": "sha512-DN3+FSBgpJEgGTNddzS8tNb+ILSn5MLcsWf15G9rGxi/sROHbpcevdRSVx6s5/nz56c/5AnBTBZWak7IXWX97A==",
             "requires": {
-                "@log4js-node/log4js-api": "1.0.2",
-                "node-fetch": "2.6.0",
-                "underscore": "1.9.1"
+                "@log4js-node/log4js-api": "^1.0.2",
+                "node-fetch": "^2.3.0",
+                "underscore": "^1.8.3"
             },
             "dependencies": {
                 "node-fetch": {
@@ -15142,11 +15161,11 @@
             "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
             "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==",
             "requires": {
-                "atob": "2.1.2",
-                "decode-uri-component": "0.2.0",
-                "resolve-url": "0.2.1",
-                "source-map-url": "0.4.0",
-                "urix": "0.1.0"
+                "atob": "^2.1.1",
+                "decode-uri-component": "^0.2.0",
+                "resolve-url": "^0.2.1",
+                "source-map-url": "^0.4.0",
+                "urix": "^0.1.0"
             }
         },
         "source-map-support": {
@@ -15154,8 +15173,8 @@
             "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz",
             "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==",
             "requires": {
-                "buffer-from": "1.1.1",
-                "source-map": "0.6.1"
+                "buffer-from": "^1.0.0",
+                "source-map": "^0.6.0"
             },
             "dependencies": {
                 "source-map": {
@@ -15176,7 +15195,7 @@
             "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
             "optional": true,
             "requires": {
-                "memory-pager": "1.5.0"
+                "memory-pager": "^1.0.2"
             }
         },
         "spdx-correct": {
@@ -15184,8 +15203,8 @@
             "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
             "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
             "requires": {
-                "spdx-expression-parse": "3.0.0",
-                "spdx-license-ids": "3.0.5"
+                "spdx-expression-parse": "^3.0.0",
+                "spdx-license-ids": "^3.0.0"
             }
         },
         "spdx-exceptions": {
@@ -15198,8 +15217,8 @@
             "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
             "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
             "requires": {
-                "spdx-exceptions": "2.2.0",
-                "spdx-license-ids": "3.0.5"
+                "spdx-exceptions": "^2.1.0",
+                "spdx-license-ids": "^3.0.0"
             }
         },
         "spdx-license-ids": {
@@ -15213,11 +15232,11 @@
             "integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==",
             "dev": true,
             "requires": {
-                "debug": "4.1.1",
-                "handle-thing": "2.0.0",
-                "http-deceiver": "1.2.7",
-                "select-hose": "2.0.0",
-                "spdy-transport": "3.0.0"
+                "debug": "^4.1.0",
+                "handle-thing": "^2.0.0",
+                "http-deceiver": "^1.2.7",
+                "select-hose": "^2.0.0",
+                "spdy-transport": "^3.0.0"
             },
             "dependencies": {
                 "debug": {
@@ -15226,7 +15245,7 @@
                     "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
                     "dev": true,
                     "requires": {
-                        "ms": "2.1.2"
+                        "ms": "^2.1.1"
                     }
                 },
                 "ms": {
@@ -15243,12 +15262,12 @@
             "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
             "dev": true,
             "requires": {
-                "debug": "4.1.1",
-                "detect-node": "2.0.4",
-                "hpack.js": "2.1.6",
-                "obuf": "1.1.2",
-                "readable-stream": "3.4.0",
-                "wbuf": "1.7.3"
+                "debug": "^4.1.0",
+                "detect-node": "^2.0.4",
+                "hpack.js": "^2.1.6",
+                "obuf": "^1.1.2",
+                "readable-stream": "^3.0.6",
+                "wbuf": "^1.7.3"
             },
             "dependencies": {
                 "debug": {
@@ -15257,7 +15276,7 @@
                     "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
                     "dev": true,
                     "requires": {
-                        "ms": "2.1.2"
+                        "ms": "^2.1.1"
                     }
                 },
                 "ms": {
@@ -15272,9 +15291,9 @@
                     "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
                     "dev": true,
                     "requires": {
-                        "inherits": "2.0.3",
-                        "string_decoder": "1.1.1",
-                        "util-deprecate": "1.0.2"
+                        "inherits": "^2.0.3",
+                        "string_decoder": "^1.1.1",
+                        "util-deprecate": "^1.0.1"
                     }
                 }
             }
@@ -15294,7 +15313,7 @@
             "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
             "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
             "requires": {
-                "extend-shallow": "3.0.2"
+                "extend-shallow": "^3.0.0"
             }
         },
         "sprintf-js": {
@@ -15307,15 +15326,15 @@
             "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
             "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
             "requires": {
-                "asn1": "0.2.4",
-                "assert-plus": "1.0.0",
-                "bcrypt-pbkdf": "1.0.2",
-                "dashdash": "1.14.1",
-                "ecc-jsbn": "0.1.2",
-                "getpass": "0.1.7",
-                "jsbn": "0.1.1",
-                "safer-buffer": "2.1.2",
-                "tweetnacl": "0.14.5"
+                "asn1": "~0.2.3",
+                "assert-plus": "^1.0.0",
+                "bcrypt-pbkdf": "^1.0.0",
+                "dashdash": "^1.12.0",
+                "ecc-jsbn": "~0.1.1",
+                "getpass": "^0.1.1",
+                "jsbn": "~0.1.0",
+                "safer-buffer": "^2.0.2",
+                "tweetnacl": "~0.14.0"
             },
             "dependencies": {
                 "assert-plus": {
@@ -15331,7 +15350,7 @@
             "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==",
             "dev": true,
             "requires": {
-                "safe-buffer": "5.1.2"
+                "safe-buffer": "^5.1.1"
             }
         },
         "standard-error": {
@@ -15344,7 +15363,7 @@
             "resolved": "https://registry.npmjs.org/standard-http-error/-/standard-http-error-2.0.1.tgz",
             "integrity": "sha1-+K6RcuPO+cs40ucIShkl9Xp8NL0=",
             "requires": {
-                "standard-error": "1.1.0"
+                "standard-error": ">= 1.1.0 < 2"
             }
         },
         "static-extend": {
@@ -15352,8 +15371,8 @@
             "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
             "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
             "requires": {
-                "define-property": "0.2.5",
-                "object-copy": "0.1.0"
+                "define-property": "^0.2.5",
+                "object-copy": "^0.1.0"
             },
             "dependencies": {
                 "define-property": {
@@ -15361,7 +15380,7 @@
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
                     "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
                     "requires": {
-                        "is-descriptor": "0.1.6"
+                        "is-descriptor": "^0.1.0"
                     }
                 }
             }
@@ -15376,7 +15395,7 @@
             "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz",
             "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==",
             "requires": {
-                "readable-stream": "2.3.6"
+                "readable-stream": "^2.0.1"
             }
         },
         "stealthy-require": {
@@ -15390,8 +15409,8 @@
             "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==",
             "dev": true,
             "requires": {
-                "inherits": "2.0.3",
-                "readable-stream": "2.3.6"
+                "inherits": "~2.0.1",
+                "readable-stream": "^2.0.2"
             }
         },
         "stream-each": {
@@ -15400,8 +15419,8 @@
             "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==",
             "dev": true,
             "requires": {
-                "end-of-stream": "1.4.1",
-                "stream-shift": "1.0.0"
+                "end-of-stream": "^1.1.0",
+                "stream-shift": "^1.0.0"
             }
         },
         "stream-http": {
@@ -15410,11 +15429,11 @@
             "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==",
             "dev": true,
             "requires": {
-                "builtin-status-codes": "3.0.0",
-                "inherits": "2.0.3",
-                "readable-stream": "2.3.6",
-                "to-arraybuffer": "1.0.1",
-                "xtend": "4.0.2"
+                "builtin-status-codes": "^3.0.0",
+                "inherits": "^2.0.1",
+                "readable-stream": "^2.3.6",
+                "to-arraybuffer": "^1.0.0",
+                "xtend": "^4.0.0"
             }
         },
         "stream-parser": {
@@ -15422,7 +15441,7 @@
             "resolved": "https://registry.npmjs.org/stream-parser/-/stream-parser-0.3.1.tgz",
             "integrity": "sha1-FhhUhpRCACGhGC/wrxkRwSl2F3M=",
             "requires": {
-                "debug": "2.6.9"
+                "debug": "2"
             }
         },
         "stream-shift": {
@@ -15441,9 +15460,9 @@
             "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
             "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
             "requires": {
-                "code-point-at": "1.1.0",
-                "is-fullwidth-code-point": "1.0.0",
-                "strip-ansi": "3.0.1"
+                "code-point-at": "^1.0.0",
+                "is-fullwidth-code-point": "^1.0.0",
+                "strip-ansi": "^3.0.0"
             }
         },
         "string.prototype.trimleft": {
@@ -15451,8 +15470,8 @@
             "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz",
             "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==",
             "requires": {
-                "define-properties": "1.1.3",
-                "function-bind": "1.1.1"
+                "define-properties": "^1.1.3",
+                "function-bind": "^1.1.1"
             }
         },
         "string.prototype.trimright": {
@@ -15460,8 +15479,8 @@
             "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz",
             "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==",
             "requires": {
-                "define-properties": "1.1.3",
-                "function-bind": "1.1.1"
+                "define-properties": "^1.1.3",
+                "function-bind": "^1.1.1"
             }
         },
         "string_decoder": {
@@ -15469,7 +15488,7 @@
             "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
             "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
             "requires": {
-                "safe-buffer": "5.1.2"
+                "safe-buffer": "~5.1.0"
             }
         },
         "stringify-parameters": {
@@ -15486,10 +15505,10 @@
                     "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz",
                     "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=",
                     "requires": {
-                        "commander": "2.20.0",
-                        "get-stdin": "5.0.1",
-                        "inspect-function": "0.2.2",
-                        "pipe-functions": "1.3.0"
+                        "commander": "^2.9.0",
+                        "get-stdin": "^5.0.1",
+                        "inspect-function": "^0.2.1",
+                        "pipe-functions": "^1.2.0"
                     }
                 }
             }
@@ -15499,7 +15518,7 @@
             "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
             "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
             "requires": {
-                "ansi-regex": "2.1.1"
+                "ansi-regex": "^2.0.0"
             }
         },
         "strip-bom": {
@@ -15507,7 +15526,7 @@
             "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
             "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
             "requires": {
-                "is-utf8": "0.2.1"
+                "is-utf8": "^0.2.0"
             }
         },
         "strip-eof": {
@@ -15520,7 +15539,7 @@
             "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
             "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
             "requires": {
-                "get-stdin": "4.0.1"
+                "get-stdin": "^4.0.1"
             },
             "dependencies": {
                 "get-stdin": {
@@ -15541,8 +15560,8 @@
             "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==",
             "dev": true,
             "requires": {
-                "loader-utils": "1.2.3",
-                "schema-utils": "1.0.0"
+                "loader-utils": "^1.1.0",
+                "schema-utils": "^1.0.0"
             },
             "dependencies": {
                 "schema-utils": {
@@ -15551,9 +15570,9 @@
                     "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
                     "dev": true,
                     "requires": {
-                        "ajv": "6.10.2",
-                        "ajv-errors": "1.0.1",
-                        "ajv-keywords": "3.4.1"
+                        "ajv": "^6.1.0",
+                        "ajv-errors": "^1.0.0",
+                        "ajv-keywords": "^3.1.0"
                     }
                 }
             }
@@ -15574,11 +15593,11 @@
             "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz",
             "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==",
             "requires": {
-                "array-back": "2.0.0",
-                "deep-extend": "0.6.0",
-                "lodash.padend": "4.6.1",
-                "typical": "2.6.1",
-                "wordwrapjs": "3.0.0"
+                "array-back": "^2.0.0",
+                "deep-extend": "~0.6.0",
+                "lodash.padend": "^4.6.1",
+                "typical": "^2.6.1",
+                "wordwrapjs": "^3.0.0"
             }
         },
         "tapable": {
@@ -15592,13 +15611,13 @@
             "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.10.tgz",
             "integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==",
             "requires": {
-                "chownr": "1.1.2",
-                "fs-minipass": "1.2.6",
-                "minipass": "2.3.5",
-                "minizlib": "1.2.1",
-                "mkdirp": "0.5.1",
-                "safe-buffer": "5.1.2",
-                "yallist": "3.0.3"
+                "chownr": "^1.1.1",
+                "fs-minipass": "^1.2.5",
+                "minipass": "^2.3.5",
+                "minizlib": "^1.2.1",
+                "mkdirp": "^0.5.0",
+                "safe-buffer": "^5.1.2",
+                "yallist": "^3.0.3"
             }
         },
         "tar-fs": {
@@ -15606,10 +15625,10 @@
             "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz",
             "integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==",
             "requires": {
-                "chownr": "1.1.2",
-                "mkdirp": "0.5.1",
-                "pump": "1.0.3",
-                "tar-stream": "1.6.2"
+                "chownr": "^1.0.1",
+                "mkdirp": "^0.5.1",
+                "pump": "^1.0.0",
+                "tar-stream": "^1.1.2"
             },
             "dependencies": {
                 "pump": {
@@ -15617,8 +15636,8 @@
                     "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz",
                     "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==",
                     "requires": {
-                        "end-of-stream": "1.4.1",
-                        "once": "1.4.0"
+                        "end-of-stream": "^1.1.0",
+                        "once": "^1.3.1"
                     }
                 }
             }
@@ -15628,13 +15647,13 @@
             "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz",
             "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==",
             "requires": {
-                "bl": "1.2.2",
-                "buffer-alloc": "1.2.0",
-                "end-of-stream": "1.4.1",
-                "fs-constants": "1.0.0",
-                "readable-stream": "2.3.6",
-                "to-buffer": "1.1.1",
-                "xtend": "4.0.2"
+                "bl": "^1.0.0",
+                "buffer-alloc": "^1.2.0",
+                "end-of-stream": "^1.0.0",
+                "fs-constants": "^1.0.0",
+                "readable-stream": "^2.3.0",
+                "to-buffer": "^1.1.1",
+                "xtend": "^4.0.0"
             }
         },
         "term-size": {
@@ -15642,7 +15661,7 @@
             "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz",
             "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=",
             "requires": {
-                "execa": "0.7.0"
+                "execa": "^0.7.0"
             }
         },
         "terser": {
@@ -15651,9 +15670,9 @@
             "integrity": "sha512-jvNoEQSPXJdssFwqPSgWjsOrb+ELoE+ILpHPKXC83tIxOlh2U75F1KuB2luLD/3a6/7K3Vw5pDn+hvu0C4AzSw==",
             "dev": true,
             "requires": {
-                "commander": "2.20.0",
-                "source-map": "0.6.1",
-                "source-map-support": "0.5.12"
+                "commander": "^2.20.0",
+                "source-map": "~0.6.1",
+                "source-map-support": "~0.5.12"
             },
             "dependencies": {
                 "source-map": {
@@ -15670,16 +15689,16 @@
             "integrity": "sha512-W2YWmxPjjkUcOWa4pBEv4OP4er1aeQJlSo2UhtCFQCuRXEHjOFscO8VyWHj9JLlA0RzQb8Y2/Ta78XZvT54uGg==",
             "dev": true,
             "requires": {
-                "cacache": "11.3.3",
-                "find-cache-dir": "2.1.0",
-                "is-wsl": "1.1.0",
-                "loader-utils": "1.2.3",
-                "schema-utils": "1.0.0",
-                "serialize-javascript": "1.7.0",
-                "source-map": "0.6.1",
-                "terser": "4.1.2",
-                "webpack-sources": "1.3.0",
-                "worker-farm": "1.7.0"
+                "cacache": "^11.3.2",
+                "find-cache-dir": "^2.0.0",
+                "is-wsl": "^1.1.0",
+                "loader-utils": "^1.2.3",
+                "schema-utils": "^1.0.0",
+                "serialize-javascript": "^1.7.0",
+                "source-map": "^0.6.1",
+                "terser": "^4.0.0",
+                "webpack-sources": "^1.3.0",
+                "worker-farm": "^1.7.0"
             },
             "dependencies": {
                 "cacache": {
@@ -15688,20 +15707,20 @@
                     "integrity": "sha512-p8WcneCytvzPxhDvYp31PD039vi77I12W+/KfR9S8AZbaiARFBCpsPJS+9uhWfeBfeAtW7o/4vt3MUqLkbY6nA==",
                     "dev": true,
                     "requires": {
-                        "bluebird": "3.5.5",
-                        "chownr": "1.1.2",
-                        "figgy-pudding": "3.5.1",
-                        "glob": "7.1.4",
-                        "graceful-fs": "4.2.0",
-                        "lru-cache": "5.1.1",
-                        "mississippi": "3.0.0",
-                        "mkdirp": "0.5.1",
-                        "move-concurrently": "1.0.1",
-                        "promise-inflight": "1.0.1",
-                        "rimraf": "2.7.1",
-                        "ssri": "6.0.1",
-                        "unique-filename": "1.1.1",
-                        "y18n": "4.0.0"
+                        "bluebird": "^3.5.5",
+                        "chownr": "^1.1.1",
+                        "figgy-pudding": "^3.5.1",
+                        "glob": "^7.1.4",
+                        "graceful-fs": "^4.1.15",
+                        "lru-cache": "^5.1.1",
+                        "mississippi": "^3.0.0",
+                        "mkdirp": "^0.5.1",
+                        "move-concurrently": "^1.0.1",
+                        "promise-inflight": "^1.0.1",
+                        "rimraf": "^2.6.3",
+                        "ssri": "^6.0.1",
+                        "unique-filename": "^1.1.1",
+                        "y18n": "^4.0.0"
                     },
                     "dependencies": {
                         "rimraf": {
@@ -15710,7 +15729,7 @@
                             "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
                             "dev": true,
                             "requires": {
-                                "glob": "7.1.4"
+                                "glob": "^7.1.3"
                             }
                         }
                     }
@@ -15721,9 +15740,9 @@
                     "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
                     "dev": true,
                     "requires": {
-                        "commondir": "1.0.1",
-                        "make-dir": "2.1.0",
-                        "pkg-dir": "3.0.0"
+                        "commondir": "^1.0.1",
+                        "make-dir": "^2.0.0",
+                        "pkg-dir": "^3.0.0"
                     }
                 },
                 "find-up": {
@@ -15732,7 +15751,7 @@
                     "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
                     "dev": true,
                     "requires": {
-                        "locate-path": "3.0.0"
+                        "locate-path": "^3.0.0"
                     }
                 },
                 "locate-path": {
@@ -15741,8 +15760,8 @@
                     "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
                     "dev": true,
                     "requires": {
-                        "p-locate": "3.0.0",
-                        "path-exists": "3.0.0"
+                        "p-locate": "^3.0.0",
+                        "path-exists": "^3.0.0"
                     }
                 },
                 "lru-cache": {
@@ -15751,7 +15770,7 @@
                     "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
                     "dev": true,
                     "requires": {
-                        "yallist": "3.0.3"
+                        "yallist": "^3.0.2"
                     }
                 },
                 "make-dir": {
@@ -15760,8 +15779,8 @@
                     "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
                     "dev": true,
                     "requires": {
-                        "pify": "4.0.1",
-                        "semver": "5.7.0"
+                        "pify": "^4.0.1",
+                        "semver": "^5.6.0"
                     }
                 },
                 "mississippi": {
@@ -15770,16 +15789,16 @@
                     "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
                     "dev": true,
                     "requires": {
-                        "concat-stream": "1.6.2",
-                        "duplexify": "3.7.1",
-                        "end-of-stream": "1.4.1",
-                        "flush-write-stream": "1.1.1",
-                        "from2": "2.3.0",
-                        "parallel-transform": "1.1.0",
-                        "pump": "3.0.0",
-                        "pumpify": "1.5.1",
-                        "stream-each": "1.2.3",
-                        "through2": "2.0.5"
+                        "concat-stream": "^1.5.0",
+                        "duplexify": "^3.4.2",
+                        "end-of-stream": "^1.1.0",
+                        "flush-write-stream": "^1.0.0",
+                        "from2": "^2.1.0",
+                        "parallel-transform": "^1.1.0",
+                        "pump": "^3.0.0",
+                        "pumpify": "^1.3.3",
+                        "stream-each": "^1.1.0",
+                        "through2": "^2.0.0"
                     }
                 },
                 "p-locate": {
@@ -15788,7 +15807,7 @@
                     "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
                     "dev": true,
                     "requires": {
-                        "p-limit": "2.2.0"
+                        "p-limit": "^2.0.0"
                     }
                 },
                 "pify": {
@@ -15803,7 +15822,7 @@
                     "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
                     "dev": true,
                     "requires": {
-                        "find-up": "3.0.0"
+                        "find-up": "^3.0.0"
                     }
                 },
                 "pump": {
@@ -15812,8 +15831,8 @@
                     "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
                     "dev": true,
                     "requires": {
-                        "end-of-stream": "1.4.1",
-                        "once": "1.4.0"
+                        "end-of-stream": "^1.1.0",
+                        "once": "^1.3.1"
                     }
                 },
                 "schema-utils": {
@@ -15822,9 +15841,9 @@
                     "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
                     "dev": true,
                     "requires": {
-                        "ajv": "6.10.2",
-                        "ajv-errors": "1.0.1",
-                        "ajv-keywords": "3.4.1"
+                        "ajv": "^6.1.0",
+                        "ajv-errors": "^1.0.0",
+                        "ajv-keywords": "^3.1.0"
                     }
                 },
                 "source-map": {
@@ -15839,7 +15858,7 @@
                     "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
                     "dev": true,
                     "requires": {
-                        "figgy-pudding": "3.5.1"
+                        "figgy-pudding": "^3.5.1"
                     }
                 },
                 "y18n": {
@@ -15855,8 +15874,8 @@
             "resolved": "https://registry.npmjs.org/threads/-/threads-0.8.1.tgz",
             "integrity": "sha1-40ARW1lHMW0vfuMSPEwsW/nHbXI=",
             "requires": {
-                "eventemitter3": "2.0.3",
-                "native-promise-only": "0.8.1"
+                "eventemitter3": "^2.0.2",
+                "native-promise-only": "^0.8.1"
             }
         },
         "through": {
@@ -15870,8 +15889,8 @@
             "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
             "dev": true,
             "requires": {
-                "readable-stream": "2.3.6",
-                "xtend": "4.0.2"
+                "readable-stream": "~2.3.6",
+                "xtend": "~4.0.1"
             }
         },
         "thunky": {
@@ -15891,7 +15910,7 @@
             "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==",
             "dev": true,
             "requires": {
-                "setimmediate": "1.0.5"
+                "setimmediate": "^1.0.4"
             }
         },
         "tinycolor2": {
@@ -15925,7 +15944,7 @@
             "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
             "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
             "requires": {
-                "kind-of": "3.2.2"
+                "kind-of": "^3.0.2"
             },
             "dependencies": {
                 "kind-of": {
@@ -15933,7 +15952,7 @@
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
                     "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
-                        "is-buffer": "1.1.6"
+                        "is-buffer": "^1.1.5"
                     }
                 }
             }
@@ -15943,10 +15962,10 @@
             "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
             "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
             "requires": {
-                "define-property": "2.0.2",
-                "extend-shallow": "3.0.2",
-                "regex-not": "1.0.2",
-                "safe-regex": "1.1.0"
+                "define-property": "^2.0.2",
+                "extend-shallow": "^3.0.2",
+                "regex-not": "^1.0.2",
+                "safe-regex": "^1.1.0"
             }
         },
         "to-regex-range": {
@@ -15954,8 +15973,8 @@
             "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
             "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
             "requires": {
-                "is-number": "3.0.0",
-                "repeat-string": "1.6.1"
+                "is-number": "^3.0.0",
+                "repeat-string": "^1.6.1"
             }
         },
         "toidentifier": {
@@ -15973,7 +15992,7 @@
             "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
             "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
             "requires": {
-                "nopt": "1.0.10"
+                "nopt": "~1.0.10"
             },
             "dependencies": {
                 "nopt": {
@@ -15981,7 +16000,7 @@
                     "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
                     "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
                     "requires": {
-                        "abbrev": "1.1.1"
+                        "abbrev": "1"
                     }
                 }
             }
@@ -15991,8 +16010,8 @@
             "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
             "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
             "requires": {
-                "psl": "1.2.0",
-                "punycode": "1.4.1"
+                "psl": "^1.1.24",
+                "punycode": "^1.4.1"
             },
             "dependencies": {
                 "punycode": {
@@ -16008,7 +16027,7 @@
             "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
             "dev": true,
             "requires": {
-                "punycode": "2.1.1"
+                "punycode": "^2.1.0"
             }
         },
         "traverse-chain": {
@@ -16032,7 +16051,7 @@
             "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz",
             "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==",
             "requires": {
-                "glob": "7.1.4"
+                "glob": "^7.1.2"
             }
         },
         "ts-loader": {
@@ -16041,11 +16060,11 @@
             "integrity": "sha512-XYsjfnRQCBum9AMRZpk2rTYSVpdZBpZK+kDh0TeT3kxmQNBDVIeUjdPjY5RZry4eIAb8XHc4gYSUiUWPYvzSRw==",
             "dev": true,
             "requires": {
-                "chalk": "2.4.2",
-                "enhanced-resolve": "4.1.0",
-                "loader-utils": "1.2.3",
-                "micromatch": "3.1.10",
-                "semver": "5.7.0"
+                "chalk": "^2.3.0",
+                "enhanced-resolve": "^4.0.0",
+                "loader-utils": "^1.0.2",
+                "micromatch": "^3.1.4",
+                "semver": "^5.0.1"
             },
             "dependencies": {
                 "ansi-styles": {
@@ -16054,7 +16073,7 @@
                     "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
                     "dev": true,
                     "requires": {
-                        "color-convert": "1.9.3"
+                        "color-convert": "^1.9.0"
                     }
                 },
                 "chalk": {
@@ -16063,9 +16082,9 @@
                     "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
                     "dev": true,
                     "requires": {
-                        "ansi-styles": "3.2.1",
-                        "escape-string-regexp": "1.0.5",
-                        "supports-color": "5.5.0"
+                        "ansi-styles": "^3.2.1",
+                        "escape-string-regexp": "^1.0.5",
+                        "supports-color": "^5.3.0"
                     }
                 },
                 "supports-color": {
@@ -16074,7 +16093,7 @@
                     "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
                     "dev": true,
                     "requires": {
-                        "has-flag": "3.0.0"
+                        "has-flag": "^3.0.0"
                     }
                 }
             }
@@ -16085,14 +16104,14 @@
             "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==",
             "dev": true,
             "requires": {
-                "arrify": "1.0.1",
-                "buffer-from": "1.1.1",
-                "diff": "3.5.0",
-                "make-error": "1.3.5",
-                "minimist": "1.2.0",
-                "mkdirp": "0.5.1",
-                "source-map-support": "0.5.12",
-                "yn": "2.0.0"
+                "arrify": "^1.0.0",
+                "buffer-from": "^1.1.0",
+                "diff": "^3.1.0",
+                "make-error": "^1.1.1",
+                "minimist": "^1.2.0",
+                "mkdirp": "^0.5.1",
+                "source-map-support": "^0.5.6",
+                "yn": "^2.0.0"
             },
             "dependencies": {
                 "diff": {
@@ -16115,18 +16134,18 @@
             "integrity": "sha512-78CptStf6oA5wKkRXQPEMBR5zowhnw2bvCETRMhkz2DsuussA56s6lKgUX4EiMMiPkyYdSm8jkJ875j4eo4nkQ==",
             "dev": true,
             "requires": {
-                "dateformat": "1.0.12",
-                "dynamic-dedupe": "0.3.0",
-                "filewatcher": "3.0.1",
-                "minimist": "1.2.0",
-                "mkdirp": "0.5.1",
-                "node-notifier": "5.4.0",
-                "resolve": "1.11.1",
-                "rimraf": "2.7.1",
-                "source-map-support": "0.5.12",
-                "tree-kill": "1.2.1",
-                "ts-node": "7.0.1",
-                "tsconfig": "7.0.0"
+                "dateformat": "~1.0.4-1.2.3",
+                "dynamic-dedupe": "^0.3.0",
+                "filewatcher": "~3.0.0",
+                "minimist": "^1.1.3",
+                "mkdirp": "^0.5.1",
+                "node-notifier": "^5.4.0",
+                "resolve": "^1.0.0",
+                "rimraf": "^2.6.1",
+                "source-map-support": "^0.5.12",
+                "tree-kill": "^1.2.1",
+                "ts-node": "*",
+                "tsconfig": "^7.0.0"
             },
             "dependencies": {
                 "minimist": {
@@ -16141,7 +16160,7 @@
                     "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
                     "dev": true,
                     "requires": {
-                        "glob": "7.1.4"
+                        "glob": "^7.1.3"
                     }
                 }
             }
@@ -16152,10 +16171,10 @@
             "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==",
             "dev": true,
             "requires": {
-                "@types/strip-bom": "3.0.0",
+                "@types/strip-bom": "^3.0.0",
                 "@types/strip-json-comments": "0.0.30",
-                "strip-bom": "3.0.0",
-                "strip-json-comments": "2.0.1"
+                "strip-bom": "^3.0.0",
+                "strip-json-comments": "^2.0.0"
             },
             "dependencies": {
                 "strip-bom": {
@@ -16178,19 +16197,19 @@
             "integrity": "sha512-Q3kXkuDEijQ37nXZZLKErssQVnwCV/+23gFEMROi8IlbaBG6tXqLPQJ5Wjcyt/yHPKBC+hD5SzuGaMora+ZS6w==",
             "dev": true,
             "requires": {
-                "@babel/code-frame": "7.5.5",
-                "builtin-modules": "1.1.1",
-                "chalk": "2.4.2",
-                "commander": "2.20.0",
-                "diff": "3.5.0",
-                "glob": "7.1.4",
-                "js-yaml": "3.13.1",
-                "minimatch": "3.0.4",
-                "mkdirp": "0.5.1",
-                "resolve": "1.11.1",
-                "semver": "5.7.0",
-                "tslib": "1.10.0",
-                "tsutils": "2.29.0"
+                "@babel/code-frame": "^7.0.0",
+                "builtin-modules": "^1.1.1",
+                "chalk": "^2.3.0",
+                "commander": "^2.12.1",
+                "diff": "^3.2.0",
+                "glob": "^7.1.1",
+                "js-yaml": "^3.13.1",
+                "minimatch": "^3.0.4",
+                "mkdirp": "^0.5.1",
+                "resolve": "^1.3.2",
+                "semver": "^5.3.0",
+                "tslib": "^1.8.0",
+                "tsutils": "^2.29.0"
             },
             "dependencies": {
                 "ansi-styles": {
@@ -16199,7 +16218,7 @@
                     "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
                     "dev": true,
                     "requires": {
-                        "color-convert": "1.9.3"
+                        "color-convert": "^1.9.0"
                     }
                 },
                 "chalk": {
@@ -16208,9 +16227,9 @@
                     "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
                     "dev": true,
                     "requires": {
-                        "ansi-styles": "3.2.1",
-                        "escape-string-regexp": "1.0.5",
-                        "supports-color": "5.5.0"
+                        "ansi-styles": "^3.2.1",
+                        "escape-string-regexp": "^1.0.5",
+                        "supports-color": "^5.3.0"
                     }
                 },
                 "diff": {
@@ -16225,7 +16244,7 @@
                     "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
                     "dev": true,
                     "requires": {
-                        "has-flag": "3.0.0"
+                        "has-flag": "^3.0.0"
                     }
                 }
             }
@@ -16236,11 +16255,11 @@
             "integrity": "sha512-jBHNNppXut6SgZ7CsTBh+6oMwVum9n8azbmcYSeMlsABhWWoHwjq631vIFXef3VSd75cCdX3rc6kstsB7rSVVw==",
             "dev": true,
             "requires": {
-                "loader-utils": "1.2.3",
-                "mkdirp": "0.5.1",
-                "object-assign": "4.1.1",
-                "rimraf": "2.7.1",
-                "semver": "5.7.0"
+                "loader-utils": "^1.0.2",
+                "mkdirp": "^0.5.1",
+                "object-assign": "^4.1.1",
+                "rimraf": "^2.4.4",
+                "semver": "^5.3.0"
             },
             "dependencies": {
                 "rimraf": {
@@ -16249,7 +16268,7 @@
                     "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
                     "dev": true,
                     "requires": {
-                        "glob": "7.1.4"
+                        "glob": "^7.1.3"
                     }
                 }
             }
@@ -16260,7 +16279,7 @@
             "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
             "dev": true,
             "requires": {
-                "tslib": "1.10.0"
+                "tslib": "^1.8.1"
             }
         },
         "tty-browserify": {
@@ -16274,7 +16293,7 @@
             "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
             "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
             "requires": {
-                "safe-buffer": "5.1.2"
+                "safe-buffer": "^5.0.1"
             }
         },
         "tweetnacl": {
@@ -16294,7 +16313,7 @@
             "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
             "dev": true,
             "requires": {
-                "prelude-ls": "1.1.2"
+                "prelude-ls": "~1.1.2"
             }
         },
         "type-detect": {
@@ -16308,7 +16327,7 @@
             "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
             "requires": {
                 "media-typer": "0.3.0",
-                "mime-types": "2.1.24"
+                "mime-types": "~2.1.24"
             }
         },
         "typed-styles": {
@@ -16347,9 +16366,9 @@
             "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
             "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
             "requires": {
-                "source-map": "0.5.7",
-                "uglify-to-browserify": "1.0.2",
-                "yargs": "3.10.0"
+                "source-map": "~0.5.1",
+                "uglify-to-browserify": "~1.0.0",
+                "yargs": "~3.10.0"
             },
             "dependencies": {
                 "camelcase": {
@@ -16362,8 +16381,8 @@
                     "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
                     "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
                     "requires": {
-                        "center-align": "0.1.3",
-                        "right-align": "0.1.3",
+                        "center-align": "^0.1.1",
+                        "right-align": "^0.1.1",
                         "wordwrap": "0.0.2"
                     }
                 },
@@ -16372,9 +16391,9 @@
                     "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
                     "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
                     "requires": {
-                        "camelcase": "1.2.1",
-                        "cliui": "2.1.0",
-                        "decamelize": "1.2.0",
+                        "camelcase": "^1.0.2",
+                        "cliui": "^2.1.0",
+                        "decamelize": "^1.0.0",
                         "window-size": "0.1.0"
                     }
                 }
@@ -16391,7 +16410,7 @@
             "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
             "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
             "requires": {
-                "random-bytes": "1.0.0"
+                "random-bytes": "~1.0.0"
             }
         },
         "uid2": {
@@ -16404,7 +16423,7 @@
             "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-5.1.0.tgz",
             "integrity": "sha512-5FXYaFANKaafg4IVZXUNtGyzsnYEvqlr9wQ3WpZxFpEUxl29A3H6Q4G1Dnnorvq9TGOGATBApWR4YpLAh+F5hw==",
             "requires": {
-                "invariant": "2.2.4"
+                "invariant": "^2.2.4"
             }
         },
         "undefsafe": {
@@ -16412,7 +16431,7 @@
             "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz",
             "integrity": "sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=",
             "requires": {
-                "debug": "2.6.9"
+                "debug": "^2.2.0"
             }
         },
         "underscore": {
@@ -16425,10 +16444,10 @@
             "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
             "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
             "requires": {
-                "arr-union": "3.1.0",
-                "get-value": "2.0.6",
-                "is-extendable": "0.1.1",
-                "set-value": "2.0.1"
+                "arr-union": "^3.1.0",
+                "get-value": "^2.0.6",
+                "is-extendable": "^0.1.1",
+                "set-value": "^2.0.1"
             }
         },
         "uniq": {
@@ -16443,7 +16462,7 @@
             "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
             "dev": true,
             "requires": {
-                "unique-slug": "2.0.2"
+                "unique-slug": "^2.0.0"
             }
         },
         "unique-slug": {
@@ -16452,7 +16471,7 @@
             "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==",
             "dev": true,
             "requires": {
-                "imurmurhash": "0.1.4"
+                "imurmurhash": "^0.1.4"
             }
         },
         "unique-string": {
@@ -16460,7 +16479,7 @@
             "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz",
             "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=",
             "requires": {
-                "crypto-random-string": "1.0.0"
+                "crypto-random-string": "^1.0.0"
             }
         },
         "unpack-string": {
@@ -16478,8 +16497,8 @@
             "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
             "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
             "requires": {
-                "has-value": "0.3.1",
-                "isobject": "3.0.1"
+                "has-value": "^0.3.1",
+                "isobject": "^3.0.0"
             },
             "dependencies": {
                 "has-value": {
@@ -16487,9 +16506,9 @@
                     "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
                     "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
                     "requires": {
-                        "get-value": "2.0.6",
-                        "has-values": "0.1.4",
-                        "isobject": "2.1.0"
+                        "get-value": "^2.0.3",
+                        "has-values": "^0.1.4",
+                        "isobject": "^2.0.0"
                     },
                     "dependencies": {
                         "isobject": {
@@ -16524,16 +16543,16 @@
             "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz",
             "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==",
             "requires": {
-                "boxen": "1.3.0",
-                "chalk": "2.4.2",
-                "configstore": "3.1.2",
-                "import-lazy": "2.1.0",
-                "is-ci": "1.2.1",
-                "is-installed-globally": "0.1.0",
-                "is-npm": "1.0.0",
-                "latest-version": "3.1.0",
-                "semver-diff": "2.1.0",
-                "xdg-basedir": "3.0.0"
+                "boxen": "^1.2.1",
+                "chalk": "^2.0.1",
+                "configstore": "^3.0.0",
+                "import-lazy": "^2.1.0",
+                "is-ci": "^1.0.10",
+                "is-installed-globally": "^0.1.0",
+                "is-npm": "^1.0.0",
+                "latest-version": "^3.0.0",
+                "semver-diff": "^2.0.0",
+                "xdg-basedir": "^3.0.0"
             },
             "dependencies": {
                 "ansi-styles": {
@@ -16541,7 +16560,7 @@
                     "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
                     "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
                     "requires": {
-                        "color-convert": "1.9.3"
+                        "color-convert": "^1.9.0"
                     }
                 },
                 "chalk": {
@@ -16549,9 +16568,9 @@
                     "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
                     "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
                     "requires": {
-                        "ansi-styles": "3.2.1",
-                        "escape-string-regexp": "1.0.5",
-                        "supports-color": "5.5.0"
+                        "ansi-styles": "^3.2.1",
+                        "escape-string-regexp": "^1.0.5",
+                        "supports-color": "^5.3.0"
                     }
                 },
                 "supports-color": {
@@ -16559,7 +16578,7 @@
                     "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
                     "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
                     "requires": {
-                        "has-flag": "3.0.0"
+                        "has-flag": "^3.0.0"
                     }
                 }
             }
@@ -16569,7 +16588,7 @@
             "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
             "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
             "requires": {
-                "punycode": "2.1.1"
+                "punycode": "^2.1.0"
             }
         },
         "urix": {
@@ -16600,9 +16619,9 @@
             "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz",
             "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==",
             "requires": {
-                "loader-utils": "1.2.3",
-                "mime": "2.4.4",
-                "schema-utils": "1.0.0"
+                "loader-utils": "^1.1.0",
+                "mime": "^2.0.3",
+                "schema-utils": "^1.0.0"
             },
             "dependencies": {
                 "mime": {
@@ -16615,9 +16634,9 @@
                     "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
                     "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
                     "requires": {
-                        "ajv": "6.10.2",
-                        "ajv-errors": "1.0.1",
-                        "ajv-keywords": "3.4.1"
+                        "ajv": "^6.1.0",
+                        "ajv-errors": "^1.0.0",
+                        "ajv-keywords": "^3.1.0"
                     }
                 }
             }
@@ -16628,8 +16647,8 @@
             "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==",
             "dev": true,
             "requires": {
-                "querystringify": "2.1.1",
-                "requires-port": "1.0.0"
+                "querystringify": "^2.1.1",
+                "requires-port": "^1.0.0"
             }
         },
         "url-parse-lax": {
@@ -16637,7 +16656,7 @@
             "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
             "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=",
             "requires": {
-                "prepend-http": "1.0.4"
+                "prepend-http": "^1.0.1"
             }
         },
         "url-template": {
@@ -16685,8 +16704,8 @@
             "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
             "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
             "requires": {
-                "spdx-correct": "3.1.0",
-                "spdx-expression-parse": "3.0.0"
+                "spdx-correct": "^3.0.0",
+                "spdx-expression-parse": "^3.0.0"
             }
         },
         "validator": {
@@ -16704,9 +16723,9 @@
             "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
             "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
             "requires": {
-                "assert-plus": "1.0.0",
+                "assert-plus": "^1.0.0",
                 "core-util-is": "1.0.2",
-                "extsprintf": "1.3.0"
+                "extsprintf": "^1.2.0"
             },
             "dependencies": {
                 "assert-plus": {
@@ -16733,7 +16752,7 @@
             "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=",
             "dev": true,
             "requires": {
-                "browser-process-hrtime": "0.1.3"
+                "browser-process-hrtime": "^0.1.2"
             }
         },
         "w3c-keyname": {
@@ -16747,9 +16766,9 @@
             "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==",
             "dev": true,
             "requires": {
-                "domexception": "1.0.1",
-                "webidl-conversions": "4.0.2",
-                "xml-name-validator": "3.0.0"
+                "domexception": "^1.0.1",
+                "webidl-conversions": "^4.0.2",
+                "xml-name-validator": "^3.0.0"
             }
         },
         "warning": {
@@ -16757,7 +16776,7 @@
             "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
             "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
             "requires": {
-                "loose-envify": "1.4.0"
+                "loose-envify": "^1.0.0"
             }
         },
         "watchpack": {
@@ -16766,9 +16785,9 @@
             "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==",
             "dev": true,
             "requires": {
-                "chokidar": "2.1.6",
-                "graceful-fs": "4.2.0",
-                "neo-async": "2.6.1"
+                "chokidar": "^2.0.2",
+                "graceful-fs": "^4.1.2",
+                "neo-async": "^2.5.0"
             }
         },
         "wbuf": {
@@ -16777,7 +16796,7 @@
             "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==",
             "dev": true,
             "requires": {
-                "minimalistic-assert": "1.0.1"
+                "minimalistic-assert": "^1.0.0"
             }
         },
         "webidl-conversions": {
@@ -16796,25 +16815,25 @@
                 "@webassemblyjs/helper-module-context": "1.8.5",
                 "@webassemblyjs/wasm-edit": "1.8.5",
                 "@webassemblyjs/wasm-parser": "1.8.5",
-                "acorn": "6.2.1",
-                "ajv": "6.10.2",
-                "ajv-keywords": "3.4.1",
-                "chrome-trace-event": "1.0.2",
-                "enhanced-resolve": "4.1.0",
-                "eslint-scope": "4.0.3",
-                "json-parse-better-errors": "1.0.2",
-                "loader-runner": "2.4.0",
-                "loader-utils": "1.2.3",
-                "memory-fs": "0.4.1",
-                "micromatch": "3.1.10",
-                "mkdirp": "0.5.1",
-                "neo-async": "2.6.1",
-                "node-libs-browser": "2.2.1",
-                "schema-utils": "1.0.0",
-                "tapable": "1.1.3",
-                "terser-webpack-plugin": "1.3.0",
-                "watchpack": "1.6.0",
-                "webpack-sources": "1.3.0"
+                "acorn": "^6.2.0",
+                "ajv": "^6.1.0",
+                "ajv-keywords": "^3.1.0",
+                "chrome-trace-event": "^1.0.0",
+                "enhanced-resolve": "^4.1.0",
+                "eslint-scope": "^4.0.0",
+                "json-parse-better-errors": "^1.0.2",
+                "loader-runner": "^2.3.0",
+                "loader-utils": "^1.1.0",
+                "memory-fs": "~0.4.1",
+                "micromatch": "^3.1.8",
+                "mkdirp": "~0.5.0",
+                "neo-async": "^2.5.0",
+                "node-libs-browser": "^2.0.0",
+                "schema-utils": "^1.0.0",
+                "tapable": "^1.1.0",
+                "terser-webpack-plugin": "^1.1.0",
+                "watchpack": "^1.5.0",
+                "webpack-sources": "^1.3.0"
             },
             "dependencies": {
                 "acorn": {
@@ -16829,9 +16848,9 @@
                     "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
                     "dev": true,
                     "requires": {
-                        "ajv": "6.10.2",
-                        "ajv-errors": "1.0.1",
-                        "ajv-keywords": "3.4.1"
+                        "ajv": "^6.1.0",
+                        "ajv-errors": "^1.0.0",
+                        "ajv-keywords": "^3.1.0"
                     }
                 }
             }
@@ -16867,7 +16886,7 @@
                     "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
                     "dev": true,
                     "requires": {
-                        "color-convert": "1.9.3"
+                        "color-convert": "^1.9.0"
                     }
                 },
                 "camelcase": {
@@ -16882,9 +16901,9 @@
                     "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
                     "dev": true,
                     "requires": {
-                        "ansi-styles": "3.2.1",
-                        "escape-string-regexp": "1.0.5",
-                        "supports-color": "5.5.0"
+                        "ansi-styles": "^3.2.1",
+                        "escape-string-regexp": "^1.0.5",
+                        "supports-color": "^5.3.0"
                     },
                     "dependencies": {
                         "supports-color": {
@@ -16893,7 +16912,7 @@
                             "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
                             "dev": true,
                             "requires": {
-                                "has-flag": "3.0.0"
+                                "has-flag": "^3.0.0"
                             }
                         }
                     }
@@ -16904,9 +16923,9 @@
                     "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
                     "dev": true,
                     "requires": {
-                        "string-width": "3.1.0",
-                        "strip-ansi": "5.2.0",
-                        "wrap-ansi": "5.1.0"
+                        "string-width": "^3.1.0",
+                        "strip-ansi": "^5.2.0",
+                        "wrap-ansi": "^5.1.0"
                     }
                 },
                 "cross-spawn": {
@@ -16915,11 +16934,11 @@
                     "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
                     "dev": true,
                     "requires": {
-                        "nice-try": "1.0.5",
-                        "path-key": "2.0.1",
-                        "semver": "5.7.0",
-                        "shebang-command": "1.2.0",
-                        "which": "1.3.1"
+                        "nice-try": "^1.0.4",
+                        "path-key": "^2.0.1",
+                        "semver": "^5.5.0",
+                        "shebang-command": "^1.2.0",
+                        "which": "^1.2.9"
                     }
                 },
                 "execa": {
@@ -16928,13 +16947,13 @@
                     "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
                     "dev": true,
                     "requires": {
-                        "cross-spawn": "6.0.5",
-                        "get-stream": "4.1.0",
-                        "is-stream": "1.1.0",
-                        "npm-run-path": "2.0.2",
-                        "p-finally": "1.0.0",
-                        "signal-exit": "3.0.2",
-                        "strip-eof": "1.0.0"
+                        "cross-spawn": "^6.0.0",
+                        "get-stream": "^4.0.0",
+                        "is-stream": "^1.1.0",
+                        "npm-run-path": "^2.0.0",
+                        "p-finally": "^1.0.0",
+                        "signal-exit": "^3.0.0",
+                        "strip-eof": "^1.0.0"
                     }
                 },
                 "find-up": {
@@ -16943,7 +16962,7 @@
                     "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
                     "dev": true,
                     "requires": {
-                        "locate-path": "3.0.0"
+                        "locate-path": "^3.0.0"
                     }
                 },
                 "get-caller-file": {
@@ -16958,7 +16977,7 @@
                     "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
                     "dev": true,
                     "requires": {
-                        "pump": "3.0.0"
+                        "pump": "^3.0.0"
                     }
                 },
                 "invert-kv": {
@@ -16979,7 +16998,7 @@
                     "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
                     "dev": true,
                     "requires": {
-                        "invert-kv": "2.0.0"
+                        "invert-kv": "^2.0.0"
                     }
                 },
                 "locate-path": {
@@ -16988,8 +17007,8 @@
                     "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
                     "dev": true,
                     "requires": {
-                        "p-locate": "3.0.0",
-                        "path-exists": "3.0.0"
+                        "p-locate": "^3.0.0",
+                        "path-exists": "^3.0.0"
                     }
                 },
                 "os-locale": {
@@ -16998,9 +17017,9 @@
                     "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
                     "dev": true,
                     "requires": {
-                        "execa": "1.0.0",
-                        "lcid": "2.0.0",
-                        "mem": "4.3.0"
+                        "execa": "^1.0.0",
+                        "lcid": "^2.0.0",
+                        "mem": "^4.0.0"
                     }
                 },
                 "p-locate": {
@@ -17009,7 +17028,7 @@
                     "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
                     "dev": true,
                     "requires": {
-                        "p-limit": "2.2.0"
+                        "p-limit": "^2.0.0"
                     }
                 },
                 "pump": {
@@ -17018,8 +17037,8 @@
                     "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
                     "dev": true,
                     "requires": {
-                        "end-of-stream": "1.4.1",
-                        "once": "1.4.0"
+                        "end-of-stream": "^1.1.0",
+                        "once": "^1.3.1"
                     }
                 },
                 "require-main-filename": {
@@ -17034,9 +17053,9 @@
                     "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
                     "dev": true,
                     "requires": {
-                        "emoji-regex": "7.0.3",
-                        "is-fullwidth-code-point": "2.0.0",
-                        "strip-ansi": "5.2.0"
+                        "emoji-regex": "^7.0.1",
+                        "is-fullwidth-code-point": "^2.0.0",
+                        "strip-ansi": "^5.1.0"
                     }
                 },
                 "strip-ansi": {
@@ -17045,7 +17064,7 @@
                     "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
                     "dev": true,
                     "requires": {
-                        "ansi-regex": "4.1.0"
+                        "ansi-regex": "^4.1.0"
                     }
                 },
                 "supports-color": {
@@ -17054,7 +17073,7 @@
                     "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
                     "dev": true,
                     "requires": {
-                        "has-flag": "3.0.0"
+                        "has-flag": "^3.0.0"
                     }
                 },
                 "which-module": {
@@ -17069,9 +17088,9 @@
                     "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
                     "dev": true,
                     "requires": {
-                        "ansi-styles": "3.2.1",
-                        "string-width": "3.1.0",
-                        "strip-ansi": "5.2.0"
+                        "ansi-styles": "^3.2.0",
+                        "string-width": "^3.0.0",
+                        "strip-ansi": "^5.0.0"
                     }
                 },
                 "y18n": {
@@ -17086,17 +17105,17 @@
                     "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==",
                     "dev": true,
                     "requires": {
-                        "cliui": "5.0.0",
-                        "find-up": "3.0.0",
-                        "get-caller-file": "2.0.5",
-                        "os-locale": "3.1.0",
-                        "require-directory": "2.1.1",
-                        "require-main-filename": "2.0.0",
-                        "set-blocking": "2.0.0",
-                        "string-width": "3.1.0",
-                        "which-module": "2.0.0",
-                        "y18n": "4.0.0",
-                        "yargs-parser": "13.1.1"
+                        "cliui": "^5.0.0",
+                        "find-up": "^3.0.0",
+                        "get-caller-file": "^2.0.1",
+                        "os-locale": "^3.1.0",
+                        "require-directory": "^2.1.1",
+                        "require-main-filename": "^2.0.0",
+                        "set-blocking": "^2.0.0",
+                        "string-width": "^3.0.0",
+                        "which-module": "^2.0.0",
+                        "y18n": "^4.0.0",
+                        "yargs-parser": "^13.1.0"
                     }
                 },
                 "yargs-parser": {
@@ -17105,8 +17124,8 @@
                     "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
                     "dev": true,
                     "requires": {
-                        "camelcase": "5.3.1",
-                        "decamelize": "1.2.0"
+                        "camelcase": "^5.0.0",
+                        "decamelize": "^1.2.0"
                     }
                 }
             }
@@ -17117,10 +17136,10 @@
             "integrity": "sha512-qvDesR1QZRIAZHOE3iQ4CXLZZSQ1lAUsSpnQmlB1PBfoN/xdRjmge3Dok0W4IdaVLJOGJy3sGI4sZHwjRU0PCA==",
             "dev": true,
             "requires": {
-                "memory-fs": "0.4.1",
-                "mime": "2.4.4",
-                "range-parser": "1.2.1",
-                "webpack-log": "2.0.0"
+                "memory-fs": "^0.4.1",
+                "mime": "^2.4.2",
+                "range-parser": "^1.2.1",
+                "webpack-log": "^2.0.0"
             },
             "dependencies": {
                 "mime": {
@@ -17135,8 +17154,8 @@
                     "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==",
                     "dev": true,
                     "requires": {
-                        "ansi-colors": "3.2.4",
-                        "uuid": "3.3.2"
+                        "ansi-colors": "^3.0.0",
+                        "uuid": "^3.3.2"
                     }
                 }
             }
@@ -17148,35 +17167,35 @@
             "dev": true,
             "requires": {
                 "ansi-html": "0.0.7",
-                "bonjour": "3.5.0",
-                "chokidar": "2.1.6",
-                "compression": "1.7.4",
-                "connect-history-api-fallback": "1.6.0",
-                "debug": "4.1.1",
-                "del": "4.1.1",
-                "express": "4.17.1",
-                "html-entities": "1.2.1",
-                "http-proxy-middleware": "0.19.1",
-                "import-local": "2.0.0",
-                "internal-ip": "4.3.0",
-                "ip": "1.1.5",
-                "killable": "1.0.1",
-                "loglevel": "1.6.3",
-                "opn": "5.5.0",
-                "p-retry": "3.0.1",
-                "portfinder": "1.0.21",
-                "schema-utils": "1.0.0",
-                "selfsigned": "1.10.4",
-                "semver": "6.2.0",
-                "serve-index": "1.9.1",
+                "bonjour": "^3.5.0",
+                "chokidar": "^2.1.6",
+                "compression": "^1.7.4",
+                "connect-history-api-fallback": "^1.6.0",
+                "debug": "^4.1.1",
+                "del": "^4.1.1",
+                "express": "^4.17.1",
+                "html-entities": "^1.2.1",
+                "http-proxy-middleware": "^0.19.1",
+                "import-local": "^2.0.0",
+                "internal-ip": "^4.3.0",
+                "ip": "^1.1.5",
+                "killable": "^1.0.1",
+                "loglevel": "^1.6.3",
+                "opn": "^5.5.0",
+                "p-retry": "^3.0.1",
+                "portfinder": "^1.0.20",
+                "schema-utils": "^1.0.0",
+                "selfsigned": "^1.10.4",
+                "semver": "^6.1.1",
+                "serve-index": "^1.9.1",
                 "sockjs": "0.3.19",
                 "sockjs-client": "1.3.0",
-                "spdy": "4.0.0",
-                "strip-ansi": "3.0.1",
-                "supports-color": "6.1.0",
-                "url": "0.11.0",
-                "webpack-dev-middleware": "3.7.0",
-                "webpack-log": "2.0.0",
+                "spdy": "^4.0.0",
+                "strip-ansi": "^3.0.1",
+                "supports-color": "^6.1.0",
+                "url": "^0.11.0",
+                "webpack-dev-middleware": "^3.7.0",
+                "webpack-log": "^2.0.0",
                 "yargs": "12.0.5"
             },
             "dependencies": {
@@ -17198,9 +17217,9 @@
                     "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
                     "dev": true,
                     "requires": {
-                        "string-width": "2.1.1",
-                        "strip-ansi": "4.0.0",
-                        "wrap-ansi": "2.1.0"
+                        "string-width": "^2.1.1",
+                        "strip-ansi": "^4.0.0",
+                        "wrap-ansi": "^2.0.0"
                     },
                     "dependencies": {
                         "strip-ansi": {
@@ -17209,7 +17228,7 @@
                             "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
                             "dev": true,
                             "requires": {
-                                "ansi-regex": "3.0.0"
+                                "ansi-regex": "^3.0.0"
                             }
                         }
                     }
@@ -17220,11 +17239,11 @@
                     "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
                     "dev": true,
                     "requires": {
-                        "nice-try": "1.0.5",
-                        "path-key": "2.0.1",
-                        "semver": "5.7.0",
-                        "shebang-command": "1.2.0",
-                        "which": "1.3.1"
+                        "nice-try": "^1.0.4",
+                        "path-key": "^2.0.1",
+                        "semver": "^5.5.0",
+                        "shebang-command": "^1.2.0",
+                        "which": "^1.2.9"
                     },
                     "dependencies": {
                         "semver": {
@@ -17241,7 +17260,7 @@
                     "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
                     "dev": true,
                     "requires": {
-                        "ms": "2.1.2"
+                        "ms": "^2.1.1"
                     }
                 },
                 "execa": {
@@ -17250,13 +17269,13 @@
                     "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
                     "dev": true,
                     "requires": {
-                        "cross-spawn": "6.0.5",
-                        "get-stream": "4.1.0",
-                        "is-stream": "1.1.0",
-                        "npm-run-path": "2.0.2",
-                        "p-finally": "1.0.0",
-                        "signal-exit": "3.0.2",
-                        "strip-eof": "1.0.0"
+                        "cross-spawn": "^6.0.0",
+                        "get-stream": "^4.0.0",
+                        "is-stream": "^1.1.0",
+                        "npm-run-path": "^2.0.0",
+                        "p-finally": "^1.0.0",
+                        "signal-exit": "^3.0.0",
+                        "strip-eof": "^1.0.0"
                     }
                 },
                 "find-up": {
@@ -17265,7 +17284,7 @@
                     "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
                     "dev": true,
                     "requires": {
-                        "locate-path": "3.0.0"
+                        "locate-path": "^3.0.0"
                     }
                 },
                 "get-stream": {
@@ -17274,7 +17293,7 @@
                     "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
                     "dev": true,
                     "requires": {
-                        "pump": "3.0.0"
+                        "pump": "^3.0.0"
                     }
                 },
                 "invert-kv": {
@@ -17295,7 +17314,7 @@
                     "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
                     "dev": true,
                     "requires": {
-                        "invert-kv": "2.0.0"
+                        "invert-kv": "^2.0.0"
                     }
                 },
                 "locate-path": {
@@ -17304,8 +17323,8 @@
                     "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
                     "dev": true,
                     "requires": {
-                        "p-locate": "3.0.0",
-                        "path-exists": "3.0.0"
+                        "p-locate": "^3.0.0",
+                        "path-exists": "^3.0.0"
                     }
                 },
                 "ms": {
@@ -17320,9 +17339,9 @@
                     "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
                     "dev": true,
                     "requires": {
-                        "execa": "1.0.0",
-                        "lcid": "2.0.0",
-                        "mem": "4.3.0"
+                        "execa": "^1.0.0",
+                        "lcid": "^2.0.0",
+                        "mem": "^4.0.0"
                     }
                 },
                 "p-locate": {
@@ -17331,7 +17350,7 @@
                     "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
                     "dev": true,
                     "requires": {
-                        "p-limit": "2.2.0"
+                        "p-limit": "^2.0.0"
                     }
                 },
                 "pump": {
@@ -17340,8 +17359,8 @@
                     "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
                     "dev": true,
                     "requires": {
-                        "end-of-stream": "1.4.1",
-                        "once": "1.4.0"
+                        "end-of-stream": "^1.1.0",
+                        "once": "^1.3.1"
                     }
                 },
                 "schema-utils": {
@@ -17350,9 +17369,9 @@
                     "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
                     "dev": true,
                     "requires": {
-                        "ajv": "6.10.2",
-                        "ajv-errors": "1.0.1",
-                        "ajv-keywords": "3.4.1"
+                        "ajv": "^6.1.0",
+                        "ajv-errors": "^1.0.0",
+                        "ajv-keywords": "^3.1.0"
                     }
                 },
                 "semver": {
@@ -17367,8 +17386,8 @@
                     "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
                     "dev": true,
                     "requires": {
-                        "is-fullwidth-code-point": "2.0.0",
-                        "strip-ansi": "4.0.0"
+                        "is-fullwidth-code-point": "^2.0.0",
+                        "strip-ansi": "^4.0.0"
                     },
                     "dependencies": {
                         "strip-ansi": {
@@ -17377,7 +17396,7 @@
                             "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
                             "dev": true,
                             "requires": {
-                                "ansi-regex": "3.0.0"
+                                "ansi-regex": "^3.0.0"
                             }
                         }
                     }
@@ -17388,7 +17407,7 @@
                     "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
                     "dev": true,
                     "requires": {
-                        "has-flag": "3.0.0"
+                        "has-flag": "^3.0.0"
                     }
                 },
                 "webpack-log": {
@@ -17397,8 +17416,8 @@
                     "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==",
                     "dev": true,
                     "requires": {
-                        "ansi-colors": "3.2.4",
-                        "uuid": "3.3.2"
+                        "ansi-colors": "^3.0.0",
+                        "uuid": "^3.3.2"
                     }
                 },
                 "which-module": {
@@ -17413,18 +17432,18 @@
                     "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
                     "dev": true,
                     "requires": {
-                        "cliui": "4.1.0",
-                        "decamelize": "1.2.0",
-                        "find-up": "3.0.0",
-                        "get-caller-file": "1.0.3",
-                        "os-locale": "3.1.0",
-                        "require-directory": "2.1.1",
-                        "require-main-filename": "1.0.1",
-                        "set-blocking": "2.0.0",
-                        "string-width": "2.1.1",
-                        "which-module": "2.0.0",
-                        "y18n": "3.2.1",
-                        "yargs-parser": "11.1.1"
+                        "cliui": "^4.0.0",
+                        "decamelize": "^1.2.0",
+                        "find-up": "^3.0.0",
+                        "get-caller-file": "^1.0.1",
+                        "os-locale": "^3.0.0",
+                        "require-directory": "^2.1.1",
+                        "require-main-filename": "^1.0.1",
+                        "set-blocking": "^2.0.0",
+                        "string-width": "^2.0.0",
+                        "which-module": "^2.0.0",
+                        "y18n": "^3.2.1 || ^4.0.0",
+                        "yargs-parser": "^11.1.1"
                     }
                 },
                 "yargs-parser": {
@@ -17433,8 +17452,8 @@
                     "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
                     "dev": true,
                     "requires": {
-                        "camelcase": "5.3.1",
-                        "decamelize": "1.2.0"
+                        "camelcase": "^5.0.0",
+                        "decamelize": "^1.2.0"
                     }
                 }
             }
@@ -17446,9 +17465,9 @@
             "dev": true,
             "requires": {
                 "ansi-html": "0.0.7",
-                "html-entities": "1.2.1",
-                "querystring": "0.2.0",
-                "strip-ansi": "3.0.1"
+                "html-entities": "^1.2.0",
+                "querystring": "^0.2.0",
+                "strip-ansi": "^3.0.0"
             }
         },
         "webpack-log": {
@@ -17457,10 +17476,10 @@
             "integrity": "sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA==",
             "dev": true,
             "requires": {
-                "chalk": "2.4.2",
-                "log-symbols": "2.2.0",
-                "loglevelnext": "1.0.5",
-                "uuid": "3.3.2"
+                "chalk": "^2.1.0",
+                "log-symbols": "^2.1.0",
+                "loglevelnext": "^1.0.1",
+                "uuid": "^3.1.0"
             },
             "dependencies": {
                 "ansi-styles": {
@@ -17469,7 +17488,7 @@
                     "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
                     "dev": true,
                     "requires": {
-                        "color-convert": "1.9.3"
+                        "color-convert": "^1.9.0"
                     }
                 },
                 "chalk": {
@@ -17478,9 +17497,9 @@
                     "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
                     "dev": true,
                     "requires": {
-                        "ansi-styles": "3.2.1",
-                        "escape-string-regexp": "1.0.5",
-                        "supports-color": "5.5.0"
+                        "ansi-styles": "^3.2.1",
+                        "escape-string-regexp": "^1.0.5",
+                        "supports-color": "^5.3.0"
                     }
                 },
                 "supports-color": {
@@ -17489,7 +17508,7 @@
                     "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
                     "dev": true,
                     "requires": {
-                        "has-flag": "3.0.0"
+                        "has-flag": "^3.0.0"
                     }
                 }
             }
@@ -17500,8 +17519,8 @@
             "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==",
             "dev": true,
             "requires": {
-                "source-list-map": "2.0.1",
-                "source-map": "0.6.1"
+                "source-list-map": "^2.0.0",
+                "source-map": "~0.6.1"
             },
             "dependencies": {
                 "source-map": {
@@ -17518,9 +17537,9 @@
             "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==",
             "dev": true,
             "requires": {
-                "http-parser-js": "0.4.10",
-                "safe-buffer": "5.1.2",
-                "websocket-extensions": "0.1.3"
+                "http-parser-js": ">=0.4.0 <0.4.11",
+                "safe-buffer": ">=5.1.0",
+                "websocket-extensions": ">=0.1.1"
             }
         },
         "websocket-extensions": {
@@ -17555,9 +17574,9 @@
             "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==",
             "dev": true,
             "requires": {
-                "lodash.sortby": "4.7.0",
-                "tr46": "1.0.1",
-                "webidl-conversions": "4.0.2"
+                "lodash.sortby": "^4.7.0",
+                "tr46": "^1.0.1",
+                "webidl-conversions": "^4.0.2"
             }
         },
         "which": {
@@ -17565,7 +17584,7 @@
             "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
             "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
             "requires": {
-                "isexe": "2.0.0"
+                "isexe": "^2.0.0"
             }
         },
         "which-module": {
@@ -17583,7 +17602,7 @@
             "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
             "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
             "requires": {
-                "string-width": "1.0.2"
+                "string-width": "^1.0.2 || 2"
             }
         },
         "widest-line": {
@@ -17591,7 +17610,7 @@
             "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
             "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==",
             "requires": {
-                "string-width": "2.1.1"
+                "string-width": "^2.1.1"
             },
             "dependencies": {
                 "ansi-regex": {
@@ -17609,8 +17628,8 @@
                     "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
                     "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
                     "requires": {
-                        "is-fullwidth-code-point": "2.0.0",
-                        "strip-ansi": "4.0.0"
+                        "is-fullwidth-code-point": "^2.0.0",
+                        "strip-ansi": "^4.0.0"
                     }
                 },
                 "strip-ansi": {
@@ -17618,7 +17637,7 @@
                     "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
                     "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
                     "requires": {
-                        "ansi-regex": "3.0.0"
+                        "ansi-regex": "^3.0.0"
                     }
                 }
             }
@@ -17628,8 +17647,8 @@
             "resolved": "https://registry.npmjs.org/wikijs/-/wikijs-6.0.1.tgz",
             "integrity": "sha512-67ZtXyVPspYM5/B5ci0NIwvPJyG23HPk33QQLgLbCcORQ6N0I3Mhxd/KsPRh3xyly87KDs/bh1xuIG6PVTCKGw==",
             "requires": {
-                "cheerio": "1.0.0-rc.3",
-                "cross-fetch": "3.0.4",
+                "cheerio": "^1.0.0-rc.3",
+                "cross-fetch": "^3.0.2",
                 "infobox-parser": "3.3.1"
             }
         },
@@ -17643,8 +17662,8 @@
             "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz",
             "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=",
             "requires": {
-                "acorn": "3.3.0",
-                "acorn-globals": "3.1.0"
+                "acorn": "^3.1.0",
+                "acorn-globals": "^3.0.0"
             },
             "dependencies": {
                 "acorn": {
@@ -17659,9 +17678,9 @@
             "resolved": "https://registry.npmjs.org/words-to-numbers/-/words-to-numbers-1.5.1.tgz",
             "integrity": "sha512-uvz7zSCKmmA7o5f5zp4Z5l24RQhy6HSNu10URhNxQWv1I82RsFaZX3qD07RLFUMJsCV38oAuaca13AvhO+9yGw==",
             "requires": {
-                "babel-runtime": "6.26.0",
-                "clj-fuzzy": "0.3.3",
-                "its-set": "1.2.3"
+                "babel-runtime": "6.x.x",
+                "clj-fuzzy": "^0.3.2",
+                "its-set": "^1.1.5"
             }
         },
         "wordwrap": {
@@ -17674,8 +17693,8 @@
             "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz",
             "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==",
             "requires": {
-                "reduce-flatten": "1.0.1",
-                "typical": "2.6.1"
+                "reduce-flatten": "^1.0.1",
+                "typical": "^2.6.1"
             }
         },
         "worker-farm": {
@@ -17684,7 +17703,7 @@
             "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
             "dev": true,
             "requires": {
-                "errno": "0.1.7"
+                "errno": "~0.1.7"
             }
         },
         "worker-loader": {
@@ -17692,8 +17711,8 @@
             "resolved": "https://registry.npmjs.org/worker-loader/-/worker-loader-2.0.0.tgz",
             "integrity": "sha512-tnvNp4K3KQOpfRnD20m8xltE3eWh89Ye+5oj7wXEEHKac1P4oZ6p9oTj8/8ExqoSBnk9nu5Pr4nKfQ1hn2APJw==",
             "requires": {
-                "loader-utils": "1.2.3",
-                "schema-utils": "0.4.7"
+                "loader-utils": "^1.0.0",
+                "schema-utils": "^0.4.0"
             }
         },
         "worker-rpc": {
@@ -17702,7 +17721,7 @@
             "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==",
             "dev": true,
             "requires": {
-                "microevent.ts": "0.1.1"
+                "microevent.ts": "~0.1.1"
             }
         },
         "wrap-ansi": {
@@ -17710,8 +17729,8 @@
             "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
             "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
             "requires": {
-                "string-width": "1.0.2",
-                "strip-ansi": "3.0.1"
+                "string-width": "^1.0.1",
+                "strip-ansi": "^3.0.1"
             }
         },
         "wrappy": {
@@ -17724,9 +17743,9 @@
             "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz",
             "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==",
             "requires": {
-                "graceful-fs": "4.2.0",
-                "imurmurhash": "0.1.4",
-                "signal-exit": "3.0.2"
+                "graceful-fs": "^4.1.11",
+                "imurmurhash": "^0.1.4",
+                "signal-exit": "^3.0.2"
             }
         },
         "ws": {
@@ -17734,7 +17753,7 @@
             "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
             "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
             "requires": {
-                "async-limiter": "1.0.0"
+                "async-limiter": "~1.0.0"
             }
         },
         "xdg-basedir": {
@@ -17753,7 +17772,7 @@
             "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.1.9.tgz",
             "integrity": "sha1-wm/Qgm4Bor5xEHSKNPD4OFvkWfE=",
             "requires": {
-                "sax": "1.2.4"
+                "sax": ">=0.1.1"
             }
         },
         "xmlchars": {
@@ -17792,7 +17811,7 @@
             "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.7.2.tgz",
             "integrity": "sha512-qXROVp90sb83XtAoqE8bP9RwAkTTZbugRUTm5YeFCBfNRPEp2YzTeqWiz7m5OORHzEvrA/qcGS8hp/E+MMROYw==",
             "requires": {
-                "@babel/runtime": "7.7.6"
+                "@babel/runtime": "^7.6.3"
             },
             "dependencies": {
                 "@babel/runtime": {
@@ -17800,7 +17819,7 @@
                     "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.6.tgz",
                     "integrity": "sha512-BWAJxpNVa0QlE5gZdWjSxXtemZyZ9RmrmVozxt3NUXeZhVIJ5ANyqmMc0JDrivBZyxUuQvFxlvH4OWWOogGfUw==",
                     "requires": {
-                        "regenerator-runtime": "0.13.3"
+                        "regenerator-runtime": "^0.13.2"
                     }
                 }
             }
@@ -17810,19 +17829,19 @@
             "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
             "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
             "requires": {
-                "camelcase": "3.0.0",
-                "cliui": "3.2.0",
-                "decamelize": "1.2.0",
-                "get-caller-file": "1.0.3",
-                "os-locale": "1.4.0",
-                "read-pkg-up": "1.0.1",
-                "require-directory": "2.1.1",
-                "require-main-filename": "1.0.1",
-                "set-blocking": "2.0.0",
-                "string-width": "1.0.2",
-                "which-module": "1.0.0",
-                "y18n": "3.2.1",
-                "yargs-parser": "5.0.0"
+                "camelcase": "^3.0.0",
+                "cliui": "^3.2.0",
+                "decamelize": "^1.1.1",
+                "get-caller-file": "^1.0.1",
+                "os-locale": "^1.4.0",
+                "read-pkg-up": "^1.0.1",
+                "require-directory": "^2.1.1",
+                "require-main-filename": "^1.0.1",
+                "set-blocking": "^2.0.0",
+                "string-width": "^1.0.2",
+                "which-module": "^1.0.0",
+                "y18n": "^3.2.1",
+                "yargs-parser": "^5.0.0"
             },
             "dependencies": {
                 "camelcase": {
@@ -17835,7 +17854,7 @@
                     "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
                     "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
                     "requires": {
-                        "camelcase": "3.0.0"
+                        "camelcase": "^3.0.0"
                     }
                 }
             }
@@ -17845,7 +17864,7 @@
             "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz",
             "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=",
             "requires": {
-                "camelcase": "4.1.0"
+                "camelcase": "^4.1.0"
             }
         },
         "yargs-unparser": {
@@ -17853,9 +17872,9 @@
             "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz",
             "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==",
             "requires": {
-                "flat": "4.1.0",
-                "lodash": "4.17.15",
-                "yargs": "13.3.0"
+                "flat": "^4.1.0",
+                "lodash": "^4.17.15",
+                "yargs": "^13.3.0"
             },
             "dependencies": {
                 "ansi-regex": {
@@ -17868,7 +17887,7 @@
                     "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
                     "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
                     "requires": {
-                        "color-convert": "1.9.3"
+                        "color-convert": "^1.9.0"
                     }
                 },
                 "camelcase": {
@@ -17881,9 +17900,9 @@
                     "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
                     "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
                     "requires": {
-                        "string-width": "3.1.0",
-                        "strip-ansi": "5.2.0",
-                        "wrap-ansi": "5.1.0"
+                        "string-width": "^3.1.0",
+                        "strip-ansi": "^5.2.0",
+                        "wrap-ansi": "^5.1.0"
                     }
                 },
                 "find-up": {
@@ -17891,7 +17910,7 @@
                     "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
                     "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
                     "requires": {
-                        "locate-path": "3.0.0"
+                        "locate-path": "^3.0.0"
                     }
                 },
                 "get-caller-file": {
@@ -17909,8 +17928,8 @@
                     "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
                     "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
                     "requires": {
-                        "p-locate": "3.0.0",
-                        "path-exists": "3.0.0"
+                        "p-locate": "^3.0.0",
+                        "path-exists": "^3.0.0"
                     }
                 },
                 "p-locate": {
@@ -17918,7 +17937,7 @@
                     "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
                     "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
                     "requires": {
-                        "p-limit": "2.2.0"
+                        "p-limit": "^2.0.0"
                     }
                 },
                 "require-main-filename": {
@@ -17931,9 +17950,9 @@
                     "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
                     "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
                     "requires": {
-                        "emoji-regex": "7.0.3",
-                        "is-fullwidth-code-point": "2.0.0",
-                        "strip-ansi": "5.2.0"
+                        "emoji-regex": "^7.0.1",
+                        "is-fullwidth-code-point": "^2.0.0",
+                        "strip-ansi": "^5.1.0"
                     }
                 },
                 "strip-ansi": {
@@ -17941,7 +17960,7 @@
                     "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
                     "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
                     "requires": {
-                        "ansi-regex": "4.1.0"
+                        "ansi-regex": "^4.1.0"
                     }
                 },
                 "which-module": {
@@ -17954,9 +17973,9 @@
                     "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
                     "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
                     "requires": {
-                        "ansi-styles": "3.2.1",
-                        "string-width": "3.1.0",
-                        "strip-ansi": "5.2.0"
+                        "ansi-styles": "^3.2.0",
+                        "string-width": "^3.0.0",
+                        "strip-ansi": "^5.0.0"
                     }
                 },
                 "y18n": {
@@ -17969,16 +17988,16 @@
                     "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
                     "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==",
                     "requires": {
-                        "cliui": "5.0.0",
-                        "find-up": "3.0.0",
-                        "get-caller-file": "2.0.5",
-                        "require-directory": "2.1.1",
-                        "require-main-filename": "2.0.0",
-                        "set-blocking": "2.0.0",
-                        "string-width": "3.1.0",
-                        "which-module": "2.0.0",
-                        "y18n": "4.0.0",
-                        "yargs-parser": "13.1.1"
+                        "cliui": "^5.0.0",
+                        "find-up": "^3.0.0",
+                        "get-caller-file": "^2.0.1",
+                        "require-directory": "^2.1.1",
+                        "require-main-filename": "^2.0.0",
+                        "set-blocking": "^2.0.0",
+                        "string-width": "^3.0.0",
+                        "which-module": "^2.0.0",
+                        "y18n": "^4.0.0",
+                        "yargs-parser": "^13.1.1"
                     }
                 },
                 "yargs-parser": {
@@ -17986,8 +18005,8 @@
                     "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
                     "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
                     "requires": {
-                        "camelcase": "5.3.1",
-                        "decamelize": "1.2.0"
+                        "camelcase": "^5.0.0",
+                        "decamelize": "^1.2.0"
                     }
                 }
             }
@@ -18036,9 +18055,9 @@
             "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-2.1.0.tgz",
             "integrity": "sha512-F/xoLqlQShgvn1BzHQCNiYIoo2R93GQIMH+tA6JC3ckMDkme4bnhEEXSferZcG5ea/6bZNx3GqSUHqT8TUO6uQ==",
             "requires": {
-                "archiver-utils": "2.1.0",
-                "compress-commons": "2.0.0",
-                "readable-stream": "3.4.0"
+                "archiver-utils": "^2.1.0",
+                "compress-commons": "^2.0.0",
+                "readable-stream": "^3.4.0"
             },
             "dependencies": {
                 "readable-stream": {
@@ -18046,12 +18065,12 @@
                     "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
                     "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
                     "requires": {
-                        "inherits": "2.0.3",
-                        "string_decoder": "1.1.1",
-                        "util-deprecate": "1.0.2"
+                        "inherits": "^2.0.3",
+                        "string_decoder": "^1.1.1",
+                        "util-deprecate": "^1.0.1"
                     }
                 }
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/util/ProseMirrorEditorView.tsx b/src/client/util/ProseMirrorEditorView.tsx
new file mode 100644
index 000000000..3e5fd0604
--- /dev/null
+++ b/src/client/util/ProseMirrorEditorView.tsx
@@ -0,0 +1,74 @@
+import React from "react";
+import { EditorView } from "prosemirror-view";
+import { EditorState } from "prosemirror-state";
+
+export interface ProseMirrorEditorViewProps {
+    /* EditorState instance to use. */
+    editorState: EditorState;
+    /* Called when EditorView produces new EditorState. */
+    onEditorState: (editorState: EditorState) => any;
+}
+
+/**
+ * This wraps ProseMirror's EditorView into React component.
+ * This code was found on https://discuss.prosemirror.net/t/using-with-react/904
+ */
+export class ProseMirrorEditorView extends React.Component<ProseMirrorEditorViewProps>  {
+
+    private _editorView?: EditorView;
+
+    _createEditorView = (element: HTMLDivElement | null) => {
+        if (element != null) {
+            this._editorView = new EditorView(element, {
+                state: this.props.editorState,
+                dispatchTransaction: this.dispatchTransaction,
+            });
+        }
+    };
+
+    dispatchTransaction = (tx: any) => {
+        // In case EditorView makes any modification to a state we funnel those
+        // modifications up to the parent and apply to the EditorView itself.
+        const editorState = this.props.editorState.apply(tx);
+        if (this._editorView != null) {
+            this._editorView.updateState(editorState);
+        }
+        this.props.onEditorState(editorState);
+    };
+
+    focus() {
+        if (this._editorView) {
+            this._editorView.focus();
+        }
+    }
+
+    componentWillReceiveProps(nextProps: { editorState: EditorState<any>; }) {
+        // In case we receive new EditorState through props — we apply it to the
+        // EditorView instance.
+        if (this._editorView) {
+            if (nextProps.editorState !== this.props.editorState) {
+                this._editorView.updateState(nextProps.editorState);
+            }
+        }
+    }
+
+    componentWillUnmount() {
+        if (this._editorView) {
+            this._editorView.destroy();
+        }
+    }
+
+    shouldComponentUpdate() {
+        // Note that EditorView manages its DOM itself so we'd ratrher don't mess
+        // with it.
+        return false;
+    }
+
+    render() {
+        // Render just an empty div which is then used as a container for an
+        // EditorView instance.
+        return (
+            <div ref={this._createEditorView} />
+        );
+    }
+}
\ No newline at end of file
diff --git a/src/client/util/RichTextMenu.tsx b/src/client/util/RichTextMenu.tsx
new file mode 100644
index 000000000..7e20d126c
--- /dev/null
+++ b/src/client/util/RichTextMenu.tsx
@@ -0,0 +1,337 @@
+import React = require("react");
+import AntimodeMenu from "../views/AntimodeMenu";
+import { observable, action, } from "mobx";
+import { observer } from "mobx-react";
+import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos, Schema } from "prosemirror-model";
+import { schema } from "./RichTextSchema";
+import { EditorView } from "prosemirror-view";
+import { EditorState, NodeSelection } from "prosemirror-state";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
+import { faBold, faItalic, faUnderline, faStrikethrough, faSubscript, faSuperscript } from "@fortawesome/free-solid-svg-icons";
+import { MenuItem, Dropdown } from "prosemirror-menu";
+import { updateBullets } from "./ProsemirrorExampleTransfer";
+import { FieldViewProps } from "../views/nodes/FieldView";
+import { NumCast } from "../../new_fields/Types";
+import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox";
+import { unimplementedFunction } from "../../Utils";
+const { toggleMark, setBlockType } = require("prosemirror-commands");
+
+library.add(faBold, faItalic, faUnderline, faStrikethrough, faSuperscript, faSubscript);
+
+@observer
+export default class RichTextMenu extends AntimodeMenu {
+    static Instance: RichTextMenu;
+
+    private view?: EditorView;
+    private editorProps: FieldViewProps & FormattedTextBoxProps | undefined;
+
+    private _marksToDoms: Map<Mark, JSX.Element> = new Map();
+
+    @observable private activeFontSize: string = "";
+    @observable private activeFontFamily: string = "";
+
+    constructor(props: Readonly<{}>) {
+        super(props);
+        RichTextMenu.Instance = this;
+    }
+
+    @action
+    changeView(view: EditorView) {
+        this.view = view;
+    }
+
+    // update() {
+    //     console.log("update");
+    // }
+
+    update(view: EditorView, lastState: EditorState | undefined) {
+        console.log("update");
+        this.updateFromDash(view, lastState, this.editorProps);
+    }
+
+    @action
+    public async updateFromDash(view: EditorView, lastState: EditorState | undefined, props: any) {
+        if (!view) {
+            console.log("no editor?  why?");
+            return;
+        }
+        this.view = view;
+        const state = view.state;
+        console.log("update from dash");
+        // DocumentDecorations.Instance.showTextBar();
+        props && (this.editorProps = props);
+        // // Don't do anything if the document/selection didn't change
+        // if (lastState && lastState.doc.eq(state.doc) &&
+        //     lastState.selection.eq(state.selection)) return;
+
+        // this.reset_mark_doms();
+
+        // // update link dropdown
+        // const linkDropdown = await this.createLinkDropdown();
+        // const newLinkDropdowndom = linkDropdown.render(this.view).dom;
+        // this._linkDropdownDom && this.tooltip.replaceChild(newLinkDropdowndom, this._linkDropdownDom);
+        // this._linkDropdownDom = newLinkDropdowndom;
+
+        // update active font family and size
+        const active = this.getActiveFontStylesOnSelection();
+        const activeFamilies = active && active.get("families");
+        const activeSizes = active && active.get("sizes");
+
+        this.activeFontFamily = !activeFamilies || activeFamilies.length == 0 ? "default" : activeFamilies.length === 1 ? String(activeFamilies[0]) : "various";
+        this.activeFontSize = !activeSizes || activeSizes.length == 0 ? "default" : activeSizes.length === 1 ? String(activeSizes[0]) + "pt" : "various";
+
+        // this.update_mark_doms();
+    }
+
+
+    destroy() {
+        console.log("destroy");
+    }
+
+    createButton(faIcon: string, title: string, command: any) {
+        const self = this;
+        function onClick(e: React.PointerEvent) {
+            // dom.addEventListener("pointerdown", e => {
+            e.preventDefault();
+            self.view && self.view.focus();
+            //     if (dom.contains(e.target as Node)) {
+            e.stopPropagation();
+            //         command(this.view.state, this.view.dispatch, this.view);
+            //     }
+            // });
+            self.view && command(self.view!.state, self.view!.dispatch, self.view);
+        }
+
+        return (
+            <button className="antimodeMenu-button" title="title" onPointerDown={onClick}>
+                <FontAwesomeIcon icon={faIcon as IconProp} size="lg" />
+            </button>
+        );
+    }
+
+    createMarksDropdown(activeOption: string, options: { mark: Mark | null, title: string, label: string, command: (mark: Mark, view: EditorView) => void, hidden?: boolean }[]): JSX.Element {
+        let items = options.map(({ title, label, hidden }) => {
+            if (hidden) {
+                return label === activeOption ?
+                    <option value={label} title={title} selected hidden>{label}</option> :
+                    <option value={label} title={title} hidden>{label}</option>;
+            }
+            return label === activeOption ?
+                <option value={label} title={title} selected>{label}</option> :
+                <option value={label} title={title}>{label}</option>;
+        });
+
+        const self = this;
+        function onChange(val: string) {
+            options.forEach(({ label, mark, command }) => {
+                if (val === label) {
+                    self.view && mark && command(mark, self.view);
+                }
+            });
+        }
+        return <select onChange={e => onChange(e.target.value)}>{items}</select>;
+
+        // let items: MenuItem[] = [];
+        // options.forEach(({ mark, title, label, command }) => {
+        //     const self = this;
+        //     function onSelect() {
+        //         self.view && command(mark, self.view);
+        //     }
+        //     // this.createMarksOption("Set font size", String(mark.attrs.fontSize), onSelect)
+        //     items.push(
+        //         new MenuItem({
+        //             title: title,
+        //             label: label,
+        //             execEvent: "",
+        //             class: "dropdown-item",
+        //             css: "",
+        //             run() { onSelect(); }
+        //         })
+        //     );
+        // });
+
+        // return <div>{(new Dropdown(items, { label: label }) as MenuItem).render(this.view!).dom}</div>;
+    }
+
+    setMark = (mark: Mark, state: EditorState<any>, dispatch: any) => {
+        if (mark) {
+            const node = (state.selection as NodeSelection).node;
+            if (node?.type === schema.nodes.ordered_list) {
+                let attrs = node.attrs;
+                if (mark.type === schema.marks.pFontFamily) attrs = { ...attrs, setFontFamily: mark.attrs.family };
+                if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, setFontSize: mark.attrs.fontSize };
+                if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, setFontColor: mark.attrs.color };
+                const tr = updateBullets(state.tr.setNodeMarkup(state.selection.from, node.type, attrs), state.schema);
+                dispatch(tr.setSelection(new NodeSelection(tr.doc.resolve(state.selection.from))));
+            } else {
+                toggleMark(mark.type, mark.attrs)(state, (tx: any) => {
+                    const { from, $from, to, empty } = tx.selection;
+                    if (!tx.doc.rangeHasMark(from, to, mark.type)) {
+                        toggleMark(mark.type, mark.attrs)({ tr: tx, doc: tx.doc, selection: tx.selection, storedMarks: tx.storedMarks }, dispatch);
+                    } else dispatch(tx);
+                });
+            }
+        }
+    }
+
+    changeFontSize = (mark: Mark, view: EditorView) => {
+        console.log("change font size!!");
+        const size = mark.attrs.fontSize;
+        if (this.editorProps) {
+            const ruleProvider = this.editorProps.ruleProvider;
+            const heading = NumCast(this.editorProps.Document.heading);
+            if (ruleProvider && heading) {
+                ruleProvider["ruleSize_" + heading] = size;
+            }
+        }
+        this.setMark(view.state.schema.marks.pFontSize.create({ fontSize: size }), view.state, view.dispatch);
+    }
+
+    changeFontFamily = (mark: Mark, view: EditorView) => {
+        const fontName = mark.attrs.family;
+        // if (fontName) { this.updateFontStyleDropdown(fontName); }
+        if (this.editorProps) {
+            const ruleProvider = this.editorProps.ruleProvider;
+            const heading = NumCast(this.editorProps.Document.heading);
+            if (ruleProvider && heading) {
+                ruleProvider["ruleFont_" + heading] = fontName;
+            }
+        }
+        this.setMark(view.state.schema.marks.pFontFamily.create({ family: fontName }), view.state, view.dispatch);
+    }
+
+    // finds font sizes and families in selection
+    getActiveFontStylesOnSelection() {
+        if (!this.view) return;
+
+        const activeFamilies: string[] = [];
+        const activeSizes: string[] = [];
+        const state = this.view.state;
+        const pos = this.view.state.selection.$from;
+        const ref_node = this.reference_node(pos);
+        if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) {
+            ref_node.marks.forEach(m => {
+                m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family);
+                m.type === state.schema.marks.pFontSize && activeSizes.push(String(m.attrs.fontSize) + "pt");
+            });
+        }
+
+        let styles = new Map<String, String[]>();
+        styles.set("families", activeFamilies);
+        styles.set("sizes", activeSizes);
+        return styles;
+    }
+
+    // changeToFontSize = (mark: Mark, view: EditorView) => {
+    //     const size = mark.attrs.fontSize;
+    //     if (size) { this.updateFontSizeDropdown(String(size) + " pt"); }
+    //     if (this.editorProps) {
+    //         const ruleProvider = this.editorProps.ruleProvider;
+    //         const heading = NumCast(this.editorProps.Document.heading);
+    //         if (ruleProvider && heading) {
+    //             ruleProvider["ruleSize_" + heading] = size;
+    //         }
+    //     }
+    //     this.setMark(view.state.schema.marks.pFontSize.create({ fontSize: size }), view.state, view.dispatch);
+    // }
+
+    reference_node(pos: ResolvedPos<any>): ProsNode | null {
+        if (!this.view) return null;
+
+        let ref_node: ProsNode = this.view.state.doc;
+        if (pos.nodeBefore !== null && pos.nodeBefore !== undefined) {
+            ref_node = pos.nodeBefore;
+        }
+        else if (pos.nodeAfter !== null && pos.nodeAfter !== undefined) {
+            ref_node = pos.nodeAfter;
+        }
+        else if (pos.pos > 0) {
+            let skip = false;
+            for (let i: number = pos.pos - 1; i > 0; i--) {
+                this.view.state.doc.nodesBetween(i, pos.pos, (node: ProsNode) => {
+                    if (node.isLeaf && !skip) {
+                        ref_node = node;
+                        skip = true;
+                    }
+
+                });
+            }
+        }
+        if (!ref_node.isLeaf && ref_node.childCount > 0) {
+            ref_node = ref_node.child(0);
+        }
+        return ref_node;
+    }
+
+    render() {
+        console.log("render");
+        const fontSizeOptions = [
+            { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 8 }), title: "Set font size", label: "8pt", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 9 }), title: "Set font size", label: "8pt", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 10 }), title: "Set font size", label: "10pt", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 12 }), title: "Set font size", label: "12pt", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 14 }), title: "Set font size", label: "14pt", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 16 }), title: "Set font size", label: "16pt", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 18 }), title: "Set font size", label: "18pt", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 20 }), title: "Set font size", label: "20pt", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 24 }), title: "Set font size", label: "24pt", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 32 }), title: "Set font size", label: "32pt", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 48 }), title: "Set font size", label: "48pt", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 72 }), title: "Set font size", label: "72pt", command: this.changeFontSize },
+            { mark: null, title: "", label: "various", command: unimplementedFunction, hidden: true },
+            { mark: null, title: "", label: "default", command: unimplementedFunction, hidden: true },
+        ]
+
+        const fontFamilyOptions = [
+            { mark: schema.marks.pFontFamily.create({ family: "Times New Roman" }), title: "Set font family", label: "Times New Roman", command: this.changeFontFamily },
+            { mark: schema.marks.pFontFamily.create({ family: "Arial" }), title: "Set font family", label: "Arial", command: this.changeFontFamily },
+            { mark: schema.marks.pFontFamily.create({ family: "Georgia" }), title: "Set font family", label: "Georgia", command: this.changeFontFamily },
+            { mark: schema.marks.pFontFamily.create({ family: "Comic Sans MS" }), title: "Set font family", label: "Comic Sans MS", command: this.changeFontFamily },
+            { mark: schema.marks.pFontFamily.create({ family: "Tahoma" }), title: "Set font family", label: "Tahoma", command: this.changeFontFamily },
+            { mark: schema.marks.pFontFamily.create({ family: "Impact" }), title: "Set font family", label: "Impact", command: this.changeFontFamily },
+            { mark: schema.marks.pFontFamily.create({ family: "Crimson Text" }), title: "Set font family", label: "Crimson Text", command: this.changeFontFamily },
+            { mark: null, title: "", label: "various", command: unimplementedFunction, hidden: true },
+            { mark: null, title: "", label: "default", command: unimplementedFunction, hidden: true },
+        ]
+
+        const buttons = [
+            this.createButton("bold", "Bold", toggleMark(schema.marks.strong)),
+            this.createButton("italic", "Italic", toggleMark(schema.marks.em)),
+            this.createButton("underline", "Underline", toggleMark(schema.marks.underline)),
+            this.createButton("strikethrough", "Strikethrough", toggleMark(schema.marks.strikethrough)),
+            this.createButton("superscript", "Superscript", toggleMark(schema.marks.superscript)),
+            this.createButton("subscript", "Subscript", toggleMark(schema.marks.subscript)),
+            this.createMarksDropdown(this.activeFontSize, fontSizeOptions),
+            this.createMarksDropdown(this.activeFontFamily, fontFamilyOptions),
+        ];
+
+        // this._marksToDoms = new Map();
+        // items.forEach(({ title, dom, command }) => {
+        //     // this.tooltip.appendChild(dom);
+        //     switch (title) {
+        //         case "Bold":
+        //             this._marksToDoms.set(schema.mark(schema.marks.strong), dom);
+        //             // this.basicTools && this.basicTools.appendChild(dom.cloneNode(true));
+        //             break;
+        //         case "Italic":
+        //             this._marksToDoms.set(schema.mark(schema.marks.em), dom);
+        //             // this.basicTools && this.basicTools.appendChild(dom.cloneNode(true));
+        //             break;
+        //     }
+
+        //     //pointer down handler to activate button effects
+        //     dom.addEventListener("pointerdown", e => {
+        //         e.preventDefault();
+        //         this.view.focus();
+        //         if (dom.contains(e.target as Node)) {
+        //             e.stopPropagation();
+        //             command(this.view.state, this.view.dispatch, this.view);
+        //         }
+        //     });
+        // });
+
+        return this.getElement(buttons);
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 4bc24fa93..76b6a8834 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -26,6 +26,8 @@ import { IconBox } from "./nodes/IconBox";
 import React = require("react");
 import { DocumentType } from '../documents/DocumentTypes';
 import { ScriptField } from '../../new_fields/ScriptField';
+import { render } from 'react-dom';
+import RichTextMenu from '../util/RichTextMenu';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -572,6 +574,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         if (bounds.y > bounds.b) {
             bounds.y = bounds.b - (this._resizeBorderWidth + this._linkBoxHeight + this._titleHeight);
         }
+        // RichTextMenu.Instance.jumpTo(this._lastX, this._lastY - 50);
+        RichTextMenu.Instance.jumpTo(500, 300);
         return (<div className="documentDecorations">
             <div className="documentDecorations-background" style={{
                 width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
@@ -591,6 +595,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             }}>
                 {minimizeIcon}
 
+                {/* <RichTextMenu /> */}
+
                 {this._edtingTitle ?
                     <input ref={this._keyinput} className="title" type="text" name="dynbox" value={this._accumulatedTitle} onBlur={e => this.titleBlur(true)} onChange={this.titleChanged} onKeyPress={this.titleEntered} /> :
                     <div className="title" onPointerDown={this.onTitleDown} ><span>{`${this.selectionTitle}`}</span></div>}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index db2a3c298..9a0b7b7a2 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -40,6 +40,7 @@ import InkSelectDecorations from './InkSelectDecorations';
 import { Scripting } from '../util/Scripting';
 import { AudioBox } from './nodes/AudioBox';
 import { TraceMobx } from '../../new_fields/util';
+import RichTextMenu from '../util/RichTextMenu';
 
 @observer
 export class MainView extends React.Component {
@@ -516,6 +517,7 @@ export class MainView extends React.Component {
             <ContextMenu />
             <PDFMenu />
             <MarqueeOptionsMenu />
+            <RichTextMenu />
             <OverlayView />
         </div >);
     }
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index a08c14436..7e81876a2 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -47,6 +47,7 @@ import { AudioBox } from './AudioBox';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { InkTool } from '../../../new_fields/InkField';
 import { TraceMobx } from '../../../new_fields/util';
+import RichTextMenu from '../../util/RichTextMenu';
 
 library.add(faEdit);
 library.add(faSmile, faTextHeight, faUpload);
@@ -1032,7 +1033,9 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         const self = FormattedTextBox;
         return new Plugin({
             view(newView) {
-                return self.ToolTipTextMenu = FormattedTextBox.getToolTip(newView);
+                // return self.ToolTipTextMenu = FormattedTextBox.getToolTip(newView);
+                RichTextMenu.Instance.changeView(newView);
+                return RichTextMenu.Instance;
             }
         });
     }
@@ -1121,7 +1124,9 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
         const interactive = InkingControl.Instance.selectedTool || this.layoutDoc.isBackground;
         if (this.props.isSelected()) {
-            FormattedTextBox.ToolTipTextMenu!.updateFromDash(this._editorView!, undefined, this.props);
+            // TODO: ftong --> update from dash in richtextmenu
+            RichTextMenu.Instance.updateFromDash(this._editorView!, undefined, this.props);
+            // FormattedTextBox.ToolTipTextMenu!.updateFromDash(this._editorView!, undefined, this.props);
         } else if (FormattedTextBoxComment.textBox === this) {
             FormattedTextBoxComment.Hide();
         }
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index 503696ae9..05c70b74a 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -98,7 +98,7 @@ export default class PDFMenu extends AntimodeMenu {
     }
 
     render() {
-        const buttons = this.Status === "pdf"  ?
+        const buttons = this.Status === "pdf" ?
             [
                 <button key="1" className="antimodeMenu-button" title="Click to Highlight" onClick={this.highlightClicked} style={this.Highlighting ? { backgroundColor: "#121212" } : {}}>
                     <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} /></button>,
-- 
cgit v1.2.3-70-g09d2


From 1b68d5a89abdd8a7db4d4762a3ee44c3363e9a74 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 8 Jan 2020 12:19:28 -0500
Subject: cleaned up searchBox.  fixed parentdocument context menu placement.

---
 solr-8.3.1/server/solr/dash/core.properties        |   6 ++
 solr-8.3.1/server/solr/dash/data/index/_hs.dii     | Bin 0 -> 118 bytes
 solr-8.3.1/server/solr/dash/data/index/_hs.dim     | Bin 0 -> 12859 bytes
 solr-8.3.1/server/solr/dash/data/index/_hs.fdt     | Bin 0 -> 65104 bytes
 solr-8.3.1/server/solr/dash/data/index/_hs.fdx     | Bin 0 -> 107 bytes
 solr-8.3.1/server/solr/dash/data/index/_hs.fnm     | Bin 0 -> 10795 bytes
 solr-8.3.1/server/solr/dash/data/index/_hs.nvd     | Bin 0 -> 8896 bytes
 solr-8.3.1/server/solr/dash/data/index/_hs.nvm     | Bin 0 -> 1543 bytes
 solr-8.3.1/server/solr/dash/data/index/_hs.si      | Bin 0 -> 568 bytes
 solr-8.3.1/server/solr/dash/data/index/_hs_1.liv   | Bin 0 -> 187 bytes
 .../server/solr/dash/data/index/_hs_Lucene50_0.doc | Bin 0 -> 98608 bytes
 .../server/solr/dash/data/index/_hs_Lucene50_0.pos | Bin 0 -> 94372 bytes
 .../server/solr/dash/data/index/_hs_Lucene50_0.tim | Bin 0 -> 255019 bytes
 .../server/solr/dash/data/index/_hs_Lucene50_0.tip | Bin 0 -> 4992 bytes
 .../server/solr/dash/data/index/_hs_Lucene80_0.dvd | Bin 0 -> 37542 bytes
 .../server/solr/dash/data/index/_hs_Lucene80_0.dvm | Bin 0 -> 250 bytes
 solr-8.3.1/server/solr/dash/data/index/_j7.dii     | Bin 0 -> 97 bytes
 solr-8.3.1/server/solr/dash/data/index/_j7.dim     | Bin 0 -> 1395 bytes
 solr-8.3.1/server/solr/dash/data/index/_j7.fdt     | Bin 0 -> 4956 bytes
 solr-8.3.1/server/solr/dash/data/index/_j7.fdx     | Bin 0 -> 84 bytes
 solr-8.3.1/server/solr/dash/data/index/_j7.fnm     | Bin 0 -> 7628 bytes
 solr-8.3.1/server/solr/dash/data/index/_j7.nvd     | Bin 0 -> 1121 bytes
 solr-8.3.1/server/solr/dash/data/index/_j7.nvm     | Bin 0 -> 1003 bytes
 solr-8.3.1/server/solr/dash/data/index/_j7.si      | Bin 0 -> 569 bytes
 solr-8.3.1/server/solr/dash/data/index/_j7_1.liv   | Bin 0 -> 75 bytes
 .../server/solr/dash/data/index/_j7_Lucene50_0.doc | Bin 0 -> 6814 bytes
 .../server/solr/dash/data/index/_j7_Lucene50_0.pos | Bin 0 -> 8190 bytes
 .../server/solr/dash/data/index/_j7_Lucene50_0.tim | Bin 0 -> 38547 bytes
 .../server/solr/dash/data/index/_j7_Lucene50_0.tip | Bin 0 -> 1461 bytes
 .../server/solr/dash/data/index/_j7_Lucene80_0.dvd | Bin 0 -> 2851 bytes
 .../server/solr/dash/data/index/_j7_Lucene80_0.dvm | Bin 0 -> 249 bytes
 solr-8.3.1/server/solr/dash/data/index/_jf.dii     | Bin 0 -> 75 bytes
 solr-8.3.1/server/solr/dash/data/index/_jf.dim     | Bin 0 -> 570 bytes
 solr-8.3.1/server/solr/dash/data/index/_jf.fdt     | Bin 0 -> 1949 bytes
 solr-8.3.1/server/solr/dash/data/index/_jf.fdx     | Bin 0 -> 84 bytes
 solr-8.3.1/server/solr/dash/data/index/_jf.fnm     | Bin 0 -> 2578 bytes
 solr-8.3.1/server/solr/dash/data/index/_jf.nvd     | Bin 0 -> 1146 bytes
 solr-8.3.1/server/solr/dash/data/index/_jf.nvm     | Bin 0 -> 607 bytes
 solr-8.3.1/server/solr/dash/data/index/_jf.si      | Bin 0 -> 531 bytes
 solr-8.3.1/server/solr/dash/data/index/_jf_2.liv   | Bin 0 -> 75 bytes
 .../server/solr/dash/data/index/_jf_Lucene50_0.doc | Bin 0 -> 7866 bytes
 .../server/solr/dash/data/index/_jf_Lucene50_0.pos | Bin 0 -> 7835 bytes
 .../server/solr/dash/data/index/_jf_Lucene50_0.tim | Bin 0 -> 12788 bytes
 .../server/solr/dash/data/index/_jf_Lucene50_0.tip | Bin 0 -> 735 bytes
 .../server/solr/dash/data/index/_jf_Lucene80_0.dvd | Bin 0 -> 683 bytes
 .../server/solr/dash/data/index/_jf_Lucene80_0.dvm | Bin 0 -> 249 bytes
 solr-8.3.1/server/solr/dash/data/index/_ji.dii     | Bin 0 -> 72 bytes
 solr-8.3.1/server/solr/dash/data/index/_ji.dim     | Bin 0 -> 242 bytes
 solr-8.3.1/server/solr/dash/data/index/_ji.fdt     | Bin 0 -> 215 bytes
 solr-8.3.1/server/solr/dash/data/index/_ji.fdx     | Bin 0 -> 84 bytes
 solr-8.3.1/server/solr/dash/data/index/_ji.fnm     | Bin 0 -> 377 bytes
 solr-8.3.1/server/solr/dash/data/index/_ji.nvd     | Bin 0 -> 59 bytes
 solr-8.3.1/server/solr/dash/data/index/_ji.nvm     | Bin 0 -> 103 bytes
 solr-8.3.1/server/solr/dash/data/index/_ji.si      | Bin 0 -> 531 bytes
 solr-8.3.1/server/solr/dash/data/index/_ji_1.liv   | Bin 0 -> 67 bytes
 .../server/solr/dash/data/index/_ji_Lucene50_0.doc | Bin 0 -> 360 bytes
 .../server/solr/dash/data/index/_ji_Lucene50_0.pos | Bin 0 -> 725 bytes
 .../server/solr/dash/data/index/_ji_Lucene50_0.tim | Bin 0 -> 2775 bytes
 .../server/solr/dash/data/index/_ji_Lucene50_0.tip | Bin 0 -> 145 bytes
 .../server/solr/dash/data/index/_ji_Lucene80_0.dvd | Bin 0 -> 110 bytes
 .../server/solr/dash/data/index/_ji_Lucene80_0.dvm | Bin 0 -> 249 bytes
 solr-8.3.1/server/solr/dash/data/index/_jo.dii     | Bin 0 -> 72 bytes
 solr-8.3.1/server/solr/dash/data/index/_jo.dim     | Bin 0 -> 218 bytes
 solr-8.3.1/server/solr/dash/data/index/_jo.fdt     | Bin 0 -> 651 bytes
 solr-8.3.1/server/solr/dash/data/index/_jo.fdx     | Bin 0 -> 84 bytes
 solr-8.3.1/server/solr/dash/data/index/_jo.fnm     | Bin 0 -> 2347 bytes
 solr-8.3.1/server/solr/dash/data/index/_jo.nvd     | Bin 0 -> 353 bytes
 solr-8.3.1/server/solr/dash/data/index/_jo.nvm     | Bin 0 -> 643 bytes
 solr-8.3.1/server/solr/dash/data/index/_jo.si      | Bin 0 -> 531 bytes
 solr-8.3.1/server/solr/dash/data/index/_jo_1.liv   | Bin 0 -> 67 bytes
 .../server/solr/dash/data/index/_jo_Lucene50_0.doc | Bin 0 -> 2526 bytes
 .../server/solr/dash/data/index/_jo_Lucene50_0.pos | Bin 0 -> 2723 bytes
 .../server/solr/dash/data/index/_jo_Lucene50_0.tim | Bin 0 -> 9422 bytes
 .../server/solr/dash/data/index/_jo_Lucene50_0.tip | Bin 0 -> 657 bytes
 .../server/solr/dash/data/index/_jo_Lucene80_0.dvd | Bin 0 -> 205 bytes
 .../server/solr/dash/data/index/_jo_Lucene80_0.dvm | Bin 0 -> 249 bytes
 solr-8.3.1/server/solr/dash/data/index/_jp.dii     | Bin 0 -> 72 bytes
 solr-8.3.1/server/solr/dash/data/index/_jp.dim     | Bin 0 -> 229 bytes
 solr-8.3.1/server/solr/dash/data/index/_jp.fdt     | Bin 0 -> 398 bytes
 solr-8.3.1/server/solr/dash/data/index/_jp.fdx     | Bin 0 -> 84 bytes
 solr-8.3.1/server/solr/dash/data/index/_jp.fnm     | Bin 0 -> 1081 bytes
 solr-8.3.1/server/solr/dash/data/index/_jp.nvd     | Bin 0 -> 235 bytes
 solr-8.3.1/server/solr/dash/data/index/_jp.nvm     | Bin 0 -> 355 bytes
 solr-8.3.1/server/solr/dash/data/index/_jp.si      | Bin 0 -> 531 bytes
 solr-8.3.1/server/solr/dash/data/index/_jp_1.liv   | Bin 0 -> 67 bytes
 .../server/solr/dash/data/index/_jp_Lucene50_0.doc | Bin 0 -> 2635 bytes
 .../server/solr/dash/data/index/_jp_Lucene50_0.pos | Bin 0 -> 2579 bytes
 .../server/solr/dash/data/index/_jp_Lucene50_0.tim | Bin 0 -> 3558 bytes
 .../server/solr/dash/data/index/_jp_Lucene50_0.tip | Bin 0 -> 304 bytes
 .../server/solr/dash/data/index/_jp_Lucene80_0.dvd | Bin 0 -> 121 bytes
 .../server/solr/dash/data/index/_jp_Lucene80_0.dvm | Bin 0 -> 249 bytes
 solr-8.3.1/server/solr/dash/data/index/_jq.dii     | Bin 0 -> 78 bytes
 solr-8.3.1/server/solr/dash/data/index/_jq.dim     | Bin 0 -> 291 bytes
 solr-8.3.1/server/solr/dash/data/index/_jq.fdt     | Bin 0 -> 719 bytes
 solr-8.3.1/server/solr/dash/data/index/_jq.fdx     | Bin 0 -> 84 bytes
 solr-8.3.1/server/solr/dash/data/index/_jq.fnm     | Bin 0 -> 1282 bytes
 solr-8.3.1/server/solr/dash/data/index/_jq.nvd     | Bin 0 -> 131 bytes
 solr-8.3.1/server/solr/dash/data/index/_jq.nvm     | Bin 0 -> 283 bytes
 solr-8.3.1/server/solr/dash/data/index/_jq.si      | Bin 0 -> 531 bytes
 solr-8.3.1/server/solr/dash/data/index/_jq_2.liv   | Bin 0 -> 67 bytes
 .../server/solr/dash/data/index/_jq_Lucene50_0.doc | Bin 0 -> 116 bytes
 .../server/solr/dash/data/index/_jq_Lucene50_0.pos | Bin 0 -> 458 bytes
 .../server/solr/dash/data/index/_jq_Lucene50_0.tim | Bin 0 -> 5813 bytes
 .../server/solr/dash/data/index/_jq_Lucene50_0.tip | Bin 0 -> 366 bytes
 .../server/solr/dash/data/index/_jq_Lucene80_0.dvd | Bin 0 -> 190 bytes
 .../server/solr/dash/data/index/_jq_Lucene80_0.dvm | Bin 0 -> 249 bytes
 solr-8.3.1/server/solr/dash/data/index/_jr.dii     | Bin 0 -> 69 bytes
 solr-8.3.1/server/solr/dash/data/index/_jr.dim     | Bin 0 -> 124 bytes
 solr-8.3.1/server/solr/dash/data/index/_jr.fdt     | Bin 0 -> 226 bytes
 solr-8.3.1/server/solr/dash/data/index/_jr.fdx     | Bin 0 -> 84 bytes
 solr-8.3.1/server/solr/dash/data/index/_jr.fnm     | Bin 0 -> 445 bytes
 solr-8.3.1/server/solr/dash/data/index/_jr.si      | Bin 0 -> 496 bytes
 solr-8.3.1/server/solr/dash/data/index/_jr_1.liv   | Bin 0 -> 67 bytes
 .../server/solr/dash/data/index/_jr_Lucene50_0.doc | Bin 0 -> 114 bytes
 .../server/solr/dash/data/index/_jr_Lucene50_0.tim | Bin 0 -> 520 bytes
 .../server/solr/dash/data/index/_jr_Lucene50_0.tip | Bin 0 -> 145 bytes
 .../server/solr/dash/data/index/_jr_Lucene80_0.dvd | Bin 0 -> 110 bytes
 .../server/solr/dash/data/index/_jr_Lucene80_0.dvm | Bin 0 -> 249 bytes
 solr-8.3.1/server/solr/dash/data/index/segments_d  | Bin 0 -> 649 bytes
 solr-8.3.1/server/solr/dash/data/index/write.lock  |   0
 .../solr/dash/data/tlog/tlog.0000000000000000011   | Bin 0 -> 1291205 bytes
 .../solr/dash/data/tlog/tlog.0000000000000000012   | Bin 0 -> 196943 bytes
 solr-8.3.1/server/solr/dash/data2/index/_199_1.liv | Bin 0 -> 67 bytes
 solr-8.3.1/server/solr/dash/data2/index/_19h_1.liv | Bin 0 -> 67 bytes
 solr-8.3.1/server/solr/dash/data2/index/write.lock |   0
 .../solr/dash/data2/tlog/tlog.0000000000000000012  | Bin 0 -> 269779 bytes
 .../tmp/start_1669159736908453662.properties       |  11 +++
 .../tmp/start_5906520416860122978.properties       |  11 +++
 src/client/views/MainView.tsx                      |   2 +-
 .../views/collections/ParentDocumentSelector.tsx   |  10 +-
 src/client/views/nodes/DocumentView.tsx            |   3 +-
 src/client/views/search/SearchBox.scss             |   3 +-
 src/client/views/search/SearchBox.tsx              |   3 +-
 src/client/views/search/SearchItem.scss            | 107 +++++----------------
 src/client/views/search/SearchItem.tsx             |  87 +++++++++--------
 135 files changed, 112 insertions(+), 131 deletions(-)
 create mode 100644 solr-8.3.1/server/solr/dash/core.properties
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_hs.dii
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_hs.dim
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_hs.fdt
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_hs.fdx
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_hs.fnm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_hs.nvd
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_hs.nvm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_hs.si
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_hs_1.liv
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_hs_Lucene50_0.doc
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_hs_Lucene50_0.pos
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_hs_Lucene50_0.tim
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_hs_Lucene50_0.tip
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_hs_Lucene80_0.dvd
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_hs_Lucene80_0.dvm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_j7.dii
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_j7.dim
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_j7.fdt
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_j7.fdx
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_j7.fnm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_j7.nvd
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_j7.nvm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_j7.si
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_j7_1.liv
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_j7_Lucene50_0.doc
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_j7_Lucene50_0.pos
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_j7_Lucene50_0.tim
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_j7_Lucene50_0.tip
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_j7_Lucene80_0.dvd
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_j7_Lucene80_0.dvm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jf.dii
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jf.dim
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jf.fdt
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jf.fdx
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jf.fnm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jf.nvd
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jf.nvm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jf.si
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jf_2.liv
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jf_Lucene50_0.doc
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jf_Lucene50_0.pos
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jf_Lucene50_0.tim
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jf_Lucene50_0.tip
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jf_Lucene80_0.dvd
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jf_Lucene80_0.dvm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_ji.dii
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_ji.dim
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_ji.fdt
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_ji.fdx
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_ji.fnm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_ji.nvd
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_ji.nvm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_ji.si
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_ji_1.liv
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_ji_Lucene50_0.doc
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_ji_Lucene50_0.pos
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_ji_Lucene50_0.tim
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_ji_Lucene50_0.tip
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_ji_Lucene80_0.dvd
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_ji_Lucene80_0.dvm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jo.dii
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jo.dim
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jo.fdt
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jo.fdx
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jo.fnm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jo.nvd
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jo.nvm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jo.si
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jo_1.liv
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jo_Lucene50_0.doc
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jo_Lucene50_0.pos
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jo_Lucene50_0.tim
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jo_Lucene50_0.tip
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jo_Lucene80_0.dvd
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jo_Lucene80_0.dvm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jp.dii
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jp.dim
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jp.fdt
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jp.fdx
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jp.fnm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jp.nvd
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jp.nvm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jp.si
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jp_1.liv
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jp_Lucene50_0.doc
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jp_Lucene50_0.pos
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jp_Lucene50_0.tim
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jp_Lucene50_0.tip
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jp_Lucene80_0.dvd
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jp_Lucene80_0.dvm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jq.dii
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jq.dim
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jq.fdt
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jq.fdx
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jq.fnm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jq.nvd
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jq.nvm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jq.si
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jq_2.liv
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jq_Lucene50_0.doc
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jq_Lucene50_0.pos
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jq_Lucene50_0.tim
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jq_Lucene50_0.tip
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jq_Lucene80_0.dvd
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jq_Lucene80_0.dvm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jr.dii
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jr.dim
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jr.fdt
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jr.fdx
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jr.fnm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jr.si
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jr_1.liv
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jr_Lucene50_0.doc
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jr_Lucene50_0.tim
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jr_Lucene50_0.tip
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jr_Lucene80_0.dvd
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/_jr_Lucene80_0.dvm
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/segments_d
 create mode 100644 solr-8.3.1/server/solr/dash/data/index/write.lock
 create mode 100644 solr-8.3.1/server/solr/dash/data/tlog/tlog.0000000000000000011
 create mode 100644 solr-8.3.1/server/solr/dash/data/tlog/tlog.0000000000000000012
 create mode 100644 solr-8.3.1/server/solr/dash/data2/index/_199_1.liv
 create mode 100644 solr-8.3.1/server/solr/dash/data2/index/_19h_1.liv
 create mode 100644 solr-8.3.1/server/solr/dash/data2/index/write.lock
 create mode 100644 solr-8.3.1/server/solr/dash/data2/tlog/tlog.0000000000000000012
 create mode 100644 solr-8.3.1/server/tmp/start_1669159736908453662.properties
 create mode 100644 solr-8.3.1/server/tmp/start_5906520416860122978.properties

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

diff --git a/solr-8.3.1/server/solr/dash/core.properties b/solr-8.3.1/server/solr/dash/core.properties
new file mode 100644
index 000000000..0912a8303
--- /dev/null
+++ b/solr-8.3.1/server/solr/dash/core.properties
@@ -0,0 +1,6 @@
+#Written by CorePropertiesLocator
+#Wed Jan 08 14:44:34 UTC 2020
+name=dash
+config=solrconfig.xml
+schema=schema.xml
+dataDir=data
diff --git a/solr-8.3.1/server/solr/dash/data/index/_hs.dii b/solr-8.3.1/server/solr/dash/data/index/_hs.dii
new file mode 100644
index 000000000..c2207a7b5
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_hs.dii differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_hs.dim b/solr-8.3.1/server/solr/dash/data/index/_hs.dim
new file mode 100644
index 000000000..808fdf537
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_hs.dim differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_hs.fdt b/solr-8.3.1/server/solr/dash/data/index/_hs.fdt
new file mode 100644
index 000000000..54fb57b98
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_hs.fdt differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_hs.fdx b/solr-8.3.1/server/solr/dash/data/index/_hs.fdx
new file mode 100644
index 000000000..02a54c916
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_hs.fdx differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_hs.fnm b/solr-8.3.1/server/solr/dash/data/index/_hs.fnm
new file mode 100644
index 000000000..2bb9517ac
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_hs.fnm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_hs.nvd b/solr-8.3.1/server/solr/dash/data/index/_hs.nvd
new file mode 100644
index 000000000..994cb90ad
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_hs.nvd differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_hs.nvm b/solr-8.3.1/server/solr/dash/data/index/_hs.nvm
new file mode 100644
index 000000000..a452ff4f2
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_hs.nvm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_hs.si b/solr-8.3.1/server/solr/dash/data/index/_hs.si
new file mode 100644
index 000000000..eeb2bc77e
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_hs.si differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_hs_1.liv b/solr-8.3.1/server/solr/dash/data/index/_hs_1.liv
new file mode 100644
index 000000000..9e1e90cb8
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_hs_1.liv differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_hs_Lucene50_0.doc b/solr-8.3.1/server/solr/dash/data/index/_hs_Lucene50_0.doc
new file mode 100644
index 000000000..3f3c7dffa
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_hs_Lucene50_0.doc differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_hs_Lucene50_0.pos b/solr-8.3.1/server/solr/dash/data/index/_hs_Lucene50_0.pos
new file mode 100644
index 000000000..9f2d9f805
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_hs_Lucene50_0.pos differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_hs_Lucene50_0.tim b/solr-8.3.1/server/solr/dash/data/index/_hs_Lucene50_0.tim
new file mode 100644
index 000000000..1fb544956
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_hs_Lucene50_0.tim differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_hs_Lucene50_0.tip b/solr-8.3.1/server/solr/dash/data/index/_hs_Lucene50_0.tip
new file mode 100644
index 000000000..9c97fbbf2
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_hs_Lucene50_0.tip differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_hs_Lucene80_0.dvd b/solr-8.3.1/server/solr/dash/data/index/_hs_Lucene80_0.dvd
new file mode 100644
index 000000000..e566bb7ed
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_hs_Lucene80_0.dvd differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_hs_Lucene80_0.dvm b/solr-8.3.1/server/solr/dash/data/index/_hs_Lucene80_0.dvm
new file mode 100644
index 000000000..722d76cf9
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_hs_Lucene80_0.dvm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_j7.dii b/solr-8.3.1/server/solr/dash/data/index/_j7.dii
new file mode 100644
index 000000000..7d4c5af95
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_j7.dii differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_j7.dim b/solr-8.3.1/server/solr/dash/data/index/_j7.dim
new file mode 100644
index 000000000..c6101943f
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_j7.dim differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_j7.fdt b/solr-8.3.1/server/solr/dash/data/index/_j7.fdt
new file mode 100644
index 000000000..e89db4ed5
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_j7.fdt differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_j7.fdx b/solr-8.3.1/server/solr/dash/data/index/_j7.fdx
new file mode 100644
index 000000000..a589f2dcd
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_j7.fdx differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_j7.fnm b/solr-8.3.1/server/solr/dash/data/index/_j7.fnm
new file mode 100644
index 000000000..78d59368b
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_j7.fnm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_j7.nvd b/solr-8.3.1/server/solr/dash/data/index/_j7.nvd
new file mode 100644
index 000000000..984fb854c
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_j7.nvd differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_j7.nvm b/solr-8.3.1/server/solr/dash/data/index/_j7.nvm
new file mode 100644
index 000000000..aa478973c
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_j7.nvm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_j7.si b/solr-8.3.1/server/solr/dash/data/index/_j7.si
new file mode 100644
index 000000000..5ce51e20f
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_j7.si differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_j7_1.liv b/solr-8.3.1/server/solr/dash/data/index/_j7_1.liv
new file mode 100644
index 000000000..65e825f18
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_j7_1.liv differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_j7_Lucene50_0.doc b/solr-8.3.1/server/solr/dash/data/index/_j7_Lucene50_0.doc
new file mode 100644
index 000000000..3158e0d7f
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_j7_Lucene50_0.doc differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_j7_Lucene50_0.pos b/solr-8.3.1/server/solr/dash/data/index/_j7_Lucene50_0.pos
new file mode 100644
index 000000000..e5b3f12f9
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_j7_Lucene50_0.pos differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_j7_Lucene50_0.tim b/solr-8.3.1/server/solr/dash/data/index/_j7_Lucene50_0.tim
new file mode 100644
index 000000000..db0ddd7dd
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_j7_Lucene50_0.tim differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_j7_Lucene50_0.tip b/solr-8.3.1/server/solr/dash/data/index/_j7_Lucene50_0.tip
new file mode 100644
index 000000000..6d06bbc8a
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_j7_Lucene50_0.tip differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_j7_Lucene80_0.dvd b/solr-8.3.1/server/solr/dash/data/index/_j7_Lucene80_0.dvd
new file mode 100644
index 000000000..06dc34e64
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_j7_Lucene80_0.dvd differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_j7_Lucene80_0.dvm b/solr-8.3.1/server/solr/dash/data/index/_j7_Lucene80_0.dvm
new file mode 100644
index 000000000..7f69f1665
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_j7_Lucene80_0.dvm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jf.dii b/solr-8.3.1/server/solr/dash/data/index/_jf.dii
new file mode 100644
index 000000000..0627e1127
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jf.dii differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jf.dim b/solr-8.3.1/server/solr/dash/data/index/_jf.dim
new file mode 100644
index 000000000..1a489cd18
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jf.dim differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jf.fdt b/solr-8.3.1/server/solr/dash/data/index/_jf.fdt
new file mode 100644
index 000000000..5fce34b2c
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jf.fdt differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jf.fdx b/solr-8.3.1/server/solr/dash/data/index/_jf.fdx
new file mode 100644
index 000000000..14aaf8fc0
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jf.fdx differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jf.fnm b/solr-8.3.1/server/solr/dash/data/index/_jf.fnm
new file mode 100644
index 000000000..eff2f74b7
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jf.fnm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jf.nvd b/solr-8.3.1/server/solr/dash/data/index/_jf.nvd
new file mode 100644
index 000000000..2bffbd106
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jf.nvd differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jf.nvm b/solr-8.3.1/server/solr/dash/data/index/_jf.nvm
new file mode 100644
index 000000000..02a616913
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jf.nvm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jf.si b/solr-8.3.1/server/solr/dash/data/index/_jf.si
new file mode 100644
index 000000000..b776600c1
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jf.si differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jf_2.liv b/solr-8.3.1/server/solr/dash/data/index/_jf_2.liv
new file mode 100644
index 000000000..49e851d49
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jf_2.liv differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jf_Lucene50_0.doc b/solr-8.3.1/server/solr/dash/data/index/_jf_Lucene50_0.doc
new file mode 100644
index 000000000..f951b0e3a
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jf_Lucene50_0.doc differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jf_Lucene50_0.pos b/solr-8.3.1/server/solr/dash/data/index/_jf_Lucene50_0.pos
new file mode 100644
index 000000000..7bc297568
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jf_Lucene50_0.pos differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jf_Lucene50_0.tim b/solr-8.3.1/server/solr/dash/data/index/_jf_Lucene50_0.tim
new file mode 100644
index 000000000..874b3807c
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jf_Lucene50_0.tim differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jf_Lucene50_0.tip b/solr-8.3.1/server/solr/dash/data/index/_jf_Lucene50_0.tip
new file mode 100644
index 000000000..b835a3e5f
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jf_Lucene50_0.tip differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jf_Lucene80_0.dvd b/solr-8.3.1/server/solr/dash/data/index/_jf_Lucene80_0.dvd
new file mode 100644
index 000000000..04b2e5a6c
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jf_Lucene80_0.dvd differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jf_Lucene80_0.dvm b/solr-8.3.1/server/solr/dash/data/index/_jf_Lucene80_0.dvm
new file mode 100644
index 000000000..b68fbf656
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jf_Lucene80_0.dvm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_ji.dii b/solr-8.3.1/server/solr/dash/data/index/_ji.dii
new file mode 100644
index 000000000..fdef5b441
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_ji.dii differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_ji.dim b/solr-8.3.1/server/solr/dash/data/index/_ji.dim
new file mode 100644
index 000000000..2155d1932
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_ji.dim differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_ji.fdt b/solr-8.3.1/server/solr/dash/data/index/_ji.fdt
new file mode 100644
index 000000000..e7fdec3fb
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_ji.fdt differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_ji.fdx b/solr-8.3.1/server/solr/dash/data/index/_ji.fdx
new file mode 100644
index 000000000..86c01bbd4
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_ji.fdx differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_ji.fnm b/solr-8.3.1/server/solr/dash/data/index/_ji.fnm
new file mode 100644
index 000000000..d3925b485
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_ji.fnm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_ji.nvd b/solr-8.3.1/server/solr/dash/data/index/_ji.nvd
new file mode 100644
index 000000000..205e2b1cd
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_ji.nvd differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_ji.nvm b/solr-8.3.1/server/solr/dash/data/index/_ji.nvm
new file mode 100644
index 000000000..21f18e60a
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_ji.nvm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_ji.si b/solr-8.3.1/server/solr/dash/data/index/_ji.si
new file mode 100644
index 000000000..b7126893f
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_ji.si differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_ji_1.liv b/solr-8.3.1/server/solr/dash/data/index/_ji_1.liv
new file mode 100644
index 000000000..753deb733
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_ji_1.liv differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_ji_Lucene50_0.doc b/solr-8.3.1/server/solr/dash/data/index/_ji_Lucene50_0.doc
new file mode 100644
index 000000000..060372c58
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_ji_Lucene50_0.doc differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_ji_Lucene50_0.pos b/solr-8.3.1/server/solr/dash/data/index/_ji_Lucene50_0.pos
new file mode 100644
index 000000000..466c55484
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_ji_Lucene50_0.pos differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_ji_Lucene50_0.tim b/solr-8.3.1/server/solr/dash/data/index/_ji_Lucene50_0.tim
new file mode 100644
index 000000000..ca4000e9c
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_ji_Lucene50_0.tim differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_ji_Lucene50_0.tip b/solr-8.3.1/server/solr/dash/data/index/_ji_Lucene50_0.tip
new file mode 100644
index 000000000..a749cb1b9
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_ji_Lucene50_0.tip differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_ji_Lucene80_0.dvd b/solr-8.3.1/server/solr/dash/data/index/_ji_Lucene80_0.dvd
new file mode 100644
index 000000000..de1318ae2
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_ji_Lucene80_0.dvd differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_ji_Lucene80_0.dvm b/solr-8.3.1/server/solr/dash/data/index/_ji_Lucene80_0.dvm
new file mode 100644
index 000000000..c273ad653
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_ji_Lucene80_0.dvm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jo.dii b/solr-8.3.1/server/solr/dash/data/index/_jo.dii
new file mode 100644
index 000000000..d621dc010
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jo.dii differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jo.dim b/solr-8.3.1/server/solr/dash/data/index/_jo.dim
new file mode 100644
index 000000000..a9d846c55
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jo.dim differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jo.fdt b/solr-8.3.1/server/solr/dash/data/index/_jo.fdt
new file mode 100644
index 000000000..69d7ce59c
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jo.fdt differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jo.fdx b/solr-8.3.1/server/solr/dash/data/index/_jo.fdx
new file mode 100644
index 000000000..71a73ac09
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jo.fdx differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jo.fnm b/solr-8.3.1/server/solr/dash/data/index/_jo.fnm
new file mode 100644
index 000000000..b02414ef0
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jo.fnm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jo.nvd b/solr-8.3.1/server/solr/dash/data/index/_jo.nvd
new file mode 100644
index 000000000..993fa7ddc
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jo.nvd differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jo.nvm b/solr-8.3.1/server/solr/dash/data/index/_jo.nvm
new file mode 100644
index 000000000..4397e5533
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jo.nvm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jo.si b/solr-8.3.1/server/solr/dash/data/index/_jo.si
new file mode 100644
index 000000000..2f0aefd63
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jo.si differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jo_1.liv b/solr-8.3.1/server/solr/dash/data/index/_jo_1.liv
new file mode 100644
index 000000000..46316505e
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jo_1.liv differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jo_Lucene50_0.doc b/solr-8.3.1/server/solr/dash/data/index/_jo_Lucene50_0.doc
new file mode 100644
index 000000000..1cbf85053
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jo_Lucene50_0.doc differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jo_Lucene50_0.pos b/solr-8.3.1/server/solr/dash/data/index/_jo_Lucene50_0.pos
new file mode 100644
index 000000000..97206ebfa
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jo_Lucene50_0.pos differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jo_Lucene50_0.tim b/solr-8.3.1/server/solr/dash/data/index/_jo_Lucene50_0.tim
new file mode 100644
index 000000000..d06a85746
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jo_Lucene50_0.tim differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jo_Lucene50_0.tip b/solr-8.3.1/server/solr/dash/data/index/_jo_Lucene50_0.tip
new file mode 100644
index 000000000..fd9ff3dae
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jo_Lucene50_0.tip differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jo_Lucene80_0.dvd b/solr-8.3.1/server/solr/dash/data/index/_jo_Lucene80_0.dvd
new file mode 100644
index 000000000..602ba23e7
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jo_Lucene80_0.dvd differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jo_Lucene80_0.dvm b/solr-8.3.1/server/solr/dash/data/index/_jo_Lucene80_0.dvm
new file mode 100644
index 000000000..1fd81041f
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jo_Lucene80_0.dvm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jp.dii b/solr-8.3.1/server/solr/dash/data/index/_jp.dii
new file mode 100644
index 000000000..c39beb1bd
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jp.dii differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jp.dim b/solr-8.3.1/server/solr/dash/data/index/_jp.dim
new file mode 100644
index 000000000..87a5071d7
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jp.dim differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jp.fdt b/solr-8.3.1/server/solr/dash/data/index/_jp.fdt
new file mode 100644
index 000000000..a232efdf8
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jp.fdt differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jp.fdx b/solr-8.3.1/server/solr/dash/data/index/_jp.fdx
new file mode 100644
index 000000000..0b5e95a9a
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jp.fdx differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jp.fnm b/solr-8.3.1/server/solr/dash/data/index/_jp.fnm
new file mode 100644
index 000000000..c09282147
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jp.fnm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jp.nvd b/solr-8.3.1/server/solr/dash/data/index/_jp.nvd
new file mode 100644
index 000000000..12dcf3d4d
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jp.nvd differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jp.nvm b/solr-8.3.1/server/solr/dash/data/index/_jp.nvm
new file mode 100644
index 000000000..4bde4b5f0
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jp.nvm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jp.si b/solr-8.3.1/server/solr/dash/data/index/_jp.si
new file mode 100644
index 000000000..40cd02b98
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jp.si differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jp_1.liv b/solr-8.3.1/server/solr/dash/data/index/_jp_1.liv
new file mode 100644
index 000000000..be94523f4
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jp_1.liv differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jp_Lucene50_0.doc b/solr-8.3.1/server/solr/dash/data/index/_jp_Lucene50_0.doc
new file mode 100644
index 000000000..90774bb6f
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jp_Lucene50_0.doc differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jp_Lucene50_0.pos b/solr-8.3.1/server/solr/dash/data/index/_jp_Lucene50_0.pos
new file mode 100644
index 000000000..b4814c1f7
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jp_Lucene50_0.pos differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jp_Lucene50_0.tim b/solr-8.3.1/server/solr/dash/data/index/_jp_Lucene50_0.tim
new file mode 100644
index 000000000..f8a7871e1
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jp_Lucene50_0.tim differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jp_Lucene50_0.tip b/solr-8.3.1/server/solr/dash/data/index/_jp_Lucene50_0.tip
new file mode 100644
index 000000000..c29ac6ed5
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jp_Lucene50_0.tip differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jp_Lucene80_0.dvd b/solr-8.3.1/server/solr/dash/data/index/_jp_Lucene80_0.dvd
new file mode 100644
index 000000000..41afd53b9
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jp_Lucene80_0.dvd differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jp_Lucene80_0.dvm b/solr-8.3.1/server/solr/dash/data/index/_jp_Lucene80_0.dvm
new file mode 100644
index 000000000..527eed668
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jp_Lucene80_0.dvm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jq.dii b/solr-8.3.1/server/solr/dash/data/index/_jq.dii
new file mode 100644
index 000000000..a0a86dfd0
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jq.dii differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jq.dim b/solr-8.3.1/server/solr/dash/data/index/_jq.dim
new file mode 100644
index 000000000..e69c06061
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jq.dim differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jq.fdt b/solr-8.3.1/server/solr/dash/data/index/_jq.fdt
new file mode 100644
index 000000000..c49ed904f
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jq.fdt differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jq.fdx b/solr-8.3.1/server/solr/dash/data/index/_jq.fdx
new file mode 100644
index 000000000..85d62a39c
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jq.fdx differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jq.fnm b/solr-8.3.1/server/solr/dash/data/index/_jq.fnm
new file mode 100644
index 000000000..b3aa23794
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jq.fnm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jq.nvd b/solr-8.3.1/server/solr/dash/data/index/_jq.nvd
new file mode 100644
index 000000000..fb74de020
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jq.nvd differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jq.nvm b/solr-8.3.1/server/solr/dash/data/index/_jq.nvm
new file mode 100644
index 000000000..b407b1b63
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jq.nvm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jq.si b/solr-8.3.1/server/solr/dash/data/index/_jq.si
new file mode 100644
index 000000000..cdf2673ca
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jq.si differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jq_2.liv b/solr-8.3.1/server/solr/dash/data/index/_jq_2.liv
new file mode 100644
index 000000000..0ae2a2dc3
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jq_2.liv differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jq_Lucene50_0.doc b/solr-8.3.1/server/solr/dash/data/index/_jq_Lucene50_0.doc
new file mode 100644
index 000000000..91ae597e1
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jq_Lucene50_0.doc differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jq_Lucene50_0.pos b/solr-8.3.1/server/solr/dash/data/index/_jq_Lucene50_0.pos
new file mode 100644
index 000000000..047dd7ae9
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jq_Lucene50_0.pos differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jq_Lucene50_0.tim b/solr-8.3.1/server/solr/dash/data/index/_jq_Lucene50_0.tim
new file mode 100644
index 000000000..352a6b592
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jq_Lucene50_0.tim differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jq_Lucene50_0.tip b/solr-8.3.1/server/solr/dash/data/index/_jq_Lucene50_0.tip
new file mode 100644
index 000000000..ca0b2688d
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jq_Lucene50_0.tip differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jq_Lucene80_0.dvd b/solr-8.3.1/server/solr/dash/data/index/_jq_Lucene80_0.dvd
new file mode 100644
index 000000000..7a5fbcef3
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jq_Lucene80_0.dvd differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jq_Lucene80_0.dvm b/solr-8.3.1/server/solr/dash/data/index/_jq_Lucene80_0.dvm
new file mode 100644
index 000000000..228fc7b0a
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jq_Lucene80_0.dvm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jr.dii b/solr-8.3.1/server/solr/dash/data/index/_jr.dii
new file mode 100644
index 000000000..7bec10455
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jr.dii differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jr.dim b/solr-8.3.1/server/solr/dash/data/index/_jr.dim
new file mode 100644
index 000000000..cdf02c5fb
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jr.dim differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jr.fdt b/solr-8.3.1/server/solr/dash/data/index/_jr.fdt
new file mode 100644
index 000000000..183dda424
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jr.fdt differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jr.fdx b/solr-8.3.1/server/solr/dash/data/index/_jr.fdx
new file mode 100644
index 000000000..96942bc74
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jr.fdx differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jr.fnm b/solr-8.3.1/server/solr/dash/data/index/_jr.fnm
new file mode 100644
index 000000000..34731760f
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jr.fnm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jr.si b/solr-8.3.1/server/solr/dash/data/index/_jr.si
new file mode 100644
index 000000000..bc131eb25
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jr.si differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jr_1.liv b/solr-8.3.1/server/solr/dash/data/index/_jr_1.liv
new file mode 100644
index 000000000..22da1aa4f
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jr_1.liv differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jr_Lucene50_0.doc b/solr-8.3.1/server/solr/dash/data/index/_jr_Lucene50_0.doc
new file mode 100644
index 000000000..94df5353c
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jr_Lucene50_0.doc differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jr_Lucene50_0.tim b/solr-8.3.1/server/solr/dash/data/index/_jr_Lucene50_0.tim
new file mode 100644
index 000000000..2f078b0f1
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jr_Lucene50_0.tim differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jr_Lucene50_0.tip b/solr-8.3.1/server/solr/dash/data/index/_jr_Lucene50_0.tip
new file mode 100644
index 000000000..b6239f17d
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jr_Lucene50_0.tip differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jr_Lucene80_0.dvd b/solr-8.3.1/server/solr/dash/data/index/_jr_Lucene80_0.dvd
new file mode 100644
index 000000000..171be787e
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jr_Lucene80_0.dvd differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/_jr_Lucene80_0.dvm b/solr-8.3.1/server/solr/dash/data/index/_jr_Lucene80_0.dvm
new file mode 100644
index 000000000..f76033c91
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/_jr_Lucene80_0.dvm differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/segments_d b/solr-8.3.1/server/solr/dash/data/index/segments_d
new file mode 100644
index 000000000..1e98e2f77
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/index/segments_d differ
diff --git a/solr-8.3.1/server/solr/dash/data/index/write.lock b/solr-8.3.1/server/solr/dash/data/index/write.lock
new file mode 100644
index 000000000..e69de29bb
diff --git a/solr-8.3.1/server/solr/dash/data/tlog/tlog.0000000000000000011 b/solr-8.3.1/server/solr/dash/data/tlog/tlog.0000000000000000011
new file mode 100644
index 000000000..12ce36e19
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/tlog/tlog.0000000000000000011 differ
diff --git a/solr-8.3.1/server/solr/dash/data/tlog/tlog.0000000000000000012 b/solr-8.3.1/server/solr/dash/data/tlog/tlog.0000000000000000012
new file mode 100644
index 000000000..c2322ffbe
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data/tlog/tlog.0000000000000000012 differ
diff --git a/solr-8.3.1/server/solr/dash/data2/index/_199_1.liv b/solr-8.3.1/server/solr/dash/data2/index/_199_1.liv
new file mode 100644
index 000000000..d339cd0c7
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data2/index/_199_1.liv differ
diff --git a/solr-8.3.1/server/solr/dash/data2/index/_19h_1.liv b/solr-8.3.1/server/solr/dash/data2/index/_19h_1.liv
new file mode 100644
index 000000000..de5c10cc7
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data2/index/_19h_1.liv differ
diff --git a/solr-8.3.1/server/solr/dash/data2/index/write.lock b/solr-8.3.1/server/solr/dash/data2/index/write.lock
new file mode 100644
index 000000000..e69de29bb
diff --git a/solr-8.3.1/server/solr/dash/data2/tlog/tlog.0000000000000000012 b/solr-8.3.1/server/solr/dash/data2/tlog/tlog.0000000000000000012
new file mode 100644
index 000000000..039bba561
Binary files /dev/null and b/solr-8.3.1/server/solr/dash/data2/tlog/tlog.0000000000000000012 differ
diff --git a/solr-8.3.1/server/tmp/start_1669159736908453662.properties b/solr-8.3.1/server/tmp/start_1669159736908453662.properties
new file mode 100644
index 000000000..07a40cbdc
--- /dev/null
+++ b/solr-8.3.1/server/tmp/start_1669159736908453662.properties
@@ -0,0 +1,11 @@
+#start.jar properties
+#Wed Jan 08 14:37:59 UTC 2020
+java.version.platform=8
+java.version=1.8.0_211
+java.version.micro=0
+jetty.home=C\:\\gitstuff\\GitCode\\Dash-Web\\solr-8.3.1\\server
+java.version.minor=8
+jetty.home.uri=file\:///C\:/gitstuff/GitCode/Dash-Web/solr-8.3.1/server
+jetty.base=C\:\\gitstuff\\GitCode\\Dash-Web\\solr-8.3.1\\server
+java.version.major=1
+jetty.base.uri=file\:///C\:/gitstuff/GitCode/Dash-Web/solr-8.3.1/server
diff --git a/solr-8.3.1/server/tmp/start_5906520416860122978.properties b/solr-8.3.1/server/tmp/start_5906520416860122978.properties
new file mode 100644
index 000000000..4177968f4
--- /dev/null
+++ b/solr-8.3.1/server/tmp/start_5906520416860122978.properties
@@ -0,0 +1,11 @@
+#start.jar properties
+#Wed Jan 08 14:30:05 UTC 2020
+java.version.platform=8
+java.version=1.8.0_211
+java.version.micro=0
+jetty.home=c\:\\gitstuff\\GitCode\\Dash-Web\\solr-8.3.1\\server
+java.version.minor=8
+jetty.home.uri=file\:///c\:/gitstuff/GitCode/Dash-Web/solr-8.3.1/server
+jetty.base=c\:\\gitstuff\\GitCode\\Dash-Web\\solr-8.3.1\\server
+java.version.major=1
+jetty.base.uri=file\:///c\:/gitstuff/GitCode/Dash-Web/solr-8.3.1/server
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index db2a3c298..a1196ee1c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -386,7 +386,7 @@ export class MainView extends React.Component {
                     getScale={returnOne}>
                 </DocumentView>
             </div>
-            <div style={{ position: "relative", height: `calc(100% - ${this._buttonBarHeight}px)`, width: "100%", overflow: "auto" }}>
+            <div className="mainView-contentArea" style={{ position: "relative", height: `calc(100% - ${this._buttonBarHeight}px)`, width: "100%", overflow: "visible" }}>
                 <DocumentView
                     Document={sidebarContent}
                     DataDoc={undefined}
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index 422d01cee..24aa6ddfa 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -11,7 +11,7 @@ import { CollectionViewType } from "./CollectionView";
 import { DocumentButtonBar } from "../DocumentButtonBar";
 import { DocumentManager } from "../../util/DocumentManager";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faEdit } from "@fortawesome/free-solid-svg-icons";
+import { faEdit, faChevronCircleUp } from "@fortawesome/free-solid-svg-icons";
 import { library } from "@fortawesome/fontawesome-svg-core";
 import { MetadataEntryMenu } from "../MetadataEntryMenu";
 import { DocumentView } from "../nodes/DocumentView";
@@ -86,11 +86,11 @@ export class ParentDocSelector extends React.Component<SelectorProps> {
                 <SelectorContextMenu {...this.props} />
             </div>
         );
-        return <div title="Drag(create link) Tap(view links)" onPointerDown={e => e.stopPropagation()} className="parentDocumentSelector-linkFlyout">
-            <Flyout anchorPoint={anchorPoints.RIGHT_TOP}
+        return <div title="Tap to View Contexts/Metadata" onPointerDown={e => e.stopPropagation()} className="parentDocumentSelector-linkFlyout">
+            <Flyout anchorPoint={anchorPoints.LEFT_TOP}
                 content={flyout}>
                 <span className="parentDocumentSelector-button" >
-                    <p>^</p>
+                    <FontAwesomeIcon icon={faChevronCircleUp} size={"lg"} />
                 </span>
             </Flyout>
         </div>;
@@ -124,7 +124,7 @@ export class ButtonSelector extends React.Component<{ Document: Doc, Stack: any
             </div>
         );
         return <span title="Tap for menu" onPointerDown={e => e.stopPropagation()} className="buttonSelector">
-            <Flyout anchorPoint={anchorPoints.RIGHT_TOP} content={flyout} stylesheet={this.customStylesheet}>
+            <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout} stylesheet={this.customStylesheet}>
                 <FontAwesomeIcon icon={faEdit} size={"sm"} />
             </Flyout>
         </span>;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 63c17b1f6..29e7edd97 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -742,7 +742,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
         const highlightColors = ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"];
         const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"];
-        const highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc.viewType !== CollectionViewType.Linear;
+        let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc.viewType !== CollectionViewType.Linear;
+        highlighting = highlighting && this.props.focus !== emptyFunction;  // bcz: hack to turn off highlighting onsidebar panel documents.  need to flag a document as not highlightable in a more direct way
         return <div className={`documentView-node${this.topMost ? "-topmost" : ""}`} ref={this._mainCont} onKeyDown={this.onKeyDown}
             onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
             onPointerEnter={e => Doc.BrushDoc(this.props.Document)} onPointerLeave={e => Doc.UnBrushDoc(this.props.Document)}
diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss
index 4eb992d36..0825580b7 100644
--- a/src/client/views/search/SearchBox.scss
+++ b/src/client/views/search/SearchBox.scss
@@ -70,8 +70,7 @@
     display: flex;
     flex-direction: column;
     height: 100%;
-    overflow: hidden;
-    overflow-y: auto;
+    overflow: visible;
 
     .no-result {
         width: 500px;
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 2f28ebf76..dd1ac7421 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -353,7 +353,8 @@ export class SearchBox extends React.Component {
                 </div>
                 <div className="searchBox-results" onScroll={this.resultsScrolled} style={{
                     display: this._resultsOpen ? "flex" : "none",
-                    height: this.resFull ? "auto" : this.resultHeight, overflow: this.resFull ? "auto" : "visible"
+                    height: this.resFull ? "auto" : this.resultHeight,
+                    overflow: "visibile" // this.resFull ? "auto" : "visible"
                 }} ref={this.resultsRef}>
                     {this._visibleElements}
                 </div>
diff --git a/src/client/views/search/SearchItem.scss b/src/client/views/search/SearchItem.scss
index 82ff96700..469f062b2 100644
--- a/src/client/views/search/SearchItem.scss
+++ b/src/client/views/search/SearchItem.scss
@@ -1,22 +1,14 @@
 @import "../globalCssVariables";
 
-.search-overview {
+.searchItem-overview {
     display: flex;
     flex-direction: reverse;
     justify-content: flex-end;
     z-index: 0;
 }
-.link-count {
-     background: black;
-    border-radius: 20px;
-    color: white;
-    width: 15px;
-    text-align: center;
-    margin-top: 5px;
-}
 
 .searchBox-placeholder,
-.search-overview .search-item {
+.searchItem-overview .searchItem {
     width: 100%;
     background: $light-color-secondary;
     border-color: $intermediate-color;
@@ -26,19 +18,19 @@
     max-height: 150px;
     height: auto;
     z-index: 0;
-    display: inline-block;
-    overflow: auto;
+    display: flex;
+    overflow: visible;
 
-    .main-search-info {
+    .searchItem-body {
         display: flex;
         flex-direction: row;
         width: 100%;
 
-        .search-title-container {
+        .searchItem-title-container {
             width: 100%;
             overflow: hidden;
 
-            .search-title {
+            .searchItem-title {
                 text-transform: uppercase;
                 text-align: left;
                 width: 100%;
@@ -46,75 +38,28 @@
             }
         }
 
-        .search-info {
+        .searchItem-info {
             display: flex;
             justify-content: flex-end;
 
-            .link-container.item {
-                margin-left: auto;
-                margin-right: auto;
-                height: 26px;
-                width: 26px;
-                border-radius: 13px;
-                background: $dark-color;
-                color: $light-color-secondary;
-                display: flex;
-                justify-content: center;
-                align-items: center;
-                -webkit-transition: all 0.2s ease-in-out;
-                -moz-transition: all 0.2s ease-in-out;
-                -o-transition: all 0.2s ease-in-out;
-                transition: all 0.2s ease-in-out;
-                transform-origin: top right;
-                overflow: hidden;
-                position: relative;
-
-
-                .link-extended {
-                    // display: none;
-                    visibility: hidden;
-                    opacity: 0;
-                    position: relative;
-                    z-index: 500;
-                    overflow: hidden;
-                    -webkit-transition: opacity 0.2s ease-in-out .2s, visibility 0s linear 0s;
-                    -moz-transition: opacity 0.2s ease-in-out .2s, visibility 0s linear 0s;
-                    -o-transition: opacity 0.2s ease-in-out .2s, visibility 0s linear 0s;
-                    transition: opacity 0.2s ease-in-out .2s, visibility 0s linear 0s;
-                    // transition-delay: 1s;
-                }
-
-            }
-
-            .link-container.item:hover {
-                width: 70px;
-            }
-
-            .link-container.item:hover .link-count {
-                opacity: 0;
-            }
-
-            .link-container.item:hover .link-extended {
-                opacity: 1;
-                visibility: visible;
-                // display: inline;
-            }
-
             .icon-icons {
                 width: 50px
             }
 
             .icon-live {
                 width: 175px;
+                height: 0px;
             }
 
+            .icon-icons {
+                height:auto;
+            }
             .icon-icons,
             .icon-live {
-                height: auto;
                 margin: auto;
-                overflow: hidden;
+                overflow: visible;
 
-                .search-type {
+                .searchItem-type {
                     display: inline-block;
                     width: 100%;
                     position: absolute;
@@ -133,11 +78,11 @@
                     }
                 }
 
-                .search-type:hover+.search-label {
+                .searchItem-type:hover+.searchItem-label {
                     opacity: 1;
                 }
 
-                .search-label {
+                .searchItem-label {
                     font-size: 10;
                     position: relative;
                     right: 0px;
@@ -151,8 +96,6 @@
             }
 
             .icon-live:hover {
-                height: 175px;
-
                 .pdfBox-cont {
                     img {
                         width: 100% !important;
@@ -161,42 +104,44 @@
             }
         }
 
-        .search-info:hover {
+        .searchItem-info:hover {
             width: 60%;
         }
     }
 }
 
-.search-item:hover~.searchBox-instances,
+.searchItem:hover~.searchBox-instances,
 .searchBox-instances:hover,
 .searchBox-instances:active {
     opacity: 1;
     background: $lighter-alt-accent;
-    width:150px
 }
 
-.search-item:hover {
+.searchItem:hover {
     transition: all 0.2s;
     background: $lighter-alt-accent;
 }
 
-.search-highlighting {
+.searchItem-highlighting {
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: pre;
 }
 
 .searchBox-instances {
-    float: left;
     opacity: 1;
-    width: 0px;
+    width:40px;
+    height:40px;
+    background: gray;
     transition: all 0.2s ease;
     color: black;
     overflow: hidden;
+    right:-100;
+    display:inline-block;
 }
 
 
-.search-overview:hover {
+.searchItem-overview:hover {
     z-index: 1;
 }
 
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 673cb7937..32ba5d19d 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -17,11 +17,11 @@ import { SEARCH_THUMBNAIL_SIZE } from "../../views/globalCssVariables.scss";
 import { CollectionViewType } from "../collections/CollectionView";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
 import { ContextMenu } from "../ContextMenu";
-import { DocumentView } from "../nodes/DocumentView";
 import { SearchBox } from "./SearchBox";
 import "./SearchItem.scss";
 import "./SelectorContextMenu.scss";
 import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
+import { ButtonSelector, ParentDocSelector } from "../collections/ParentDocumentSelector";
 
 export interface SearchItemProps {
     doc: Doc;
@@ -188,24 +188,12 @@ export class SearchItem extends React.Component<SearchItemProps> {
                                     layoutresult.indexOf(DocumentType.HIST) !== -1 ? faChartBar :
                                         layoutresult.indexOf(DocumentType.WEB) !== -1 ? faGlobeAsia :
                                             faCaretUp;
-        return <div onPointerDown={action(() => { this._useIcons = false; this._displayDim = Number(SEARCH_THUMBNAIL_SIZE); })} >
+        return <div onClick={action(() => { this._useIcons = false; this._displayDim = Number(SEARCH_THUMBNAIL_SIZE); })} >
             <FontAwesomeIcon icon={button} size="2x" />
         </div>;
     }
 
     collectionRef = React.createRef<HTMLDivElement>();
-    startDocDrag = () => {
-        const doc = this.props.doc;
-        const isProto = Doc.GetT(doc, "isPrototype", "boolean", true);
-        if (isProto) {
-            return Doc.MakeDelegate(doc);
-        } else {
-            return Doc.MakeAlias(doc);
-        }
-    }
-
-    @computed
-    get linkCount() { return DocListCast(this.props.doc.links).length; }
 
     @action
     pointerDown = (e: React.PointerEvent) => { e.preventDefault(); e.button === 0 && SearchBox.Instance.openSearch(e); }
@@ -258,43 +246,62 @@ export class SearchItem extends React.Component<SearchItemProps> {
         ContextMenu.Instance.displayMenu(e.clientX, e.clientY);
     }
 
+    _downX = 0;
+    _downY = 0;
+    _target: any;
     onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
+        this._downX = e.clientX;
+        this._downY = e.clientY;
         e.stopPropagation();
-        const doc = Doc.IsPrototype(this.props.doc) ? Doc.MakeDelegate(this.props.doc) : this.props.doc;
-        DragManager.StartDocumentDrag([e.currentTarget], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY);
+        this._target = e.currentTarget;
+        document.removeEventListener("pointermove", this.onPointerMoved);
+        document.removeEventListener("pointerup", this.onPointerUp);
+        document.addEventListener("pointermove", this.onPointerMoved);
+        document.addEventListener("pointerup", this.onPointerUp);
+    }
+    onPointerMoved = (e: PointerEvent) => {
+        if (Math.abs(e.clientX - this._downX) > Utils.DRAG_THRESHOLD ||
+            Math.abs(e.clientY - this._downY) > Utils.DRAG_THRESHOLD) {
+            console.log("DRAGGIGNG");
+            document.removeEventListener("pointermove", this.onPointerMoved);
+            document.removeEventListener("pointerup", this.onPointerUp);
+            const doc = Doc.IsPrototype(this.props.doc) ? Doc.MakeDelegate(this.props.doc) : this.props.doc;
+            DragManager.StartDocumentDrag([this._target], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY);
+        }
+    }
+    onPointerUp = (e: PointerEvent) => {
+        document.removeEventListener("pointermove", this.onPointerMoved);
+        document.removeEventListener("pointerup", this.onPointerUp);
+    }
+
+    @computed
+    get contextButton() {
+        return <ParentDocSelector Views={DocumentManager.Instance.DocumentViews} Document={this.props.doc} addDocTab={(doc, data, where) => CollectionDockingView.AddRightSplit(doc, data)} />;
     }
 
     render() {
         const doc1 = Cast(this.props.doc.anchor1, Doc);
         const doc2 = Cast(this.props.doc.anchor2, Doc);
-        return (
-            <div className="search-overview" onPointerDown={this.pointerDown} onContextMenu={this.onContextMenu}>
-                <div className="search-item" onPointerDown={this.nextHighlight} onPointerEnter={this.highlightDoc} onPointerLeave={this.unHighlightDoc} id="result"
-                    onClick={this.onClick}>
-                    <div className="main-search-info">
-                        <div title="Drag as document" onPointerDown={this.onPointerDown} style={{ marginRight: "7px" }}> <FontAwesomeIcon icon="file" size="lg" />
-                            <div className="link-container item">
-                                <div className="link-count" title={`${this.linkCount + " links"}`}>{this.linkCount}</div>
-                            </div>
-                        </div>
-                        <div className="search-title-container">
-                            <div className="search-title">{StrCast(this.props.doc.title)}</div>
-                            <div className="search-highlighting">{this.props.highlighting.length ? "Matched fields:" + this.props.highlighting.join(", ") : this.props.lines.length ? this.props.lines[0] : ""}</div>
-                            {this.props.lines.filter((m, i) => i).map((l, i) => <div id={i.toString()} className="search-highlighting">`${l}`</div>)}
-                        </div>
-                        <div className="search-info" style={{ width: this._useIcons ? "15%" : "100%" }}>
-                            <div className={`icon-${this._useIcons ? "icons" : "live"}`}>
-                                <div className="search-type" title="Click to Preview">{this.DocumentIcon()}</div>
-                                <div className="search-label">{this.props.doc.type ? this.props.doc.type : "Other"}</div>
-                            </div>
-                        </div>
+        return <div className="searchItem-overview" onPointerDown={this.pointerDown} onContextMenu={this.onContextMenu}>
+            <div className="searchItem" onPointerDown={this.nextHighlight} onPointerEnter={this.highlightDoc} onPointerLeave={this.unHighlightDoc}>
+                <div className="searchItem-body" onClick={this.onClick}>
+                    <div className="searchItem-title-container">
+                        <div className="searchItem-title">{StrCast(this.props.doc.title)}</div>
+                        <div className="searchItem-highlighting">{this.props.highlighting.length ? "Matched fields:" + this.props.highlighting.join(", ") : this.props.lines.length ? this.props.lines[0] : ""}</div>
+                        {this.props.lines.filter((m, i) => i).map((l, i) => <div id={i.toString()} className="searchItem-highlighting">`${l}`</div>)}
                     </div>
                 </div>
-                <div className="searchBox-instances">
+                <div className="searchItem-info" style={{ width: this._useIcons ? "30px" : "100%" }}>
+                    <div className={`icon-${this._useIcons ? "icons" : "live"}`}>
+                        <div className="searchItem-type" title="Click to Preview" onPointerDown={this.onPointerDown}>{this.DocumentIcon()}</div>
+                        <div className="searchItem-label">{this.props.doc.type ? this.props.doc.type : "Other"}</div>
+                    </div>
+                </div>
+                <div className="searchItem-context" title="Drag as document">
                     {(doc1 instanceof Doc && doc2 instanceof Doc) && this.props.doc.type === DocumentType.LINK ? <LinkContextMenu doc1={doc1} doc2={doc2} /> :
-                        <SelectorContextMenu {...this.props} />}
+                        this.contextButton}
                 </div>
             </div>
-        );
+        </div>;
     }
 } 
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 9e18edc6c2b178d9d0960aa95d2b2a9f198ab6d1 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 13 Jan 2020 16:48:45 -0500
Subject: fixed onChildClick handlers to work for freeform views. added padding
 for fitTocontents views.  fixed performance with templates.  added
 openOnRight script func.  switched filter to ||,

---
 src/Utils.ts                                               |  5 +++--
 src/client/views/MainView.tsx                              |  2 +-
 src/client/views/collections/CollectionDockingView.tsx     | 11 ++++-------
 src/client/views/collections/CollectionSchemaHeaders.tsx   |  4 ++--
 .../collectionFreeForm/CollectionFreeFormLayoutEngines.tsx |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx          |  8 +++++---
 src/new_fields/Doc.ts                                      | 14 ++++++++------
 7 files changed, 24 insertions(+), 22 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 04fe6750b..562d9d83f 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -328,8 +328,8 @@ export function timenow() {
     return now.toLocaleDateString() + ' ' + h + ':' + m + ' ' + ampm;
 }
 
-export function aggregateBounds(boundsList: { x: number, y: number, width: number, height: number }[]) {
-    return boundsList.reduce((bounds, b) => {
+export function aggregateBounds(boundsList: { x: number, y: number, width: number, height: number }[], xpad: number, ypad: number) {
+    let bounds = boundsList.reduce((bounds, b) => {
         const [sptX, sptY] = [b.x, b.y];
         const [bptX, bptY] = [sptX + b.width, sptY + b.height];
         return {
@@ -337,6 +337,7 @@ export function aggregateBounds(boundsList: { x: number, y: number, width: numbe
             r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b)
         };
     }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE });
+    return { x: bounds.x !== Number.MAX_VALUE ? bounds.x - xpad : bounds.x, y: bounds.y !== Number.MAX_VALUE ? bounds.y - ypad : bounds.y, r: bounds.r !== -Number.MAX_VALUE ? bounds.r + 2 * xpad : bounds.r, b: bounds.b !== -Number.MAX_VALUE ? bounds.b + 2 * ypad : bounds.b }
 }
 export function intersectRect(r1: { left: number, top: number, width: number, height: number },
     r2: { left: number, top: number, width: number, height: number }) {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 305966160..91c7f909b 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -349,7 +349,7 @@ export class MainView extends React.Component {
     addDocTabFunc = (doc: Doc, data: Opt<Doc>, where: string, libraryPath?: Doc[]): boolean => {
         return where === "close" ? CollectionDockingView.CloseRightSplit(doc) :
             doc.dockingConfig ? this.openWorkspace(doc) :
-                CollectionDockingView.AddRightSplit(doc, undefined, undefined, libraryPath);
+                CollectionDockingView.AddRightSplit(doc, undefined, libraryPath);
     }
     mainContainerXf = () => new Transform(0, -this._buttonBarHeight, 1);
 
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 151b84c50..6c50ea0f2 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -34,6 +34,7 @@ import { DocumentType } from '../../documents/DocumentTypes';
 import { ComputedField } from '../../../new_fields/ScriptField';
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { TraceMobx } from '../../../new_fields/util';
+import { Scripting } from '../../util/Scripting';
 library.add(faFile);
 const _global = (window /* browser */ || global /* node */) as any;
 
@@ -177,7 +178,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     //
     @undoBatch
     @action
-    public static AddRightSplit(document: Doc, dataDoc: Doc | undefined, minimize: boolean = false, libraryPath?: Doc[]) {
+    public static AddRightSplit(document: Doc, dataDoc: Doc | undefined, libraryPath?: Doc[]) {
         if (!CollectionDockingView.Instance) return false;
         const instance = CollectionDockingView.Instance;
         const newItemStackConfig = {
@@ -202,11 +203,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             collayout.config.width = 50;
             newContentItem.config.width = 50;
         }
-        if (minimize) {
-            // bcz: this makes the drag image show up better, but it also messes with fixed layout sizes
-            // newContentItem.config.width = 10;
-            // newContentItem.config.height = 10;
-        }
         newContentItem.callDownwards('_$init');
         instance.layoutChanged();
         return true;
@@ -674,7 +670,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         if (doc.dockingConfig) {
             return MainView.Instance.openWorkspace(doc);
         } else if (location === "onRight") {
-            return CollectionDockingView.AddRightSplit(doc, dataDoc, undefined, libraryPath);
+            return CollectionDockingView.AddRightSplit(doc, dataDoc, libraryPath);
         } else if (location === "close") {
             return CollectionDockingView.CloseRightSplit(doc);
         } else {
@@ -724,3 +720,4 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             </div >);
     }
 }
+Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddRightSplit(doc, undefined); });
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index a965b2e9b..92dc8780e 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -1,5 +1,5 @@
 import React = require("react");
-import { action, observable, runInAction } from "mobx";
+import { action, observable } from "mobx";
 import { observer } from "mobx-react";
 import "./CollectionSchemaView.scss";
 import { faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faSortAmountDown, faSortAmountUp, faTimes } from '@fortawesome/free-solid-svg-icons';
@@ -295,7 +295,7 @@ class KeysDropdown extends React.Component<KeysDropdownProps> {
             if (!exactFound && this._searchTerm !== "" && this.props.canAddNew) {
                 this.onSelect(this._searchTerm);
             } else {
-                runInAction(() => this._searchTerm = this._key);
+                this.setSearchTerm(this._key);
             }
         }
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index a965a6cc9..8c8da63cc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -78,7 +78,7 @@ export function computePivotLayout(poolData: ObservableMap<string, any>, pivotDo
             x,
             y: pivotAxisWidth + 50,
             width: pivotAxisWidth * expander * numCols,
-            height: 100,
+            height: NumCast(pivotDoc.pivotFontSize, 10),
             fontSize: NumCast(pivotDoc.pivotFontSize, 10)
         });
         for (const doc of val) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index e3780261d..29f9ccfcd 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -9,7 +9,7 @@ import { Id } from "../../../../new_fields/FieldSymbols";
 import { InkTool, InkField, InkData } from "../../../../new_fields/InkField";
 import { createSchema, makeInterface } from "../../../../new_fields/Schema";
 import { ScriptField } from "../../../../new_fields/ScriptField";
-import { BoolCast, Cast, DateCast, NumCast, StrCast } from "../../../../new_fields/Types";
+import { BoolCast, Cast, DateCast, NumCast, StrCast, ScriptCast } from "../../../../new_fields/Types";
 import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
 import { aggregateBounds, emptyFunction, intersectRect, returnOne, Utils } from "../../../../Utils";
 import { DocServer } from "../../../DocServer";
@@ -82,7 +82,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     @computed get fitToContent() { return (this.props.fitToBox || this.Document.fitToBox) && !this.isAnnotationOverlay; }
     @computed get parentScaling() { return this.props.ContentScaling && this.fitToContent && !this.isAnnotationOverlay ? this.props.ContentScaling() : 1; }
-    @computed get contentBounds() { return aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!)); }
+    @computed get contentBounds() { return aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc.xPadding, 10), NumCast(this.layoutDoc.yPadding, 10)); }
     @computed get nativeWidth() { return this.Document.fitToContent ? 0 : this.Document.nativeWidth || 0; }
     @computed get nativeHeight() { return this.fitToContent ? 0 : this.Document.nativeHeight || 0; }
     private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; }
@@ -710,6 +710,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     getScale = () => this.Document.scale || 1;
 
     @computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; }
+    @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
 
     getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
         return {
@@ -719,7 +720,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             LibraryPath: this.libraryPath,
             layoutKey: undefined,
             ruleProvider: this.Document.isRuleProvider && childLayout.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider, //bcz: hack! - currently ruleProviders apply to documents in nested colleciton, not direct children of themselves
-            onClick: undefined, // this.props.onClick,  // bcz: check this out -- I don't think we want to inherit click handlers, or we at least need a way to ignore them
+            //onClick: undefined, // this.props.onClick,  // bcz: check this out -- I don't think we want to inherit click handlers, or we at least need a way to ignore them
+            onClick: this.onChildClickHandler,
             ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform,
             renderDepth: this.props.renderDepth + 1,
             PanelWidth: childLayout[WidthSym],
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 32ab36c0f..c809ad17a 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -572,13 +572,15 @@ export namespace Doc {
             return;
         }
 
-        const layoutCustomLayout = Doc.MakeDelegate(templateDoc);
+        if ((target[targetKey] as Doc)?.proto !== templateDoc) {
+            const layoutCustomLayout = Doc.MakeDelegate(templateDoc);
 
-        titleTarget && (Doc.GetProto(target).title = titleTarget);
-        Doc.GetProto(target).type = DocumentType.TEMPLATE;
-        target.onClick = templateDoc.onClick instanceof ObjectField && templateDoc.onClick[Copy]();
+            titleTarget && (Doc.GetProto(target).title = titleTarget);
+            Doc.GetProto(target).type = DocumentType.TEMPLATE;
+            target.onClick = templateDoc.onClick instanceof ObjectField && templateDoc.onClick[Copy]();
 
-        Doc.GetProto(target)[targetKey] = layoutCustomLayout;
+            Doc.GetProto(target)[targetKey] = layoutCustomLayout;
+        }
         target.layoutKey = targetKey;
         return target;
     }
@@ -750,7 +752,7 @@ export namespace Doc {
             const value = docFilters[i + 1];
             const modifiers = docFilters[i + 2];
             const scriptText = `${modifiers === "x" ? "!" : ""}matchFieldValue(doc, "${key}", "${value}")`;
-            docFilterText = docFilterText ? docFilterText + " && " + scriptText : scriptText;
+            docFilterText = docFilterText ? docFilterText + " || " + scriptText : scriptText;
         };
         return docFilterText ? "(" + docFilterText + ")" : "";
     }
-- 
cgit v1.2.3-70-g09d2


From 12f3a4a1bfce522c7e647317035ec0deda5c73d5 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 17 Jan 2020 10:28:52 -0500
Subject: moving things around.  changed multiColumnView to not use
 intermediate config docs.

---
 src/client/views/CollectionLinearView.scss         |  75 ---------------
 src/client/views/CollectionLinearView.tsx          | 106 ---------------------
 src/client/views/CollectionMulticolumnView.tsx     |  85 +++++------------
 src/client/views/MainView.tsx                      |   2 +-
 .../views/collections/CollectionLinearView.scss    |  75 +++++++++++++++
 .../views/collections/CollectionLinearView.tsx     | 106 +++++++++++++++++++++
 .../views/collections/CollectionPivotView.tsx      |   1 -
 src/client/views/collections/CollectionView.tsx    |  43 ++++-----
 8 files changed, 227 insertions(+), 266 deletions(-)
 delete mode 100644 src/client/views/CollectionLinearView.scss
 delete mode 100644 src/client/views/CollectionLinearView.tsx
 create mode 100644 src/client/views/collections/CollectionLinearView.scss
 create mode 100644 src/client/views/collections/CollectionLinearView.tsx

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

diff --git a/src/client/views/CollectionLinearView.scss b/src/client/views/CollectionLinearView.scss
deleted file mode 100644
index 81210d7ae..000000000
--- a/src/client/views/CollectionLinearView.scss
+++ /dev/null
@@ -1,75 +0,0 @@
-@import "globalCssVariables";
-@import "nodeModuleOverrides";
-
-.collectionLinearView-outer{
-    overflow: hidden;
-    height:100%;
-    .collectionLinearView {
-        display:flex;
-        height: 100%;
-        >label {
-            background: $dark-color;
-            color: $light-color;
-            display: inline-block;
-            border-radius: 18px;
-            font-size: 12.5px;
-            width: 18px;
-            height: 18px;
-            margin-top:auto;
-            margin-bottom:auto;
-            margin-right: 3px;
-            cursor: pointer;
-            transition: transform 0.2s;
-        }
-
-        label p {
-            padding-left:5px;
-        }
-
-        label:hover {
-            background: $main-accent;
-            transform: scale(1.15);
-        }
-
-        >input {
-            display: none;
-        }
-        >input:not(:checked)~.collectionLinearView-content {
-            display: none;
-        }
-
-        >input:checked~label {
-            transform: rotate(45deg);
-            transition: transform 0.5s;
-            cursor: pointer;
-        }
-
-        .collectionLinearView-content {
-            display: flex;
-            opacity: 1;
-            position: relative;
-            margin-top: auto;
-
-            .collectionLinearView-docBtn, .collectionLinearView-docBtn-scalable {
-                position:relative;
-                margin:auto;
-                margin-left: 3px;
-                transform-origin: center 80%;
-            }
-            .collectionLinearView-docBtn-scalable:hover {
-                transform: scale(1.15);
-            }
-
-            .collectionLinearView-round-button {
-                width: 18px;
-                height: 18px;
-                border-radius: 18px;
-                font-size: 15px;
-            }
-            
-            .collectionLinearView-round-button:hover {
-                transform: scale(1.15);
-            }
-        }
-    }
-}
diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx
deleted file mode 100644
index 5ca861f71..000000000
--- a/src/client/views/CollectionLinearView.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-import { action, IReactionDisposer, observable, reaction } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc, HeightSym, WidthSym } from '../../new_fields/Doc';
-import { makeInterface } from '../../new_fields/Schema';
-import { BoolCast, NumCast, StrCast } from '../../new_fields/Types';
-import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils } from '../../Utils';
-import { DragManager } from '../util/DragManager';
-import { Transform } from '../util/Transform';
-import "./CollectionLinearView.scss";
-import { CollectionViewType } from './collections/CollectionView';
-import { CollectionSubView } from './collections/CollectionSubView';
-import { DocumentView } from './nodes/DocumentView';
-import { documentSchema } from '../../new_fields/documentSchemas';
-import { Id } from '../../new_fields/FieldSymbols';
-
-
-type LinearDocument = makeInterface<[typeof documentSchema,]>;
-const LinearDocument = makeInterface(documentSchema);
-
-@observer
-export class CollectionLinearView extends CollectionSubView(LinearDocument) {
-    @observable public addMenuToggle = React.createRef<HTMLInputElement>();
-    private _dropDisposer?: DragManager.DragDropDisposer;
-    private _widthDisposer?: IReactionDisposer;
-
-    componentWillUnmount() {
-        this._dropDisposer && this._dropDisposer();
-        this._widthDisposer && this._widthDisposer();
-    }
-
-    componentDidMount() {
-        // is there any reason this needs to exist? -syip.  yes, it handles autoHeight for stacking views (masonry isn't yet supported).
-        this._widthDisposer = reaction(() => NumCast(this.props.Document.height, 0) + this.childDocs.length + (this.props.Document.isExpanded ? 1 : 0),
-            () => this.props.Document.width = 5 + (this.props.Document.isExpanded ? this.childDocs.length * (this.props.Document[HeightSym]()) : 10),
-            { fireImmediately: true }
-        );
-    }
-    protected createDropTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
-        this._dropDisposer && this._dropDisposer();
-        if (ele) {
-            this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
-        }
-    }
-
-    public isCurrent(doc: Doc) { return !doc.isMinimized && (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
-
-    dimension = () => NumCast(this.props.Document.height); // 2 * the padding
-    getTransform = (ele: React.RefObject<HTMLDivElement>) => () => {
-        if (!ele.current) return Transform.Identity();
-        const { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current);
-        return new Transform(-translateX, -translateY, 1 / scale);
-    }
-
-    render() {
-        const guid = Utils.GenerateGuid();
-        return <div className="collectionLinearView-outer">
-            <div className="collectionLinearView" ref={this.createDropTarget} >
-                <input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.isExpanded)} ref={this.addMenuToggle}
-                    onChange={action((e: any) => this.props.Document.isExpanded = this.addMenuToggle.current!.checked)} />
-                <label htmlFor={`${guid}`} style={{ marginTop: "auto", marginBottom: "auto", background: StrCast(this.props.Document.backgroundColor, "black") === StrCast(this.props.Document.color, "white") ? "black" : StrCast(this.props.Document.backgroundColor, "black") }} title="Close Menu"><p>+</p></label>
-
-                <div className="collectionLinearView-content" style={{ height: this.dimension(), width: NumCast(this.props.Document.width, 25) }}>
-                    {this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => {
-                        const nested = pair.layout.viewType === CollectionViewType.Linear;
-                        const dref = React.createRef<HTMLDivElement>();
-                        const nativeWidth = NumCast(pair.layout.nativeWidth, this.dimension());
-                        const deltaSize = nativeWidth * .15 / 2;
-                        return <div className={`collectionLinearView-docBtn` + (pair.layout.onClick || pair.layout.onDragStart ? "-scalable" : "")} key={pair.layout[Id]} ref={dref}
-                            style={{
-                                width: nested ? pair.layout[WidthSym]() : this.dimension() - deltaSize,
-                                height: nested && pair.layout.isExpanded ? pair.layout[HeightSym]() : this.dimension() - deltaSize,
-                            }}  >
-                            <DocumentView
-                                Document={pair.layout}
-                                DataDoc={pair.data}
-                                LibraryPath={this.props.LibraryPath}
-                                addDocument={this.props.addDocument}
-                                moveDocument={this.props.moveDocument}
-                                addDocTab={this.props.addDocTab}
-                                pinToPres={emptyFunction}
-                                removeDocument={this.props.removeDocument}
-                                ruleProvider={undefined}
-                                onClick={undefined}
-                                ScreenToLocalTransform={this.getTransform(dref)}
-                                ContentScaling={returnOne}
-                                PanelWidth={nested ? pair.layout[WidthSym] : () => this.dimension()}// ugh - need to get rid of this inline function to avoid recomputing
-                                PanelHeight={nested ? pair.layout[HeightSym] : () => this.dimension()}
-                                renderDepth={this.props.renderDepth + 1}
-                                focus={emptyFunction}
-                                backgroundColor={returnEmptyString}
-                                parentActive={returnTrue}
-                                whenActiveChanged={emptyFunction}
-                                bringToFront={emptyFunction}
-                                ContainingCollectionView={undefined}
-                                ContainingCollectionDoc={undefined}
-                                zoomToScale={emptyFunction}
-                                getScale={returnOne}>
-                            </DocumentView>
-                        </div>;
-                    })}
-                </div>
-            </div>
-        </div>;
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/CollectionMulticolumnView.tsx b/src/client/views/CollectionMulticolumnView.tsx
index 0b2ca82f1..99242e8be 100644
--- a/src/client/views/CollectionMulticolumnView.tsx
+++ b/src/client/views/CollectionMulticolumnView.tsx
@@ -4,9 +4,8 @@ import { documentSchema } from '../../new_fields/documentSchemas';
 import { CollectionSubView } from './collections/CollectionSubView';
 import { DragManager } from '../util/DragManager';
 import * as React from "react";
-import { Doc, DocListCast } from '../../new_fields/Doc';
+import { Doc } from '../../new_fields/Doc';
 import { NumCast, StrCast } from '../../new_fields/Types';
-import { List } from '../../new_fields/List';
 import { ContentFittingDocumentView } from './nodes/ContentFittingDocumentView';
 import { Utils } from '../../Utils';
 import "./collectionMulticolumnView.scss";
@@ -16,14 +15,12 @@ type MulticolumnDocument = makeInterface<[typeof documentSchema]>;
 const MulticolumnDocument = makeInterface(documentSchema);
 
 interface Unresolved {
-    config: Doc;
     target: Doc;
     magnitude: number;
     unit: string;
 }
 
 interface Resolved {
-    config: Doc;
     target: Doc;
     pixels: number;
 }
@@ -40,43 +37,24 @@ const resizerWidth = 2;
 
 @observer
 export default class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocument) {
-    private _dropDisposer?: DragManager.DragDropDisposer;
-
-    /**
-     * Returns the list of so-called configuration documents.
-     * Each one is a wrapper around what we typically think of as
-     * the child document, just also encoding the magnitude and unit
-     * of the specified width.
-     */
-    private get configuration() {
-        const { Document } = this.props;
-        if (!Document.multicolumnData) {
-            Document.multicolumnData = new List<Doc>();
-        }
-        return DocListCast(this.Document.multicolumnData);
-    }
-
     @computed
     private get resolvedLayoutInformation(): LayoutData {
         const unresolved: Unresolved[] = [];
         let starSum = 0, numFixed = 0, numRatio = 0;
-        for (const config of this.configuration) {
-            const { target, widthMagnitude, widthUnit } = config;
-            if (target instanceof Doc) {
-                const unit = StrCast(widthUnit);
-                const magnitude = NumCast(widthMagnitude);
-                if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) {
-                    if (unit === "*") {
-                        starSum += magnitude;
-                        numRatio++;
-                    } else {
-                        numFixed++;
-                    }
-                    unresolved.push({ config, target, magnitude, unit });
+        for (const target of this.childDocs) {
+            const unit = StrCast(target.widthUnit);
+            const magnitude = NumCast(target.widthMagnitude);
+            if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) {
+                if (unit === "*") {
+                    starSum += magnitude;
+                    numRatio++;
+                } else {
+                    numFixed++;
                 }
-                // otherwise, the particular configuration entry is ignored and the remaining
-                // space is allocated as if the document were absent from the configuration list
+                unresolved.push({ target, magnitude, unit });
             }
+            // otherwise, the particular configuration entry is ignored and the remaining
+            // space is allocated as if the document were absent from the configuration list
         }
         return { unresolved, numRatio, numFixed, starSum };
     }
@@ -91,17 +69,8 @@ export default class CollectionMulticolumnView extends CollectionSubView(Multico
      */
     @computed
     private get totalFixedAllocation(): number | undefined {
-        const layout = this.resolvedLayoutInformation;
-        if (!layout) {
-            return undefined;
-        }
-        let sum = 0;
-        for (const { magnitude, unit } of layout.unresolved) {
-            if (unit === "px") {
-                sum += magnitude;
-            }
-        }
-        return sum;
+        return this.resolvedLayoutInformation?.unresolved.reduce(
+            (sum, { magnitude, unit }) => sum + (unit === "px" ? magnitude : 0), 0);
     }
 
     /**
@@ -115,12 +84,9 @@ export default class CollectionMulticolumnView extends CollectionSubView(Multico
      */
     @computed
     private get totalRatioAllocation(): number | undefined {
-        const { totalFixedAllocation } = this;
-        const layout = this.resolvedLayoutInformation;
-        if (!layout) {
-            return undefined;
-        }
-        return totalFixedAllocation !== undefined ? this.props.PanelWidth() - (totalFixedAllocation + resizerWidth * (layout.unresolved.length - 1)) : undefined;
+        const layoutInfoLen = this.resolvedLayoutInformation?.unresolved.length;
+        if (layoutInfoLen > 0 && this.totalFixedAllocation !== undefined)
+            return this.props.PanelWidth() - (this.totalFixedAllocation + resizerWidth * (layoutInfoLen - 1));
     }
 
     /**
@@ -137,12 +103,9 @@ export default class CollectionMulticolumnView extends CollectionSubView(Multico
      */
     @computed
     private get columnUnitLength(): number | undefined {
-        const layout = this.resolvedLayoutInformation;
-        const { totalRatioAllocation } = this;
-        if (layout === null || totalRatioAllocation === undefined) {
-            return undefined;
+        if (this.resolvedLayoutInformation && this.totalRatioAllocation !== undefined) {
+            return this.totalRatioAllocation / this.resolvedLayoutInformation.starSum;
         }
-        return totalRatioAllocation / layout.starSum;
     }
 
     @computed
@@ -163,7 +126,7 @@ export default class CollectionMulticolumnView extends CollectionSubView(Multico
         });
         const collector: JSX.Element[] = [];
         for (let i = 0; i < resolved.length; i++) {
-            const { target, pixels, config } = resolved[i];
+            const { target, pixels } = resolved[i];
             collector.push(
                 <div className={"fish"}>
                     <ContentFittingDocumentView
@@ -174,14 +137,14 @@ export default class CollectionMulticolumnView extends CollectionSubView(Multico
                         PanelWidth={() => pixels}
                         getTransform={this.props.ScreenToLocalTransform}
                     />
-                    <span className={"display"}>{NumCast(config.widthMagnitude).toFixed(3)} {StrCast(config.widthUnit)}</span>
+                    <span className={"display"}>{NumCast(target.widthMagnitude).toFixed(3)} {StrCast(target.widthUnit)}</span>
                 </div>,
                 <ResizeBar
                     width={resizerWidth}
                     key={Utils.GenerateGuid()}
                     columnUnitLength={columnUnitLength}
-                    toLeft={config}
-                    toRight={resolved[i + 1]?.config}
+                    toLeft={target}
+                    toRight={resolved[i + 1]?.target}
                 />
             );
         }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 91c7f909b..3648ddccf 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -22,7 +22,7 @@ import { Docs, DocumentOptions } from '../documents/Documents';
 import { HistoryUtil } from '../util/History';
 import SharingManager from '../util/SharingManager';
 import { Transform } from '../util/Transform';
-import { CollectionLinearView } from './CollectionLinearView';
+import { CollectionLinearView } from './collections/CollectionLinearView';
 import { CollectionViewType, CollectionView } from './collections/CollectionView';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { ContextMenu } from './ContextMenu';
diff --git a/src/client/views/collections/CollectionLinearView.scss b/src/client/views/collections/CollectionLinearView.scss
new file mode 100644
index 000000000..eae9e0220
--- /dev/null
+++ b/src/client/views/collections/CollectionLinearView.scss
@@ -0,0 +1,75 @@
+@import "../globalCssVariables";
+@import "../_nodeModuleOverrides";
+
+.collectionLinearView-outer{
+    overflow: hidden;
+    height:100%;
+    .collectionLinearView {
+        display:flex;
+        height: 100%;
+        >label {
+            background: $dark-color;
+            color: $light-color;
+            display: inline-block;
+            border-radius: 18px;
+            font-size: 12.5px;
+            width: 18px;
+            height: 18px;
+            margin-top:auto;
+            margin-bottom:auto;
+            margin-right: 3px;
+            cursor: pointer;
+            transition: transform 0.2s;
+        }
+
+        label p {
+            padding-left:5px;
+        }
+
+        label:hover {
+            background: $main-accent;
+            transform: scale(1.15);
+        }
+
+        >input {
+            display: none;
+        }
+        >input:not(:checked)~.collectionLinearView-content {
+            display: none;
+        }
+
+        >input:checked~label {
+            transform: rotate(45deg);
+            transition: transform 0.5s;
+            cursor: pointer;
+        }
+
+        .collectionLinearView-content {
+            display: flex;
+            opacity: 1;
+            position: relative;
+            margin-top: auto;
+
+            .collectionLinearView-docBtn, .collectionLinearView-docBtn-scalable {
+                position:relative;
+                margin:auto;
+                margin-left: 3px;
+                transform-origin: center 80%;
+            }
+            .collectionLinearView-docBtn-scalable:hover {
+                transform: scale(1.15);
+            }
+
+            .collectionLinearView-round-button {
+                width: 18px;
+                height: 18px;
+                border-radius: 18px;
+                font-size: 15px;
+            }
+            
+            .collectionLinearView-round-button:hover {
+                transform: scale(1.15);
+            }
+        }
+    }
+}
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
new file mode 100644
index 000000000..9191bf822
--- /dev/null
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -0,0 +1,106 @@
+import { action, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc, HeightSym, WidthSym } from '../../../new_fields/Doc';
+import { makeInterface } from '../../../new_fields/Schema';
+import { BoolCast, NumCast, StrCast } from '../../../new_fields/Types';
+import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils } from '../../../Utils';
+import { DragManager } from '../../util/DragManager';
+import { Transform } from '../../util/Transform';
+import "./CollectionLinearView.scss";
+import { CollectionViewType } from './CollectionView';
+import { CollectionSubView } from './CollectionSubView';
+import { DocumentView } from '../nodes/DocumentView';
+import { documentSchema } from '../../../new_fields/documentSchemas';
+import { Id } from '../../../new_fields/FieldSymbols';
+
+
+type LinearDocument = makeInterface<[typeof documentSchema,]>;
+const LinearDocument = makeInterface(documentSchema);
+
+@observer
+export class CollectionLinearView extends CollectionSubView(LinearDocument) {
+    @observable public addMenuToggle = React.createRef<HTMLInputElement>();
+    private _dropDisposer?: DragManager.DragDropDisposer;
+    private _widthDisposer?: IReactionDisposer;
+
+    componentWillUnmount() {
+        this._dropDisposer && this._dropDisposer();
+        this._widthDisposer && this._widthDisposer();
+    }
+
+    componentDidMount() {
+        // is there any reason this needs to exist? -syip.  yes, it handles autoHeight for stacking views (masonry isn't yet supported).
+        this._widthDisposer = reaction(() => NumCast(this.props.Document.height, 0) + this.childDocs.length + (this.props.Document.isExpanded ? 1 : 0),
+            () => this.props.Document.width = 5 + (this.props.Document.isExpanded ? this.childDocs.length * (this.props.Document[HeightSym]()) : 10),
+            { fireImmediately: true }
+        );
+    }
+    protected createDropTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+        this._dropDisposer && this._dropDisposer();
+        if (ele) {
+            this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
+        }
+    }
+
+    public isCurrent(doc: Doc) { return !doc.isMinimized && (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
+
+    dimension = () => NumCast(this.props.Document.height); // 2 * the padding
+    getTransform = (ele: React.RefObject<HTMLDivElement>) => () => {
+        if (!ele.current) return Transform.Identity();
+        const { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current);
+        return new Transform(-translateX, -translateY, 1 / scale);
+    }
+
+    render() {
+        const guid = Utils.GenerateGuid();
+        return <div className="collectionLinearView-outer">
+            <div className="collectionLinearView" ref={this.createDropTarget} >
+                <input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.isExpanded)} ref={this.addMenuToggle}
+                    onChange={action((e: any) => this.props.Document.isExpanded = this.addMenuToggle.current!.checked)} />
+                <label htmlFor={`${guid}`} style={{ marginTop: "auto", marginBottom: "auto", background: StrCast(this.props.Document.backgroundColor, "black") === StrCast(this.props.Document.color, "white") ? "black" : StrCast(this.props.Document.backgroundColor, "black") }} title="Close Menu"><p>+</p></label>
+
+                <div className="collectionLinearView-content" style={{ height: this.dimension(), width: NumCast(this.props.Document.width, 25) }}>
+                    {this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => {
+                        const nested = pair.layout.viewType === CollectionViewType.Linear;
+                        const dref = React.createRef<HTMLDivElement>();
+                        const nativeWidth = NumCast(pair.layout.nativeWidth, this.dimension());
+                        const deltaSize = nativeWidth * .15 / 2;
+                        return <div className={`collectionLinearView-docBtn` + (pair.layout.onClick || pair.layout.onDragStart ? "-scalable" : "")} key={pair.layout[Id]} ref={dref}
+                            style={{
+                                width: nested ? pair.layout[WidthSym]() : this.dimension() - deltaSize,
+                                height: nested && pair.layout.isExpanded ? pair.layout[HeightSym]() : this.dimension() - deltaSize,
+                            }}  >
+                            <DocumentView
+                                Document={pair.layout}
+                                DataDoc={pair.data}
+                                LibraryPath={this.props.LibraryPath}
+                                addDocument={this.props.addDocument}
+                                moveDocument={this.props.moveDocument}
+                                addDocTab={this.props.addDocTab}
+                                pinToPres={emptyFunction}
+                                removeDocument={this.props.removeDocument}
+                                ruleProvider={undefined}
+                                onClick={undefined}
+                                ScreenToLocalTransform={this.getTransform(dref)}
+                                ContentScaling={returnOne}
+                                PanelWidth={nested ? pair.layout[WidthSym] : () => this.dimension()}// ugh - need to get rid of this inline function to avoid recomputing
+                                PanelHeight={nested ? pair.layout[HeightSym] : () => this.dimension()}
+                                renderDepth={this.props.renderDepth + 1}
+                                focus={emptyFunction}
+                                backgroundColor={returnEmptyString}
+                                parentActive={returnTrue}
+                                whenActiveChanged={emptyFunction}
+                                bringToFront={emptyFunction}
+                                ContainingCollectionView={undefined}
+                                ContainingCollectionDoc={undefined}
+                                zoomToScale={emptyFunction}
+                                getScale={returnOne}>
+                            </DocumentView>
+                        </div>;
+                    })}
+                </div>
+            </div>
+        </div>;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx
index d6261c7ee..6af7cce70 100644
--- a/src/client/views/collections/CollectionPivotView.tsx
+++ b/src/client/views/collections/CollectionPivotView.tsx
@@ -75,7 +75,6 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) {
                     Object.keys(Doc.GetProto(child)).forEach(key => child[key] instanceof Doc && facetValues.add((child[key] as Doc)[facet]?.toString() || "(null)"));
                     facetValues.add(child[facet]?.toString() || "(null)");
                 });
-                this.childDocs
 
                 const newFacetVals = facetValues.toArray().map(val => Docs.Create.TextDocument({ title: val.toString() }));
                 const newFacet = Docs.Create.FreeformDocument(newFacetVals, { title: facet, treeViewOpen: true, isFacetFilter: true });
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 022fc8f48..ce3aab579 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -4,39 +4,38 @@ import { faColumns, faCopy, faEllipsisV, faFingerprint, faImage, faProjectDiagra
 import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from "mobx-react";
 import * as React from 'react';
+import Lightbox from 'react-image-lightbox-with-rotate';
+import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
+import { DateField } from '../../../new_fields/DateField';
+import { Doc, DocListCast } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
-import { StrCast, BoolCast, Cast } from '../../../new_fields/Types';
+import { listSpec } from '../../../new_fields/Schema';
+import { BoolCast, Cast, StrCast } from '../../../new_fields/Types';
+import { ImageField } from '../../../new_fields/URLField';
+import { TraceMobx } from '../../../new_fields/util';
 import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
+import { Utils } from '../../../Utils';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { DocumentManager } from '../../util/DocumentManager';
+import { ImageUtils } from '../../util/Import & Export/ImageUtils';
+import { SelectionManager } from '../../util/SelectionManager';
+import CollectionMulticolumnView from '../CollectionMulticolumnView';
 import { ContextMenu } from "../ContextMenu";
+import { FieldView, FieldViewProps } from '../nodes/FieldView';
+import { ScriptBox } from '../ScriptBox';
+import { Touchable } from '../Touchable';
 import { CollectionDockingView } from "./CollectionDockingView";
 import { AddCustomFreeFormLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines';
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
+import { CollectionLinearView } from './CollectionLinearView';
+import { CollectionPivotView } from './CollectionPivotView';
 import { CollectionSchemaView } from "./CollectionSchemaView";
 import { CollectionStackingView } from './CollectionStackingView';
+import { CollectionStaffView } from './CollectionStaffView';
 import { CollectionTreeView } from "./CollectionTreeView";
+import './CollectionView.scss';
 import { CollectionViewBaseChrome } from './CollectionViewChromes';
-import { ImageUtils } from '../../util/Import & Export/ImageUtils';
-import { CollectionLinearView } from '../CollectionLinearView';
-import { CollectionStaffView } from './CollectionStaffView';
-import { DocumentType } from '../../documents/DocumentTypes';
-import { ImageField } from '../../../new_fields/URLField';
-import { DocListCast } from '../../../new_fields/Doc';
-import Lightbox from 'react-image-lightbox-with-rotate';
-import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
 export const COLLECTION_BORDER_WIDTH = 2;
-import { DateField } from '../../../new_fields/DateField';
-import { Doc, } from '../../../new_fields/Doc';
-import { listSpec } from '../../../new_fields/Schema';
-import { DocumentManager } from '../../util/DocumentManager';
-import { SelectionManager } from '../../util/SelectionManager';
-import './CollectionView.scss';
-import { FieldViewProps, FieldView } from '../nodes/FieldView';
-import { Touchable } from '../Touchable';
-import { TraceMobx } from '../../../new_fields/util';
-import { Utils } from '../../../Utils';
-import { ScriptBox } from '../ScriptBox';
-import CollectionMulticolumnView from '../CollectionMulticolumnView';
-import { CollectionPivotView } from './CollectionPivotView';
 const path = require('path');
 library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy);
 
-- 
cgit v1.2.3-70-g09d2

